In [ ]:
include("cryptofuncs.jl");
In [ ]:
suffix = """Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK"""
In [ ]:
suffixbytes = base64decode(suffix);
In [ ]:
function ECBEncryptor(ptbytes::Array{UInt8, 1}, keybytes::Array{UInt8,1}=rand(0x00:0xff, 16))
blocksize = length(keybytes)
nblocks = convert(Int64, ceil(length(ptbytes)/blocksize))
ptbytespadded = padToNBytes(ptbytes, nblocks*blocksize)
return encryptAES128ECBWithKey(ptbytespadded, keybytes)
end;
In [ ]:
blocksize=16
randomkey = rand(0x00:0xff, blocksize)
function encryptionOracle(ptbytes::Array{UInt8,1})
newptbytes = copy(ptbytes)
append!(newptbytes, suffixbytes)
return ECBEncryptor(newptbytes, randomkey)
end;
First, learn the blocksize.
In [ ]:
pt = stringToBytes("A")
ctlength = length(encryptionOracle(pt))
newctlength = ctlength
while newctlength == ctlength
append!(pt, pt[1])
newctlength = length(encryptionOracle(pt))
end
blocksize = newctlength - ctlength
Next, identify the cipher type.
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 [ ]:
probe = repeat("abcdefghijklmnop", 32)
ctbytes = encryptionOracle(stringToBytes(probe))
isThisECBOrCBC(ctbytes)
Because the oracle is appending some unknown ciphertext to the end of its input, the $pexactsame$ threshold might have needed to be adjusted, but there are still too many identical blocks for this to be CBC.
In [ ]:
probebytes = stringToBytes(repeat("A", blocksize))
for ii in 1:blocksize
targetctbytes = encryptionOracle(probebytes[1:(blocksize-ii)])
for xx in 0x00:0xff
if isascii(Char(xx))
probebytes[blocksize] = xx
ctcandidate = encryptionOracle(probebytes) # the first block of this should match targetctbytes when xx is right
if ctcandidate[1:blocksize] == targetctbytes[1:blocksize]
println("found position $ii")
if ii < blocksize
for jj in 1:(length(probebytes)-1)
probebytes[jj] = probebytes[jj+1] # shift the bytes back so we can crack the next position
end
end
break
end
end
end
end
In [ ]:
String(probebytes)
So now we've cracked the first block. Since this is ECB maybe now we can crack the key and be done, but I don't know how to do that. Or we can pump out the second block so that the byte we're learning is the last byte of the second block, and so on.
In [ ]:
nblocks = convert(Int64, length(encryptionOracle(stringToBytes("")))/blocksize)
decrypt = Array{UInt8,1}()
blockpt = stringToBytes(repeat("A", blocksize)) # set the initial blockpt to be repeating As
for kk in 1:nblocks
for ii in 1:blocksize
targetctbytes = encryptionOracle(stringToBytes(repeat("A", blocksize-ii))) # this is wasteful. sorry.
for xx in 0x00:0xff
if isascii(Char(xx))
blockpt[blocksize] = xx
ctcandidate = encryptionOracle(blockpt)
if ctcandidate[1:blocksize] == targetctbytes[((kk-1)*blocksize+1):(kk*blocksize)]
println("found position $ii in block $kk") # TODO: fail if we fail to get here for any xx
append!(decrypt, xx)
for jj in 1:(length(probebytes)-1)
blockpt[jj] = blockpt[jj+1] # shift the bytes back so we can crack the next position
end
break
end
end
end
end
end
In [ ]:
String(decrypt)
Beginning to sense a theme here.