Last active
August 6, 2021 17:57
-
-
Save anyu/a9ebf0c8c7ac18323efd09166d04185a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Simple Go implementation of AES-GCM-256 encryption/decryption, with explanations | |
// https://play.golang.org/p/sQABEg5fR1T | |
package main | |
import ( | |
"crypto/aes" | |
"crypto/cipher" | |
"crypto/rand" | |
"fmt" | |
"io" | |
"log" | |
) | |
func main() { | |
plaintext := "the text to encrypt" | |
// AES-256 requires key size of 32 bytes (32 hexademical chars) | |
bytes := make([]byte, 32) | |
// Encode key to some storable format (string, base64, etc), save in secrets manager | |
key := string(bytes) | |
encrypted, err := encrypt(plaintext, key) | |
if err != nil { | |
log.Fatalf("error encrypting: %v", err) | |
} | |
fmt.Printf("encrypted: %s\n", encrypted) | |
decrypted, err := decrypt(encrypted, key) | |
if err != nil { | |
log.Fatalf("error decrypting: %v", err) | |
} | |
fmt.Printf("decrypted: %s\n", decrypted) | |
} | |
func encrypt(plaintextStr, keyStr string) (string, error) { | |
// Decode key from some stored format | |
key := []byte(keyStr) | |
// Create AES block cipher | |
c, err := aes.NewCipher(key) | |
if err != nil { | |
return "", fmt.Errorf("error creating AES block cipher: %s", err) | |
} | |
// Wrap cipher block in GCM | |
gcm, err := cipher.NewGCM(c) | |
if err != nil { | |
return "", fmt.Errorf("error wrapping block cipher in GCM: %s", err) | |
} | |
// Make a byte array the size of the nonce that must be passed to Seal below | |
// nonce needs to be NonceSize() bytes long for Seal/Open | |
nonce := make([]byte, gcm.NonceSize()) | |
// populate nonce with secure random sequence | |
if _, err := io.ReadFull(rand.Reader, nonce); err != nil { | |
return "", fmt.Errorf("error populating nonce: %s", err) | |
} | |
// Same nonce must be used for both encryption+decryption. | |
// Methods of ensuring this: | |
// - store nonce alongside encrypted data | |
// - prepend or append nonce to encrypted data | |
// Seal encrypts and authenticates plaintext, authenticates additional data (if any), appends nonce | |
ciphertext := gcm.Seal(nonce, nonce, []byte(plaintextStr), nil) | |
return string(ciphertext), nil | |
} | |
func decrypt(ciphertextStr, key string) (string, error) { | |
// Create AES block cipher from key | |
c, err := aes.NewCipher([]byte(key)) | |
if err != nil { | |
return "", fmt.Errorf("error creating block cipher from key: %s", err) | |
} | |
// Wrap cipher block in GCM | |
gcm, err := cipher.NewGCM(c) | |
if err != nil { | |
return "", fmt.Errorf("error wrapping block cipher in GCM: %s", err) | |
} | |
// Decode ciphertext to bytes | |
ciphertext := []byte(ciphertextStr) | |
// Extract nonce from ciphertext | |
nonce := ciphertext[:gcm.NonceSize()] | |
ciphertextWithoutNonce := ciphertext[gcm.NonceSize():] | |
// Decrypt and authenticate ciphertext | |
plaintext, err := gcm.Open(nil, nonce, ciphertextWithoutNonce, nil) | |
if err != nil { | |
return "", fmt.Errorf("error decrypting/authenticating ciphertext: %s", err) | |
} | |
return string(plaintext), nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment