package gnarl // Scalar arithmetic modulo Q (~213-bit prime subgroup order). // 4x uint64 little-endian limbs, NOT Montgomery form. import ( "crypto/rand" "math/big" "math/bits" ) type scalar [4]uint64 var qLimbs scalar var scZero scalar var scOneVal scalar func initScalarConstants() { qLimbs = scalar{0xdda9942fad452629, 0xd85a69a223c0776e, 0x1e7959f44807ba64, 0x000000000018e5f1} scOneVal = scalar{1, 0, 0, 0} } func scIsZero(s *scalar) (result bool) { return (s[0] | s[1] | s[2] | s[3]) == 0 } func scGte(a, b *scalar) (result bool) { var borrow uint64 _, borrow = bits.Sub64(a[0], b[0], 0) _, borrow = bits.Sub64(a[1], b[1], borrow) _, borrow = bits.Sub64(a[2], b[2], borrow) _, borrow = bits.Sub64(a[3], b[3], borrow) return borrow == 0 } // scReduce reduces s to [0, Q) using math/big. func scReduce(s *scalar) { if !scGte(s, &qLimbs) { return } val := &big.Int{} val.SetUint64(s[3]) val.Lsh(val, 64) val.Or(val, (&big.Int{}).SetUint64(s[2])) val.Lsh(val, 64) val.Or(val, (&big.Int{}).SetUint64(s[1])) val.Lsh(val, 64) val.Or(val, (&big.Int{}).SetUint64(s[0])) val.Mod(val, qBig) mask64 := (&big.Int{}).SetUint64(0xFFFFFFFFFFFFFFFF) s[0] = (&big.Int{}).And(val, mask64).Uint64() val.Rsh(val, 64) s[1] = (&big.Int{}).And(val, mask64).Uint64() val.Rsh(val, 64) s[2] = (&big.Int{}).And(val, mask64).Uint64() val.Rsh(val, 64) s[3] = val.Uint64() } func scAdd(r, a, b *scalar) { var carry, borrow, underflow uint64 var d0, d1, d2, d3 uint64 r[0], carry = bits.Add64(a[0], b[0], 0) r[1], carry = bits.Add64(a[1], b[1], carry) r[2], carry = bits.Add64(a[2], b[2], carry) r[3], carry = bits.Add64(a[3], b[3], carry) d0, borrow = bits.Sub64(r[0], qLimbs[0], 0) d1, borrow = bits.Sub64(r[1], qLimbs[1], borrow) d2, borrow = bits.Sub64(r[2], qLimbs[2], borrow) d3, borrow = bits.Sub64(r[3], qLimbs[3], borrow) underflow = borrow &^ carry mask := uint64(0) - underflow r[0] = (r[0] & mask) | (d0 & ^mask) r[1] = (r[1] & mask) | (d1 & ^mask) r[2] = (r[2] & mask) | (d2 & ^mask) r[3] = (r[3] & mask) | (d3 & ^mask) } func scSub(r, a, b *scalar) { var borrow, carry uint64 r[0], borrow = bits.Sub64(a[0], b[0], 0) r[1], borrow = bits.Sub64(a[1], b[1], borrow) r[2], borrow = bits.Sub64(a[2], b[2], borrow) r[3], borrow = bits.Sub64(a[3], b[3], borrow) mask := uint64(0) - borrow r[0], carry = bits.Add64(r[0], qLimbs[0]&mask, 0) r[1], carry = bits.Add64(r[1], qLimbs[1]&mask, carry) r[2], carry = bits.Add64(r[2], qLimbs[2]&mask, carry) r[3], _ = bits.Add64(r[3], qLimbs[3]&mask, carry) } func scNeg(r, a *scalar) { scSub(r, &scZero, a) } // scMul computes r = a * b mod Q using math/big. func scMul(r, a, b *scalar) { var abuf, bbuf [27]byte scToBytes27(abuf[:], a) scToBytes27(bbuf[:], b) aBig := (&big.Int{}).SetBytes(abuf[:]) bBig := (&big.Int{}).SetBytes(bbuf[:]) prod := (&big.Int{}).Mul(aBig, bBig) prod.Mod(prod, qBig) var rbuf [27]byte prodBytes := prod.Bytes() copy(rbuf[27-int32(len(prodBytes)):], prodBytes) scFromBytes27(r, rbuf[:]) } func scFromBytes27(s *scalar, b []byte) { if int32(len(b)) < 27 { *s = scZero return } s[3] = uint64(b[0])<<16 | uint64(b[1])<<8 | uint64(b[2]) s[2] = uint64(b[3])<<56 | uint64(b[4])<<48 | uint64(b[5])<<40 | uint64(b[6])<<32 | uint64(b[7])<<24 | uint64(b[8])<<16 | uint64(b[9])<<8 | uint64(b[10]) s[1] = uint64(b[11])<<56 | uint64(b[12])<<48 | uint64(b[13])<<40 | uint64(b[14])<<32 | uint64(b[15])<<24 | uint64(b[16])<<16 | uint64(b[17])<<8 | uint64(b[18]) s[0] = uint64(b[19])<<56 | uint64(b[20])<<48 | uint64(b[21])<<40 | uint64(b[22])<<32 | uint64(b[23])<<24 | uint64(b[24])<<16 | uint64(b[25])<<8 | uint64(b[26]) scReduce(s) } func scToBytes27(b []byte, s *scalar) { b[0] = byte(s[3] >> 16) b[1] = byte(s[3] >> 8) b[2] = byte(s[3]) b[3] = byte(s[2] >> 56) b[4] = byte(s[2] >> 48) b[5] = byte(s[2] >> 40) b[6] = byte(s[2] >> 32) b[7] = byte(s[2] >> 24) b[8] = byte(s[2] >> 16) b[9] = byte(s[2] >> 8) b[10] = byte(s[2]) b[11] = byte(s[1] >> 56) b[12] = byte(s[1] >> 48) b[13] = byte(s[1] >> 40) b[14] = byte(s[1] >> 32) b[15] = byte(s[1] >> 24) b[16] = byte(s[1] >> 16) b[17] = byte(s[1] >> 8) b[18] = byte(s[1]) b[19] = byte(s[0] >> 56) b[20] = byte(s[0] >> 48) b[21] = byte(s[0] >> 40) b[22] = byte(s[0] >> 32) b[23] = byte(s[0] >> 24) b[24] = byte(s[0] >> 16) b[25] = byte(s[0] >> 8) b[26] = byte(s[0]) } func scRandom(s *scalar) (err error) { var buf [27]byte for { _, err = rand.Read(buf[:]) if err != nil { return err } buf[0] &= 0x1F scFromBytes27(s, buf[:]) if !scIsZero(s) { return nil } } }