summaryrefslogtreecommitdiffstats
path: root/challenge
diff options
context:
space:
mode:
Diffstat (limited to 'challenge')
-rw-r--r--challenge/c14.go128
1 files changed, 128 insertions, 0 deletions
diff --git a/challenge/c14.go b/challenge/c14.go
new file mode 100644
index 0000000..9ed4e5f
--- /dev/null
+++ b/challenge/c14.go
@@ -0,0 +1,128 @@
+// Copyright © 2021 rsiddharth <s@ricketyspace.net>
+// SPDX-License-Identifier: ISC
+
+package challenge
+
+import (
+ "fmt"
+ "ricketyspace.net/cryptopals/lib"
+)
+
+func C14() {
+ blocksize := findBlockSizeForVarEncryptECB()
+ rpl, nrpb, rpo := findRandPrefixLen(blocksize)
+ nbl, us_sz := findUnknownStringNumBlocksLength(
+ rpl, blocksize)
+ in := append(freshSheepBlock(rpo), // random prefix offset
+ freshSheepBlock(blocksize)...)
+ ds := make([]byte, 0) // deciphered unknown string in bytes
+ for i := 0; i < nbl; i++ {
+ nby := blocksize
+ if i == nbl-1 {
+ nby = us_sz % blocksize
+ }
+ for j := 0; j < nby; j++ {
+ in, ds = decipherOneByteUS(nrpb, rpo, blocksize,
+ i+1, j+1, in, ds)
+ }
+ s := 16 * i
+ e := s + 16
+ in = append(freshSheepBlock(rpo), ds[s:e]...)
+ }
+ fmt.Printf("Unknown String:\n%v", lib.BytesToStr(ds))
+}
+
+func findBlockSizeForVarEncryptECB() int {
+ in := make([]byte, 0)
+
+ in = append(in, sheep)
+ is := len(lib.OracleAESVarEncryptECB(in)) // initial size
+ bs := 0
+ for {
+ in = append(in, sheep)
+ bs = len(lib.OracleAESVarEncryptECB(in))
+ if bs != is {
+ return (bs - is)
+ }
+ }
+}
+
+func findRandPrefixLen(blocksize int) (int, int, int) {
+ // Make two sheep blocks.
+ tsb := append(freshSheepBlock(blocksize), freshSheepBlock(blocksize)...)
+
+ v := []byte{}
+ index := 0
+ found := false
+ for {
+ in := append(v, tsb...)
+ c := lib.OracleAESVarEncryptECB(in)
+ index, found = lib.HasConsecutiveMatchingBlocks(c, blocksize)
+ if found {
+ break
+ }
+ // Add another sheep
+ v = append(v, sheep)
+ }
+ l := index - len(v)
+ nrpb := l / blocksize // number of randbox prefix blocks
+ rpo := 0 // random prefix offset
+ if l%blocksize != 0 {
+ nrpb += 1
+ rpo = blocksize - (l % blocksize)
+ }
+ return l, nrpb, rpo
+}
+
+// Finds the cipher block size and the length of the unknown string
+// (target-bytes).
+func findUnknownStringNumBlocksLength(rpl, blocksize int) (int, int) {
+ padding := blocksize - (rpl % blocksize)
+ in := make([]byte, padding)
+ c_sz := len(lib.OracleAESVarEncryptECB(in)) // Cipher size
+
+ nblocks := c_sz / blocksize // total number of blocks
+ rblocks := (rpl + padding) / blocksize // number of blocks of random prefix
+ ublocks := nblocks - rblocks // number of blocks of unknown string
+ // Figure out size of unknown string.
+ for {
+ in = append(in, sheep)
+ bs := len(lib.OracleAESVarEncryptECB(in))
+ if bs != c_sz {
+ return ublocks, (c_sz - len(in) - rpl)
+ }
+ }
+}
+
+// `nrpb` number of random prefix blocks
+// `rpo` random prefix offset
+// `blocksize` is the size of a block
+// `block` is the nth block that is being deciphered
+// `n` is the nth byte of the block `block` that is going to be deciphered.
+// `in` (n-1)th block that is known
+// `ds` deciphered unknown string
+func decipherOneByteUS(nrpb, rpo, blocksize, block, n int, in, ds []byte) ([]byte, []byte) {
+ oo := lib.OracleAESVarEncryptECB(in[0:(len(in) - n)])
+
+ s := (nrpb * blocksize) + 16*(block-1)
+ e := s + 16
+ nbl := oo[s:e] // nth block of the cipher
+
+ // Shift `in` to the left by one place.
+ for i := rpo; i < len(in)-1; i++ {
+ in[i] = in[i+1]
+ }
+
+ // Try all combinations.
+ for i := 0; i < 256; i++ {
+ in[len(in)-1] = byte(i)
+ oo = lib.OracleAESVarEncryptECB(in)
+
+ if lib.BlocksEqual(nbl,
+ oo[(nrpb*blocksize):(nrpb*blocksize)+16]) {
+ ds = append(ds, in[len(in)-1])
+ return in, ds
+ }
+ }
+ panic("not found!")
+}