-1

I am following this documentation and was trying to implement a simple AES encryption and decryption with using GoLang. For plain text it is working fine however, for UUID it is not working. Excepting a resolution of this and why this is happening. Here is my sample code

package main
import (
    "crypto/aes"
    "encoding/hex"
    "fmt"
)

func main() {

    key := "thisis32bitlongpassphraseimusing"
    pt := "a30a1777-e9f4-ed45-4755-add00172ebae"

    c := EncryptAES([]byte(key), pt)
    fmt.Println(pt)
    fmt.Println(c)
    DecryptAES([]byte(key), c)
}

func EncryptAES(key []byte, plaintext string) string {
    c, err := aes.NewCipher(key)
    CheckError(err)
    out := make([]byte, len(plaintext))
    c.Encrypt(out, []byte(plaintext))
    return hex.EncodeToString(out)
}

func DecryptAES(key []byte, ct string) {
    ciphertext, _ := hex.DecodeString(ct)
    c, err := aes.NewCipher(key)
    CheckError(err)
    pt := make([]byte, len(ciphertext))
    c.Decrypt(pt, ciphertext)
    s := string(pt[:])
    fmt.Println("DECRYPTED:", s)
}

func CheckError(err error) {
    if err != nil {
        panic(err)
    }
}

And here is the output

a30a1777-e9f4-ed45-4755-add00172ebae
e0f32a5bcf576754da4206cc967157ae0000000000000000000000000000000000000000
DECRYPTED: a30a1777-e9f4-ed

As you can see in the remaining last part of the UUID is disappearing. I have attached as a snap which says it didn't decrypt the last part properly. enter image description here Does anyone know reasoning behind this? I have seen a close question like that but not and exactly one.

Ananda G
  • 2,389
  • 23
  • 39
  • 1
    From the docs: Encrypt encrypts the first block in src into dst. Dst and src must overlap entirely or not at all. https://pkg.go.dev/crypto/cipher#Block – Jake Holzinger Jan 10 '23 at 00:40
  • 1
    Further to the above - to encrypt more data you need a [block mode](https://pkg.go.dev/crypto/cipher#BlockMode) (see [wikipedia](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation) for background info and some common choices) - the docs contain examples of a few modes [including CBC](https://pkg.go.dev/crypto/cipher#NewCBCDecrypter) and [GCM](https://pkg.go.dev/crypto/cipher#example-NewGCM-Decrypt) (which you select will depend upon your requirements). – Brits Jan 10 '23 at 07:12
  • @JakeHolzinger Not sure I understand you entirely. Is `Dst` means destination? – Ananda G Jan 12 '23 at 15:21
  • 1
    @AnandaG Yes, dst means destination. Your problem, as @Brits points out in more detail, is that you are using `Encrypt` directly. Instead you should create a `BlockMode` cipher so that you encrypt all of the data, not just the first block. This means you will need to handle other things like the initialization vector and padding. I suggest you review the examples to get started. https://go.dev/src/crypto/cipher/example_test.go – Jake Holzinger Jan 12 '23 at 18:01

2 Answers2

3

If you look at the page for the AES block cipher you'll find out that aes.NewCipher returns a Block as mentioned by Jake in the comments.

Now if you go to that page you'll see that this page points out various modes that you can use to create a real, secure cipher out of a block cipher. A block cipher only handles blocks of data, which are always 128 bits / 16 bytes in the case of AES. So this is precisely why there are all those zero's after the ciphertext, it encrypted 16 bytes and that was that. Note that ciphertext should always look as randomized bytes.

Unfortunately it doesn't directly list the authenticated (AEAD) modes, so please take a look here as well. That said, you can see that CTR mode is in there including examples, and that's the main idea missing from the question + my answer that you've linked to. Nothing in your code shows a mode of operation, and certainly not counter mode.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
-1

The problem with your code is that AES encryption requires the input to be a multiple of the block size (16 bytes for AES-128), but the UUID you're trying to encrypt ("a30a1777-e9f4-ed45-4755-add00172ebae")is 36 bytes long and it is causing the cipher to error.

One way to fix this issue is by padding the plaintext so that its length is a multiple of the block size before encryption. The Go standard library has a package called crypto/padding that provides functions for adding padding to plaintext.

You can modify your EncryptAES function like this:

func EncryptAES(key []byte, plaintext string) string {
c, err := aes.NewCipher(key)
CheckError(err)
plaintextBytes := []byte(plaintext)

// Add padding to plaintext
blockSize := c.BlockSize()
padding := blockSize - (len(plaintextBytes) % blockSize)
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
plaintextBytes = append(plaintextBytes, padtext...)

out := make([]byte, len(plaintextBytes))
c.Encrypt(out, plaintextBytes)
return hex.EncodeToString(out)
}

Then in the DecryptAES function, you can remove the padding before decrypting the ciphertext like this:

func DecryptAES(key []byte, ct string) {
ciphertext, _ := hex.DecodeString(ct)
c, err := aes.NewCipher(key)
CheckError(err)
pt := make([]byte, len(ciphertext))
c.Decrypt(pt, ciphertext)
//Remove padding
padLen := int(pt[len(pt)-1])
s := string(pt[:len(pt)-padLen])
fmt.Println("DECRYPTED:", s)
}

As for padding schemes, you might want to try padding scheme like pkcs#5 or pkcs#7.

Houdini
  • 7
  • 2
  • 1
    [Here](https://go.dev/play/p/xAyQ3siOH1O) is the OP's question with your functions in the playground. It does not resolve the issue (because the issue is that ["Encrypt encrypts the first block"](https://pkg.go.dev/crypto/cipher#Block). – Brits Jan 11 '23 at 01:27