Browse Source

Add NewHope-Simple.

Yawning Angel 2 years ago
parent
commit
58751d4e19
4 changed files with 5380 additions and 0 deletions
  1. 166 0
      newhope_simple.go
  2. 50 0
      newhope_simple_test.go
  3. 5065 0
      newhope_simple_testvectors_test.go
  4. 99 0
      poly_simple.go

+ 166 - 0
newhope_simple.go

@@ -0,0 +1,166 @@
+// newhope_simple.go - NewHope-Simple interface.
+//
+// To the extent possible under law, Yawning Angel has waived all copyright
+// and related or neighboring rights to newhope, using the Creative
+// Commons "CC0" public domain dedication. See LICENSE or
+// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
+
+package newhope
+
+import (
+	"io"
+
+	"golang.org/x/crypto/sha3"
+)
+
+const (
+	// HighBytes is the length of the encoded secret in bytes.
+	HighBytes = 384
+
+	// SendASimpleSize is the length of Alice's NewHope-Simple public key in
+	// bytes.
+	SendASimpleSize = PolyBytes + SeedBytes
+
+	// SendBSimpleSize is the length of Bob's NewHope-Simple public key in
+	// bytes.
+	SendBSimpleSize = PolyBytes + HighBytes
+)
+
+func encodeBSimple(r []byte, b *poly, v *poly) {
+	b.toBytes(r)
+	v.compress(r[PolyBytes:])
+}
+
+func decodeBSimple(b *poly, v *poly, r []byte) {
+	b.fromBytes(r)
+	v.decompress(r[PolyBytes:])
+}
+
+// PublicKeySimpleAlice is Alice's NewHope-Simple public key.
+type PublicKeySimpleAlice struct {
+	Send [SendASimpleSize]byte
+}
+
+// PrivateKeySimpleAlice is Alice's NewHope-Simple private key.
+type PrivateKeySimpleAlice struct {
+	sk poly
+}
+
+// Reset clears all sensitive information such that it no longer appears in
+// memory.
+func (k *PrivateKeySimpleAlice) Reset() {
+	k.sk.reset()
+}
+
+// GenerateSimpleKeyPairAlice returns a NewHope-Simple private/public key pair.
+// The private key is generated using the given reader, which must return
+// random data.  The receiver side of the key exchange (aka "Bob") MUST use
+// KeyExchangeSimpleBob() instead of this routine.
+func GenerateSimpleKeyPairAlice(rand io.Reader) (*PrivateKeySimpleAlice, *PublicKeySimpleAlice, error) {
+	var a, e, pk, r poly
+	var seed, noiseSeed [SeedBytes]byte
+
+	if _, err := io.ReadFull(rand, seed[:]); err != nil {
+		return nil, nil, err
+	}
+	seed = sha3.Sum256(seed[:]) // Don't send output of system RNG.
+	a.uniform(&seed, TorSampling)
+
+	if _, err := io.ReadFull(rand, noiseSeed[:]); err != nil {
+		return nil, nil, err
+	}
+	defer memwipe(noiseSeed[:])
+
+	privKey := new(PrivateKeySimpleAlice)
+	privKey.sk.getNoise(&noiseSeed, 0)
+	privKey.sk.ntt()
+	e.getNoise(&noiseSeed, 1)
+	e.ntt()
+
+	pubKey := new(PublicKeySimpleAlice)
+	r.pointwise(&privKey.sk, &a)
+	pk.add(&e, &r)
+	encodeA(pubKey.Send[:], &pk, &seed)
+
+	return privKey, pubKey, nil
+}
+
+// PublicKeySimpleBob is Bob's NewHope-Simple public key.
+type PublicKeySimpleBob struct {
+	Send [SendBSimpleSize]byte
+}
+
+// KeyExchangeSimpleBob is the Responder side of the NewHope-Simple key
+// exchange.  The shared secret and "public key" are generated using the
+// given reader, which must return random data.
+func KeyExchangeSimpleBob(rand io.Reader, alicePk *PublicKeySimpleAlice) (*PublicKeySimpleBob, []byte, error) {
+	var pka, a, sp, ep, bp, v, epp, m poly
+	var seed, noiseSeed [SeedBytes]byte
+
+	if _, err := io.ReadFull(rand, noiseSeed[:]); err != nil {
+		return nil, nil, err
+	}
+	defer memwipe(noiseSeed[:])
+
+	var sharedKey [SharedSecretSize]byte
+	if _, err := io.ReadFull(rand, sharedKey[:]); err != nil {
+		return nil, nil, err
+	}
+	defer memwipe(sharedKey[:])
+	sharedKey = sha3.Sum256(sharedKey[:])
+	m.fromMsg(sharedKey[:])
+
+	decodeA(&pka, &seed, alicePk.Send[:])
+	a.uniform(&seed, TorSampling)
+
+	sp.getNoise(&noiseSeed, 0)
+	sp.ntt()
+	ep.getNoise(&noiseSeed, 1)
+	ep.ntt()
+
+	bp.pointwise(&a, &sp)
+	bp.add(&bp, &ep)
+
+	v.pointwise(&pka, &sp)
+	v.invNtt()
+
+	epp.getNoise(&noiseSeed, 2)
+	v.add(&v, &epp)
+	v.add(&v, &m) // add key
+
+	pubKey := new(PublicKeySimpleBob)
+	encodeBSimple(pubKey.Send[:], &bp, &v)
+	mu := sha3.Sum256(sharedKey[:])
+
+	// Scrub the sensitive stuff...
+	sp.reset()
+	v.reset()
+	m.reset()
+
+	return pubKey, mu[:], nil
+}
+
+// KeyExchangeSimpleAlice is the Initiaitor side of the NewHope-Simple key
+// exchange.  The provided private key is obliterated prior to returning.
+func KeyExchangeSimpleAlice(bobPk *PublicKeySimpleBob, aliceSk *PrivateKeySimpleAlice) ([]byte, error) {
+	var v, bp, k poly
+
+	decodeBSimple(&bp, &v, bobPk.Send[:])
+	k.pointwise(&aliceSk.sk, &bp)
+	k.invNtt()
+
+	k.sub(&k, &v)
+
+	var sharedKey [SharedSecretSize]byte
+	k.toMsg(sharedKey[:])
+
+	// mu <- Sha3-256(v')
+	mu := sha3.Sum256(sharedKey[:])
+
+	// Scrub the sensitive stuff...
+	memwipe(sharedKey[:])
+	k.reset()
+	aliceSk.Reset()
+
+	return mu[:], nil
+}

+ 50 - 0
newhope_simple_test.go

@@ -0,0 +1,50 @@
+// newhope_simple_test.go - NewHope-Simple Integration tests.
+//
+// To the extent possible under law, Yawning Angel has waived all copyright
+// and related or neighboring rights to newhope, using the Creative
+// Commons "CC0" public domain dedication. See LICENSE or
+// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
+
+package newhope
+
+import (
+	"bytes"
+	"crypto/rand"
+	"testing"
+)
+
+func testSimpleIntegration(t *testing.T) {
+	for i := 0; i < 1024; i++ {
+		// Generate Alice's key's.
+		alicePriv, alicePub, err := GenerateSimpleKeyPairAlice(rand.Reader)
+		if err != nil {
+			t.Fatalf("GenerateSimpleKeyPair failed: %v", err)
+		}
+
+		// Finish Bob's handshake.
+		bobPub, bobShared, err := KeyExchangeSimpleBob(rand.Reader, alicePub)
+		if err != nil {
+			t.Fatalf("KeyExchangeSimpleBob failed: %v", err)
+		}
+
+		// Finish Alice's handshake.
+		aliceShared, err := KeyExchangeSimpleAlice(bobPub, alicePriv)
+		if err != nil {
+			t.Fatalf("KeyExchangeSimpleAlice failed: %v", err)
+		}
+
+		if !bytes.Equal(aliceShared, bobShared) {
+			t.Fatalf("shared secrets mismatched")
+		}
+	}
+}
+
+func TestSimpleIntegration(t *testing.T) {
+	TorSampling = false
+	testSimpleIntegration(t)
+}
+
+func TestSimpleIntegrationTor(t *testing.T) {
+	TorSampling = true
+	testSimpleIntegration(t)
+}

File diff suppressed because it is too large
+ 5065 - 0
newhope_simple_testvectors_test.go


+ 99 - 0
poly_simple.go

@@ -0,0 +1,99 @@
+// poly_simple.go - NewHope-Simple polynomial.
+//
+// To the extent possible under law, Yawning Angel has waived all copyright
+// and related or neighboring rights to newhope, using the Creative
+// Commons "CC0" public domain dedication. See LICENSE or
+// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
+
+package newhope
+
+func coeffFreeze(x uint16) uint16 {
+	var c int16
+
+	r := barrettReduce(x)
+	m := r - paramQ
+	c = int16(m)
+	c >>= 15
+	r = m ^ ((r ^ m) & uint16(c))
+
+	return r
+}
+
+// Computes abs(x-Q/2)
+func flipAbs(x uint16) uint16 {
+	r := int16(coeffFreeze(x))
+	r = r - paramQ/2
+	m := r >> 15
+	return uint16((r + m) ^ m)
+}
+
+func (p *poly) compress(r []byte) {
+	var t [8]uint32
+
+	for i, k := 0, 0; i < paramN; i, k = i+8, k+3 {
+		for j := range t {
+			t[j] = uint32(coeffFreeze(p.coeffs[i+j]))
+			t[j] = (((t[j] << 3) + paramQ/2) / paramQ) & 0x7
+		}
+
+		r[k] = byte(t[0]) | byte(t[1]<<3) | byte(t[2]<<6)
+		r[k+1] = byte(t[2]>>2) | byte(t[3]<<1) | byte(t[4]<<4) | byte(t[5]<<7)
+		r[k+2] = byte(t[5]>>1) | byte(t[6]<<2) | byte(t[7]<<5)
+	}
+
+	for i := range t {
+		t[i] = 0
+	}
+}
+
+func (p *poly) decompress(a []byte) {
+	for i := 0; i < paramN; i += 8 {
+		a0, a1, a2 := uint16(a[0]), uint16(a[1]), uint16(a[2])
+		p.coeffs[i+0] = a0 & 7
+		p.coeffs[i+1] = (a0 >> 3) & 7
+		p.coeffs[i+2] = (a0 >> 6) | ((a1 << 2) & 4)
+		p.coeffs[i+3] = (a1 >> 1) & 7
+		p.coeffs[i+4] = (a1 >> 4) & 7
+		p.coeffs[i+5] = (a1 >> 7) | ((a2 << 1) & 6)
+		p.coeffs[i+6] = (a2 >> 2) & 7
+		p.coeffs[i+7] = (a2 >> 5)
+		a = a[3:]
+		for j := 0; j < 8; j++ {
+			p.coeffs[i+j] = uint16((uint32(p.coeffs[i+j])*paramQ + 4) >> 3)
+		}
+	}
+}
+
+func (p *poly) fromMsg(msg []byte) {
+	for i := uint(0); i < 32; i++ { // XXX: const for 32
+		for j := uint(0); j < 8; j++ {
+			mask := -(uint16((msg[i] >> j) & 1))
+			p.coeffs[8*i+j+0] = mask & (paramQ / 2)
+			p.coeffs[8*i+j+256] = mask & (paramQ / 2)
+			p.coeffs[8*i+j+512] = mask & (paramQ / 2)
+			p.coeffs[8*i+j+768] = mask & (paramQ / 2)
+		}
+	}
+}
+
+func (p *poly) toMsg(msg []byte) {
+	memwipe(msg[0:32])
+
+	for i := uint(0); i < 256; i++ {
+		t := flipAbs(p.coeffs[i+0])
+		t += flipAbs(p.coeffs[i+256])
+		t += flipAbs(p.coeffs[i+512])
+		t += flipAbs(p.coeffs[i+768])
+
+		//t = (~(t - PARAM_Q));
+		t = (t - paramQ)
+		t >>= 15
+		msg[i>>3] |= byte(t << (i & 7))
+	}
+}
+
+func (p *poly) sub(a, b *poly) {
+	for i := range p.coeffs {
+		p.coeffs[i] = barrettReduce(a.coeffs[i] + 3*paramQ - b.coeffs[i])
+	}
+}