package hpke import ( "bytes" "crypto" "crypto/aes" "crypto/cipher" "crypto/elliptic" "crypto/hmac" "crypto/rand" "crypto/subtle" "encoding/binary" "fmt" "io" "math/big" mrand "math/rand" _ "crypto/sha256" _ "crypto/sha512" "git.schwanenlied.me/yawning/x448.git" "github.com/cloudflare/circl/dh/sidh" "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/curve25519" ) //////// // DHKEM type dhScheme interface { ID() KEMID DeriveKeyPair(ikm []byte) (KEMPrivateKey, KEMPublicKey, error) Serialize(pk KEMPublicKey) []byte Deserialize(enc []byte) (KEMPublicKey, error) DH(priv KEMPrivateKey, pub KEMPublicKey) ([]byte, error) PublicKeySize() int PrivateKeySize() int SerializePrivate(sk KEMPrivateKey) []byte DeserializePrivate(enc []byte) (KEMPrivateKey, error) internalKDF() KDFScheme } type dhkemScheme struct { group dhScheme skE KEMPrivateKey } func (s dhkemScheme) ID() KEMID { return s.group.ID() } func (s dhkemScheme) DeriveKeyPair(ikm []byte) (KEMPrivateKey, KEMPublicKey, error) { return s.group.DeriveKeyPair(ikm) } func (s dhkemScheme) Serialize(pk KEMPublicKey) []byte { return s.group.Serialize(pk) } func (s dhkemScheme) SerializePrivate(sk KEMPrivateKey) []byte { return s.group.SerializePrivate(sk) } func (s dhkemScheme) Deserialize(enc []byte) (KEMPublicKey, error) { return s.group.Deserialize(enc) } func (s dhkemScheme) DeserializePrivate(enc []byte) (KEMPrivateKey, error) { return s.group.DeserializePrivate(enc) } func (s *dhkemScheme) setEphemeralKeyPair(skE KEMPrivateKey) { s.skE = skE } func (s dhkemScheme) getEphemeralKeyPair(rand io.Reader) (KEMPrivateKey, KEMPublicKey, error) { if s.skE != nil { return s.skE, s.skE.PublicKey(), nil } ikm := make([]byte, s.PrivateKeySize()) rand.Read(ikm) return s.group.DeriveKeyPair(ikm) } func (s dhkemScheme) extractAndExpand(dh []byte, kemContext []byte, Nsecret int) []byte { suiteID := kemSuiteFromID(s.ID()) eae_prk := s.group.internalKDF().LabeledExtract(nil, suiteID, "eae_prk", dh) return s.group.internalKDF().LabeledExpand(eae_prk, suiteID, "shared_secret", kemContext, Nsecret) } func (s dhkemScheme) Encap(rand io.Reader, pkR KEMPublicKey) ([]byte, []byte, error) { skE, pkE, err := s.getEphemeralKeyPair(rand) if err != nil { return nil, nil, err } dh, err := s.group.DH(skE, pkR) if err != nil { return nil, nil, err } enc := s.group.Serialize(pkE) pkRm := s.group.Serialize(pkR) kemContext := make([]byte, len(enc)+len(pkRm)) copy(kemContext, enc) copy(kemContext[len(enc):], pkRm) Nsecret := s.group.internalKDF().OutputSize() sharedSecret := s.extractAndExpand(dh, kemContext, Nsecret) return sharedSecret, enc, nil } func (s dhkemScheme) Decap(enc []byte, skR KEMPrivateKey) ([]byte, error) { pkE, err := s.group.Deserialize(enc) if err != nil { return nil, err } dh, err := s.group.DH(skR, pkE) if err != nil { return nil, err } pkRm := s.group.Serialize(skR.PublicKey()) kemContext := make([]byte, len(enc)+len(pkRm)) copy(kemContext, enc) copy(kemContext[len(enc):], pkRm) Nsecret := s.group.internalKDF().OutputSize() sharedSecret := s.extractAndExpand(dh, kemContext, Nsecret) return sharedSecret, nil } func (s dhkemScheme) AuthEncap(rand io.Reader, pkR KEMPublicKey, skS KEMPrivateKey) ([]byte, []byte, error) { skE, pkE, err := s.getEphemeralKeyPair(rand) if err != nil { return nil, nil, err } dhER, err := s.group.DH(skE, pkR) if err != nil { return nil, nil, err } dhIR, err := s.group.DH(skS, pkR) if err != nil { return nil, nil, err } dh := append(dhER, dhIR...) enc := s.group.Serialize(pkE) pkRm := s.group.Serialize(pkR) pkSm := s.group.Serialize(skS.PublicKey()) Nenc := len(enc) Npk := len(pkRm) Nsk := len(pkSm) kemContext := make([]byte, Nenc+Npk+Nsk) copy(kemContext[:Nenc], enc) copy(kemContext[Nenc:Nenc+Npk], pkRm) copy(kemContext[Nenc+Npk:], pkSm) Nsecret := s.group.internalKDF().OutputSize() sharedSecret := s.extractAndExpand(dh, kemContext, Nsecret) return sharedSecret, enc, nil } func (s dhkemScheme) AuthDecap(enc []byte, skR KEMPrivateKey, pkS KEMPublicKey) ([]byte, error) { pkE, err := s.group.Deserialize(enc) if err != nil { return nil, err } dhER, err := s.group.DH(skR, pkE) if err != nil { return nil, err } dhIR, err := s.group.DH(skR, pkS) if err != nil { return nil, err } dh := append(dhER, dhIR...) pkRm := s.group.Serialize(skR.PublicKey()) pkSm := s.group.Serialize(pkS) Nenc := len(enc) Npk := len(pkRm) Nsk := len(pkSm) kemContext := make([]byte, Nenc+Npk+Nsk) copy(kemContext[:Nenc], enc) copy(kemContext[Nenc:Nenc+Npk], pkRm) copy(kemContext[Nenc+Npk:], pkSm) Nsecret := s.group.internalKDF().OutputSize() sharedSecret := s.extractAndExpand(dh, kemContext, Nsecret) return sharedSecret, nil } func (s dhkemScheme) PublicKeySize() int { return s.group.PublicKeySize() } func (s dhkemScheme) PrivateKeySize() int { return s.group.PrivateKeySize() } //////////////////////// // ECDH with NIST curves type ecdhPrivateKey struct { curve elliptic.Curve d []byte x, y *big.Int } func (priv ecdhPrivateKey) PublicKey() KEMPublicKey { return &ecdhPublicKey{priv.curve, priv.x, priv.y} } type ecdhPublicKey struct { curve elliptic.Curve x, y *big.Int } type ecdhScheme struct { curve elliptic.Curve KDF KDFScheme skE KEMPrivateKey } func (s ecdhScheme) internalKDF() KDFScheme { return s.KDF } func (s ecdhScheme) ID() KEMID { switch s.curve.Params().Name { case "P-256": return DHKEM_P256 case "P-521": return DHKEM_P521 } panic(fmt.Sprintf("Unsupported curve: %s", s.curve.Params().Name)) } func (s ecdhScheme) privateKeyBitmask() uint8 { switch s.curve.Params().Name { case "P-256": return 0xFF case "P-521": return 0x01 } panic(fmt.Sprintf("Unsupported curve: %s", s.curve.Params().Name)) } func (s ecdhScheme) DeriveKeyPair(ikm []byte) (KEMPrivateKey, KEMPublicKey, error) { suiteID := kemSuiteFromID(s.ID()) dkp_prk := s.KDF.LabeledExtract(nil, suiteID, "dkp_prk", ikm) counter := 0 for { if counter > 255 { return nil, nil, fmt.Errorf("Error deriving key pair") } bytes := s.KDF.LabeledExpand(dkp_prk, suiteID, "candidate", []byte{uint8(counter)}, s.PrivateKeySize()) bytes[0] = bytes[0] & s.privateKeyBitmask() sk, err := s.DeserializePrivate(bytes) if err == nil { return sk, sk.PublicKey(), nil } counter = counter + 1 } return nil, nil, fmt.Errorf("Error deriving key pair") } func (s ecdhScheme) Serialize(pk KEMPublicKey) []byte { if pk == nil { return nil } raw := pk.(*ecdhPublicKey) return elliptic.Marshal(raw.curve, raw.x, raw.y) } func (s ecdhScheme) SerializePrivate(sk KEMPrivateKey) []byte { if sk == nil { return nil } raw := sk.(*ecdhPrivateKey) copied := make([]byte, len(raw.d)) copy(copied, raw.d) return copied } func (s ecdhScheme) Deserialize(enc []byte) (KEMPublicKey, error) { x, y := elliptic.Unmarshal(s.curve, enc) if x == nil { return nil, fmt.Errorf("Error deserializing public key") } return &ecdhPublicKey{s.curve, x, y}, nil } func (s ecdhScheme) DeserializePrivate(enc []byte) (KEMPrivateKey, error) { if enc == nil { return nil, fmt.Errorf("Invalid input") } x, y := s.curve.Params().ScalarBaseMult(enc) return &ecdhPrivateKey{s.curve, enc, x, y}, nil } func (s ecdhScheme) DH(priv KEMPrivateKey, pub KEMPublicKey) ([]byte, error) { ecdhPriv, ok := priv.(*ecdhPrivateKey) if !ok { return nil, fmt.Errorf("Private key not suitable for ECDH") } ecdhPub, ok := pub.(*ecdhPublicKey) if !ok { return nil, fmt.Errorf("Public key not suitable for ECDH") } x, _ := s.curve.Params().ScalarMult(ecdhPub.x, ecdhPub.y, ecdhPriv.d) xx := x.Bytes() size := (s.curve.Params().BitSize + 7) >> 3 pad := make([]byte, size-len(xx)) dh := append(pad, xx...) return dh, nil } func (s ecdhScheme) PublicKeySize() int { feSize := (s.curve.Params().BitSize + 7) >> 3 return 1 + 2*feSize } func (s ecdhScheme) PrivateKeySize() int { return (s.curve.Params().BitSize + 7) >> 3 } /////////////////// // ECDH with X25519 type x25519PrivateKey struct { val [32]byte } func (priv x25519PrivateKey) PublicKey() KEMPublicKey { pub := &x25519PublicKey{} curve25519.ScalarBaseMult(&pub.val, &priv.val) return pub } type x25519PublicKey struct { val [32]byte } type x25519Scheme struct { skE KEMPrivateKey } func (s x25519Scheme) internalKDF() KDFScheme { return hkdfScheme{hash: crypto.SHA256} } func (s x25519Scheme) ID() KEMID { return DHKEM_X25519 } func (s x25519Scheme) DeriveKeyPair(ikm []byte) (KEMPrivateKey, KEMPublicKey, error) { suiteID := kemSuiteFromID(s.ID()) dkp_prk := s.internalKDF().LabeledExtract(nil, suiteID, "dkp_prk", ikm) sk_bytes := s.internalKDF().LabeledExpand(dkp_prk, suiteID, "sk", nil, s.PrivateKeySize()) sk, err := s.DeserializePrivate(sk_bytes) if err != nil { return nil, nil, err } else { return sk, sk.PublicKey(), nil } } func (s x25519Scheme) Serialize(pk KEMPublicKey) []byte { if pk == nil { return nil } raw := pk.(*x25519PublicKey) return raw.val[:] } func (s x25519Scheme) SerializePrivate(sk KEMPrivateKey) []byte { if sk == nil { return nil } raw := sk.(*x25519PrivateKey) return raw.val[:] } func (s x25519Scheme) Deserialize(enc []byte) (KEMPublicKey, error) { if len(enc) != 32 { return nil, fmt.Errorf("Error deserializing X25519 public key") } pub := &x25519PublicKey{} copy(pub.val[:], enc) return pub, nil } func (s x25519Scheme) DeserializePrivate(enc []byte) (KEMPrivateKey, error) { if enc == nil { return nil, fmt.Errorf("Invalid input") } if len(enc) != 32 { return nil, fmt.Errorf("Error deserializing X25519 private key") } key := &x25519PrivateKey{} copy(key.val[:], enc[0:32]) return key, nil } func (s x25519Scheme) DH(priv KEMPrivateKey, pub KEMPublicKey) ([]byte, error) { xPriv, ok := priv.(*x25519PrivateKey) if !ok { return nil, fmt.Errorf("Private key not suitable for X25519: %+v", priv) } xPub, ok := pub.(*x25519PublicKey) if !ok { return nil, fmt.Errorf("Private key not suitable for X25519") } sharedSecret, err := curve25519.X25519(xPriv.val[:], xPub.val[:]) return sharedSecret, err } func (s x25519Scheme) PublicKeySize() int { return 32 } func (s x25519Scheme) PrivateKeySize() int { return 32 } /////////////////// // ECDH with X448 type x448PrivateKey struct { val [56]byte } func (priv x448PrivateKey) PublicKey() KEMPublicKey { pub := &x448PublicKey{} x448.ScalarBaseMult(&pub.val, &priv.val) return pub } type x448PublicKey struct { val [56]byte } type x448Scheme struct { skE KEMPrivateKey } func (s x448Scheme) internalKDF() KDFScheme { return hkdfScheme{hash: crypto.SHA512} } func (s x448Scheme) ID() KEMID { return DHKEM_X448 } func (s x448Scheme) DeriveKeyPair(ikm []byte) (KEMPrivateKey, KEMPublicKey, error) { suiteID := kemSuiteFromID(s.ID()) dkp_prk := s.internalKDF().LabeledExtract(nil, suiteID, "dkp_prk", ikm) sk_bytes := s.internalKDF().LabeledExpand(dkp_prk, suiteID, "sk", nil, s.PrivateKeySize()) sk, err := s.DeserializePrivate(sk_bytes) if err != nil { return nil, nil, err } else { return sk, sk.PublicKey(), nil } } func (s x448Scheme) Serialize(pk KEMPublicKey) []byte { if pk == nil { return nil } raw := pk.(*x448PublicKey) return raw.val[:] } func (s x448Scheme) SerializePrivate(sk KEMPrivateKey) []byte { if sk == nil { return nil } raw := sk.(*x448PrivateKey) return raw.val[:] } func (s x448Scheme) Deserialize(enc []byte) (KEMPublicKey, error) { if len(enc) != 56 { return nil, fmt.Errorf("Error deserializing X448 public key") } pub := &x448PublicKey{} copy(pub.val[:], enc) return pub, nil } func (s x448Scheme) DeserializePrivate(enc []byte) (KEMPrivateKey, error) { if enc == nil { return nil, fmt.Errorf("Invalid input") } if len(enc) != 56 { return nil, fmt.Errorf("Error deserializing X448 private key") } key := &x448PrivateKey{} copy(key.val[:], enc[0:56]) return key, nil } func (s x448Scheme) DH(priv KEMPrivateKey, pub KEMPublicKey) ([]byte, error) { xPriv, ok := priv.(*x448PrivateKey) if !ok { return nil, fmt.Errorf("Private key not suitable for X448: %+v", priv) } xPub, ok := pub.(*x448PublicKey) if !ok { return nil, fmt.Errorf("Public key not suitable for X448: %+v", pub) } var sharedSecret, zero [56]byte x448.ScalarMult(&sharedSecret, &xPriv.val, &xPub.val) if subtle.ConstantTimeCompare(sharedSecret[:], zero[:]) == 1 { return nil, fmt.Errorf("bad input point: low order point") } return sharedSecret[:], nil } func (s x448Scheme) PublicKeySize() int { return 56 } func (s x448Scheme) PrivateKeySize() int { return 56 } /////// // SIKE type sikePublicKey struct { field uint8 pub *sidh.PublicKey } type sikePrivateKey struct { field uint8 priv *sidh.PrivateKey pub *sidh.PublicKey } func (priv sikePrivateKey) PublicKey() KEMPublicKey { return &sikePublicKey{priv.field, priv.pub} } type sikeScheme struct { field uint8 KDF KDFScheme } func (s sikeScheme) internalKDF() KDFScheme { return s.KDF } func (s sikeScheme) ID() KEMID { switch s.field { case sidh.Fp503: return KEM_SIKE503 case sidh.Fp751: return KEM_SIKE751 } panic(fmt.Sprintf("Unsupported field: %d", s.field)) } func (s sikeScheme) generateKeyPair(rand io.Reader) (KEMPrivateKey, KEMPublicKey, error) { rawPriv := sidh.NewPrivateKey(s.field, sidh.KeyVariantSike) err := rawPriv.Generate(rand) if err != nil { return nil, nil, err } rawPub := sidh.NewPublicKey(s.field, sidh.KeyVariantSike) rawPriv.GeneratePublicKey(rawPub) priv := &sikePrivateKey{s.field, rawPriv, rawPub} return priv, priv.PublicKey(), nil } func (s sikeScheme) DeriveKeyPair(ikm []byte) (KEMPrivateKey, KEMPublicKey, error) { // Note: DeriveKeyPair is not specified for SIKE, so we just use IKM to // seed a DRBG, and then re-use the other APIs for generating key pairs // from randomness. var seed int64 ikmReader := bytes.NewReader(ikm) if err := binary.Read(ikmReader, binary.BigEndian, &seed); err != nil { return nil, nil, fmt.Errorf("Error deriving key pair") } source := mrand.NewSource(seed) return s.generateKeyPair(mrand.New(source)) } func (s sikeScheme) Serialize(pk KEMPublicKey) []byte { if pk == nil { return nil } raw := pk.(*sikePublicKey) out := make([]byte, raw.pub.Size()) raw.pub.Export(out) return out } func (s sikeScheme) SerializePrivate(sk KEMPrivateKey) []byte { panic("Not implemented") return nil } func (s sikeScheme) Deserialize(enc []byte) (KEMPublicKey, error) { rawPub := sidh.NewPublicKey(s.field, sidh.KeyVariantSike) if len(enc) != rawPub.Size() { return nil, fmt.Errorf("Invalid public key size: got %d, expected %d", len(enc), rawPub.Size()) } err := rawPub.Import(enc) if err != nil { return nil, err } return &sikePublicKey{s.field, rawPub}, nil } func (s sikeScheme) DeserializePrivate(enc []byte) (KEMPrivateKey, error) { panic("Not implemented") return nil, nil } func (s sikeScheme) newKEM(rand io.Reader) (*sidh.KEM, error) { switch s.field { case sidh.Fp503: return sidh.NewSike503(rand), nil case sidh.Fp751: return sidh.NewSike751(rand), nil } return nil, fmt.Errorf("Invalid field") } func (s sikeScheme) Encap(rand io.Reader, pkR KEMPublicKey) ([]byte, []byte, error) { raw := pkR.(*sikePublicKey) kem, err := s.newKEM(rand) if err != nil { return nil, nil, err } enc := make([]byte, kem.CiphertextSize()) sharedSecret := make([]byte, s.KDF.OutputSize()) err = kem.Encapsulate(enc, sharedSecret, raw.pub) if err != nil { return nil, nil, err } return sharedSecret, enc, nil } type panicReader struct{} func (p panicReader) Read(unused []byte) (int, error) { panic("Should not read") } func (s sikeScheme) Decap(enc []byte, skR KEMPrivateKey) ([]byte, error) { raw := skR.(*sikePrivateKey) kem, err := s.newKEM(panicReader{}) if err != nil { return nil, err } sharedSecret := make([]byte, s.KDF.OutputSize()) err = kem.Decapsulate(sharedSecret, raw.priv, raw.pub, enc) if err != nil { return nil, err } return sharedSecret, nil } func (s sikeScheme) PublicKeySize() int { rawPub := sidh.NewPublicKey(s.field, sidh.KeyVariantSike) return rawPub.Size() } func (s sikeScheme) PrivateKeySize() int { rawPriv := sidh.NewPrivateKey(s.field, sidh.KeyVariantSike) err := rawPriv.Generate(rand.Reader) if err != nil { panic("PrivateKeySize failed") } return rawPriv.Size() } func (s sikeScheme) setEphemeralKeyPair(skE KEMPrivateKey) { panic("SIKE cannot use a pre-set ephemeral key pair") } ////////// // AES-GCM type aesgcmScheme struct { keySize int } func (s aesgcmScheme) ID() AEADID { switch s.keySize { case 16: return AEAD_AESGCM128 case 32: return AEAD_AESGCM256 } panic(fmt.Sprintf("Unsupported key size: %d", s.keySize)) } func (s aesgcmScheme) New(key []byte) (cipher.AEAD, error) { if len(key) != s.keySize { return nil, fmt.Errorf("Incorrect key size %d != %d", len(key), s.keySize) } block, err := aes.NewCipher(key) if err != nil { return nil, err } return cipher.NewGCM(block) } func (s aesgcmScheme) KeySize() int { return s.keySize } func (s aesgcmScheme) NonceSize() int { return 12 } ////////// // ChaCha20-Poly1305 type chachaPolyScheme struct { } func (s chachaPolyScheme) ID() AEADID { return AEAD_CHACHA20POLY1305 } func (s chachaPolyScheme) New(key []byte) (cipher.AEAD, error) { return chacha20poly1305.New(key) } func (s chachaPolyScheme) KeySize() int { return chacha20poly1305.KeySize } func (s chachaPolyScheme) NonceSize() int { return chacha20poly1305.NonceSize } /////// // HKDF type hkdfScheme struct { hash crypto.Hash } func (s hkdfScheme) ID() KDFID { switch s.hash { case crypto.SHA256: return KDF_HKDF_SHA256 case crypto.SHA384: return KDF_HKDF_SHA384 case crypto.SHA512: return KDF_HKDF_SHA512 } panic(fmt.Sprintf("Unsupported hash: %d", s.hash)) } func (s hkdfScheme) Hash(message []byte) []byte { h := s.hash.New() h.Write(message) return h.Sum(nil) } func (s hkdfScheme) Extract(salt, ikm []byte) []byte { saltOrZero := salt // if [salt is] not provided, it is set to a string of HashLen zeros if salt == nil { saltOrZero = make([]byte, s.hash.Size()) } h := hmac.New(s.hash.New, saltOrZero) h.Write(ikm) return h.Sum(nil) } func (s hkdfScheme) Expand(prk, info []byte, outLen int) []byte { out := []byte{} T := []byte{} i := byte(1) for len(out) < outLen { block := append(T, info...) block = append(block, i) h := hmac.New(s.hash.New, prk) h.Write(block) T = h.Sum(nil) out = append(out, T...) i++ } return out[:outLen] } func (s hkdfScheme) LabeledExtract(salt []byte, suiteID []byte, label string, ikm []byte) []byte { labeledIKM := append([]byte(rfcLabel), suiteID...) labeledIKM = append(labeledIKM, []byte(label)...) labeledIKM = append(labeledIKM, ikm...) return s.Extract(salt, labeledIKM) } func (s hkdfScheme) LabeledExpand(prk []byte, suiteID []byte, label string, info []byte, L int) []byte { if L > (1 << 16) { panic("Expand length cannot be larger than 2^16") } lengthBuffer := make([]byte, 2) binary.BigEndian.PutUint16(lengthBuffer, uint16(L)) labeledLength := append(lengthBuffer, []byte(rfcLabel)...) labeledInfo := append(labeledLength, suiteID...) labeledInfo = append(labeledInfo, []byte(label)...) labeledInfo = append(labeledInfo, info...) return s.Expand(prk, labeledInfo, L) } func (s hkdfScheme) OutputSize() int { return s.hash.Size() } /////////////////////////// // Pre-defined KEM identifiers 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 ) 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}}, } func newKEMScheme(kemID KEMID) (KEMScheme, bool) { switch kemID { case DHKEM_X25519: return &dhkemScheme{group: x25519Scheme{}}, true case DHKEM_X448: return &dhkemScheme{group: x448Scheme{}}, true case DHKEM_P256: 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_SIKE503: return &sikeScheme{field: sidh.Fp503, KDF: hkdfScheme{hash: crypto.SHA512}}, true case KEM_SIKE751: return &sikeScheme{field: sidh.Fp751, KDF: hkdfScheme{hash: crypto.SHA512}}, true default: return nil, false } } /////////////////////////// // Pre-defined KDF identifiers type KDFID uint16 const ( KDF_HKDF_SHA256 KDFID = 0x0001 KDF_HKDF_SHA384 KDFID = 0x0002 KDF_HKDF_SHA512 KDFID = 0x0003 ) var kdfs = map[KDFID]KDFScheme{ KDF_HKDF_SHA256: hkdfScheme{hash: crypto.SHA256}, KDF_HKDF_SHA384: hkdfScheme{hash: crypto.SHA384}, KDF_HKDF_SHA512: hkdfScheme{hash: crypto.SHA512}, } /////////////////////////// // Pre-defined AEAD identifiers type AEADID uint16 const ( AEAD_AESGCM128 AEADID = 0x0001 AEAD_AESGCM256 AEADID = 0x0002 AEAD_CHACHA20POLY1305 AEADID = 0x0003 ) var aeads = map[AEADID]AEADScheme{ AEAD_AESGCM128: aesgcmScheme{keySize: 16}, AEAD_AESGCM256: aesgcmScheme{keySize: 32}, AEAD_CHACHA20POLY1305: chachaPolyScheme{}, } func AssembleCipherSuite(kemID KEMID, kdfID KDFID, aeadID AEADID) (CipherSuite, error) { kem, ok := newKEMScheme(kemID) if !ok { return CipherSuite{}, fmt.Errorf("Unknown KEM id") } kdf, ok := kdfs[kdfID] if !ok { return CipherSuite{}, fmt.Errorf("Unknown KDF id") } aead, ok := aeads[aeadID] if !ok { return CipherSuite{}, fmt.Errorf("Unknown AEAD id") } return CipherSuite{ KEM: kem, KDF: kdf, AEAD: aead, }, nil } ////////// // Helpers func kemSuiteFromID(id KEMID) []byte { idBuffer := make([]byte, 2) binary.BigEndian.PutUint16(idBuffer, uint16(id)) return append([]byte("KEM"), idBuffer...) }