In [ ]:
function decryptAES128ECBWithKey(ciphertext::String, key::String)
decrypt = chomp(
readstring(
pipeline(`echo -n $ciphertext`,
`base64 --decode`,
`openssl enc -nopad -d -aes-128-ecb -K $key`,
`base64`)))
return decrypt
end
# takes bytes and returns bytes
function decryptAES128ECBWithKey(cipherbytes::Array{UInt8,1}, keybytes::Array{UInt8,1})
ciphertext = base64encode(cipherbytes)
keytext = bytes2hex(keybytes)
return base64decode(decryptAES128ECBWithKey(ciphertext, keytext))
end
function encryptAES128ECBWithKey(plaintext::String, key::String)
encrypt = chomp(
readstring(
pipeline(`echo -n $plaintext`,
`base64 --decode`,
`openssl enc -nopad -e -aes-128-ecb -K $key`,
`base64`)))
return encrypt
end
function encryptAES128ECBWithKey(plaintextbytes::Array{UInt8,1}, keybytes::Array{UInt8,1})
plaintext = base64encode(plaintextbytes)
keytext = bytes2hex(keybytes)
return base64decode(encryptAES128ECBWithKey(plaintext, keytext))
end
function encryptCBCByHand(ptbytes::Array{UInt8,1}, blocksize, ivbytes::Array{UInt8,1}, keybytes::Array{UInt8,1})
cipherbytes = Array{UInt8,1}()
nblocks = convert(Int64, ceil(length(ptbytes)/blocksize))
ptbytes = padToNBytes(ptbytes, blocksize*nblocks)
lastblock = ivbytes
for ii in 1:nblocks
bytestoencrypt = ptbytes[((ii-1)*blocksize+1):(ii*blocksize)] $ lastblock
encryptedbytes = encryptAES128ECBWithKey(bytestoencrypt, keybytes)
append!(cipherbytes, encryptedbytes)
lastblock = encryptedbytes
end
return cipherbytes
end;
function decryptCBCByHand(ctbytes::Array{UInt8,1}, blocksize, ivbytes::Array{UInt8,1}, keybytes::Array{UInt8,1})
ptbytes = Array{UInt8,1}()
nblocks = convert(Int64, length(ctbytes)/blocksize)
lastblock = ivbytes
for ii in 1:nblocks
bytestodecrypt = ctbytes[((ii-1)*blocksize+1):(ii*blocksize)]
decryptedbytes = decryptAES128ECBWithKey(bytestodecrypt, keybytes) $ lastblock
append!(ptbytes, decryptedbytes)
lastblock = bytestodecrypt
end
return ptbytes
end;
In [ ]:
function padToNBytes(a::Array{UInt8,1}, nbytes)
newa = Array{UInt8,1}(nbytes)
ntopad = nbytes - length(a)
newa[1:length(a)] = a
newa[(length(a)+1):nbytes] = UInt8(ntopad)
return newa
end
function padToNBytes(a::String, nbytes)
return String(padToNBytes(stringToBytes(a), nbytes))
end;
In [ ]:
function stringToBytes(X::String)
return [UInt8(X[ii]) for ii in 1:length(X)]
end;
In [ ]:
function encryptionOracle(ptbytes::Array{UInt8, 1})
blocksize=16
keybytes = rand(0x00:0xff, blocksize)
ptbytespadded = rand(0x00:0xff, rand(5:10))
append!(ptbytespadded, ptbytes)
append!(ptbytespadded, rand(0x00:0xff, rand(5:10)))
nblocks = convert(Int64, ceil(length(ptbytespadded)/blocksize))
ptbytespadded = padToNBytes(ptbytespadded, nblocks*blocksize)
if rand() < 0.5
println("ecb")
return encryptAES128ECBWithKey(ptbytespadded, keybytes)
else
println("cbc")
iv = rand(0x00:0xff, blocksize)
return encryptCBCByHand(ptbytespadded, blocksize, iv, keybytes)
end
end;
Given a thing that is either encrypting with ECB or CBC, we can figure out which its doing by probing it with a repeating block of plaintext which will, under ECB, even with the random padding, still result in some of the encrypted blocks being identical.
In [ ]:
probe = repeat("abcdefghijklmnop", 32)
In [ ]:
ctbytes = encryptionOracle(stringToBytes(probe));
In [ ]:
function isThisECBOrCBC(ctbytes::Array{UInt8,1}, blocksize=16)
nblocks = convert(Int64, length(ctbytes)/blocksize)
interblocksimilarity = []
for ii in 1:(nblocks-1)
for jj in (ii+1):nblocks
blockii = ctbytes[((ii-1)*blocksize+1):(ii*blocksize)]
blockjj = ctbytes[((jj-1)*blocksize+1):(jj*blocksize)]
samebits = ~(blockii $ blockjj)
similarity = 0
for cc in samebits
ccbits = bits(cc)
for dd in 1:length(ccbits)
if ccbits[dd]=='1'
similarity+=1
end
end
end
push!(interblocksimilarity, similarity/(8*blocksize))
end
end
pexactsame = sum(interblocksimilarity.==1)/length(interblocksimilarity)
cipher = "cbc"
if pexactsame > 0.5
cipher = "ecb"
end
return (cipher, pexactsame, interblocksimilarity)
end;
In [ ]:
(ciphertype, pexactsame, interblocksimilarity) = isThisECBOrCBC(ctbytes)
In [ ]:
using Gadfly;
using DataFrames;
In [ ]:
plot(DataFrame(similarity=interblocksimilarity), x="similarity", Geom.histogram)
Under CBC the interblock similarities will be distributed around 0.5. Under ECB, there will be a large fraction of pairs with similarity=1.0
In [ ]:
ctbytes = encryptionOracle(stringToBytes(probe));
In [ ]:
(ciphertype, pexactsame, interblocksimilarity) = isThisECBOrCBC(ctbytes)
In [ ]:
plot(DataFrame(similarity=interblocksimilarity), x="similarity", Geom.histogram)