by Anish
Posted on Wednesday April 3 , 2019
This sample chapter extracted from the book, Go Lang Cryptography for Developers . The Book theme isCryptography is for EveryOne. Learn from Crypto Principle to Applied Cryptography With Practical Example Grab a Copy
Advanced Encryption Standard (AES) a symmetric block cipher that can process data blocks of 128 bits, using cipher keys with lengths of 128, 192, and 256 bits. AES ( Formerly Rijndael) was designed to handle additional block sizes and key lengths, however they are not adopted in this standard.
Key-Block-Round Combinations.
The only Key-Block-Round combinations that conform to this standard are given in the below table.
Algo | key length | Block Size | Number of Rounds |
---|---|---|---|
AES-128 | 4 | 4 | 10 |
AES-192 | 6 | 4 | 12 |
AES-256 | 8 | 4 | 14 |
Go lang standard library aes implements AES encryption (formerly Rijndael), as defined in U.S. Federal Information Processing Standards Publication 197
func NewCipher
creates and returns a new cipher.Block. The key argument should be the AES key, either 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256
func NewCipher(key []byte) (cipher.Block, error)
The following example will show how to perform AES encryption and decryption of the given plain text message
package main
import (
"crypto/aes"
"encoding/hex"
"fmt"
)
func main() {
key := "myverystrongpasswordo32bitlength"
plainText := "Hello 8gwifi.org"
ct := Encrypt([]byte(key),plainText)
fmt.Printf("Original Text: %s\n",plainText)
fmt.Printf("AES Encrypted Text: %s\n", ct)
Decrypt([]byte(key),ct)
}
func Encrypt(key []byte, plaintext string) string {
c, err := aes.NewCipher(key)
if err != nil {
fmt.Errorf("NewCipher(%d bytes) = %s", len(key), err)
panic(err)
}
out := make([]byte, len(plaintext))
c.Encrypt(out, []byte(plaintext))
return hex.EncodeToString(out)
}
func Decrypt(key []byte, ct string) {
ciphertext, _ := hex.DecodeString(ct)
c, err := aes.NewCipher(key)
if err != nil {
fmt.Errorf("NewCipher(%d bytes) = %s", len(key), err)
panic(err)
}
plain := make([]byte, len(ciphertext))
c.Decrypt(plain, ciphertext)
s := string(plain[:])
fmt.Printf("AES Decrypyed Text: %s\n", s)
}
The Output
$go run aesencdec.go
Original Text: Hello 8gwifi.org
AES Encrypted Text: 3ed419795150ede4820f19d104d1654a
AES Decrypyed Text: Hello 8gwifi.org
This example will show how to encrypt and decrypt a given file using AES key.
package main
import (
"crypto/aes"
"crypto/cipher"
"fmt"
"io"
"os"
)
func main() {
// Store/Load this Key secretrly
// This Shounot be hardcoded in the code.
key := "myverystrongpasswordo32bitlength"
fmt.Println("====File Encryption/ Decryption====")
infileName := "/tmp/hello.txt"
encfileName := "/tmp/hello.txt.enc"
decfileName := "/tmp/hello.txt.dec"
FileEncryption(key,infileName,encfileName)
FileDecryption(key,encfileName,decfileName)
}
func FileEncryption(key string, infileName string, encfileName string) {
inFile, err := os.Open(infileName)
if err != nil {
panic(err)
}
defer inFile.Close()
block, err := aes.NewCipher([]byte(key))
if err != nil {
panic(err)
}
// If the key is unique for each ciphertext, then it's ok to use a zero
// IV. var iv [aes.BlockSize]byte
stream := cipher.NewOFB(block, iv[:])
outFile, err := os.OpenFile(encfileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
panic(err)
}
defer outFile.Close()
writer := &cipher.StreamWriter{S: stream, W: outFile}
// Copy the input file to the output file, encrypting as we go.
if _, err := io.Copy(writer, inFile); err != nil {
panic(err)
}
}
func FileDecryption(key string, encfileName string, decfileName string) {
inFile, err := os.Open(encfileName)
if err != nil {
panic(err)
}
defer inFile.Close()
block, err := aes.NewCipher([]byte(key))
if err != nil {
panic(err)
}
// If the key is unique for each ciphertext, then it's ok to use a zero
// IV. var iv [aes.BlockSize]byte
stream := cipher.NewOFB(block, iv[:])
outFile, err := os.OpenFile(decfileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
panic(err)
}
defer outFile.Close()
reader := &cipher.StreamReader{S: stream, R: inFile}
// Copy the input file to the output file, decrypting as we go.
if _, err := io.Copy(outFile, reader); err != nil {
panic(err)
}
}
$go run aesfileencdec.go
====File Encryption/ Decryption====
$ cat /tmp/hello.txt
Hello 8gwifi.org
$ cat /tmp/hello.txt.enc
!/=)0??7?PIS????0A
$ cat /tmp/hello.txt.dec
Hello 8gwifi.org
GCM is a block cipher counter mode with authentication. A Counter mode effectively turns a block cipher into a stream cipher, and therefore many of the rules for stream ciphers still apply.
The following example will show how to perform encryption and decryption in GCM Mode with and without AAD data.
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
)
func main() {
// Must Kept Secret No Hardcoding , This is for Demo purpose.
key := "myverystrongpasswordo32bitlength"
plainText := "Hello 8gwifi.org"
fmt.Printf("Original Text: %s\n",plainText)
fmt.Println()
fmt.Println("====GCM Encryption/ Decryption Without AAD====")
// Never use more than 2^32 random nonces with a given key because of the risk of a repeat.
iv := make([]byte, 12)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err.Error())
}
ciphertext := GCM_encrypt(key,plainText,iv,nil)
fmt.Printf("GCM Encrypted Text: %s\n", ciphertext)
ret := GCM_decrypt(key,ciphertext,iv,nil)
fmt.Printf("GCM Decrypted Text: %s\n", ret)
fmt.Println()
fmt.Println("====GCM Encryption/ Decryption Using AAD====")
// Never Use Same IV or Nonce
iv = make([]byte, 12)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err.Error())
}
additionalData := "Not Secret AAD Value"
ciphertext = GCM_encrypt(key,plainText,iv, []byte(additionalData))
fmt.Printf("GCM Encrypted Text: %s\n", ciphertext)
ret = GCM_decrypt(key,ciphertext,iv, []byte(additionalData))
fmt.Printf("GCM Decrypted Text: %s\n", ret)
}
func GCM_encrypt(key string, plaintext string, iv []byte, additionalData []byte) string {
block, err := aes.NewCipher([]byte(key))
if err != nil {
panic(err.Error())
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
panic(err.Error())
}
ciphertext := aesgcm.Seal(nil, iv, []byte(plaintext), additionalData)
return hex.EncodeToString(ciphertext)
}
func GCM_decrypt(key string,ct string,iv []byte, additionalData []byte) string {
ciphertext, _ := hex.DecodeString(ct)
block, err := aes.NewCipher([]byte(key))
if err != nil {
panic(err.Error())
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
panic(err.Error())
}
plaintext, err := aesgcm.Open(nil, iv, ciphertext, additionalData)
if err != nil {
panic(err.Error())
}
s := string(plaintext[:])
return s
}
The Output
$ go run aesgcm-encdec.go
Original Text: Hello 8gwifi.org
====GCM Encryption/ Decryption Without AAD====
GCM Encrypted Text: 00cefd9fb96388fcec4ba862164e9ff97428cfa01cdb962c041f825e06c40659
GCM Decrypted Text: Hello 8gwifi.org
====GCM Encryption/ Decryption Using AAD====
GCM Encrypted Text: bb0b4485d9fb0874d1e051eab6a93931d2817d855a23a4446dd0e94211a92afb
GCM Decrypted Text: Hello 8gwifi.org
In CBC mode, each block of plaintext is XORed with the previous ciphertext block before being encrypted.
The following example will show how to perform encryption and decryption in CBC Mode
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
)
func main() {
// Must Kept Secret No Hardcoding , This is for Demo purpose.
key := "myverystrongpasswordo32bitlength"
// IN CBC Must be Block Size of AES (Multiple of 16)
// Other WIse Paddign needs to be perfomed
plainText := "Hello 8gwifi.org"
if len(plainText)%aes.BlockSize != 0 {
panic("plaintext is not a multiple of the block size")
}
fmt.Printf("Original Text: %s\n",plainText)
fmt.Println("====CBC Encryption/ Decryption====")
// IV Length Must be equal to Block Size.
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err.Error())
}
ciphertext := CBCEncrypter(key,plainText,iv,nil)
fmt.Printf("CBC Encrypted Text: %s\n", ciphertext)
ret := CBCDecrypter(key,ciphertext,iv,nil)
fmt.Printf("CBC Decrypted Text: %s\n", ret)
}
func CBCEncrypter(key string, plaintext string, iv []byte, additionalData []byte) string {
block, err := aes.NewCipher([]byte(key))
if err != nil {
panic(err)
}
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], []byte(plaintext))
return hex.EncodeToString(ciphertext)
}
func CBCDecrypter(key string,ct string,iv []byte, additionalData []byte) string {
ciphertext, _ := hex.DecodeString(ct)
block, err := aes.NewCipher([]byte(key))
if err != nil {
panic(err)
}
ciphertext = ciphertext[aes.BlockSize:]
// CBC mode always works in whole blocks.
if len(ciphertext)%aes.BlockSize != 0 {
panic("ciphertext is not a multiple of the block size")
}
mode := cipher.NewCBCDecrypter(block, iv)
// CryptBlocks can work in-place if the two arguments are the same.
mode.CryptBlocks(ciphertext, ciphertext)
s := string(ciphertext[:])
return s
}
The Output
$ go run aescbc-encdec.go
Original Text: Hello 8gwifi.org
====CBC Encryption/ Decryption====
CBC Encrypted Text: 000000000000000000000000000000000c22a1fa436a571e6b16dd258b56dcd4
CBC Decrypted Text: Hello 8gwifi.org
The Cipher Feedback (CFB) mode, a close relative of CBC, makes a block cipher into a self-synchronizing stream cipher. Operation is very similar; in particular, CFB decryption is almost identical to CBC encryption performed in reverse:
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
)
func main() {
// 32 bit length key, for demo pupose hardcoded
// Secure this key in production No hardcoding key := "myverystrongpasswordo32bitlength"
plainText := "Hello 8gwifi.org "
fmt.Printf("Original Text: %s\n",plainText)
fmt.Println("====CFB Encryption/ Decryption====")
// IV Length Must be equal to Block Size.
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err.Error())
}
ciphertext := CFBEncrypter(key,plainText,iv,nil)
fmt.Printf("CFB Encrypted Text: %s\n", ciphertext)
ret := CFBDecrypter(key,ciphertext,iv,nil)
fmt.Printf("CFB Decrypted Text: %s\n", ret)
}
func CFBEncrypter(key string, plaintext string, iv []byte, additionalData []byte) string {
block, err := aes.NewCipher([]byte(key))
if err != nil {
panic(err)
}
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], []byte(plaintext))
return hex.EncodeToString(ciphertext)
}
func CFBDecrypter(key string,ct string,iv []byte, additionalData []byte) string {
ciphertext, _ := hex.DecodeString(ct)
block, err := aes.NewCipher([]byte(key))
if err != nil {
panic(err)
}
ciphertext = ciphertext[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
// XORKeyStream can work in-place if the two arguments are the same.
stream.XORKeyStream(ciphertext, ciphertext)
s := string(ciphertext[:])
return s
}
The Output
$ go run aescfb-encdec.go
Original Text: Hello 8gwifi.org
====CFB Encryption/ Decryption====
CFB Encrypted Text: 00000000000000000000000000000000966561e216b0757f9e0d6093511fba9b6b
CFB Decrypted Text: Hello 8gwifi.org
CTR mode has similar characteristics to OFB, but also allows a random access property during decryption. CTR mode is well suited to operate on a multi-processor machine where blocks can be encrypted in parallel. Furthermore, it does not suffer from the short-cycle problem that can affect OFB.
The following example will show how to perform encryption and decryption in CTR Mode
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
)
func main() {
// 32 bit length key, Most be secured in production ENV
// No Hardcoding of the key
key := "myverystrongpasswordo32bitlength"
// IN CTR Mode the plaintext shoun't be Must be Block Size of AES (Multiple of 16)
// Other WIse Paddign needs to be perfomed
plainText := "Hello 8gwifi.org"
if len(plainText)%aes.BlockSize != 0 {
panic("plaintext is not a multiple of the block size")
}
fmt.Printf("Original Text: %s\n",plainText)
fmt.Println("====CTR Encryption/ Decryption====")
// IV Length Must be equal to Block Size.
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err.Error())
}
ciphertext := CTREncrypter(key,plainText,iv)
fmt.Printf("CTR Encrypted Text: %s\n", ciphertext)
ret := CTRDecrypter(key,ciphertext,iv)
fmt.Printf("CTR Decrypted Text: %s\n", ret)
}
func CTREncrypter(key string, plaintext string, iv []byte) string {
block, err := aes.NewCipher([]byte(key))
if err != nil {
panic(err)
}
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], []byte(plaintext))
return hex.EncodeToString(ciphertext)
}
func CTRDecrypter(key string,ct string,iv []byte) string {
ciphertext, _ := hex.DecodeString(ct)
block, err := aes.NewCipher([]byte(key))
if err != nil {
panic(err)
}
ciphertext = ciphertext[aes.BlockSize:]
// CBC mode always works in whole blocks.
if len(ciphertext)%aes.BlockSize != 0 {
panic("ciphertext is not a multiple of the block size")
}
mode := cipher.NewCTR(block, iv)
mode.XORKeyStream(ciphertext, ciphertext)
s := string(ciphertext[:])
return s
}
The output
$ go run aesctr-encdec.go
Original Text: Hello 8gwifi.org
====CTR Encryption/ Decryption====
CTR Encrypted Text: 00000000000000000000000000000000df59bc82ae31c833a58fc8641a7ef119
CTR Decrypted Text: Hello 8gwifi.org
The Output Feedback (OFB) mode makes a block cipher into a synchronous stream cipher. It generates keystream blocks, which are then XORed with the plaintext blocks to get the ciphertext. Just as with other stream ciphers, flipping a bit in the ciphertext produces a flipped bit in the plaintext at the same location
The following example will show how to perform encryption and decryption in OFB Mode
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
)
func main() {
// 32 bit length key, Most be secured in production ENV
// No Hardcoding of the key
key := "myverystrongpasswordo32bitlength"
// IN OFB Mode the plaintext shoun't be Must be Block Size of AES (Multiple of 16)
// Other WIse Paddign needs to be perfomed
plainText := "Hello 8gwifi.org"
if len(plainText)%aes.BlockSize != 0 {
panic("plaintext is not a multiple of the block size")
}
fmt.Printf("Original Text: %s\n",plainText)
fmt.Println("====OFB Encryption/ Decryption====")
// IV Length Must be equal to Block Size.
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err.Error())
}
ciphertext := OFBEncrypter(key,plainText,iv)
fmt.Printf("OFB Encrypted Text: %s\n", ciphertext)
ret := OFBDecrypter(key,ciphertext,iv)
fmt.Printf("OFB Decrypted Text: %s\n", ret)
}
func OFBEncrypter(key string, plaintext string, iv []byte) string {
block, err := aes.NewCipher([]byte(key))
if err != nil {
panic(err)
}
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
stream := cipher.NewOFB(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], []byte(plaintext))
return hex.EncodeToString(ciphertext)
}
func OFBDecrypter(key string,ct string,iv []byte) string {
ciphertext, _ := hex.DecodeString(ct)
block, err := aes.NewCipher([]byte(key))
if err != nil {
panic(err)
}
ciphertext = ciphertext[aes.BlockSize:]
// CBC mode always works in whole blocks.
if len(ciphertext)%aes.BlockSize != 0 {
panic("ciphertext is not a multiple of the block size")
}
mode := cipher.NewOFB(block, iv)
mode.XORKeyStream(ciphertext, ciphertext)
s := string(ciphertext[:])
return s
}
The Output
$ go run aesofb-encdec.go
Original Text: Hello 8gwifi.org
====OFB Encryption/ Decryption====
OFB Encrypted Text: 00000000000000000000000000000000eb53ce7fe03a5cd2ea4551acbe5ea2b7
OFB Decrypted Text: Hello 8gwifi.org
Thanku for reading !!! Give a Share for Support
Instead of directly asking for donations, I'm thrilled to offer you all nine of my books for just $9 on leanpub By grabbing this bundle you not only help cover my coffee, beer, and Amazon bills but also play a crucial role in advancing and refining this project. Your contribution is indispensable, and I'm genuinely grateful for your involvement in this journey!
Any private key value that you enter or we generate is not stored on this site, this tool is provided via an HTTPS URL to ensure that private keys cannot be stolen, for extra security run this software on your network, no cloud dependency