mirror of https://gogs.blitter.com/RLabs/xs
221 lines
5.6 KiB
Go
221 lines
5.6 KiB
Go
|
// newhope.go - NewHope 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 implements a key exchange based on the Ring Learning with
|
||
|
// Errors Problem. It is a mechanical port of the Public Domain implementation
|
||
|
// by Erdem Alkim, Léo Ducas, Thomas Pöppelmann, and Peter Schwabe.
|
||
|
//
|
||
|
// For more information see:
|
||
|
// https://cryptojedi.org/papers/newhope-20161119.pdf
|
||
|
// https://cryptojedi.org/papers/newhopesimple-20161217.pdf
|
||
|
//
|
||
|
package newhope
|
||
|
|
||
|
import (
|
||
|
"io"
|
||
|
|
||
|
"golang.org/x/crypto/sha3"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// SharedSecretSize is the length of a Shared Secret in bytes.
|
||
|
SharedSecretSize = 32
|
||
|
|
||
|
// UpstreamVersion is the version of the upstream package this
|
||
|
// implementation is compatible with.
|
||
|
UpstreamVersion = "20160815"
|
||
|
|
||
|
// RecBytes is the length of the reconciliation data in bytes.
|
||
|
RecBytes = 256
|
||
|
|
||
|
// SendASize is the length of Alice's public key in bytes.
|
||
|
SendASize = PolyBytes + SeedBytes
|
||
|
|
||
|
// SendBSize is the length of Bob's public key in bytes.
|
||
|
SendBSize = PolyBytes + RecBytes
|
||
|
)
|
||
|
|
||
|
// TorSampling enables the constant time generation of the `a` parameter,
|
||
|
// where every successful `a` generation will take the same amount of time.
|
||
|
// Most users will probably not want to enable this as it does come with a
|
||
|
// performance penalty. Alice and Bob *MUST* agree on the sampling method,
|
||
|
// or the key exchange will fail.
|
||
|
var TorSampling = false
|
||
|
|
||
|
func encodeA(r []byte, pk *poly, seed *[SeedBytes]byte) {
|
||
|
pk.toBytes(r)
|
||
|
for i := 0; i < SeedBytes; i++ {
|
||
|
r[PolyBytes+i] = seed[i]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func decodeA(pk *poly, seed *[SeedBytes]byte, r []byte) {
|
||
|
pk.fromBytes(r)
|
||
|
for i := range seed {
|
||
|
seed[i] = r[PolyBytes+i]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func encodeB(r []byte, b *poly, c *poly) {
|
||
|
b.toBytes(r)
|
||
|
for i := 0; i < paramN/4; i++ {
|
||
|
r[PolyBytes+i] = byte(c.coeffs[4*i]) | byte(c.coeffs[4*i+1]<<2) | byte(c.coeffs[4*i+2]<<4) | byte(c.coeffs[4*i+3]<<6)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func decodeB(b *poly, c *poly, r []byte) {
|
||
|
b.fromBytes(r)
|
||
|
for i := 0; i < paramN/4; i++ {
|
||
|
c.coeffs[4*i+0] = uint16(r[PolyBytes+i]) & 0x03
|
||
|
c.coeffs[4*i+1] = uint16(r[PolyBytes+i]>>2) & 0x03
|
||
|
c.coeffs[4*i+2] = uint16(r[PolyBytes+i]>>4) & 0x03
|
||
|
c.coeffs[4*i+3] = uint16(r[PolyBytes+i] >> 6)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func memwipe(b []byte) {
|
||
|
for i := range b {
|
||
|
b[i] = 0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// PublicKeyAlice is Alice's NewHope public key.
|
||
|
type PublicKeyAlice struct {
|
||
|
Send [SendASize]byte
|
||
|
}
|
||
|
|
||
|
// PrivateKeyAlice is Alice's NewHope private key.
|
||
|
type PrivateKeyAlice struct {
|
||
|
sk poly
|
||
|
}
|
||
|
|
||
|
// Reset clears all sensitive information such that it no longer appears in
|
||
|
// memory.
|
||
|
func (k *PrivateKeyAlice) Reset() {
|
||
|
k.sk.reset()
|
||
|
}
|
||
|
|
||
|
// GenerateKeyPairAlice returns a 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 KeyExchangeBob()
|
||
|
// instead of this routine.
|
||
|
func GenerateKeyPairAlice(rand io.Reader) (*PrivateKeyAlice, *PublicKeyAlice, error) {
|
||
|
var a, e, pk, r poly
|
||
|
var seed, noiseSeed [SeedBytes]byte
|
||
|
|
||
|
// seed <- Sample({0, 1}^256)
|
||
|
if _, err := io.ReadFull(rand, seed[:]); err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
seed = sha3.Sum256(seed[:]) // Don't send output of system RNG.
|
||
|
// a <- Parse(SHAKE-128(seed))
|
||
|
a.uniform(&seed, TorSampling)
|
||
|
|
||
|
// s, e <- Sample(psi(n, 12))
|
||
|
if _, err := io.ReadFull(rand, noiseSeed[:]); err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
defer memwipe(noiseSeed[:])
|
||
|
privKey := new(PrivateKeyAlice)
|
||
|
privKey.sk.getNoise(&noiseSeed, 0)
|
||
|
privKey.sk.ntt()
|
||
|
e.getNoise(&noiseSeed, 1)
|
||
|
e.ntt()
|
||
|
|
||
|
// b <- as + e
|
||
|
pubKey := new(PublicKeyAlice)
|
||
|
r.pointwise(&privKey.sk, &a)
|
||
|
pk.add(&e, &r)
|
||
|
encodeA(pubKey.Send[:], &pk, &seed)
|
||
|
|
||
|
return privKey, pubKey, nil
|
||
|
}
|
||
|
|
||
|
// PublicKeyBob is Bob's NewHope public key.
|
||
|
type PublicKeyBob struct {
|
||
|
Send [SendBSize]byte
|
||
|
}
|
||
|
|
||
|
// KeyExchangeBob is the Responder side of the NewHope key exchange. The
|
||
|
// shared secret and "public key" (key + reconciliation data) are generated
|
||
|
// using the given reader, which must return random data.
|
||
|
func KeyExchangeBob(rand io.Reader, alicePk *PublicKeyAlice) (*PublicKeyBob, []byte, error) {
|
||
|
var pka, a, sp, ep, u, v, epp, r poly
|
||
|
var seed, noiseSeed [SeedBytes]byte
|
||
|
|
||
|
if _, err := io.ReadFull(rand, noiseSeed[:]); err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
defer memwipe(noiseSeed[:])
|
||
|
|
||
|
// a <- Parse(SHAKE-128(seed))
|
||
|
decodeA(&pka, &seed, alicePk.Send[:])
|
||
|
a.uniform(&seed, TorSampling)
|
||
|
|
||
|
// s', e', e'' <- Sample(psi(n, 12))
|
||
|
sp.getNoise(&noiseSeed, 0)
|
||
|
sp.ntt()
|
||
|
ep.getNoise(&noiseSeed, 1)
|
||
|
ep.ntt()
|
||
|
epp.getNoise(&noiseSeed, 2)
|
||
|
|
||
|
// u <- as' + e'
|
||
|
u.pointwise(&a, &sp)
|
||
|
u.add(&u, &ep)
|
||
|
|
||
|
// v <- bs' + e''
|
||
|
v.pointwise(&pka, &sp)
|
||
|
v.invNtt()
|
||
|
v.add(&v, &epp)
|
||
|
|
||
|
// r <- Sample(HelpRec(v))
|
||
|
r.helpRec(&v, &noiseSeed, 3)
|
||
|
|
||
|
pubKey := new(PublicKeyBob)
|
||
|
encodeB(pubKey.Send[:], &u, &r)
|
||
|
|
||
|
// nu <- Rec(v, r)
|
||
|
var nu [SharedSecretSize]byte
|
||
|
rec(&nu, &v, &r)
|
||
|
|
||
|
// mu <- SHA3-256(nu)
|
||
|
mu := sha3.Sum256(nu[:])
|
||
|
|
||
|
// Scrub the sensitive stuff...
|
||
|
memwipe(nu[:])
|
||
|
sp.reset()
|
||
|
v.reset()
|
||
|
|
||
|
return pubKey, mu[:], nil
|
||
|
}
|
||
|
|
||
|
// KeyExchangeAlice is the Initiaitor side of the NewHope key exchange. The
|
||
|
// provided private key is obliterated prior to returning.
|
||
|
func KeyExchangeAlice(bobPk *PublicKeyBob, aliceSk *PrivateKeyAlice) ([]byte, error) {
|
||
|
var u, r, vp poly
|
||
|
|
||
|
decodeB(&u, &r, bobPk.Send[:])
|
||
|
|
||
|
// v' <- us
|
||
|
vp.pointwise(&aliceSk.sk, &u)
|
||
|
vp.invNtt()
|
||
|
|
||
|
// nu <- Rec(v', r)
|
||
|
var nu [SharedSecretSize]byte
|
||
|
rec(&nu, &vp, &r)
|
||
|
|
||
|
// mu <- Sha3-256(nu)
|
||
|
mu := sha3.Sum256(nu[:])
|
||
|
|
||
|
// Scrub the sensitive stuff...
|
||
|
memwipe(nu[:])
|
||
|
vp.reset()
|
||
|
aliceSk.Reset()
|
||
|
|
||
|
return mu[:], nil
|
||
|
}
|