Break Repeating Key XOR

See http://cryptopals.com/sets/1/challenges/6


In [ ]:
function englishiness(ptarray::Array{UInt8, 1})
EnglishFrequencies = Dict( # from http://scottbryce.com/cryptograms/stats.htm
    'e'=>12.51, 
    't'=>9.25,
    'a'=>8.04,
    'o'=>7.6,
    'i'=>7.26,
    'n'=>7.09,
    's'=>6.54,
    'r'=>6.12,
    'h'=>5.49,
    'l'=>4.14,
    'd'=>3.99,
    'c'=>3.06,
    'u'=>2.71,
    'm'=>2.53,
    'f'=>2.3,
    'p'=>2.0,
    'g'=>1.96,
    'w'=>1.92,
    'y'=>1.73,
    'b'=>1.54,
    'v'=>0.99,
    'k'=>0.67,
    'x'=>0.19,
    'j'=>0.16,
    'q'=>0.11,
    'z'=>0.09,
    )
    s=0
    for ll in ptarray
        llchar = Char(ll)
        if isalpha(llchar)
            s+=get(EnglishFrequencies, lowercase(llchar), -5)
        else
            s+=-20
        end
    end
    return s
end
function genBestSingleByteXORDecrypt(ctbytes::Array{UInt8, 1})
    bestdecrypt = []
    bestscore = -Inf
    bestkey = 0x00
    for xx in 0x00:0xff
        if isascii(Char(xx))
            ptbytes = ctbytes $ xx
            ptscore = englishiness(ptbytes)
            if ptscore > bestscore
                bestscore = ptscore
                bestdecrypt = ptbytes
                bestkey = xx
            end
        end
    end
    return (bestscore, bestdecrypt, bestkey)
end;

In [ ]:
function stringToBytes(X::String)
    return [UInt8(X[ii]) for ii in 1:length(X)]
end;

In [ ]:
function hammingDistance(a::Array{UInt8,1}, b::Array{UInt8,1})
    differentbits = a $ b
    d = 0
    for ii in 1:length(differentbits)
        c = bits(differentbits[ii])
        for jj in 1:8
            if c[jj] == '1'
                d+=1
            end
        end
    end
    return d
end

function hammingDistance(a::String, b::String)
    return hammingDistance(stringToBytes(a), stringToBytes(b))
end;

In [ ]:
function repeatToLength(a::Array{UInt8,1}, len::Int64)
    if len < length(a)
        return a[1:len]
    else
        b = repeat(a, outer=convert(Int64, ceil(len / length(a))))
        return b[1:len]
    end
end
function repeatingKeyXOR(pt::Array{UInt8,1}, key::Array{UInt8,1})
    return pt $ repeatToLength(key, length(pt))
end;

In [ ]:
a = "this is a test"
b = "wokka wokka!!!";

In [ ]:
abytes = stringToBytes(a)
bbytes = stringToBytes(b);

In [ ]:
hammingDistance(abytes, bbytes)

In [ ]:
hammingDistance(a,b)

In [ ]:
cipher2 = read("6.txt");

In [ ]:
cipherbytes = base64decode(String(cipher2));

In [ ]:
dists = []
for kk in 1:40
    d = 0
    nblocks = 10
    for bb in 1:nblocks
        d += hammingDistance(cipherbytes[(1+(kk*(bb-1))):(kk*bb)], cipherbytes[(kk*bb+1):(kk*(bb+1))])
    end
    d = d / nblocks / kk
    push!(dists, (kk, d))
end;

In [ ]:
sort!(dists,lt=(x,y)->x[2]<y[2])

In [ ]:
keysize = dists[1][1]
nbytes = length(cipherbytes)
decryptkey = Array{UInt8,1}()
for ii in 1:keysize
    block = cipherbytes[ii:keysize:nbytes]
    (score, ptbytes, key) = genBestSingleByteXORDecrypt(block);
    push!(decryptkey, key)
end
fullptbytes = repeatingKeyXOR(cipherbytes, decryptkey);

In [ ]:
String(decryptkey)

In [ ]:
String(fullptbytes)