Example #1
0
// cleanLinear finds the linear error on the input and output of each middle S-box. It removes it from the S-box and
// returns it. After this function is applied, all S-boxes will be equal to the whitened standard S-box, Sbar.
func (sbl *sboxLayer) cleanLinear() (in, out encoding.ConcatenatedBlock) {
	Sbar := encoding.ComposedBytes{encoding.ByteAdditive(0x52), sbox{}}

	for pos := 0; pos < 16; pos++ {
		eqs := equivalence.FindLinear((*sbl)[pos], Sbar, 1)

		(*sbl)[pos] = encoding.ComposedBytes{eqs[0].A, (*sbl)[pos], encoding.InverseByte{eqs[0].B}}
		in[pos], out[pos] = encoding.InverseByte{eqs[0].A}, eqs[0].B
	}

	return
}
Example #2
0
// whiten puts an xor-mask on the input to each S-box so that S(0x00) = 0x00.
func (sbl *sboxLayer) whiten() (mask [16]byte) {
	for pos := 0; pos < 16; pos++ {
		m := (*sbl)[pos].Decode(0x00)

		mask[pos] = m
		(*sbl)[pos] = encoding.ComposedBytes{
			encoding.ByteAdditive(m),
			(*sbl)[pos],
		}
	}

	return
}
Example #3
0
// RecoverKey returns the AES key used to generate the given white-box construction.
func RecoverKey(constr *chow.Construction) []byte {
	round1, round2 := round{
		construction: constr,
		round:        1,
	}, round{
		construction: constr,
		round:        2,
	}

	// Decomposition Phase
	constr1 := aspn.DecomposeSPN(round1, cspn.SAS)
	constr2 := aspn.DecomposeSPN(round2, cspn.SAS)

	var (
		leading, middle, trailing sboxLayer
		left, right               = affineLayer(constr1[1].(encoding.BlockAffine)), affineLayer(constr2[1].(encoding.BlockAffine))
	)

	for pos := 0; pos < 16; pos++ {
		leading[pos] = constr1[0].(encoding.ConcatenatedBlock)[pos]
		middle[pos] = encoding.ComposedBytes{
			constr1[2].(encoding.ConcatenatedBlock)[pos],
			constr2[0].(encoding.ConcatenatedBlock)[common.ShiftRows(pos)],
		}
		trailing[pos] = constr2[2].(encoding.ConcatenatedBlock)[pos]
	}

	// Disambiguation Phase
	// Disambiguate the affine layer.
	lin, lout := left.clean()
	rin, rout := right.clean()

	leading.rightCompose(lin, common.NoShift)
	middle.leftCompose(lout, common.NoShift).rightCompose(rin, common.ShiftRows)
	trailing.leftCompose(rout, common.NoShift)

	// The SPN decomposition naturally leaves the affine layers without a constant part.
	// We would push it into the S-boxes here if that wasn't the case.

	// Move the constant off of the input and output of the S-boxes.
	mcin, mcout := middle.cleanConstant()
	mcin, mcout = left.Decode(mcin), right.Encode(mcout)

	leading.rightCompose(encoding.DecomposeConcatenatedBlock(encoding.BlockAdditive(mcin)), common.NoShift)
	trailing.leftCompose(encoding.DecomposeConcatenatedBlock(encoding.BlockAdditive(mcout)), common.NoShift)

	// Move the multiplication off of the input and output of the middle S-boxes.
	mlin, mlout := middle.cleanLinear()

	leading.rightCompose(mlin, common.NoShift)
	trailing.leftCompose(mlout, common.NoShift)

	// fmt.Println(encoding.ProbablyEquivalentBlocks(
	// 	encoding.ComposedBlocks{aspn.Encoding{round1}, ShiftRows{}, aspn.Encoding{round2}},
	// 	encoding.ComposedBlocks{leading, left, middle, ShiftRows{}, right, trailing},
	// ))
	// Output: true

	// Extract the key from the leading S-boxes.
	key := [16]byte{}

	for pos := 0; pos < 16; pos++ {
		for guess := 0; guess < 256; guess++ {
			cand := encoding.ComposedBytes{
				leading[pos], encoding.ByteAdditive(guess), encoding.InverseByte{sbox{}},
			}

			if isAS(cand) {
				key[pos] = byte(guess)
				break
			}
		}
	}

	key = left.Encode(key)

	return backOneRound(backOneRound(key[:], 2), 1)
}
Example #4
0
// RecoverKey returns the AES key used to generate the given white-box construction.
func RecoverKey(constr *xiao.Construction) []byte {
	round1 := round{
		construction: constr,
		round:        1,
	}

	// Decomposition Phase
	constr1 := aspn.DecomposeSPN(round1, cspn.ASA)

	var (
		first, last = affineLayer(constr1[0].(encoding.BlockAffine)), affineLayer(constr1[2].(encoding.BlockAffine))
		middle      = sboxLayer(constr1[1].(encoding.ConcatenatedBlock))
	)

	// Disambiguation Phase
	// The SPN decomposition naturally leaves the last affine layer without a constant part. We would push it into the
	// middle S-boxes if that wasn't the case.

	// Put the affine layers in diagonal form.
	perm := first.findPermutation()
	permEnc := encoding.NewBlockLinear(perm)

	first.rightCompose(encoding.InverseBlock{permEnc})
	middle.permuteBy(perm, false)
	last.leftCompose(permEnc)

	// Whiten the S-boxes so that they are linearly equivalent to Sbar.
	mask := middle.whiten()
	encoding.XOR(first.BlockAdditive[:], first.BlockAdditive[:], mask[:])

	// Fix the S-boxes so that they are equal to Sbar.
	in, out := middle.cleanLinear()

	first.rightCompose(in)
	last.leftCompose(out)

	// Add ShiftRows matrix to make search possible.
	last.rightCompose(encoding.NewBlockLinear(constr.ShiftRows[2]))

	// Clean off remaining noise from self-equivalences of Sbar.
	left := last.cleanLeft()
	right := encoding.ComposedBlocks{
		middle, left, encoding.InverseBlock{middle},
	}

	first.rightCompose(right)

	// Convert Sbar back to AES's "standard" S-box.
	for pos := 0; pos < 16; pos++ {
		first.BlockAdditive[pos] ^= 0x52
		middle[pos] = encoding.ComposedBytes{encoding.ByteAdditive(0x52), middle[pos]}
	}

	// fmt.Println(encoding.ProbablyEquivalentBlocks(
	//   encoding.ComposedBlocks{first, middle, last},
	//   encoding.ComposedBlocks{aspn.Encoding{round1}, encoding.NewBlockLinear(constr.ShiftRows[2])},
	// ))
	// fmt.Println(encoding.ProbablyEquivalentBlocks(
	//   aspn.Encoding{constr1},
	//   aspn.Encoding{round1},
	// ))
	//
	// Output:
	//   true
	//   true

	roundKey := shiftrows{}.Decode(first.BlockAdditive)
	return backOneRound(roundKey[:], 1)
}