Golang generating a 32 byte key - go

I am using this library for sessions.
https://github.com/codegangsta/martini-contrib/tree/master/sessions
It says that:
It is recommended to use an authentication key with 32 or 64 bytes. The encryption key, if set, must be either 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256 modes.
How do I generate a 64 byte key, is it as straightforward as []byte"64characterslongstring", I thought it is not always so straight forward?

To generate a slice of 64 random bytes:
package main
import "crypto/rand"
func main() {
key := make([]byte, 64)
_, err := rand.Read(key)
if err != nil {
// handle error here
}
}
Demo here.

Related

Golang- AES Decryption is not returning same Text

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. Does anyone know reasoning behind this? I have seen a close question like that but not and exactly one.
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.
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.

Golang (Go) AES CBC ciphertext gets padded with 16 0x00 bytes for some reason

I am testing out the AES 256 CBC implementation in Golang (Go).
plaintext: {"key1": "value1", "key2": "value2"}
Because the plaintext is 36 B and needs to be a multiple of the block size (16 B) I pad it manually with 12 random bytes to 48 B.
I understand that this is not the most secure way of doing it, but I am just testing, I will find a better way for production setups.
Inputs:
plaintext: aaaaaaaaaaaa{"key1": "value1", "key2": "value2"}
AES 256 key: b8ae2fe8669c0401fb289e6ab6247924
AES IV: e0332fc2a9743e4f
The code excerpt extracted, but modified a bit, from here:
block, err := aes.NewCipher(key)
if err != nil {
fmt.Println("Error creating a new AES cipher by using your key!");
fmt.Println(err);
os.Exit(1);
}
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext, plaintext)
fmt.Printf("%x\n", ciphertext)
fmt.Println("len(ciphertext):",len(ciphertext))
CipherText = PlainText + Block - (PlainText MOD Block)
This equation gives the length of the ciphertext for CBC.
So, the line ciphertext := make([]byte, aes.BlockSize+len(plaintext)) satisfies this requirement since my plaintext is always padded to be a multiple of the block size.
Problem:
With Go I get the following ciphertext:
caf8fe667f4087e1b67d8c9c57fcb1f56b368cafb4bfecbda1e481661ab7b93d87703fb140368d3034d5187c53861c7400000000000000000000000000000000
I always get 16 0x00 bytes at the end of my ciphertext, no matter the length of my plaintext.
If i do the same with an online AES calculator I get this ciphertext:
caf8fe667f4087e1b67d8c9c57fcb1f56b368cafb4bfecbda1e481661ab7b93d87703fb140368d3034d5187c53861c74ccd202bac41937be75731f23796f1516
The first 48 bytes caf8fe667f4087e1b67d8c9c57fcb1f56b368cafb4bfecbda1e481661ab7b93d87703fb140368d3034d5187c53861c74 are the same. But I am missing the last 16 bytes.
This says:
It is acceptable to pass a dst bigger than src, and in that case,
CryptBlocks will only update dst[:len(src)] and will not touch the
rest of dst.
But why is this the case ? The length of the ciphertext needs to be longer than the length of the plaintext and the online AES calculators prove that.
The ciphertext of the online tool results, if the plaintext:
aaaaaaaaaaaa{"key1": "value1", "key2": "value2"}
is padded with PKCS#7 and the posted key and IV are UTF8 encoded. Since the size of the plaintext (48 bytes) is already an integer multiple of the blocksize (16 bytes for AES), a full block is padded according to the rules of PKCS#7 padding, resulting in a 64 bytes plaintext and ciphertext.
It is not clear from the question which online tool was used, but the posted ciphertext can be reconstructed with any reliable encryption tool, e.g. CyberChef, s. this online calcualtion. CyberChef applies PKCS#7 padding for AES/CBC by default.
The posted code produces a different ciphertext because:
no PKCS#7 padding is applied. This makes the ciphertext one block shorter (i.e. the last block ccd202bac41937be75731f23796f1516 is missing).
a size of aes.BlockSize + len(plaintext) bytes is allocated for the ciphertext. This causes the allocated size to be too large by aes.BlockSize bytes (i.e. the ciphertext contains 16 0x00 values at the end).
Therefore, for the Go code to produce the same ciphertext as the online tool, 1. the PKCS#7 padding must be added and 2. a size of only len(plaintext) bytes must be allocated for the ciphertext.
The following code is a possible implementation (for PKCS#7 padding pkcs7pad is used):
import (
...
"github.com/zenazn/pkcs7pad"
)
...
key := []byte("b8ae2fe8669c0401fb289e6ab6247924")
iv := []byte("e0332fc2a9743e4f")
plaintext := []byte("aaaaaaaaaaaa{\"key1\": \"value1\", \"key2\": \"value2\"}")
plaintext = pkcs7pad.Pad(plaintext, aes.BlockSize) // 1. pad the plaintext with PKCS#7
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
ciphertext := make([]byte, len(plaintext)) // 2. allocate len(plaintext)
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext, plaintext)
fmt.Printf("%x\n", ciphertext) // caf8fe667f4087e1b67d8c9c57fcb1f56b368cafb4bfecbda1e481661ab7b93d87703fb140368d3034d5187c53861c74ccd202bac41937be75731f23796f1516
Note that because of the PKCS#7 padding, explicit padding with a is no longer required.
The static IV used in the above code is a vulnerability as it leads to reuse of key/IV pairs, which is insecure. In practice, therefore, a random IV is usually generated for each encryption. The IV is not secret, is needed for decryption, and is typically concatenated with the ciphertext. On the decryption side, IV and ciphertext are separated and used for decryption.
Since the size of the IV corresponds to the blocksize, a size of aes.BlockSize + len(plaintext) must be allocated for the ciphertext, which is equal to the size in the original code. Possibly this is not accidental and was designed with a random IV in mind, but then not implemented consequently. A consequent implementation is:
import (
...
"crypto/rand"
"io"
"github.com/zenazn/pkcs7pad"
)
...
key := []byte("b8ae2fe8669c0401fb289e6ab6247924")
plaintext := []byte("{\"key1\": \"value1\", \"key2\": \"value2\"}")
plaintext = pkcs7pad.Pad(plaintext, aes.BlockSize)
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
_, err = io.ReadFull(rand.Reader, iv) // create a random IV
if err != nil {
panic(err)
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
fmt.Printf("%x\n", ciphertext)
The first 16 bytes of the output correspond to the (random) IV and the rest to the actual ciphertext.

Is Go only encrypt text in a 16-byte message length?

I try to encrypt a message using AES in Golang.
func main() {
key := "mysupersecretkey32bytecharacters"
plainText := "thisismyplaintextingolang"
fmt.Println("My Encryption")
byteCipherText := encrypt([]byte(key), []byte(plainText))
fmt.Println(byteCipherText)
}
func encrypt(key, plaintext []byte) []byte {
cphr, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
ciphertext := make([]byte, len(plaintext))
cphr.Encrypt(ciphertext, plaintext)
return ciphertext
}
That function returns : [23 96 11 10 70 223 95 118 157 250 80 92 77 26 137 224 0 0 0 0 0 0 0 0 0]
In that result, there are only 16 non-zero byte values. This means that AES encryption in Go only encrypts 16 characters.
Is it possible to encrypts more than 16 characters in Go AES without using any mode in AES (like GCM, CBC, CFB, ..etc), just pure AES?
aes.NewCipher returns an instance of cipher.Block, which encrypts in blocks of 16 bytes (this is how pure AES works).
mode of operation literally determines how messages longer than 16 bytes are encrypted. The simplest one is ECB (a "no-op" mode), which simply repeats the encryption in blocks of 16 bytes using the same key. You can do the same using a simple for-loop, though keep in mind that ECB is not very secure.
This has nothing to do with go. AES is a block cypher that encrypts a block of 16 bytes. To encrypt a longer message there are several modes that can be used to achieve this.

Convert int array to byte array, compress it then reverse it

I have a large int array that I want to persist on the filesystem. My understanding is the best way to store something like this is to use the gob package to convert it to a byte array and then to compress it with gzip.
When I need it again, I reverse the process. I am pretty sure I am storing it correctly, however recovering it is failing with EOF. Long story short, I have some example code below that demonstrates the issue. (playground link here https://play.golang.org/p/v4rGGeVkLNh).
I am not convinced gob is needed, however reading around it seems that its more efficient to store it as a byte array than an int array, but that may not be true. Thanks!
package main
import (
"bufio"
"bytes"
"compress/gzip"
"encoding/gob"
"fmt"
)
func main() {
arry := []int{1, 2, 3, 4, 5}
//now gob this
var indexBuffer bytes.Buffer
writer := bufio.NewWriter(&indexBuffer)
encoder := gob.NewEncoder(writer)
if err := encoder.Encode(arry); err != nil {
panic(err)
}
//now compress it
var compressionBuffer bytes.Buffer
compressor := gzip.NewWriter(&compressionBuffer)
compressor.Write(indexBuffer.Bytes())
defer compressor.Close()
//<--- I think all is good until here
//now decompress it
buf := bytes.NewBuffer(compressionBuffer.Bytes())
fmt.Println("byte array before unzipping: ", buf.Bytes())
if reader, err := gzip.NewReader(buf); err != nil {
fmt.Println("gzip failed ", err)
panic(err)
} else {
//now ungob it...
var intArray []int
decoder := gob.NewDecoder(reader)
defer reader.Close()
if err := decoder.Decode(&intArray); err != nil {
fmt.Println("gob failed ", err)
panic(err)
}
fmt.Println("final int Array content: ", intArray)
}
}
You are using bufio.Writer which–as its name implies–buffers bytes written to it. This means if you're using it, you have to flush it to make sure buffered data makes its way to the underlying writer:
writer := bufio.NewWriter(&indexBuffer)
encoder := gob.NewEncoder(writer)
if err := encoder.Encode(arry); err != nil {
panic(err)
}
if err := writer.Flush(); err != nil {
panic(err)
}
Although the use of bufio.Writer is completely unnecessary as you're already writing to an in-memory buffer (bytes.Buffer), so just skip that, and write directly to bytes.Buffer (and so you don't even have to flush):
var indexBuffer bytes.Buffer
encoder := gob.NewEncoder(&indexBuffer)
if err := encoder.Encode(arry); err != nil {
panic(err)
}
The next error is how you close the gzip stream:
defer compressor.Close()
This deferred closing will only happen when the enclosing function (the main() function) returns, not a second earlier. But by that time you already wanted to read the zipped data, but that might still sit in an internal cache of gzip.Writer, and not in compressionBuffer, so you obviously can't read the compressed data from compressionBuffer. Close the gzip stream without using defer:
if err := compressor.Close(); err != nil {
panic(err)
}
With these changes, you program runs and outputs (try it on the Go Playground):
byte array before unzipping: [31 139 8 0 0 0 0 0 0 255 226 249 223 200 196 200 244 191 137 129 145 133 129 129 243 127 19 3 43 19 11 27 7 23 32 0 0 255 255 110 125 126 12 23 0 0 0]
final int Array content: [1 2 3 4 5]
As a side note: buf := bytes.NewBuffer(compressionBuffer.Bytes()) – this buf is also completely unnecessary, you can just start decoding compressionBuffer itself, you can read data from it that was previously written to it.
As you might have noticed, the compressed data is much larger than the initial, compressed data. There are several reasons: both encoding/gob and compress/gzip streams have significant overhead, and they (may) only make input smaller on a larger scale (5 int numbers don't qualify to this).
Please check related question: Efficient Go serialization of struct to disk
For small arrays, you may also consider variable-length encoding, see binary.PutVarint().

Go encryption differs from Ruby encryption using same key and iv

I have the following Ruby code:
require 'base64'
require 'openssl'
data = '503666666'
key = '4768c01c4f598828ef80d9982d95f888fb952c5b12189c002123e87f751e3e82'
nonce = '4eFi6Q3PX1478767\n'
nonce = Base64.decode64(nonce)
c = OpenSSL::Cipher.new('aes-256-gcm')
c.encrypt
c.key = key
c.iv = nonce
result = c.update(data) + c.final
tag = c.auth_tag
puts Base64.encode64(result + tag) # => J3AVfNG84bz2UuXcfre7LVjSbMpX9XBq6g==\n
that I'm trying to replicate in Golang.
Here's what I have so far:
package main
import (
"fmt"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/hex"
)
func main() {
data := []byte("503666666")
key, err := hex.DecodeString(`4768c01c4f598828ef80d9982d95f888fb952c5b12189c002123e87f751e3e82`)
if err != nil {
panic(err)
}
nonceB64 := "4eFi6Q3PX1478767\n"
nonce, err := base64.StdEncoding.DecodeString(nonceB64)
if err != nil {
panic(err)
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err.Error())
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
panic(err.Error())
}
ciphertext := aesgcm.Seal(nil, nonce, data, nil)
fmt.Printf("%s\n", base64.StdEncoding.EncodeToString(ciphertext))
}
However the outcome from the Go version is:
+S52HGbLV1xp+GnF0v8VNOqc5J2GY2+SqA==
vs.
J3AVfNG84bz2UuXcfre7LVjSbMpX9XBq6g==\n
Why am I getting different results?
Thanks,
The AES 256 cipher requires a 32 byte key. The Ruby code is setting the key to a 64 byte string consisting of hexadecimal digits. OpenSSL is truncating the string to 32 bytes before use (change key to '4768c01c4f598828ef80d9982d95f888' in the Ruby code and you'll get the same output).
The Go code however is hex decoding the key before use, converting the 64 hexadecimal digits to the 32 bytes required for the key.
If you want to change the Go code so that it matches the Ruby result, then you'll need to truncate the key and remove the hex decoding step:
key := []byte("4768c01c4f598828ef80d9982d95f888")
However, I'd argue that the key handling in the Go version of the code is better. If you want to change the Ruby version to match the Go version, you can hex decode the key before use:
key = [key].pack('H*')

Resources