Reputation: 1697
I am attempting to encrypt sensitive data before storing it. First, I generate a key to be used for the encryption process:
import (
"crypto/aes"
CR "crypto/rand"
"encoding/hex"
"errors"
"log"
"os"
)
// []byte key used to encrypt tokens before saving to local file system
var key = make([]byte, 32)
func createKey(key *[]byte) {
_, err = CR.Read(*key)
if err != nil {
log.Println("Error creating key from crypto/rand package:", err)
}
}
Next I create functions that encrypt and decrypt a string respectively:
func encryptToken(t token) string {
original := t.ID // ID is string member of token
cipher, err := aes.NewCipher(key)
if err != nil {
log.Println("Error creating cipher during encrypt:", err)
}
out := make([]byte, len(original))
cipher.Encrypt(out, []byte(original))
return hex.EncodeToString(out) // this will be written to a csv file
// appears in file as: cec35df876e1b77diefg9023366c5f2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
}
func decryptToken(s string) string {
ciphertext, err := hex.DecodeString(s) // s is read from csv file
if err != nil {
log.Println("Error decoding string from hex:", err)
}
cipher, err := aes.NewCipher(key)
if err != nil {
log.Println("Error creating cipher during decrypt:", err)
}
original := make([]byte, len(ciphertext))
cipher.Decrypt(original, ciphertext)
originalAsString := string(original[:])
return originalAsString // returns: 6f928e728f485403
// original token was: 6f928e728f485403e254049f684ea5ec853adcfa9553cdfc956fr45671447c57
}
Considering that encryptToken()
returns a hex string with so many zeros, I am certain this is where my problem is. I've experimented adjusting the length of key
, but using a value other than 32 in var key = make([]byte, 32)
will result in a panic involving invalid memory address or nil pointer dereference. Why is this?
Upvotes: 2
Views: 800
Reputation: 1697
EDITED per comments below (in order to correctly use a nonce):
The accepted answer, and especially the comment from Topaco, helped me arrive to this solution, I am posting here for anyone that may come across this:
var key = make([]byte, 32)
func encryptToken(t token) string {
original := t.ID // ID is string member of token
var nonce = make([]byte, 12)
// read random bytes into nonce
_, err := rand.Read(nonce)
if err != nil {
log.Println("Error reading random bytes into nonce:", err)
}
block, err := aes.NewCipher(key)
if err != nil {
log.Println("Error creating cipher during encrypt:", err)
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
log.Println("Error creating GCM during encrypt:", err)
}
ciphertext := aesgcm.Seal(nil, nonce, []byte(original), nil)
// prepend the ciphertext with the nonce
out := append(nonce, ciphertext...)
return hex.EncodeToString(out)
}
func decryptToken(s string) string {
// read hex string describing nonce and ciphertext
enc, err := hex.DecodeString(s)
if err != nil {
log.Println("Error decoding string from hex:", err)
}
// separate ciphertext from nonce
nonce := enc[0:12]
ciphertext := enc[12:]
block, err := aes.NewCipher(key)
if err != nil {
log.Println("Error creating cipher during decrypt:", err)
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
log.Println("Error creating GCM during decrypt:", err)
}
original, err := aesgcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
log.Println("Error decrypting to string:", err)
}
originalAsString := string(original)
return originalAsString
}
Reference: https://pkg.go.dev/crypto/cipher#example-NewGCM-Encrypt
Upvotes: 0
Reputation: 2355
The shortest answer is probably: As @GradyPlayer suggested, you're using your cipher the wrong way. You have to hand it over to an encrypter instead which applies it to all the bytes. The pattern looks like that:
ciphertext = make([]byte, len(plaintext))
cbc := cipher.NewCBCEncrypter(cipher, iv)
cbc.CryptBlocks(ciphertext, plaintext)
That said, here you would still have to put the initialization vector in front of the ciphertext (that's a usual place to put it at least, and the decrypter might expect it to be located here), and you still have to think about padding (if you're using CBC as I did).
So while it's not directly addressing the question, I think it would help you do get more code for a better understanding. Here is a gist I've just created. Disclaimer: I mostly assembled code fragments from other places. So I merely put the pieces together you can see here, and gained some understanding on the road.
Upvotes: 3
Reputation: 14549
I am not familiar with the library, but it might just be doing one block of AES, AES is a 16 byte block cypher, which seems to be what you get back, one 16 byte block...
to do a whole arbitrary chunk of AES, you have to pad it to a full size mod 16 = 0
bytes (look up PKCS5/7), then do one block... then, either CTR or CBC (the IV will come from either the position or the output of the last block), then the next block and so on... so I think you are using the AES block primitive rather than a whole blob sort of thing, (which in addition to a key requires a block mode and an iv)
Upvotes: 2