diff --git a/crypto.go b/crypto.go index c33bbca..d27e222 100644 --- a/crypto.go +++ b/crypto.go @@ -20,6 +20,10 @@ import ( "github.com/cloudflare/circl/dh/sidh" "github.com/cloudflare/circl/dh/x448" + "github.com/cloudflare/circl/kem" + "github.com/cloudflare/circl/kem/kyber/kyber1024" + "github.com/cloudflare/circl/kem/kyber/kyber512" + "github.com/cloudflare/circl/kem/kyber/kyber768" "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/curve25519" ) @@ -738,6 +742,140 @@ func (s sikeScheme) setEphemeralKeyPair(skE KEMPrivateKey) { panic("SIKE cannot use a pre-set ephemeral key pair") } +//////// +// Kyber + +type kyberPublicKey = kem.PublicKey + +type kyberPrivateKey struct { + priv kem.PrivateKey +} + +func (priv kyberPrivateKey) PublicKey() KEMPublicKey { + return priv.priv.Public() +} + +type kyberScheme struct { + scheme kem.Scheme + id KEMID + newKeyFromSeed func(ikm []byte) (KEMPrivateKey, KEMPublicKey, error) +} + +func (s kyberScheme) ID() KEMID { + return s.id +} + +func (s kyberScheme) generateKeyPair(rand io.Reader) (KEMPrivateKey, KEMPublicKey, error) { + pub, priv, err := s.scheme.GenerateKeyPair() + if err != nil { + return nil, nil, err + } + + return &kyberPrivateKey{priv}, pub, nil +} + +func (s kyberScheme) DeriveKeyPair(ikm []byte) (KEMPrivateKey, KEMPublicKey, error) { + return s.newKeyFromSeed(ikm) +} + +func (s kyberScheme) SerializePublicKey(pk KEMPublicKey) []byte { + if pk == nil { + return nil + } + + out, err := pk.(kyberPublicKey).MarshalBinary() + if err != nil { + panic(err) + } + + return out +} + +func (s kyberScheme) SerializePrivateKey(sk KEMPrivateKey) []byte { + if sk == nil { + return nil + } + + out, err := sk.(*kyberPrivateKey).priv.MarshalBinary() + if err != nil { + panic(err) + } + + return out +} + +func (s kyberScheme) DeserializePublicKey(enc []byte) (KEMPublicKey, error) { + return s.scheme.UnmarshalBinaryPublicKey(enc) +} + +func (s kyberScheme) DeserializePrivateKey(enc []byte) (KEMPrivateKey, error) { + priv, err := s.scheme.UnmarshalBinaryPrivateKey(enc) + if err != nil { + return nil, err + } + + return &kyberPrivateKey{priv}, nil +} + +func (s kyberScheme) Encap(rand io.Reader, pkR KEMPublicKey) ([]byte, []byte, error) { + raw := pkR.(kyberPublicKey) + ct, ss, err := s.scheme.Encapsulate(raw) + return ss, ct, err +} + +func (s kyberScheme) Decap(enc []byte, skR KEMPrivateKey) ([]byte, error) { + raw := skR.(*kyberPrivateKey).priv + return s.scheme.Decapsulate(raw, enc) +} + +func (s kyberScheme) PublicKeySize() int { + return s.scheme.PublicKeySize() +} + +func (s kyberScheme) PrivateKeySize() int { + return s.scheme.PrivateKeySize() +} + +func (s kyberScheme) setEphemeralKeyPair(skE KEMPrivateKey) { + panic("Kyber cannot use a pre-set ephemeral key pair") +} + +func newKyber512Scheme() *kyberScheme { + return &kyberScheme{ + scheme: kyber512.Scheme(), + id: KEM_KYBER512, + newKeyFromSeed: func(ikm []byte) (KEMPrivateKey, KEMPublicKey, error) { + ikm = ikm[:kyber512.KeySeedSize] + pub, priv := kyber512.NewKeyFromSeed(ikm) + return &kyberPrivateKey{priv}, pub, nil + }, + } +} + +func newKyber768Scheme() *kyberScheme { + return &kyberScheme{ + scheme: kyber768.Scheme(), + id: KEM_KYBER768, + newKeyFromSeed: func(ikm []byte) (KEMPrivateKey, KEMPublicKey, error) { + ikm = ikm[:kyber768.KeySeedSize] + pub, priv := kyber768.NewKeyFromSeed(ikm) + return &kyberPrivateKey{priv}, pub, nil + }, + } +} + +func newKyber1024Scheme() *kyberScheme { + return &kyberScheme{ + scheme: kyber1024.Scheme(), + id: KEM_KYBER1024, + newKeyFromSeed: func(ikm []byte) (KEMPrivateKey, KEMPublicKey, error) { + ikm = ikm[:kyber1024.KeySeedSize] + pub, priv := kyber1024.NewKeyFromSeed(ikm) + return &kyberPrivateKey{priv}, pub, nil + }, + } +} + ////////// // AES-GCM @@ -909,21 +1047,27 @@ func (s hkdfScheme) OutputSize() int { type KEMID uint16 const ( - DHKEM_P256 KEMID = 0x0010 - DHKEM_P521 KEMID = 0x0012 - DHKEM_X25519 KEMID = 0x0020 - DHKEM_X448 KEMID = 0x0021 - KEM_SIKE503 KEMID = 0xFFFE - KEM_SIKE751 KEMID = 0xFFFF + DHKEM_P256 KEMID = 0x0010 + DHKEM_P521 KEMID = 0x0012 + DHKEM_X25519 KEMID = 0x0020 + DHKEM_X448 KEMID = 0x0021 + KEM_KYBER512 KEMID = 0xFFFB + KEM_KYBER768 KEMID = 0xFFFC + KEM_KYBER1024 KEMID = 0xFFFD + KEM_SIKE503 KEMID = 0xFFFE + KEM_SIKE751 KEMID = 0xFFFF ) var kems = map[KEMID]KEMScheme{ - DHKEM_X25519: &dhkemScheme{group: x25519Scheme{}}, - DHKEM_X448: &dhkemScheme{group: x448Scheme{}}, - DHKEM_P256: &dhkemScheme{group: ecdhScheme{curve: elliptic.P256(), KDF: hkdfScheme{hash: crypto.SHA256}}}, - DHKEM_P521: &dhkemScheme{group: ecdhScheme{curve: elliptic.P521(), KDF: hkdfScheme{hash: crypto.SHA512}}}, - KEM_SIKE503: &sikeScheme{field: sidh.Fp503, KDF: hkdfScheme{hash: crypto.SHA512}}, - KEM_SIKE751: &sikeScheme{field: sidh.Fp751, KDF: hkdfScheme{hash: crypto.SHA512}}, + DHKEM_X25519: &dhkemScheme{group: x25519Scheme{}}, + DHKEM_X448: &dhkemScheme{group: x448Scheme{}}, + DHKEM_P256: &dhkemScheme{group: ecdhScheme{curve: elliptic.P256(), KDF: hkdfScheme{hash: crypto.SHA256}}}, + DHKEM_P521: &dhkemScheme{group: ecdhScheme{curve: elliptic.P521(), KDF: hkdfScheme{hash: crypto.SHA512}}}, + KEM_KYBER512: newKyber512Scheme(), + KEM_KYBER768: newKyber768Scheme(), + KEM_KYBER1024: newKyber1024Scheme(), + KEM_SIKE503: &sikeScheme{field: sidh.Fp503, KDF: hkdfScheme{hash: crypto.SHA512}}, + KEM_SIKE751: &sikeScheme{field: sidh.Fp751, KDF: hkdfScheme{hash: crypto.SHA512}}, } func newKEMScheme(kemID KEMID) (KEMScheme, bool) { @@ -936,6 +1080,12 @@ func newKEMScheme(kemID KEMID) (KEMScheme, bool) { return &dhkemScheme{group: ecdhScheme{curve: elliptic.P256(), KDF: hkdfScheme{hash: crypto.SHA256}}}, true case DHKEM_P521: return &dhkemScheme{group: ecdhScheme{curve: elliptic.P521(), KDF: hkdfScheme{hash: crypto.SHA512}}}, true + case KEM_KYBER512: + return newKyber512Scheme(), true + case KEM_KYBER768: + return newKyber768Scheme(), true + case KEM_KYBER1024: + return newKyber1024Scheme(), true case KEM_SIKE503: return &sikeScheme{field: sidh.Fp503, KDF: hkdfScheme{hash: crypto.SHA512}}, true case KEM_SIKE751: diff --git a/crypto_test.go b/crypto_test.go index 7f6ba38..9a80845 100644 --- a/crypto_test.go +++ b/crypto_test.go @@ -22,6 +22,9 @@ func TestKEMSchemes(t *testing.T) { &dhkemScheme{group: x448Scheme{}}, &dhkemScheme{group: ecdhScheme{curve: elliptic.P256(), KDF: hkdfScheme{hash: crypto.SHA256}}}, &dhkemScheme{group: ecdhScheme{curve: elliptic.P521(), KDF: hkdfScheme{hash: crypto.SHA256}}}, + newKyber512Scheme(), + newKyber768Scheme(), + newKyber1024Scheme(), &sikeScheme{field: sidh.Fp503, KDF: hkdfScheme{hash: crypto.SHA512}}, &sikeScheme{field: sidh.Fp751, KDF: hkdfScheme{hash: crypto.SHA512}}, } diff --git a/go.mod b/go.mod index 77315f9..f948478 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.14 require ( github.com/cisco/go-tls-syntax v0.0.0-20200617162716-46b0cfb76b9b - github.com/cloudflare/circl v1.0.0 + github.com/cloudflare/circl v1.2.0 github.com/stretchr/testify v1.6.1 - golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a + golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd ) diff --git a/go.sum b/go.sum index 5ed3863..a40e83c 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,11 @@ +github.com/bwesterb/go-ristretto v1.2.1 h1:Xd9ZXmjKE2aY8Ub7+4bX7tXsIPsV1pIZaUlJUjI1toE= +github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cisco/go-tls-syntax v0.0.0-20200617162716-46b0cfb76b9b h1:Ves2turKTX7zruivAcUOQg155xggcbv3suVdbKCBQNM= github.com/cisco/go-tls-syntax v0.0.0-20200617162716-46b0cfb76b9b/go.mod h1:0AZAV7lYvynZQ5ErHlGMKH+4QYMyNCFd+AiL9MlrCYA= github.com/cloudflare/circl v1.0.0 h1:64b6pyfCFbYm623ncIkYGNZaOcmIbyd+CjyMi2L9vdI= github.com/cloudflare/circl v1.0.0/go.mod h1:MhjB3NEEhJbTOdLLq964NIUisXDxaE1WkQPUxtgZXiY= +github.com/cloudflare/circl v1.2.0 h1:NheeISPSUcYftKlfrLuOo4T62FkmD4t4jviLfFFYaec= +github.com/cloudflare/circl v1.2.0/go.mod h1:Ch2UgYr6ti2KTtlejELlROl0YIYj7SLjAC8M+INXlMk= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -12,12 +16,23 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed h1:uPxWBzB3+mlnjy9W58qY1j/cjyFjutgw/Vhan2zLy/A= golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc= +golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=