forked from toolshed/abra
		
	
		
			
				
	
	
		
			269 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2009 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| // Originally from: https://github.com/go/blob/master/src/crypto/sha1/sha1block.go
 | |
| // It has been modified to support collision detection.
 | |
| 
 | |
| package sha1cd
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"math/bits"
 | |
| 
 | |
| 	shared "github.com/pjbgf/sha1cd/internal"
 | |
| 	"github.com/pjbgf/sha1cd/ubc"
 | |
| )
 | |
| 
 | |
| // blockGeneric is a portable, pure Go version of the SHA-1 block step.
 | |
| // It's used by sha1block_generic.go and tests.
 | |
| func blockGeneric(dig *digest, p []byte) {
 | |
| 	var w [16]uint32
 | |
| 
 | |
| 	// cs stores the pre-step compression state for only the steps required for the
 | |
| 	// collision detection, which are 0, 58 and 65.
 | |
| 	// Refer to ubc/const.go for more details.
 | |
| 	cs := [shared.PreStepState][shared.WordBuffers]uint32{}
 | |
| 
 | |
| 	h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4]
 | |
| 	for len(p) >= shared.Chunk {
 | |
| 		m1 := [shared.Rounds]uint32{}
 | |
| 		hi := 1
 | |
| 
 | |
| 		// Collision attacks are thwarted by hashing a detected near-collision block 3 times.
 | |
| 		// Think of it as extending SHA-1 from 80-steps to 240-steps for such blocks:
 | |
| 		// 		The best collision attacks against SHA-1 have complexity about 2^60,
 | |
| 		// 		thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would be 2^180.
 | |
| 		// 		An attacker would be better off using a generic birthday search of complexity 2^80.
 | |
| 	rehash:
 | |
| 		a, b, c, d, e := h0, h1, h2, h3, h4
 | |
| 
 | |
| 		// Each of the four 20-iteration rounds
 | |
| 		// differs only in the computation of f and
 | |
| 		// the choice of K (K0, K1, etc).
 | |
| 		i := 0
 | |
| 
 | |
| 		// Store pre-step compression state for the collision detection.
 | |
| 		cs[0] = [shared.WordBuffers]uint32{a, b, c, d, e}
 | |
| 
 | |
| 		for ; i < 16; i++ {
 | |
| 			// load step
 | |
| 			j := i * 4
 | |
| 			w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3])
 | |
| 
 | |
| 			f := b&c | (^b)&d
 | |
| 			t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K0
 | |
| 			a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
 | |
| 
 | |
| 			// Store compression state for the collision detection.
 | |
| 			m1[i] = w[i&0xf]
 | |
| 		}
 | |
| 		for ; i < 20; i++ {
 | |
| 			tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
 | |
| 			w[i&0xf] = tmp<<1 | tmp>>(32-1)
 | |
| 
 | |
| 			f := b&c | (^b)&d
 | |
| 			t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K0
 | |
| 			a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
 | |
| 
 | |
| 			// Store compression state for the collision detection.
 | |
| 			m1[i] = w[i&0xf]
 | |
| 		}
 | |
| 		for ; i < 40; i++ {
 | |
| 			tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
 | |
| 			w[i&0xf] = tmp<<1 | tmp>>(32-1)
 | |
| 
 | |
| 			f := b ^ c ^ d
 | |
| 			t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K1
 | |
| 			a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
 | |
| 
 | |
| 			// Store compression state for the collision detection.
 | |
| 			m1[i] = w[i&0xf]
 | |
| 		}
 | |
| 		for ; i < 60; i++ {
 | |
| 			if i == 58 {
 | |
| 				// Store pre-step compression state for the collision detection.
 | |
| 				cs[1] = [shared.WordBuffers]uint32{a, b, c, d, e}
 | |
| 			}
 | |
| 
 | |
| 			tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
 | |
| 			w[i&0xf] = tmp<<1 | tmp>>(32-1)
 | |
| 
 | |
| 			f := ((b | c) & d) | (b & c)
 | |
| 			t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K2
 | |
| 			a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
 | |
| 
 | |
| 			// Store compression state for the collision detection.
 | |
| 			m1[i] = w[i&0xf]
 | |
| 		}
 | |
| 		for ; i < 80; i++ {
 | |
| 			if i == 65 {
 | |
| 				// Store pre-step compression state for the collision detection.
 | |
| 				cs[2] = [shared.WordBuffers]uint32{a, b, c, d, e}
 | |
| 			}
 | |
| 
 | |
| 			tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
 | |
| 			w[i&0xf] = tmp<<1 | tmp>>(32-1)
 | |
| 
 | |
| 			f := b ^ c ^ d
 | |
| 			t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + shared.K3
 | |
| 			a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
 | |
| 
 | |
| 			// Store compression state for the collision detection.
 | |
| 			m1[i] = w[i&0xf]
 | |
| 		}
 | |
| 
 | |
| 		h0 += a
 | |
| 		h1 += b
 | |
| 		h2 += c
 | |
| 		h3 += d
 | |
| 		h4 += e
 | |
| 
 | |
| 		if hi == 2 {
 | |
| 			hi++
 | |
| 			goto rehash
 | |
| 		}
 | |
| 
 | |
| 		if hi == 1 {
 | |
| 			col := checkCollision(m1, cs, [shared.WordBuffers]uint32{h0, h1, h2, h3, h4})
 | |
| 			if col {
 | |
| 				dig.col = true
 | |
| 				hi++
 | |
| 				goto rehash
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		p = p[shared.Chunk:]
 | |
| 	}
 | |
| 
 | |
| 	dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] = h0, h1, h2, h3, h4
 | |
| }
 | |
| 
 | |
| func checkCollision(
 | |
| 	m1 [shared.Rounds]uint32,
 | |
| 	cs [shared.PreStepState][shared.WordBuffers]uint32,
 | |
| 	state [shared.WordBuffers]uint32) bool {
 | |
| 
 | |
| 	if mask := ubc.CalculateDvMask(m1); mask != 0 {
 | |
| 		dvs := ubc.SHA1_dvs()
 | |
| 
 | |
| 		for i := 0; dvs[i].DvType != 0; i++ {
 | |
| 			if (mask & ((uint32)(1) << uint32(dvs[i].MaskB))) != 0 {
 | |
| 				var csState [shared.WordBuffers]uint32
 | |
| 				switch dvs[i].TestT {
 | |
| 				case 58:
 | |
| 					csState = cs[1]
 | |
| 				case 65:
 | |
| 					csState = cs[2]
 | |
| 				case 0:
 | |
| 					csState = cs[0]
 | |
| 				default:
 | |
| 					panic(fmt.Sprintf("dvs data is trying to use a testT that isn't available: %d", dvs[i].TestT))
 | |
| 				}
 | |
| 
 | |
| 				col := hasCollided(
 | |
| 					dvs[i].TestT, // testT is the step number
 | |
| 					// m2 is a secondary message created XORing with
 | |
| 					// ubc's DM prior to the SHA recompression step.
 | |
| 					m1, dvs[i].Dm,
 | |
| 					csState,
 | |
| 					state)
 | |
| 
 | |
| 				if col {
 | |
| 					return true
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func hasCollided(step uint32, m1, dm [shared.Rounds]uint32,
 | |
| 	state [shared.WordBuffers]uint32, h [shared.WordBuffers]uint32) bool {
 | |
| 	// Intermediary Hash Value.
 | |
| 	ihv := [shared.WordBuffers]uint32{}
 | |
| 
 | |
| 	a, b, c, d, e := state[0], state[1], state[2], state[3], state[4]
 | |
| 
 | |
| 	// Walk backwards from current step to undo previous compression.
 | |
| 	// The existing collision detection does not have dvs higher than 65,
 | |
| 	// start value of i accordingly.
 | |
| 	for i := uint32(64); i >= 60; i-- {
 | |
| 		a, b, c, d, e = b, c, d, e, a
 | |
| 		if step > i {
 | |
| 			b = bits.RotateLeft32(b, -30)
 | |
| 			f := b ^ c ^ d
 | |
| 			e -= bits.RotateLeft32(a, 5) + f + shared.K3 + (m1[i] ^ dm[i]) // m2 = m1 ^ dm.
 | |
| 		}
 | |
| 	}
 | |
| 	for i := uint32(59); i >= 40; i-- {
 | |
| 		a, b, c, d, e = b, c, d, e, a
 | |
| 		if step > i {
 | |
| 			b = bits.RotateLeft32(b, -30)
 | |
| 			f := ((b | c) & d) | (b & c)
 | |
| 			e -= bits.RotateLeft32(a, 5) + f + shared.K2 + (m1[i] ^ dm[i])
 | |
| 		}
 | |
| 	}
 | |
| 	for i := uint32(39); i >= 20; i-- {
 | |
| 		a, b, c, d, e = b, c, d, e, a
 | |
| 		if step > i {
 | |
| 			b = bits.RotateLeft32(b, -30)
 | |
| 			f := b ^ c ^ d
 | |
| 			e -= bits.RotateLeft32(a, 5) + f + shared.K1 + (m1[i] ^ dm[i])
 | |
| 		}
 | |
| 	}
 | |
| 	for i := uint32(20); i > 0; i-- {
 | |
| 		j := i - 1
 | |
| 		a, b, c, d, e = b, c, d, e, a
 | |
| 		if step > j {
 | |
| 			b = bits.RotateLeft32(b, -30) // undo the rotate left
 | |
| 			f := b&c | (^b)&d
 | |
| 			// subtract from e
 | |
| 			e -= bits.RotateLeft32(a, 5) + f + shared.K0 + (m1[j] ^ dm[j])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	ihv[0] = a
 | |
| 	ihv[1] = b
 | |
| 	ihv[2] = c
 | |
| 	ihv[3] = d
 | |
| 	ihv[4] = e
 | |
| 	a = state[0]
 | |
| 	b = state[1]
 | |
| 	c = state[2]
 | |
| 	d = state[3]
 | |
| 	e = state[4]
 | |
| 
 | |
| 	// Recompress blocks based on the current step.
 | |
| 	// The existing collision detection does not have dvs below 58, so they have been removed
 | |
| 	// from the source code. If new dvs are added which target rounds below 40, that logic
 | |
| 	// will need to be readded here.
 | |
| 	for i := uint32(40); i < 60; i++ {
 | |
| 		if step <= i {
 | |
| 			f := ((b | c) & d) | (b & c)
 | |
| 			t := bits.RotateLeft32(a, 5) + f + e + shared.K2 + (m1[i] ^ dm[i])
 | |
| 			a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
 | |
| 		}
 | |
| 	}
 | |
| 	for i := uint32(60); i < 80; i++ {
 | |
| 		if step <= i {
 | |
| 			f := b ^ c ^ d
 | |
| 			t := bits.RotateLeft32(a, 5) + f + e + shared.K3 + (m1[i] ^ dm[i])
 | |
| 			a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	ihv[0] += a
 | |
| 	ihv[1] += b
 | |
| 	ihv[2] += c
 | |
| 	ihv[3] += d
 | |
| 	ihv[4] += e
 | |
| 
 | |
| 	if ((ihv[0] ^ h[0]) | (ihv[1] ^ h[1]) |
 | |
| 		(ihv[2] ^ h[2]) | (ihv[3] ^ h[3]) | (ihv[4] ^ h[4])) == 0 {
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	return false
 | |
| }
 |