I am trying to port the following Python AES file encryption routines over to Go:
def derive_key_and_iv(password, salt, key_length, iv_length):
d = d_i = ''
while len(d) < key_length + iv_length:
d_i = md5(d_i + password + salt).digest()
d += d_i
return d[:key_length], d[key_length:key_length+iv_length]
def encrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
salt = Random.new().read(bs - len('Salted__'))
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
out_file.write('Salted__' + salt)
finished = False
while not finished:
chunk = in_file.read(1024 * bs)
if len(chunk) == 0 or len(chunk) % bs != 0:
padding_length = (bs - len(chunk) % bs) or bs
chunk += padding_length * chr(padding_length)
finished = True
out_file.write(cipher.encrypt(chunk))
def decrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
print(bs)
salt = in_file.read(bs)[len('Salted__'):]
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
next_chunk = ''
finished = False
while not finished:
chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
if len(next_chunk) == 0:
padding_length = ord(chunk[-1])
chunk = chunk[:-padding_length]
finished = True
out_file.write(chunk)
I have the following Go routines coded up but I'm not quite able to get it working. I am trying to get the encryption routines working in Go for callers that call the decrypt in Python and C so I'm really only interested in figuring out how to get my Golang encryption routine working but have included all the Python bits for clarity.
My current Go routines look like this:
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"io"
"log"
"crypto/md5"
"os"
)
func pyEncrypt(password []byte, pathToInFile string, pathToOutFile string){
bs := int(aes.BlockSize)
salt := make([]byte, aes.BlockSize - len("Salted__"))
_, err := rand.Read(salt)
if err != nil {panic(err)}
key, iv := deriveKeyAndIV(password, salt, bs)
block, err := aes.NewCipher(key)
if err != nil {panic(err)}
cfb := cipher.NewCFBEncrypter(block, iv)
fin, err := os.Open(pathToInFile)
if err != nil {panic(err)}
defer fin.Close()
fout, err := os.Create(pathToOutFile)
if err != nil {panic(err)}
defer fout.Close()
_, err = fout.Write([]byte("Salted__"))
if err != nil {panic(err)}
_, err = fout.Write(salt)
if err != nil {panic(err)}
for true{
ciphertext := make([]byte, 1024 *bs)
chunk := make([]byte, 1024 * bs)
_, err := fin.Read(chunk)
if err == io.EOF{
break
}else if err != nil{
panic(err)
}
cfb.XORKeyStream(ciphertext, chunk)
fout.Write(ciphertext)
}
}
func deriveKeyAndIV(password []byte, salt []byte, bs int) ([]byte, []byte) {
var digest []byte
hash := md5.New()
out := make([]byte, 32 + bs) //right now I'm just matching the default key size (32) from the python script so 32 represents the default from python
for i := 0; i < 32 + bs ; i += len(digest) {
hash.Reset()
hash.Write(digest)
hash.Write(password)
hash.Write(salt)
digest = hash.Sum(digest[:0])
copy(out[i:], digest)
}
return out[:32], out[32:32+bs] //matching the default key size from Python as that is what the application uses
}
func main() {
pwd := []byte("test123")
pyEncrypt(pwd, "/home/chris/pt.txt", "/home/chris/genc.txt")
}
Right now it runs, lol, generates an encrypted file that looks right and the Python "decrypts" without error but it generates gibberish and doesn't actually produce the clear text. The Python routines work by stand-alone but I can't get my Golang encrypt producing output that the Python decrypt can decrypt. I have to match the Python encryption routine for compatibility with the callers. Do you see what I'm doing wrong in my Golang encryption routine? Thank you so much for your assistance.