main
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 "crypto"
16 "encoding/binary"
17 "errors"
18 "hash"
19
20 shared "github.com/pjbgf/sha1cd/internal"
21)
22
23//go:generate go run -C asm . -out ../sha1cdblock_amd64.s -pkg $GOPACKAGE
24
25func init() {
26 crypto.RegisterHash(crypto.SHA1, New)
27}
28
29// The size of a SHA-1 checksum in bytes.
30const Size = shared.Size
31
32// The blocksize of SHA-1 in bytes.
33const BlockSize = shared.Chunk
34
35// digest represents the partial evaluation of a checksum.
36type digest struct {
37 h [shared.WordBuffers]uint32
38 x [shared.Chunk]byte
39 nx int
40 len uint64
41
42 // col defines whether a collision has been found.
43 col bool
44 blockFunc func(dig *digest, p []byte)
45}
46
47func (d *digest) MarshalBinary() ([]byte, error) {
48 b := make([]byte, 0, shared.MarshaledSize)
49 b = append(b, shared.Magic...)
50 b = appendUint32(b, d.h[0])
51 b = appendUint32(b, d.h[1])
52 b = appendUint32(b, d.h[2])
53 b = appendUint32(b, d.h[3])
54 b = appendUint32(b, d.h[4])
55 b = append(b, d.x[:d.nx]...)
56 b = b[:len(b)+len(d.x)-d.nx] // already zero
57 b = appendUint64(b, d.len)
58 return b, nil
59}
60
61func appendUint32(b []byte, v uint32) []byte {
62 return append(b,
63 byte(v>>24),
64 byte(v>>16),
65 byte(v>>8),
66 byte(v),
67 )
68}
69
70func appendUint64(b []byte, v uint64) []byte {
71 return append(b,
72 byte(v>>56),
73 byte(v>>48),
74 byte(v>>40),
75 byte(v>>32),
76 byte(v>>24),
77 byte(v>>16),
78 byte(v>>8),
79 byte(v),
80 )
81}
82
83func (d *digest) UnmarshalBinary(b []byte) error {
84 if len(b) < len(shared.Magic) || string(b[:len(shared.Magic)]) != shared.Magic {
85 return errors.New("crypto/sha1: invalid hash state identifier")
86 }
87 if len(b) != shared.MarshaledSize {
88 return errors.New("crypto/sha1: invalid hash state size")
89 }
90 b = b[len(shared.Magic):]
91 b, d.h[0] = consumeUint32(b)
92 b, d.h[1] = consumeUint32(b)
93 b, d.h[2] = consumeUint32(b)
94 b, d.h[3] = consumeUint32(b)
95 b, d.h[4] = consumeUint32(b)
96 b = b[copy(d.x[:], b):]
97 b, d.len = consumeUint64(b)
98 d.nx = int(d.len % shared.Chunk)
99 return nil
100}
101
102func consumeUint64(b []byte) ([]byte, uint64) {
103 _ = b[7]
104 x := uint64(b[7]) | uint64(b[6])<<8 | uint64(b[shared.WordBuffers])<<16 | uint64(b[4])<<24 |
105 uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
106 return b[8:], x
107}
108
109func consumeUint32(b []byte) ([]byte, uint32) {
110 _ = b[3]
111 x := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
112 return b[4:], x
113}
114
115func (d *digest) Reset() {
116 d.h[0] = shared.Init0
117 d.h[1] = shared.Init1
118 d.h[2] = shared.Init2
119 d.h[3] = shared.Init3
120 d.h[4] = shared.Init4
121 d.nx = 0
122 d.len = 0
123
124 d.col = false
125}
126
127// New returns a new hash.Hash computing the SHA1 checksum. The Hash also
128// implements encoding.BinaryMarshaler and encoding.BinaryUnmarshaler to
129// marshal and unmarshal the internal state of the hash.
130func New() hash.Hash {
131 d := new(digest)
132
133 d.blockFunc = block
134 d.Reset()
135 return d
136}
137
138// NewGeneric is equivalent to New but uses the Go generic implementation,
139// avoiding any processor-specific optimizations.
140func NewGeneric() hash.Hash {
141 d := new(digest)
142
143 d.blockFunc = blockGeneric
144 d.Reset()
145 return d
146}
147
148func (d *digest) Size() int { return Size }
149
150func (d *digest) BlockSize() int { return BlockSize }
151
152func (d *digest) Write(p []byte) (nn int, err error) {
153 if len(p) == 0 {
154 return
155 }
156
157 nn = len(p)
158 d.len += uint64(nn)
159 if d.nx > 0 {
160 n := copy(d.x[d.nx:], p)
161 d.nx += n
162 if d.nx == shared.Chunk {
163 d.blockFunc(d, d.x[:])
164 d.nx = 0
165 }
166 p = p[n:]
167 }
168 if len(p) >= shared.Chunk {
169 n := len(p) &^ (shared.Chunk - 1)
170 d.blockFunc(d, p[:n])
171 p = p[n:]
172 }
173 if len(p) > 0 {
174 d.nx = copy(d.x[:], p)
175 }
176 return
177}
178
179func (d *digest) Sum(in []byte) []byte {
180 // Make a copy of d so that caller can keep writing and summing.
181 d0 := *d
182 hash := d0.checkSum()
183 return append(in, hash[:]...)
184}
185
186func (d *digest) checkSum() [Size]byte {
187 len := d.len
188 // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
189 var tmp [64]byte
190 tmp[0] = 0x80
191 if len%64 < 56 {
192 d.Write(tmp[0 : 56-len%64])
193 } else {
194 d.Write(tmp[0 : 64+56-len%64])
195 }
196
197 // Length in bits.
198 len <<= 3
199 binary.BigEndian.PutUint64(tmp[:], len)
200 d.Write(tmp[0:8])
201
202 if d.nx != 0 {
203 panic("d.nx != 0")
204 }
205
206 var digest [Size]byte
207
208 binary.BigEndian.PutUint32(digest[0:], d.h[0])
209 binary.BigEndian.PutUint32(digest[4:], d.h[1])
210 binary.BigEndian.PutUint32(digest[8:], d.h[2])
211 binary.BigEndian.PutUint32(digest[12:], d.h[3])
212 binary.BigEndian.PutUint32(digest[16:], d.h[4])
213
214 return digest
215}
216
217// Sum returns the SHA-1 checksum of the data.
218func Sum(data []byte) ([Size]byte, bool) {
219 d := New().(*digest)
220 d.Write(data)
221 return d.checkSum(), d.col
222}
223
224func (d *digest) CollisionResistantSum(in []byte) ([]byte, bool) {
225 // Make a copy of d so that caller can keep writing and summing.
226 d0 := *d
227 hash := d0.checkSum()
228 return append(in, hash[:]...), d0.col
229}