Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add X25519Kyber768Draft00 experimental HPKE KEM #421

Merged
merged 1 commit into from Apr 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 8 additions & 5 deletions hpke/aead.go
Expand Up @@ -7,11 +7,14 @@ import (

type encdecContext struct {
// Serialized parameters
suite Suite
exporterSecret []byte
key []byte
baseNonce []byte
sequenceNumber []byte
suite Suite
sharedSecret []byte
chris-wood marked this conversation as resolved.
Show resolved Hide resolved
secret []byte
keyScheduleContext []byte
exporterSecret []byte
key []byte
baseNonce []byte
sequenceNumber []byte

// Operational parameters
cipher.AEAD
Expand Down
4 changes: 2 additions & 2 deletions hpke/aead_test.go
Expand Up @@ -44,12 +44,12 @@ func setupAeadTest() (*sealContext, *openContext, error) {

sealer := &sealContext{
&encdecContext{
suite, nil, nil, baseNonce, make([]byte, Nn), aead, make([]byte, Nn),
suite, nil, nil, nil, nil, nil, baseNonce, make([]byte, Nn), aead, make([]byte, Nn),
},
}
opener := &openContext{
&encdecContext{
suite, nil, nil, baseNonce, make([]byte, Nn), aead, make([]byte, Nn),
suite, nil, nil, nil, nil, nil, baseNonce, make([]byte, Nn), aead, make([]byte, Nn),
},
}
return sealer, opener, nil
Expand Down
82 changes: 35 additions & 47 deletions hpke/algs.go
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/cloudflare/circl/dh/x448"
"github.com/cloudflare/circl/ecc/p384"
"github.com/cloudflare/circl/kem"
"github.com/cloudflare/circl/kem/kyber/kyber768"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/hkdf"
)
Expand All @@ -35,6 +36,9 @@ const (
// KEM_X448_HKDF_SHA512 is a KEM using X448 Diffie-Hellman function and
// HKDF with SHA-512.
KEM_X448_HKDF_SHA512 KEM = 0x21
// KEM_X25519_KYBER768_DRAFT00 is a hybrid KEM built on DHKEM(X25519, HKDF-SHA256)
// and Kyber768Draft00
KEM_X25519_KYBER768_DRAFT00 KEM = 0x30
)

// IsValid returns true if the KEM identifier is supported by the HPKE package.
Expand All @@ -44,7 +48,8 @@ func (k KEM) IsValid() bool {
KEM_P384_HKDF_SHA384,
KEM_P521_HKDF_SHA512,
KEM_X25519_HKDF_SHA256,
KEM_X448_HKDF_SHA512:
KEM_X448_HKDF_SHA512,
KEM_X25519_KYBER768_DRAFT00:
return true
default:
return false
Expand All @@ -65,32 +70,8 @@ func (k KEM) Scheme() kem.AuthScheme {
return dhkemx25519hkdfsha256
case KEM_X448_HKDF_SHA512:
return dhkemx448hkdfsha512
default:
panic(ErrInvalidKEM)
}
}

func (k KEM) validatePublicKey(pk kem.PublicKey) bool {
switch k {
case KEM_P256_HKDF_SHA256, KEM_P384_HKDF_SHA384, KEM_P521_HKDF_SHA512:
pub, ok := pk.(*shortKEMPubKey)
return ok && k == pub.scheme.id && pub.Validate()
case KEM_X25519_HKDF_SHA256, KEM_X448_HKDF_SHA512:
pub, ok := pk.(*xKEMPubKey)
return ok && k == pub.scheme.id && pub.Validate()
default:
panic(ErrInvalidKEM)
}
}

func (k KEM) validatePrivateKey(sk kem.PrivateKey) bool {
switch k {
case KEM_P256_HKDF_SHA256, KEM_P384_HKDF_SHA384, KEM_P521_HKDF_SHA512:
priv, ok := sk.(*shortKEMPrivKey)
return ok && k == priv.scheme.id && priv.Validate()
case KEM_X25519_HKDF_SHA256, KEM_X448_HKDF_SHA512:
priv, ok := sk.(*xKEMPrivKey)
return ok && k == priv.scheme.id && priv.Validate()
case KEM_X25519_KYBER768_DRAFT00:
return hybridkemX25519Kyber768
default:
panic(ErrInvalidKEM)
}
Expand Down Expand Up @@ -243,36 +224,43 @@ func (a AEAD) CipherLen(mLen uint) uint {
var (
dhkemp256hkdfsha256, dhkemp384hkdfsha384, dhkemp521hkdfsha512 shortKEM
dhkemx25519hkdfsha256, dhkemx448hkdfsha512 xKEM
hybridkemX25519Kyber768 hybridKEM
)

func init() {
dhkemp256hkdfsha256.Curve = elliptic.P256()
dhkemp256hkdfsha256.kemBase.id = KEM_P256_HKDF_SHA256
dhkemp256hkdfsha256.kemBase.name = "HPKE_KEM_P256_HKDF_SHA256"
dhkemp256hkdfsha256.kemBase.Hash = crypto.SHA256
dhkemp256hkdfsha256.kemBase.dhKEM = dhkemp256hkdfsha256
dhkemp256hkdfsha256.dhKemBase.id = KEM_P256_HKDF_SHA256
dhkemp256hkdfsha256.dhKemBase.name = "HPKE_KEM_P256_HKDF_SHA256"
dhkemp256hkdfsha256.dhKemBase.Hash = crypto.SHA256
dhkemp256hkdfsha256.dhKemBase.dhKEM = dhkemp256hkdfsha256

dhkemp384hkdfsha384.Curve = p384.P384()
dhkemp384hkdfsha384.kemBase.id = KEM_P384_HKDF_SHA384
dhkemp384hkdfsha384.kemBase.name = "HPKE_KEM_P384_HKDF_SHA384"
dhkemp384hkdfsha384.kemBase.Hash = crypto.SHA384
dhkemp384hkdfsha384.kemBase.dhKEM = dhkemp384hkdfsha384
dhkemp384hkdfsha384.dhKemBase.id = KEM_P384_HKDF_SHA384
dhkemp384hkdfsha384.dhKemBase.name = "HPKE_KEM_P384_HKDF_SHA384"
dhkemp384hkdfsha384.dhKemBase.Hash = crypto.SHA384
dhkemp384hkdfsha384.dhKemBase.dhKEM = dhkemp384hkdfsha384

dhkemp521hkdfsha512.Curve = elliptic.P521()
dhkemp521hkdfsha512.kemBase.id = KEM_P521_HKDF_SHA512
dhkemp521hkdfsha512.kemBase.name = "HPKE_KEM_P521_HKDF_SHA512"
dhkemp521hkdfsha512.kemBase.Hash = crypto.SHA512
dhkemp521hkdfsha512.kemBase.dhKEM = dhkemp521hkdfsha512
dhkemp521hkdfsha512.dhKemBase.id = KEM_P521_HKDF_SHA512
dhkemp521hkdfsha512.dhKemBase.name = "HPKE_KEM_P521_HKDF_SHA512"
dhkemp521hkdfsha512.dhKemBase.Hash = crypto.SHA512
dhkemp521hkdfsha512.dhKemBase.dhKEM = dhkemp521hkdfsha512

dhkemx25519hkdfsha256.size = x25519.Size
dhkemx25519hkdfsha256.kemBase.id = KEM_X25519_HKDF_SHA256
dhkemx25519hkdfsha256.kemBase.name = "HPKE_KEM_X25519_HKDF_SHA256"
dhkemx25519hkdfsha256.kemBase.Hash = crypto.SHA256
dhkemx25519hkdfsha256.kemBase.dhKEM = dhkemx25519hkdfsha256
dhkemx25519hkdfsha256.dhKemBase.id = KEM_X25519_HKDF_SHA256
dhkemx25519hkdfsha256.dhKemBase.name = "HPKE_KEM_X25519_HKDF_SHA256"
dhkemx25519hkdfsha256.dhKemBase.Hash = crypto.SHA256
dhkemx25519hkdfsha256.dhKemBase.dhKEM = dhkemx25519hkdfsha256

dhkemx448hkdfsha512.size = x448.Size
dhkemx448hkdfsha512.kemBase.id = KEM_X448_HKDF_SHA512
dhkemx448hkdfsha512.kemBase.name = "HPKE_KEM_X448_HKDF_SHA512"
dhkemx448hkdfsha512.kemBase.Hash = crypto.SHA512
dhkemx448hkdfsha512.kemBase.dhKEM = dhkemx448hkdfsha512
dhkemx448hkdfsha512.dhKemBase.id = KEM_X448_HKDF_SHA512
dhkemx448hkdfsha512.dhKemBase.name = "HPKE_KEM_X448_HKDF_SHA512"
dhkemx448hkdfsha512.dhKemBase.Hash = crypto.SHA512
dhkemx448hkdfsha512.dhKemBase.dhKEM = dhkemx448hkdfsha512

hybridkemX25519Kyber768.kemBase.id = KEM_X25519_KYBER768_DRAFT00
hybridkemX25519Kyber768.kemBase.name = "HPKE_KEM_X25519_KYBER768_HKDF_SHA256"
hybridkemX25519Kyber768.kemBase.Hash = crypto.SHA256
hybridkemX25519Kyber768.kemA = dhkemx25519hkdfsha256
hybridkemX25519Kyber768.kemB = kyber768.Scheme()
}
26 changes: 1 addition & 25 deletions hpke/hpke.go
Expand Up @@ -105,10 +105,6 @@ type Sender struct {

// NewSender creates a Sender with knowledge of the receiver's public-key.
func (suite Suite) NewSender(pkR kem.PublicKey, info []byte) (*Sender, error) {
if !suite.kemID.validatePublicKey(pkR) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As part of this checking, the validation also checks whether the KEM in the suite is compatible with the KEM key.

The same applies to other checking.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's address that in a separate PR?

return nil, ErrInvalidKEMPublicKey
}

return &Sender{
state: state{Suite: suite, info: info},
pkR: pkR,
Expand All @@ -127,10 +123,6 @@ func (s *Sender) Setup(rnd io.Reader) (enc []byte, seal Sealer, err error) {
func (s *Sender) SetupAuth(rnd io.Reader, skS kem.PrivateKey) (
enc []byte, seal Sealer, err error,
) {
if !s.kemID.validatePrivateKey(skS) {
return nil, nil, ErrInvalidKEMPrivateKey
}

s.modeID = modeAuth
s.state.skS = skS
return s.allSetup(rnd)
Expand All @@ -152,10 +144,6 @@ func (s *Sender) SetupPSK(rnd io.Reader, psk, pskID []byte) (
func (s *Sender) SetupAuthPSK(rnd io.Reader, skS kem.PrivateKey, psk, pskID []byte) (
enc []byte, seal Sealer, err error,
) {
if !s.kemID.validatePrivateKey(skS) {
return nil, nil, ErrInvalidKEMPrivateKey
}

s.modeID = modeAuthPSK
s.state.skS = skS
s.state.psk = psk
Expand All @@ -174,10 +162,6 @@ type Receiver struct {
func (suite Suite) NewReceiver(skR kem.PrivateKey, info []byte) (
*Receiver, error,
) {
if !suite.kemID.validatePrivateKey(skR) {
return nil, ErrInvalidKEMPrivateKey
}

return &Receiver{state: state{Suite: suite, info: info}, skR: skR}, nil
}

Expand All @@ -192,10 +176,6 @@ func (r *Receiver) Setup(enc []byte) (Opener, error) {
// SetupAuth generates a new HPKE context used for Auth Mode encryption.
// SetupAuth takes an encapsulated key and a public key, and returns an Opener.
func (r *Receiver) SetupAuth(enc []byte, pkS kem.PublicKey) (Opener, error) {
if !r.kemID.validatePublicKey(pkS) {
return nil, ErrInvalidKEMPublicKey
}

r.modeID = modeAuth
r.enc = enc
r.state.pkS = pkS
Expand All @@ -219,10 +199,6 @@ func (r *Receiver) SetupPSK(enc, psk, pskID []byte) (Opener, error) {
func (r *Receiver) SetupAuthPSK(
enc, psk, pskID []byte, pkS kem.PublicKey,
) (Opener, error) {
if !r.kemID.validatePublicKey(pkS) {
return nil, ErrInvalidKEMPublicKey
}

r.modeID = modeAuthPSK
r.enc = enc
r.state.psk = psk
Expand All @@ -237,7 +213,7 @@ func (s *Sender) allSetup(rnd io.Reader) ([]byte, Sealer, error) {
if rnd == nil {
rnd = rand.Reader
}
seed := make([]byte, scheme.SeedSize())
seed := make([]byte, scheme.EncapsulationSeedSize())
_, err := io.ReadFull(rnd, seed)
if err != nil {
return nil, nil, err
Expand Down
1 change: 1 addition & 0 deletions hpke/hybrid-x25119-kyber768-test-vectors.json

Large diffs are not rendered by default.