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 notsha256 implements the NOTSHA256 algorithm,
6// a hash defined as bitwise NOT of SHA256.
7// It is used in situations where exact fidelity to SHA256 is unnecessary.
8// In particular, it is used in the compiler toolchain,
9// which cannot depend directly on cgo when GOEXPERIMENT=boringcrypto
10// (and in that mode the real sha256 uses cgo).
11package notsha256
12
13import (
14	"encoding/binary"
15	"hash"
16)
17
18// The size of a checksum in bytes.
19const Size = 32
20
21// The blocksize in bytes.
22const BlockSize = 64
23
24const (
25	chunk = 64
26	init0 = 0x6A09E667
27	init1 = 0xBB67AE85
28	init2 = 0x3C6EF372
29	init3 = 0xA54FF53A
30	init4 = 0x510E527F
31	init5 = 0x9B05688C
32	init6 = 0x1F83D9AB
33	init7 = 0x5BE0CD19
34)
35
36// digest represents the partial evaluation of a checksum.
37type digest struct {
38	h   [8]uint32
39	x   [chunk]byte
40	nx  int
41	len uint64
42}
43
44func (d *digest) Reset() {
45	d.h[0] = init0
46	d.h[1] = init1
47	d.h[2] = init2
48	d.h[3] = init3
49	d.h[4] = init4
50	d.h[5] = init5
51	d.h[6] = init6
52	d.h[7] = init7
53	d.nx = 0
54	d.len = 0
55}
56
57// New returns a new hash.Hash computing the NOTSHA256 checksum.
58// state of the hash.
59func New() hash.Hash {
60	d := new(digest)
61	d.Reset()
62	return d
63}
64
65func (d *digest) Size() int {
66	return Size
67}
68
69func (d *digest) BlockSize() int { return BlockSize }
70
71func (d *digest) Write(p []byte) (nn int, err error) {
72	nn = len(p)
73	d.len += uint64(nn)
74	if d.nx > 0 {
75		n := copy(d.x[d.nx:], p)
76		d.nx += n
77		if d.nx == chunk {
78			block(d, d.x[:])
79			d.nx = 0
80		}
81		p = p[n:]
82	}
83	if len(p) >= chunk {
84		n := len(p) &^ (chunk - 1)
85		block(d, p[:n])
86		p = p[n:]
87	}
88	if len(p) > 0 {
89		d.nx = copy(d.x[:], p)
90	}
91	return
92}
93
94func (d *digest) Sum(in []byte) []byte {
95	// Make a copy of d so that caller can keep writing and summing.
96	d0 := *d
97	hash := d0.checkSum()
98	return append(in, hash[:]...)
99}
100
101func (d *digest) checkSum() [Size]byte {
102	len := d.len
103	// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
104	var tmp [64]byte
105	tmp[0] = 0x80
106	if len%64 < 56 {
107		d.Write(tmp[0 : 56-len%64])
108	} else {
109		d.Write(tmp[0 : 64+56-len%64])
110	}
111
112	// Length in bits.
113	len <<= 3
114	binary.BigEndian.PutUint64(tmp[:], len)
115	d.Write(tmp[0:8])
116
117	if d.nx != 0 {
118		panic("d.nx != 0")
119	}
120
121	var digest [Size]byte
122
123	binary.BigEndian.PutUint32(digest[0:], d.h[0]^0xFFFFFFFF)
124	binary.BigEndian.PutUint32(digest[4:], d.h[1]^0xFFFFFFFF)
125	binary.BigEndian.PutUint32(digest[8:], d.h[2]^0xFFFFFFFF)
126	binary.BigEndian.PutUint32(digest[12:], d.h[3]^0xFFFFFFFF)
127	binary.BigEndian.PutUint32(digest[16:], d.h[4]^0xFFFFFFFF)
128	binary.BigEndian.PutUint32(digest[20:], d.h[5]^0xFFFFFFFF)
129	binary.BigEndian.PutUint32(digest[24:], d.h[6]^0xFFFFFFFF)
130	binary.BigEndian.PutUint32(digest[28:], d.h[7]^0xFFFFFFFF)
131
132	return digest
133}
134
135// Sum256 returns the SHA256 checksum of the data.
136func Sum256(data []byte) [Size]byte {
137	var d digest
138	d.Reset()
139	d.Write(data)
140	return d.checkSum()
141}
142