1// Copyright 2009 The Go Authors. All rights reserved.
  2// Use of this source code is governed by a BSD-style
  3// license that can be found in the LICENSE file.
  4
  5// Package sha1cd implements collision detection based on the whitepaper
  6// Counter-cryptanalysis from Marc Stevens. The original ubc implementation
  7// was done by Marc Stevens and Dan Shumow, and can be found at:
  8// https://github.com/cr-marcstevens/sha1collisiondetection
  9package sha1cd
 10
 11// This SHA1 implementation is based on Go's generic SHA1.
 12// Original: https://github.com/golang/go/blob/master/src/crypto/sha1/sha1.go
 13
 14import (
 15	"encoding/binary"
 16	"errors"
 17	"hash"
 18
 19	shared "github.com/pjbgf/sha1cd/internal"
 20)
 21
 22// The size of a SHA-1 checksum in bytes.
 23const Size = shared.Size
 24
 25// The blocksize of SHA-1 in bytes.
 26const BlockSize = shared.Chunk
 27
 28// digest represents the partial evaluation of a checksum.
 29type digest struct {
 30	h   [shared.WordBuffers]uint32
 31	x   [shared.Chunk]byte
 32	nx  int
 33	len uint64
 34
 35	// col defines whether a collision has been found.
 36	col bool
 37}
 38
 39func (d *digest) MarshalBinary() ([]byte, error) {
 40	b := make([]byte, 0, shared.MarshaledSize)
 41	b = append(b, shared.Magic...)
 42	b = appendUint32(b, d.h[0])
 43	b = appendUint32(b, d.h[1])
 44	b = appendUint32(b, d.h[2])
 45	b = appendUint32(b, d.h[3])
 46	b = appendUint32(b, d.h[4])
 47	b = append(b, d.x[:d.nx]...)
 48	b = b[:len(b)+len(d.x)-d.nx] // already zero
 49	b = appendUint64(b, d.len)
 50	return b, nil
 51}
 52
 53func appendUint32(b []byte, v uint32) []byte {
 54	return append(b,
 55		byte(v>>24),
 56		byte(v>>16),
 57		byte(v>>8),
 58		byte(v),
 59	)
 60}
 61
 62func appendUint64(b []byte, v uint64) []byte {
 63	return append(b,
 64		byte(v>>56),
 65		byte(v>>48),
 66		byte(v>>40),
 67		byte(v>>32),
 68		byte(v>>24),
 69		byte(v>>16),
 70		byte(v>>8),
 71		byte(v),
 72	)
 73}
 74
 75func (d *digest) UnmarshalBinary(b []byte) error {
 76	if len(b) < len(shared.Magic) || string(b[:len(shared.Magic)]) != shared.Magic {
 77		return errors.New("crypto/sha1: invalid hash state identifier")
 78	}
 79	if len(b) != shared.MarshaledSize {
 80		return errors.New("crypto/sha1: invalid hash state size")
 81	}
 82	b = b[len(shared.Magic):]
 83	b, d.h[0] = consumeUint32(b)
 84	b, d.h[1] = consumeUint32(b)
 85	b, d.h[2] = consumeUint32(b)
 86	b, d.h[3] = consumeUint32(b)
 87	b, d.h[4] = consumeUint32(b)
 88	b = b[copy(d.x[:], b):]
 89	b, d.len = consumeUint64(b)
 90	d.nx = int(d.len % shared.Chunk)
 91	return nil
 92}
 93
 94func consumeUint64(b []byte) ([]byte, uint64) {
 95	_ = b[7]
 96	x := uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
 97		uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
 98	return b[8:], x
 99}
100
101func consumeUint32(b []byte) ([]byte, uint32) {
102	_ = b[3]
103	x := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
104	return b[4:], x
105}
106
107func (d *digest) Reset() {
108	d.h[0] = shared.Init0
109	d.h[1] = shared.Init1
110	d.h[2] = shared.Init2
111	d.h[3] = shared.Init3
112	d.h[4] = shared.Init4
113	d.nx = 0
114	d.len = 0
115
116	d.col = false
117}
118
119// New returns a new hash.Hash computing the SHA1 checksum. The Hash also
120// implements encoding.BinaryMarshaler and encoding.BinaryUnmarshaler to
121// marshal and unmarshal the internal state of the hash.
122func New() hash.Hash {
123	var d digest
124	d.Reset()
125	return &d
126}
127
128func (d *digest) Size() int { return Size }
129
130func (d *digest) BlockSize() int { return BlockSize }
131
132func (d *digest) Write(p []byte) (nn int, err error) {
133	if len(p) == 0 {
134		return
135	}
136
137	nn = len(p)
138	d.len += uint64(nn)
139	if d.nx > 0 {
140		n := copy(d.x[d.nx:], p)
141		d.nx += n
142		if d.nx == shared.Chunk {
143			block(d, d.x[:])
144			d.nx = 0
145		}
146		p = p[n:]
147	}
148	if len(p) >= shared.Chunk {
149		n := len(p) &^ (shared.Chunk - 1)
150		block(d, p[:n])
151		p = p[n:]
152	}
153	if len(p) > 0 {
154		d.nx = copy(d.x[:], p)
155	}
156	return
157}
158
159func (d *digest) Sum(in []byte) []byte {
160	// Make a copy of d so that caller can keep writing and summing.
161	d0 := *d
162	hash := d0.checkSum()
163	return append(in, hash[:]...)
164}
165
166func (d *digest) checkSum() [Size]byte {
167	len := d.len
168	// Padding.  Add a 1 bit and 0 bits until 56 bytes mod 64.
169	var tmp [64 + 8]byte
170	tmp[0] = 0x80
171	var t uint64
172	if len%64 < 56 {
173		t = 56 - len%64
174	} else {
175		t = 64 + 56 - len%64
176	}
177
178	// Length in bits.
179	len <<= 3
180	padlen := tmp[:t+8]
181	binary.BigEndian.PutUint64(tmp[t:], len)
182	d.Write(padlen)
183
184	if d.nx != 0 {
185		panic("d.nx != 0")
186	}
187
188	var digest [Size]byte
189
190	binary.BigEndian.PutUint32(digest[0:], d.h[0])
191	binary.BigEndian.PutUint32(digest[4:], d.h[1])
192	binary.BigEndian.PutUint32(digest[8:], d.h[2])
193	binary.BigEndian.PutUint32(digest[12:], d.h[3])
194	binary.BigEndian.PutUint32(digest[16:], d.h[4])
195
196	return digest
197}
198
199// Sum returns the SHA-1 checksum of the data.
200func Sum(data []byte) ([Size]byte, bool) {
201	var d digest
202	d.Reset()
203	d.Write(data)
204	return d.checkSum(), d.col
205}
206
207func (d *digest) CollisionResistantSum(in []byte) ([]byte, bool) {
208	// Make a copy of d so that caller can keep writing and summing.
209	d0 := *d
210	hash := d0.checkSum()
211	return append(in, hash[:]...), d0.col
212}