Python AES-CTR is not compatible with Golang - python

I'm using Python2.7 with pycryptodome 3.6.6 and Golang1.10.4 on Ubuntu 16.04.
The encrypt algorithm I choose is AES-CTR-128. But the data encrypted by Python and Golang have different result. So there's a problem to communicate between the apps written by these two languages.
Here is my implement:
Python:
#coding=utf-8
from __future__ import absolute_import
import binascii
from Crypto.Cipher import AES
from Crypto.Util import Counter
def hexlify(binary):
return binascii.hexlify(binary)
class AES_CTR(object):
def __init__(self, key, iv):
assert len(key) == 16
assert len(iv) == 16
ctr = Counter.new(128)
self.aes = AES.new(key, AES.MODE_CTR, counter=ctr)
def encrypt(self, plain_data):
return self.aes.encrypt(plain_data)
def decrypt(self, encrypted_data):
return self.aes.decrypt(encrypted_data)
if __name__ == '__main__':
aes = AES_CTR('abcdef0123456789', '0123456789abcdef')
print hexlify(aes.encrypt("hello")) #print '9b1a038478'
print hexlify(aes.encrypt("hello")) #print '8751ea0448'
print hexlify(aes.encrypt("world")) #print 'b6aa7c286b'
Golang
package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"fmt"
)
type AESCipher struct {
iv []byte
stream cipher.Stream
}
func NewAESCipher(key []byte, iv []byte) *AESCipher {
if (len(iv) != 16 || len(key) != 16) {
panic("iv length or key length error")
}
block, err := aes.NewCipher(key)
if (err != nil) {
panic(err)
}
return &AESCipher {
iv: iv,
stream: cipher.NewCTR(block, iv),
}
}
func (cipher *AESCipher) Encrypt(buffer []byte) []byte {
encrypted := make([]byte, len(buffer))
cipher.stream.XORKeyStream(encrypted, buffer)
return encrypted
}
func (cipher *AESCipher) Decrypt(buffer []byte) []byte {
decrypted := make([]byte, len(buffer))
cipher.stream.XORKeyStream(decrypted, buffer)
return decrypted
}
func main() {
iv := []byte("0123456789abcdef")
key := []byte("abcdef0123456789")
cipher := NewAESCipher(key, iv)
encrypted1 := cipher.Encrypt([]byte("hello"))
fmt.Println(hex.EncodeToString(encrypted1)) // print '94ee8ac46a'
encrypted2 := cipher.Encrypt([]byte("hello"))
fmt.Println(hex.EncodeToString(encrypted2)) // print 'b36d48ad7e'
encrypted3 := cipher.Encrypt([]byte("world"))
fmt.Println(hex.EncodeToString(encrypted3)) // print '7302071a9c'
}

Problem solved.
Accroding to this answer, the default implementation of pycryptodome is not correct. We can change the Counter to make it work as expected.
ctr = Counter.new(128, initial_value=bytes_to_long(iv))
Now it works perfectly.

You can simplify your code, by not using the Counter class at all, a documented here:
self.aes = AES.new(key, AES.MODE_CTR, initial_value=iv)

Try this out:
ctr = Counter.new(128, initial_value= int.frombytes(iv.encode(),'little'))
IV is always to be passed as an integer value and Key in bytes format.
Check Documentation from above mentioned links

Related

TripleDES decryption in python

I'm trying to convert the following c# function to python
the orginal C# function
public static string Decrypt(string Key, string toDecrypt) // key.length = 16 Byte
{
if (Key != "") {
try {
byte[] toEncryptArray = Convert.FromBase64String(toDecrypt);
byte[] keyArray = UTF8Encoding.UTF8.GetBytes(Key);
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
tdes.Key = keyArray;
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.Zeros;
ICryptoTransform cTransform = tdes.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0,
toEncryptArray.Length);
tdes.Clear();
return UTF8Encoding.UTF8.GetString(resultArray);
} catch {
return "";
}
} else
return "";
}
that's my try in python
import base64
from Crypto.Cipher import DES3
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
mac='OtTL6k9yLn6VJIZRXRj1OA=='
key='nOwy4iuE8c8WcktG'
toEncryptArray = base64.b64encode(mac.encode('utf-8'))
keyArray = key.encode('utf-8')
print(DES3.adjust_key_parity(keyArray))
cipher = DES3.new(keyArray, DES3.MODE_ECB)
decriptText = cipher.decrypt(toEncryptArray)
print(decriptText)
the result that i get in python is not equal to the c# result

Problem with Python 3 AES decryption, IV lenght problem

i was trying to decrypt an AES ciphertext with Python 3.
I know that the key has been encoded as it follows (javascript) :
var k = CryptoJS.SHA256("\x93\x39\x02\x49\x83\x02\x82\xf3\x23\xf8\xd3\x13\x00");
if(u == "\x68\x34\x63\x6b\x33\x72") {
var enc = CryptoJS.AES.encrypt(p, CryptoJS.enc.Hex.parse(k.toString().substring(0,32)), { iv: CryptoJS.enc.Hex.parse(k.toString().substring(32,64)) });
if(enc == "PKhuCrfh3RUw4vie3OMa8z4kcww1i7198ly0Q4rpuyA=") {
t = true;
}
}
Now, as you can see the IV of the encryption is 32 characters long, however in python 3 i can't use a string of length 32 as my IV.
I use the following code :
def decrypt(encrypted, passphrase_full):
half1_passphrase = passphrase_full[0:32]
half2_passphrase = passphrase_full[32:]
print(len(half2_passphrase))
IV = half2_passphrase
aes = AES.new(half1_passphrase, AES.MODE_CBC, IV)
return aes.decrypt(encrypted)
And i get
ValueError: IV must be 16 bytes long
So, how can i decrypt my string correctly ?
Thank you very much

Decrypting AES CBC data from PyCrypto with Powershell

I am working on a project where I will be encrypting a string of data using the AES module from PyCrypto and then decrypting it using Powershell.
I've written a simple encryption function to do what I need here:
import base64
from Crypto import Random
from Crypto.Cipher import AES
key = "SuperSecret" #Insecure and just for testing
plaintext = "Secret message please don't look"
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
def padKey(s): #Pad key to 32 bytes for AES256
return (s * (int(32/len(s))+1))[:32]
class AESCipher:
def __init__(self, key):
self.key = key
def encrypt(self, raw):
raw = pad(raw)
iv = Random.new().read( AES.block_size )
cipher = AES.new( self.key, AES.MODE_CBC, iv )
return base64.b64encode( iv + cipher.encrypt( raw ) )
paddedKey = padKey(key)
cipher = AESCipher(paddedKey)
encrypted = str(cipher.encrypt(plaintext))
encrypted = encrypted[2:-1]
print("Key:", paddedKey)
print("Plaintext:",plaintext)
print("Encrypted and B64:",encrypted)
I am having some issues with decrypting and decoding the output with Powershell and could use some help. I was able to find a simple decryption script that I've been working with online, but the output is all garbage:
function Create-AesManagedObject($key, $IV) {
$aesManaged = New-Object "System.Security.Cryptography.AesManaged"
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
$aesManaged.BlockSize = 128
$aesManaged.KeySize = 256
if ($IV) {
if ($IV.getType().Name -eq "String") {
$aesManaged.IV = [System.Convert]::FromBase64String($IV)
}
else {
$aesManaged.IV = $IV
}
}
if ($key) {
if ($key.getType().Name -eq "String") {
$aesManaged.Key = [System.Convert]::FromBase64String($key)
}
else {
$aesManaged.Key = $key
}
}
$aesManaged
}
function Decrypt-String($key, $encryptedStringWithIV) {
$bytes = [System.Convert]::FromBase64String($encryptedStringWithIV)
$IV = $bytes[0..15]
$aesManaged = Create-AesManagedObject $key $IV
$decryptor = $aesManaged.CreateDecryptor();
$unencryptedData = $decryptor.TransformFinalBlock($bytes, 16, $bytes.Length - 16);
$aesManaged.Dispose()
[System.Text.Encoding]::UTF8.GetString($unencryptedData).Trim([char]0)
}
Sample output:
PS C:\> Decrypt-String 'SuperSecretSuperSecretSuperSecre' $encryptedString
���H�'G zM۞� �i�ZtCI���H~N�GG��A�Pc��aF��`)��GS�N�2{�[.
Related: Using PowerShell to decrypt a Python encrypted String
Closing this out with the solution (Thanks #Maarten and #t.m.adam). The issue was twofold. First, the key needs to be passed to Powershell in Base64 format, and the padding needed to me moved to PKCS7. The final code is as follows:
Python Encryption:
import base64
from Crypto import Random
from Crypto.Cipher import AES
key = "SuperSecret" #Insecure and just for testing
plaintext = "Secret message please don't look"
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
def padKey(s): #Pad key to 32 bytes for AES256
return (s * (int(32/len(s))+1))[:32]
class AESCipher:
def __init__(self, key):
self.key = key
def encrypt(self, raw):
raw = pad(raw)
iv = Random.new().read( AES.block_size )
cipher = AES.new( self.key, AES.MODE_CBC, iv )
return base64.b64encode( iv + cipher.encrypt( raw ) )
paddedKey = padKey(key)
cipher = AESCipher(paddedKey)
encrypted = str(cipher.encrypt(plaintext))
encrypted = encrypted[2:-1]
print("Key:", base64.b64encode(paddedKey))
print("Plaintext:",plaintext)
print("Encrypted and B64:",encrypted)
Powershell Decryption:
function Create-AesManagedObject($key, $IV) {
$aesManaged = New-Object "System.Security.Cryptography.AesManaged"
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7
$aesManaged.BlockSize = 128
$aesManaged.KeySize = 256
if ($IV) {
if ($IV.getType().Name -eq "String") {
$aesManaged.IV = [System.Convert]::FromBase64String($IV)
}
else {
$aesManaged.IV = $IV
}
}
if ($key) {
if ($key.getType().Name -eq "String") {
$aesManaged.Key = [System.Convert]::FromBase64String($key)
}
else {
$aesManaged.Key = $key
}
}
$aesManaged
}
function Decrypt-String($key, $encryptedStringWithIV) {
$bytes = [System.Convert]::FromBase64String($encryptedStringWithIV)
$IV = $bytes[0..15]
$aesManaged = Create-AesManagedObject $key $IV
$decryptor = $aesManaged.CreateDecryptor();
$unencryptedData = $decryptor.TransformFinalBlock($bytes, 16, $bytes.Length - 16);
$aesManaged.Dispose()
[System.Text.Encoding]::UTF8.GetString($unencryptedData).Trim([char]0)
}
Usage:
PS C:> $key = 'U3VwZXJTZWNyZXRTdXBlclNlY3JldFN1cGVyU2VjcmU='
PS C:> $encryptedString = 'Opgtr8XEcvkcYT5UzsFjZR4Wt5DI++fU4Gm0dTM/22m+xyObjP162rFphIS/xkS4I7ErJfshwI7T4X1MNz
wMog=='
PS C:> Decrypt-String $key $encryptedString
Secret message please don't look
In the Python code, the following is entirely unnecessary and should be removed:
encrypted = str(cipher.encrypt(plaintext))
encrypted = encrypted[2:-1]
In your PowerShell code, you need to use PKCS7 instead of Zeros for the padding.
In the PowerShell code you don't implement the key padding with zeros anywhere. This is required (I'm not sure how you get this to work at all without it).

Different Results in Go and Pycrypto when using AES-CFB

I am adding a go application to an already existing python codebase. I've been having trouble dealing with encryption between the languages. This is using go 1.2.1 and Python 2.7.x / PyCrypto 2.7a1.
Here is the Python sample:
import Crypto.Cipher
import Crypto.Hash.HMAC
import Crypto.Hash.SHA256
import Crypto.PublicKey.RSA
from binascii import hexlify, unhexlify
#encrypt
payload = unhexlify("abababababababababababababababababababababababababababababababab")
password = unhexlify("0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF")
iv = unhexlify("00000000000000000000000000000000")
print "IV: ", hexlify(iv), "len: ", len(iv)
print "Password length: ", len(password)
cipher = Crypto.Cipher.AES.new(
key=password,
mode=Crypto.Cipher.AES.MODE_CFB,
IV=iv)
payload = cipher.encrypt(payload)
print hexlify(payload) #dbf6b1877ba903330cb9cf0c4f530d40bf77fe2bf505820e993741c7f698ad6b
And this is the Go sample:
package main
import (
"fmt"
"crypto/cipher"
"crypto/aes"
"encoding/hex"
)
// encrypt
func main() {
payload, err1 := hex.DecodeString("abababababababababababababababababababababababababababababababab")
password, err2 := hex.DecodeString("0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF")
iv, err3 := hex.DecodeString("00000000000000000000000000000000")
if err1 != nil {
fmt.Printf("error 1: %v", err1)
return
}
if err2 != nil {
fmt.Printf("error 2: %v", err2)
return
}
if err3 != nil {
fmt.Printf("error 3: %v", err3)
return
}
aesBlock, err4 := aes.NewCipher(password)
fmt.Printf("IV length:%v\n", len(iv))
fmt.Printf("password length:%v\n", len(password))
if err4 != nil {
fmt.Printf("error 4: %v", err4)
return
}
cfbDecrypter := cipher.NewCFBEncrypter(aesBlock, iv)
cfbDecrypter.XORKeyStream(payload, payload)
fmt.Printf("%v\n", hex.EncodeToString(payload)) // db70cd9e6904359cb848410bfa38d7d0a47b594f7eff72d547d3772c9d4f5dbe
}
Here is the golang link, I could not find a Python pastebin that had PyCrypto installed.
As suggested by the title & source, the two snippets produce different cyphertext:
Python: dbf6b1877ba903330cb9cf0c4f530d40bf77fe2bf505820e993741c7f698ad6b
Golang: db70cd9e6904359cb848410bfa38d7d0a47b594f7eff72d547d3772c9d4f5dbe
Both languages can decrypt their 'native' cypthertext, but neither can decrypt the others'. Because the python implementation already exists, I'm looking for a solution that will allow Go to decrypt cyphertext encrypted with the example PyCrypto AES settings & key size.
Research on the current system has revealed that our python system uses CFB8 (8 bit segments). Go does not support this out of the box, but the source code used in the current CFBDecrypter / CFBEncrypter looks like it can be adapted fairly easily.
If anyone is looking for Go implementation of CFB mode with segment size = 8 you can use this:
import "crypto/cipher"
// CFB stream with 8 bit segment size
// See http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
type cfb8 struct {
b cipher.Block
blockSize int
in []byte
out []byte
decrypt bool
}
func (x *cfb8) XORKeyStream(dst, src []byte) {
for i := range src {
x.b.Encrypt(x.out, x.in)
copy(x.in[:x.blockSize-1], x.in[1:])
if x.decrypt {
x.in[x.blockSize-1] = src[i]
}
dst[i] = src[i] ^ x.out[0]
if !x.decrypt {
x.in[x.blockSize-1] = dst[i]
}
}
}
// NewCFB8Encrypter returns a Stream which encrypts with cipher feedback mode
// (segment size = 8), using the given Block. The iv must be the same length as
// the Block's block size.
func newCFB8Encrypter(block cipher.Block, iv []byte) cipher.Stream {
return newCFB8(block, iv, false)
}
// NewCFB8Decrypter returns a Stream which decrypts with cipher feedback mode
// (segment size = 8), using the given Block. The iv must be the same length as
// the Block's block size.
func newCFB8Decrypter(block cipher.Block, iv []byte) cipher.Stream {
return newCFB8(block, iv, true)
}
func newCFB8(block cipher.Block, iv []byte, decrypt bool) cipher.Stream {
blockSize := block.BlockSize()
if len(iv) != blockSize {
// stack trace will indicate whether it was de or encryption
panic("cipher.newCFB: IV length must equal block size")
}
x := &cfb8{
b: block,
blockSize: blockSize,
out: make([]byte, blockSize),
in: make([]byte, blockSize),
decrypt: decrypt,
}
copy(x.in, iv)
return x
}
It seems that the cipher can be made compatible to Go's crypto/cipher if we change segment_size of AES object from the default 8 to AES.block_size*8 (which is 128), like this:
Crypto.Cipher.AES.new(
key=password,
mode=Crypto.Cipher.AES.MODE_CFB,
IV=iv,
segment_size=AES.block_size*8
)
I found that easiest way to deal with this from Python side is to use M2Crypto library.
Final code looks like:
import M2Crypto.EVP
iv = ciphertext[:16]
ciphertext = ciphertext[16:]
cipher = M2Crypto.EVP.Cipher('aes_256_cfb', t, iv, 0)
text = cipher.update(ciphertext)
print text
Works perfect without need to change something in Go.
i solve by adapt python code like this (golang encode and python decode):
# golang encode
padNum := len(data) % 16
if padNum != 0 {
for i := 0; i < 16-padNum; i++ {
data = append(data, ',')
}
}
# python decode
cipher = AES.new(key=self.key, mode=AES.MODE_CFB, IV=iv,segment_size=128)

Python AES encryption(PyCrypto) -> AS3 decryption (as3Crypto) using AES

I've a django app which serves encrypted media files to Flash apps. Encryption in python is done with PyCrypto as follows (I include description too in case useful):
def encrypt_aes(key, text):
try:
raw = _pad(text)
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw))
except Exception as e:
print e.message
return text
def decrypt_aes(key, text):
try:
enc = base64.b64decode(text)
iv = enc[:AES.block_size]
cipher = AES.new(key, AES.MODE_CBC, iv)
return _unpad(cipher.decrypt(enc[AES.block_size:]))
except Exception as e:
print e.message
return text
def _pad(s):
bs = 16
return s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
def _unpad(s):
return s[:-ord(s[len(s) - 1:])]
I cannot yet decrypt the Python provided media files (downloaded with LoaderMax by GreenSock, using 'DataLoader'). My AS3 code (using AS3Crypto) is as follows:
public static function decipher(_key:String):void{
key=_key;
cipher = Crypto.getCipher("simple-aes-cbc", Hex.toArray(key));
cipher.decrypt(ByteArray(_content));
}
I get
RangeError: Error #2006
One suspicion is that in Python I have 64bit base but I think that AS3 ByteArray is 32bit base. I have tried the below, but get the same Error.
cipher.decrypt(ByteArray(com.hurlant.util.Base64.decodeToByteArray(_content)));
Another suspicion is that I have not appropriately removed 'padding' from the _content / set up my IV appropriately (which is specified by the padding I must remove from the _content). This should be done via that "simple" statement however. I have been trying this, but with no success:
var pad:IPad = new PKCS5
cipher = Crypto.getCipher("simple-aes", Hex.toArray(key),pad);
pad.setBlockSize(cipher.getBlockSize());
Could anyone advise on how I can fix this ? :)
Many thanks!
OK finally figured out what was going wrong. Besides some AS3 tweaks, we wrongly were transmitting files as MP3/image (should have been text/html).
Our Python remains as above. Our AS3 is tweaked to the below.
Here's the AS3 class we used:
package com.xperiment.preloader
{
import com.greensock.loading.DataLoader;
import com.hurlant.crypto.Crypto;
import com.hurlant.crypto.symmetric.ICipher;
import com.hurlant.util.Base64;
import flash.events.Event;
import flash.utils.ByteArray;
public class EncryptedDataLoader extends DataLoader
{
private static var backlog:Vector.<EncryptedDataLoader>;
private static var cipher:ICipher;
private var decrypted:Boolean = true;
public function EncryptedDataLoader(urlOrRequest:*, vars:Object=null)
{
this.addEventListener(Event.COMPLETE,decryptL);
super(urlOrRequest, vars);
}
public function decryptL(e:Event):void {
trace("start decrypt");
e.stopImmediatePropagation();
this.removeEventListener(Event.COMPLETE,decryptL);
backlog ||= new Vector.<EncryptedDataLoader>;
backlog.push(this);
if(cipher) pingBacklog();
}
public function decipher():void
{
_content = Base64.decodeToByteArray( _content );
cipher.decrypt( _content );
decrypted=true;
this.dispatchEvent(new Event(Event.COMPLETE));
}
public static function setCipher(_key:String):void{
var keyBA:ByteArray = new ByteArray;
keyBA.writeMultiByte(_key, "iso-8859-1");
cipher = Crypto.getCipher("simple-aes", keyBA);
pingBacklog();
}
public static function kill():void{
cipher.dispose();
cipher = null;
}
public static function pingBacklog():void{
if(backlog){
var encrypted:EncryptedDataLoader;
while(backlog.length>0){
encrypted=backlog.shift();
encrypted.decipher();
}
backlog=null;
}
}
}
}

Categories