// Copyright © 2021 siddharth ravikumar // SPDX-License-Identifier: ISC package lib // SHA-1 implementation. // Reference https://csrc.nist.gov/publications/detail/fips/180/4/final type Sha1 struct { hvs []uint32 Msg []byte MsgLen int } // Initial hash value. var sha1IHashValues []uint32 = []uint32{ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0, } // SHA-1 - Function f_t(x, y, z) func sha1FT(t int, x, y, z uint32) uint32 { switch { case t <= 19: return (x & y) ^ (^x & z) case t >= 20 && t <= 39: return x ^ y ^ z case t >= 40 && t <= 59: return (x & y) ^ (x & z) ^ (y & z) case t >= 60 && t <= 79: return x ^ y ^ z default: return uint32(0) } } // SHA-1 - Constant K_t func sha1KT(t int) uint32 { switch { case t <= 19: return uint32(0x5a827999) case t >= 20 && t <= 39: return uint32(0x6ed9eba1) case t >= 40 && t <= 59: return uint32(0x8f1bbcdc) case t >= 60 && t <= 79: return uint32(0xca62c1d6) default: return uint32(0) } } // Converts padded messages bytes `pm` into 512-bit message blocks. // Each 512-bit block is an array of 16 32-bit words. func shaMessageBlocks(pm []byte) [][]uint32 { // Break into 512-bit blocks bs := BreakIntoBlocks(pm, 64) mbs := make([][]uint32, 0) // Message blocks. for i := 0; i < len(bs); i++ { ws := make([]uint32, 0) // 32-bit words. // Break 512-bit (64 bytes) into 32-bit words. for j := 0; j < 64; j = j + 4 { // Pack 4 bytes into a 32-bit word. w := (uint32(bs[i][j])<<24 | uint32(bs[i][j+1])<<16 | uint32(bs[i][j+2])<<8 | uint32(bs[i][j+3])) ws = append(ws, w) } mbs = append(mbs, ws) } return mbs } // Returns the message schedule W_t for message black `mb` // The message schedule has 80 32-bit words. func sha1MessageSchedule(mb []uint32) []uint32 { // Message schedule. w := make([]uint32, 0) // Generate message schedule. for t := 0; t <= 79; t++ { if t <= 15 { w = append(w, mb[t]) } else { w = append(w, shaRotl(w[t-3]^w[t-8]^w[t-14]^w[t-16], 1)) } } return w } // Returns Merkle–Damgård padding in bytes for length of mesage `l` // bytes. func MDPadding(l int) []byte { l = l * 8 // msg size in bits // Reckon value of `k` k := 0 for ((l + 1 + k) % 512) != 448 { k += 1 } // Initialize padding bytes pbs := make([]byte, 0) // Add bit `1` as byte block. pbs = append(pbs, 0x80) f := 7 // unclaimed bits in last byte of `pbs` // Add `k` bit `0`s for i := 0; i < k; i++ { if f == 0 { pbs = append(pbs, 0x0) f = 8 } f = f - 1 } // Add `l` in a 64 bit block in `pbs` l64 := uint64(l) b64 := make([]byte, 8) // last 64-bits for i := 7; i >= 0; i-- { // Get 8 last bits. b64[i] = byte(l64 & 0xFF) // Get rid of the last 8 bits. l64 = l64 >> 8 } pbs = append(pbs, b64...) return pbs } func (s *Sha1) Init(hvs []uint32) { // Set Initial Hash Values. h := make([]uint32, 5) if len(hvs) == 5 { copy(h, hvs) s.hvs = h } else { copy(h, sha1IHashValues) s.hvs = h } } func (s *Sha1) Message(m []byte) { s.Msg = m s.MsgLen = len(m) } // SHA-1 - Pad message such that its length is a multiple of 512. func (s *Sha1) Pad() []byte { // Initialize padded message pm := make([]byte, len(s.Msg)) copy(pm, s.Msg) // Add padding. pm = append(pm, MDPadding(s.MsgLen)...) return pm } func (s *Sha1) Hash() []byte { // Pad message. pm := s.Pad() // Break into message blocks. mbs := shaMessageBlocks(pm) // Initialize hash values. h := make([]uint32, 5) copy(h, s.hvs) // Initial hash values. // Process each message block. for _, mb := range mbs { // Get message schedule. w := sha1MessageSchedule(mb) // Initialize working variables. a := h[0] b := h[1] c := h[2] d := h[3] e := h[4] for t := 0; t <= 79; t++ { tmp := shaAdd(shaRotl(a, 5), sha1FT(t, b, c, d), e, sha1KT(t), w[t]) e = d d = c c = shaRotl(b, 30) b = a a = tmp } // Compute intermediate hash values. h[0] = shaAdd(a, h[0]) h[1] = shaAdd(b, h[1]) h[2] = shaAdd(c, h[2]) h[3] = shaAdd(d, h[3]) h[4] = shaAdd(e, h[4]) } // Slurp sha1 digest from hash values. d := make([]byte, 0) // sha1 digest for i := 0; i < 5; i++ { // Break 32-bit hash value into 4 bytes. hb := make([]byte, 4) for j := 3; j >= 0; j-- { // Get last 8 bits. hb[j] = byte(h[i] & 0xFF) // Get rid of last 8 bits. h[i] = h[i] >> 8 } d = append(d, hb...) } return d } func (s *Sha1) Mac(secret, msg []byte) []byte { s.Message(append(secret, msg...)) return s.Hash() } func (s *Sha1) MacVerify(secret, msg, mac []byte) bool { s.Message(append(secret, msg...)) if BytesEqual(s.Hash(), mac) { return true } return false } // HMAC-SHA1. func HmacSha1(key, msg []byte) []byte { // Initialize SHA-1 object. sha1 := Sha1{} sha1.Init([]uint32{}) // Modify key based on it's size. if len(key) > 64 { // > blocksize (64 bytes) sha1.Message(key) key = sha1.Hash() } if len(key) < 64 { // < blocksize (64 bytes) // Pad with zeroes up to 64 bytes. key = append(key, make([]byte, 64-len(key))...) } // Outer padded key. opk := FixedXORBytes(key, FillBytes(0x5c, 64)) // Inner padded key. ipk := FixedXORBytes(key, FillBytes(0x36, 64)) return sha1.Mac(opk, sha1.Mac(ipk, msg)) }