|
| 1 | +// sha1.go |
| 2 | +// description: The SHA-1 hashing function as defined in RFC 3174. |
| 3 | +// author: Simon Waldherr |
| 4 | +// ref: https://datatracker.ietf.org/doc/html/rfc3174 |
| 5 | +// see sha1_test.go for testing |
| 6 | + |
| 7 | +package sha1 |
| 8 | + |
| 9 | +import ( |
| 10 | + "encoding/binary" // Used for interacting with uint at the byte level |
| 11 | +) |
| 12 | + |
| 13 | +// Constants for SHA-1 |
| 14 | +const ( |
| 15 | + h0 uint32 = 0x67452301 |
| 16 | + h1 uint32 = 0xEFCDAB89 |
| 17 | + h2 uint32 = 0x98BADCFE |
| 18 | + h3 uint32 = 0x10325476 |
| 19 | + h4 uint32 = 0xC3D2E1F0 |
| 20 | +) |
| 21 | + |
| 22 | +// pad pads the input message so that its length is congruent to 448 modulo 512 |
| 23 | +func pad(message []byte) []byte { |
| 24 | + originalLength := len(message) * 8 |
| 25 | + message = append(message, 0x80) |
| 26 | + for (len(message)*8)%512 != 448 { |
| 27 | + message = append(message, 0x00) |
| 28 | + } |
| 29 | + |
| 30 | + lengthBytes := make([]byte, 8) |
| 31 | + binary.BigEndian.PutUint64(lengthBytes, uint64(originalLength)) |
| 32 | + message = append(message, lengthBytes...) |
| 33 | + |
| 34 | + return message |
| 35 | +} |
| 36 | + |
| 37 | +// leftRotate rotates x left by n bits |
| 38 | +func leftRotate(x, n uint32) uint32 { |
| 39 | + return (x << n) | (x >> (32 - n)) |
| 40 | +} |
| 41 | + |
| 42 | +// Hash computes the SHA-1 hash of the input message |
| 43 | +func Hash(message []byte) [20]byte { |
| 44 | + message = pad(message) |
| 45 | + |
| 46 | + // Initialize variables |
| 47 | + a, b, c, d, e := h0, h1, h2, h3, h4 |
| 48 | + |
| 49 | + // Process the message in successive 512-bit chunks |
| 50 | + for i := 0; i < len(message); i += 64 { |
| 51 | + var w [80]uint32 |
| 52 | + chunk := message[i : i+64] |
| 53 | + |
| 54 | + // Break chunk into sixteen 32-bit big-endian words |
| 55 | + for j := 0; j < 16; j++ { |
| 56 | + w[j] = binary.BigEndian.Uint32(chunk[j*4 : (j+1)*4]) |
| 57 | + } |
| 58 | + |
| 59 | + // Extend the sixteen 32-bit words into eighty 32-bit words |
| 60 | + for j := 16; j < 80; j++ { |
| 61 | + w[j] = leftRotate(w[j-3]^w[j-8]^w[j-14]^w[j-16], 1) |
| 62 | + } |
| 63 | + |
| 64 | + // Initialize hash value for this chunk |
| 65 | + A, B, C, D, E := a, b, c, d, e |
| 66 | + |
| 67 | + // Main loop |
| 68 | + for j := 0; j < 80; j++ { |
| 69 | + var f, k uint32 |
| 70 | + switch { |
| 71 | + case j < 20: |
| 72 | + f = (B & C) | ((^B) & D) |
| 73 | + k = 0x5A827999 |
| 74 | + case j < 40: |
| 75 | + f = B ^ C ^ D |
| 76 | + k = 0x6ED9EBA1 |
| 77 | + case j < 60: |
| 78 | + f = (B & C) | (B & D) | (C & D) |
| 79 | + k = 0x8F1BBCDC |
| 80 | + default: |
| 81 | + f = B ^ C ^ D |
| 82 | + k = 0xCA62C1D6 |
| 83 | + } |
| 84 | + |
| 85 | + temp := leftRotate(A, 5) + f + E + k + w[j] |
| 86 | + E = D |
| 87 | + D = C |
| 88 | + C = leftRotate(B, 30) |
| 89 | + B = A |
| 90 | + A = temp |
| 91 | + } |
| 92 | + |
| 93 | + // Add this chunk's hash to result so far |
| 94 | + a += A |
| 95 | + b += B |
| 96 | + c += C |
| 97 | + d += D |
| 98 | + e += E |
| 99 | + } |
| 100 | + |
| 101 | + // Produce the final hash value (digest) |
| 102 | + var digest [20]byte |
| 103 | + binary.BigEndian.PutUint32(digest[0:4], a) |
| 104 | + binary.BigEndian.PutUint32(digest[4:8], b) |
| 105 | + binary.BigEndian.PutUint32(digest[8:12], c) |
| 106 | + binary.BigEndian.PutUint32(digest[12:16], d) |
| 107 | + binary.BigEndian.PutUint32(digest[16:20], e) |
| 108 | + |
| 109 | + return digest |
| 110 | +} |
0 commit comments