summaryrefslogblamecommitdiffstats
path: root/lib/sha1.go
blob: bfe87b18dc99bca9af5aa223256e12435d9223a7 (plain) (tree)
















































































































































































































                                                                                 




                                           





                                                          
// Copyright © 2021 rsiddharth <s@ricketyspace.net>
// SPDX-License-Identifier: ISC

package lib

// SHA-1 implementation.
// Reference https://csrc.nist.gov/publications/detail/fips/180/4/final

// Initial hash value.
var sha1IHashValue []uint32 = []uint32{
	0x67452301,
	0xefcdab89,
	0x98badcfe,
	0x10325476,
	0xc3d2e1f0,
}

// (a + b + ...) mod 2^32
func sha1Add(n ...uint32) uint32 {
	sum := uint64(0)
	for _, v := range n {
		sum += uint64(v)
	}
	return uint32(sum & 0xFFFFFFFF)
}

// Circular Right Shift
func sha1Rotr(x uint32, n uint) uint32 {
	return (x >> n) | (x << (32 - n))
}

// Circular Left Shift
func sha1Rotl(x uint32, n uint) uint32 {
	return (x << n) | (x >> (32 - n))
}

// 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)
	}
}

// SHA-1 - Pad message such that its length is a multiple of 512.
func sha1Pad(m []byte) []byte {
	l := len(m) * 8 // msg size in bits

	// Reckon value of `k`
	k := 0
	for ((l + 1 + k) % 512) != 448 {
		k += 1
	}

	// Initialize padded message
	pm := make([]byte, len(m))
	copy(pm, m)

	// Add bit `1` as byte block.
	pm = append(pm, 0x80)
	f := 7 // unclaimed bits in last byte of `pm`

	// Add `k` bit `0`s
	for i := 0; i < k; i++ {
		if f == 0 {
			pm = append(pm, 0x0)
			f = 8
		}
		f = f - 1
	}

	// Add `l` in a 64 bit block in `pm`
	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
	}
	pm = append(pm, b64...)

	return pm
}

// Converts padded messages bytes `pm` into 512-bit message blocks.
// Each 512-bit block is an array of 16 32-bit words.
func sha1MessageBlocks(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, sha1Rotl(w[t-3]^w[t-8]^w[t-14]^w[t-16], 1))
		}
	}
	return w
}

func Sha1(m []byte) []byte {
	// Pad message.
	pm := sha1Pad(m)

	// Break into message blocks.
	mbs := sha1MessageBlocks(pm)

	// Initialize hash values.
	h := make([]uint32, 5)
	copy(h, sha1IHashValue) // 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 := sha1Add(sha1Rotl(a, 5), sha1FT(t, b, c, d),
				e, sha1KT(t), w[t])
			e = d
			d = c
			c = sha1Rotl(b, 30)
			b = a
			a = tmp
		}

		// Compute intermediate hash values.
		h[0] = sha1Add(a, h[0])
		h[1] = sha1Add(b, h[1])
		h[2] = sha1Add(c, h[2])
		h[3] = sha1Add(d, h[3])
		h[4] = sha1Add(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 Sha1Mac(secret, msg []byte) []byte {
	return Sha1(append(secret, msg...))
}

func Sha1MacVerify(secret, msg, mac []byte) bool {
	if BytesEqual(Sha1(append(secret, msg...)), mac) {
		return true
	}
	return false
}