cryptopals/set1/challenge_6.go
2024-11-02 23:37:12 +02:00

170 lines
3.2 KiB
Go

package set1
import (
"os"
"slices"
)
// 00aaaaaa 00aabbbb 00bbbbcc 00cccccc
// aaaaaaaa bbbbbbbb cccccccc
func Base64Decode(block []byte) (decoded []byte) {
base64Mapping := map[byte]byte{}
for i, b := range Base64Alphabet {
base64Mapping[byte(b)] = byte(i)
}
decoded = []byte{}
for i := 0; i < len(block); i += 4 {
decodedBytes := []byte{0b00000000, 0b00000000, 0b00000000}
encodedBytes := block[i : i+4]
for i, b := range encodedBytes {
if b == Base64PaddingCharacter {
encodedBytes[i] = 0b00000000
} else {
encodedBytes[i] = base64Mapping[b]
}
}
decodedBytes[0] = (encodedBytes[0] << 2) | (encodedBytes[1] >> 4)
decodedBytes[1] = (encodedBytes[1] << 4) | (encodedBytes[2] >> 2)
decodedBytes[2] = (encodedBytes[2] << 6) | encodedBytes[3]
for _, b := range decodedBytes {
if b != 0b00000000 {
decoded = append(decoded, b)
}
}
}
return decoded
}
func BruteForceXORSingleCharacterPlainEncodedBlock(block []byte) (score int, key byte, result []byte) {
for i := 0; i < 256; i++ {
decoded := XORDecodePlain(block, byte(i))
decodedScore := ScoreTextByAlphabeticFrequency(decoded)
if decodedScore > score {
score = decodedScore
key = byte(i)
result = decoded
}
}
return
}
func HammingDistance(str1, str2 []byte) (distance int) {
for i := 0; i < len(str1); i++ {
if i >= len(str2) {
distance += CountBits(str1[i])
} else {
distance += CountBits(str1[i] ^ str2[i])
}
}
return
}
// 1111
// 1111 -1 = 1110
// 1111 & 1110 = 1110
// 1110 -1 = 1101
// 1110 & 1101 = 1100
// 1100 -1 = 1011
// 1100 & 1011 = 1000
// 1000 -1 = 0111
// 0111 & 1000 = 0000
// ------------------
// 1010
// 1010 -1 = 1001
// 1010 & 1001 = 1000
// 1000 -1 = 0111
// 1000 & 0111 = 0000
func CountBits(x byte) (count int) {
// This does as many iterations as there are 1 bits in a bitset. Magic.
for x != 0 {
x &= x - 1
count++
}
return
}
func ReadChallenge6CipherFromFile() (cipher []byte) {
file, err := os.ReadFile("./res/set-1-challenge-6.txt")
if err != nil {
panic(err)
}
return file
}
func NormalizedHammingDistance(strings ...[]byte) (normalizedDistance float32) {
for i := 1; i < len(strings); i++ {
normalizedDistance += float32(HammingDistance(strings[i-1], strings[i]))
}
normalizedDistance = normalizedDistance / float32(len(strings[0])) / float32(len(strings))
return
}
type KeySizeRank struct {
Size int
Rank float32
}
func RankKeySizeCandidates(minSize, maxSize int, cipher []byte) (keySizeCandidates []KeySizeRank) {
keySizes := []KeySizeRank{}
for keySize := minSize; keySize <= maxSize; keySize++ {
keySizes = append(keySizes, KeySizeRank{
Size: keySize,
Rank: NormalizedHammingDistance(
cipher[0:keySize],
cipher[keySize:keySize*2],
),
})
}
slices.SortFunc(keySizes, func(ksr1 KeySizeRank, ksr2 KeySizeRank) int {
if ksr1.Rank > ksr2.Rank {
return 1
} else if ksr1.Rank == ksr2.Rank {
return 0
} else {
return -1
}
})
return keySizes
}
func XORDecrypt(key []byte, cipher []byte) (result []byte) {
for i := 0; i < len(cipher); i += len(key) {
cipherBlock := cipher[i : i+len(key)]
for ib, b := range cipherBlock {
result = append(result, b^key[ib])
}
}
return
}