Skip to content

Commit

Permalink
xwing: HPKE integration
Browse files Browse the repository at this point in the history
  • Loading branch information
bwesterb committed Oct 20, 2024
1 parent f40f138 commit 5a648d6
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 1 deletion.
12 changes: 11 additions & 1 deletion hpke/algs.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/cloudflare/circl/ecc/p384"
"github.com/cloudflare/circl/kem"
"github.com/cloudflare/circl/kem/kyber/kyber768"
"github.com/cloudflare/circl/kem/xwing"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/hkdf"
)
Expand All @@ -39,6 +40,8 @@ const (
// KEM_X25519_KYBER768_DRAFT00 is a hybrid KEM built on DHKEM(X25519, HKDF-SHA256)
// and Kyber768Draft00
KEM_X25519_KYBER768_DRAFT00 KEM = 0x30
// KEM_XWING is a hybrid KEM using X25519 and ML-KEM-768.
KEM_XWING KEM = 0x66af
)

// IsValid returns true if the KEM identifier is supported by the HPKE package.
Expand All @@ -49,7 +52,8 @@ func (k KEM) IsValid() bool {
KEM_P521_HKDF_SHA512,
KEM_X25519_HKDF_SHA256,
KEM_X448_HKDF_SHA512,
KEM_X25519_KYBER768_DRAFT00:
KEM_X25519_KYBER768_DRAFT00,
KEM_XWING:
return true
default:
return false
Expand All @@ -72,6 +76,8 @@ func (k KEM) Scheme() kem.AuthScheme {
return dhkemx448hkdfsha512
case KEM_X25519_KYBER768_DRAFT00:
return hybridkemX25519Kyber768
case KEM_XWING:
return kemXwing
default:
panic(ErrInvalidKEM)
}
Expand Down Expand Up @@ -237,6 +243,7 @@ var (
dhkemp256hkdfsha256, dhkemp384hkdfsha384, dhkemp521hkdfsha512 shortKEM
dhkemx25519hkdfsha256, dhkemx448hkdfsha512 xKEM
hybridkemX25519Kyber768 hybridKEM
kemXwing otherKEM
)

func init() {
Expand Down Expand Up @@ -275,4 +282,7 @@ func init() {
hybridkemX25519Kyber768.kemBase.Hash = crypto.SHA256
hybridkemX25519Kyber768.kemA = dhkemx25519hkdfsha256
hybridkemX25519Kyber768.kemB = kyber768.Scheme()

kemXwing.kem = xwing.Scheme()
kemXwing.name = "HPKE_KEM_XWING"
}
1 change: 1 addition & 0 deletions hpke/hpke_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ func BenchmarkHpkeRoundTrip(b *testing.B) {
}{
{hpke.KEM_X25519_HKDF_SHA256, hpke.KDF_HKDF_SHA256, hpke.AEAD_AES128GCM},
{hpke.KEM_X25519_KYBER768_DRAFT00, hpke.KDF_HKDF_SHA256, hpke.AEAD_AES128GCM},
{hpke.KEM_XWING, hpke.KDF_HKDF_SHA256, hpke.AEAD_AES128GCM},
}
for _, test := range tests {
runHpkeBenchmark(b, test.kem, test.kdf, test.aead)
Expand Down
77 changes: 77 additions & 0 deletions hpke/otherkem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package hpke

// Shim to use generic KEM (kem.Scheme) as HPKE KEM.

import (
"github.com/cloudflare/circl/internal/sha3"
"github.com/cloudflare/circl/kem"
)

type otherKEM struct {
kem kem.Scheme
name string
}

func (h otherKEM) PrivateKeySize() int { return h.kem.PrivateKeySize() }
func (h otherKEM) SeedSize() int { return h.kem.SeedSize() }
func (h otherKEM) CiphertextSize() int { return h.kem.CiphertextSize() }
func (h otherKEM) PublicKeySize() int { return h.kem.PublicKeySize() }
func (h otherKEM) EncapsulationSeedSize() int { return h.kem.EncapsulationSeedSize() }
func (h otherKEM) SharedKeySize() int { return h.kem.SharedKeySize() }
func (h otherKEM) Name() string { return h.name }

func (h otherKEM) AuthDecapsulate(skR kem.PrivateKey,
ct []byte,
pkS kem.PublicKey,
) ([]byte, error) {
panic("AuthDecapsulate is not supported for this KEM")
}

func (h otherKEM) AuthEncapsulate(pkr kem.PublicKey, sks kem.PrivateKey) (
ct []byte, ss []byte, err error,
) {
panic("AuthEncapsulate is not supported for this KEM")
}

func (h otherKEM) AuthEncapsulateDeterministically(pkr kem.PublicKey, sks kem.PrivateKey, seed []byte) (ct, ss []byte, err error) {
panic("AuthEncapsulateDeterministically is not supported for this KEM")
}

func (h otherKEM) Encapsulate(pkr kem.PublicKey) (
ct []byte, ss []byte, err error,
) {
return h.kem.Encapsulate(pkr)
}

func (h otherKEM) Decapsulate(skr kem.PrivateKey, ct []byte) ([]byte, error) {
return h.kem.Decapsulate(skr, ct)
}

func (h otherKEM) EncapsulateDeterministically(
pkr kem.PublicKey, seed []byte,
) (ct, ss []byte, err error) {
return h.kem.EncapsulateDeterministically(pkr, seed)
}

// HPKE requires DeriveKeyPair() to take any seed larger than the private key
// size, whereas typical KEMs expect a specific seed size. We'll just use
// SHAKE256 to hash it to the right size as in X-Wing.
func (h otherKEM) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) {
seed2 := make([]byte, h.kem.SeedSize())
hh := sha3.NewShake256()
_, _ = hh.Write(seed)
_, _ = hh.Read(seed2)
return h.kem.DeriveKeyPair(seed2)
}

func (h otherKEM) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) {
return h.kem.GenerateKeyPair()
}

func (h otherKEM) UnmarshalBinaryPrivateKey(data []byte) (kem.PrivateKey, error) {
return h.kem.UnmarshalBinaryPrivateKey(data)
}

func (h otherKEM) UnmarshalBinaryPublicKey(data []byte) (kem.PublicKey, error) {
return h.kem.UnmarshalBinaryPublicKey(data)
}

0 comments on commit 5a648d6

Please sign in to comment.