Golang AES ECB Encryption
Asked Answered
M

5

12

Trying to emulate an algorithm in Go that is basically AES ECB Mode encryption.

Here's what I have so far

func Decrypt(data []byte) []byte {
    cipher, err := aes.NewCipher([]byte(KEY))
    if err == nil {
        cipher.Decrypt(data, PKCS5Pad(data))
        return data
    }
    return nil
}

I also have a PKCS5Padding algorithm, which is tested and working, which pads the data first. I cant find any information on how to switch the encryption mode in the Go AES package (it's definitely not in the docs).

I have this code in another language, which is how I know this algorithm isn't working quite correctly.

EDIT: Here is the method as I have interpreted from on the issue page

func AESECB(ciphertext []byte) []byte {
    cipher, _ := aes.NewCipher([]byte(KEY))
    fmt.Println("AESing the data")
    bs := 16
    if len(ciphertext)%bs != 0     {
        panic("Need a multiple of the blocksize")
    }

    plaintext := make([]byte, len(ciphertext))
    for len(plaintext) > 0 {
        cipher.Decrypt(plaintext, ciphertext)
        plaintext = plaintext[bs:]
        ciphertext = ciphertext[bs:]
    }
    return plaintext
}

This is actually not returning any data, maybe I screwed something up when changing it from encripting to decripting

Madelinemadella answered 5/6, 2014 at 23:48 Comment(5)
what does the error says ? do you have / can you provide an example playground ?Radii
You pad the plaintext before encryption, and you unpad it after. And you do that only for the last block, not anywhere in between.Denaturalize
@owlstead the implementation I saw actually padded the encrypted data before unencrypting...could this possibly be correct? github.com/martinp/pysnap/blob/master/pysnap/utils.py#L40Madelinemadella
Do you specifically need ECB mode encryption here? What are you trying to encrypt/is there a necessary interop with an API?Aviculture
@Madelinemadella No, that cannot be correct. It would mean that you add a full block of padding (as the ciphertext is always x times the blocksize). Thus the plaintext would consist of everything including the padding at the end and a block of random garbage. So it would decrypt, yes, but the result would not be correct.Denaturalize
L
11

Electronic codebook ("ECB") is a very straightforward mode of operation. The data to be encrypted is divided into byte blocks, all having the same size. For each block, a cipher is applied, in this case AES, generating the encrypted block.

The code snippet below decrypts AES-128 data in ECB (note that the block size is 16 bytes):

package main

import (
    "crypto/aes"
)

func DecryptAes128Ecb(data, key []byte) []byte {
    cipher, _ := aes.NewCipher([]byte(key))
    decrypted := make([]byte, len(data))
    size := 16

    for bs, be := 0, size; bs < len(data); bs, be = bs+size, be+size {
        cipher.Decrypt(decrypted[bs:be], data[bs:be])
    }

    return decrypted
}

As mentioned by @OneOfOne, ECB is insecure and very easy to detect, as repeated blocks will always encrypt to the same encrypted blocks. This Crypto SE answer gives a very good explanation why.

Lizethlizette answered 14/1, 2017 at 16:59 Comment(1)
Thanks for this, was helpful. Worth noting this isn't restricted to 128-bit encryption - it will also work for 256-bit by passing in a 32 byte key instead of a 16 byte key.Primavera
L
8

Why? We left ECB out intentionally: it's insecure, and if needed it's trivial to implement.

https://github.com/golang/go/issues/5597

Leal answered 5/6, 2014 at 23:58 Comment(3)
good point, also if you follow the link, you will find the simple ECB mode implementation in GoAlric
But, of course, you wouldn't put that "simple ECB mode implementation" into any software (ever) because of its significant flaws, right? :)Aviculture
@Aviculture definitely not for new software...this is a requirement for an existing API that I am trying to interact withMadelinemadella
P
4

I used your code so I feel the need to show you how I fixed it.

I am doing the cryptopals challenges for this problem in Go.

I'll walk you through the mistake since the code is mostly correct.

for len(plaintext) > 0 {
    cipher.Decrypt(plaintext, ciphertext)
    plaintext = plaintext[bs:]
    ciphertext = ciphertext[bs:]
}

The loop does decrypt the data but does not put it anywhere. It simply shifts the two arrays along producing no output.

i := 0
plaintext := make([]byte, len(ciphertext))
finalplaintext := make([]byte, len(ciphertext))
for len(ciphertext) > 0 {
    cipher.Decrypt(plaintext, ciphertext)
    ciphertext = ciphertext[bs:]
    decryptedBlock := plaintext[:bs]
    for index, element := range decryptedBlock {
        finalplaintext[(i*bs)+index] = element
    }
    i++
    plaintext = plaintext[bs:]
} 
return finalplaintext[:len(finalplaintext)-5]

What this new improvement does is store the decrypted data into a new []byte called finalplaintext. If you return that you get the data.

It's important to do it this way since the Decrypt function only works one block size at a time.

I return a slice because I suspect it's padded. I am new to cryptography and Go so anyone feel free to correct/revise this.

Pascia answered 15/5, 2016 at 23:42 Comment(0)
H
1

Ideally you want to implement the crypto/cipher#BlockMode interface. Since an official one doesn't exist, I used crypto/cipher#NewCBCEncrypter as a starting point:

package ecb
import "crypto/cipher"

type ecbEncrypter struct { cipher.Block }

func newECBEncrypter(b cipher.Block) cipher.BlockMode {
   return ecbEncrypter{b}
}

func (x ecbEncrypter) BlockSize() int {
   return x.Block.BlockSize()
}

func (x ecbEncrypter) CryptBlocks(dst, src []byte) {
   size := x.BlockSize()
   if len(src) % size != 0 {
      panic("crypto/cipher: input not full blocks")
   }
   if len(dst) < len(src) {
      panic("crypto/cipher: output smaller than input")
   }
   for len(src) > 0 {
      x.Encrypt(dst, src)
      src, dst = src[size:], dst[size:]
   }
}
Horrify answered 7/3, 2021 at 2:3 Comment(0)
M
-2

I was confused by a couple of things.

First i needed a aes-256 version of the above algorithm, but apparently the aes.Blocksize (which is 16) won't change when the given key has length 32. So it is enough to give a key of length 32 to make the algorithm aes-256

Second, the decrypted value still contains padding and the padding value changes depending on the length of the encrypted string. E.g. when there are 5 padding characters the padding character itself will be 5.

Here is my function which returns a string:

func DecryptAes256Ecb(hexString string, key string) string {
  data, _ := hex.DecodeString(hexString)

  cipher, _ := aes.NewCipher([]byte(key))

  decrypted := make([]byte, len(data))
  size := 16

  for bs, be := 0, size; bs < len(data); bs, be = bs+size, be+size {
    cipher.Decrypt(decrypted[bs:be], data[bs:be])
  }

  // remove the padding. The last character in the byte array is the number of padding chars
  paddingSize := int(decrypted[len(decrypted)-1])
  return string(decrypted[0 : len(decrypted)-paddingSize])
}
Moravian answered 1/11, 2017 at 11:16 Comment(1)
1. AES has one block size of 16-bytes, it is not related to the key size. AES has three key sizes of 128, 192 & 256 bits. 2. The padding seen here is PKCS#7, padding is necessary if the data to be encrypted is not always a multiple of the block size. 3. Most AES implementations (the aes Go implementations does not) will handle input data longer than one block and automatically handle the block calls and padding. 4. Instead see crypto/cipher which does handle blocking and padding.Indignity

© 2022 - 2024 — McMap. All rights reserved.