package hpke
import (
)
type KEM interface {
ID() uint16
GenerateKey() (PrivateKey, error)
NewPublicKey([]byte) (PublicKey, error)
NewPrivateKey([]byte) (PrivateKey, error)
DeriveKeyPair(ikm []byte) (PrivateKey, error)
encSize() int
}
func ( uint16) (KEM, error) {
switch {
case 0x0010:
return DHKEM(ecdh.P256()), nil
case 0x0011:
return DHKEM(ecdh.P384()), nil
case 0x0012:
return DHKEM(ecdh.P521()), nil
case 0x0020:
return DHKEM(ecdh.X25519()), nil
case 0x0041:
return MLKEM768(), nil
case 0x0042:
return MLKEM1024(), nil
case 0x647a:
return MLKEM768X25519(), nil
case 0x0050:
return MLKEM768P256(), nil
case 0x0051:
return MLKEM1024P384(), nil
default:
return nil, errors.New("unsupported KEM")
}
}
type PublicKey interface {
KEM() KEM
Bytes() []byte
encap() (sharedSecret, enc []byte, err error)
}
type PrivateKey interface {
KEM() KEM
Bytes() ([]byte, error)
PublicKey() PublicKey
decap(enc []byte) (sharedSecret []byte, err error)
}
type dhKEM struct {
kdf KDF
id uint16
curve ecdh.Curve
Nsecret uint16
Nsk uint16
Nenc int
}
func ( *dhKEM) (, []byte) ([]byte, error) {
:= byteorder.BEAppendUint16([]byte("KEM"), .id)
, := .kdf.labeledExtract(, nil, "eae_prk", )
if != nil {
return nil,
}
return .kdf.labeledExpand(, , "shared_secret", , .Nsecret)
}
func ( *dhKEM) () uint16 {
return .id
}
func ( *dhKEM) () int {
return .Nenc
}
var dhKEMP256 = &dhKEM{HKDFSHA256(), 0x0010, ecdh.P256(), 32, 32, 65}
var dhKEMP384 = &dhKEM{HKDFSHA384(), 0x0011, ecdh.P384(), 48, 48, 97}
var dhKEMP521 = &dhKEM{HKDFSHA512(), 0x0012, ecdh.P521(), 64, 66, 133}
var dhKEMX25519 = &dhKEM{HKDFSHA256(), 0x0020, ecdh.X25519(), 32, 32, 32}
func ( ecdh.Curve) KEM {
switch {
case ecdh.P256():
return dhKEMP256
case ecdh.P384():
return dhKEMP384
case ecdh.P521():
return dhKEMP521
case ecdh.X25519():
return dhKEMX25519
default:
return unsupportedCurveKEM{}
}
}
type unsupportedCurveKEM struct{}
func (unsupportedCurveKEM) () uint16 {
return 0
}
func (unsupportedCurveKEM) () (PrivateKey, error) {
return nil, errors.New("unsupported curve")
}
func (unsupportedCurveKEM) ([]byte) (PublicKey, error) {
return nil, errors.New("unsupported curve")
}
func (unsupportedCurveKEM) ([]byte) (PrivateKey, error) {
return nil, errors.New("unsupported curve")
}
func (unsupportedCurveKEM) ([]byte) (PrivateKey, error) {
return nil, errors.New("unsupported curve")
}
func (unsupportedCurveKEM) () int {
return 0
}
type dhKEMPublicKey struct {
kem *dhKEM
pub *ecdh.PublicKey
}
func ( *ecdh.PublicKey) (PublicKey, error) {
, := DHKEM(.Curve()).(*dhKEM)
if ! {
return nil, errors.New("unsupported curve")
}
return &dhKEMPublicKey{
kem: ,
pub: ,
}, nil
}
func ( *dhKEM) ( []byte) (PublicKey, error) {
, := .curve.NewPublicKey()
if != nil {
return nil,
}
return NewDHKEMPublicKey()
}
func ( *dhKEMPublicKey) () KEM {
return .kem
}
func ( *dhKEMPublicKey) () []byte {
return .pub.Bytes()
}
var testingOnlyGenerateKey func() *ecdh.PrivateKey
func ( *dhKEMPublicKey) () ( []byte, []byte, error) {
, := .pub.Curve().GenerateKey(rand.Reader)
if != nil {
return nil, nil,
}
if testingOnlyGenerateKey != nil {
= testingOnlyGenerateKey()
}
, := .ECDH(.pub)
if != nil {
return nil, nil,
}
:= .PublicKey().Bytes()
:= .pub.Bytes()
:= append(, ...)
, = .kem.extractAndExpand(, )
if != nil {
return nil, nil,
}
return , , nil
}
type dhKEMPrivateKey struct {
kem *dhKEM
priv ecdh.KeyExchanger
}
func ( ecdh.KeyExchanger) (PrivateKey, error) {
, := DHKEM(.Curve()).(*dhKEM)
if ! {
return nil, errors.New("unsupported curve")
}
return &dhKEMPrivateKey{
kem: ,
priv: ,
}, nil
}
func ( *dhKEM) () (PrivateKey, error) {
, := .curve.GenerateKey(rand.Reader)
if != nil {
return nil,
}
return NewDHKEMPrivateKey()
}
func ( *dhKEM) ( []byte) (PrivateKey, error) {
, := .curve.NewPrivateKey()
if != nil {
return nil,
}
return NewDHKEMPrivateKey()
}
func ( *dhKEM) ( []byte) (PrivateKey, error) {
:= byteorder.BEAppendUint16([]byte("KEM"), .id)
, := .kdf.labeledExtract(, nil, "dkp_prk", )
if != nil {
return nil,
}
if == dhKEMX25519 {
, := .kdf.labeledExpand(, , "sk", nil, .Nsk)
if != nil {
return nil,
}
return .NewPrivateKey()
}
var uint8
for < 4 {
, := .kdf.labeledExpand(, , "candidate", []byte{}, .Nsk)
if != nil {
return nil,
}
if == dhKEMP521 {
[0] &= 0x01
}
, := .NewPrivateKey()
if != nil {
++
continue
}
return , nil
}
panic("chance of four rejections is < 2^-128")
}
func ( *dhKEMPrivateKey) () KEM {
return .kem
}
func ( *dhKEMPrivateKey) () ([]byte, error) {
, := .priv.(*ecdh.PrivateKey)
if ! {
return nil, errors.New("ecdh: private key does not support Bytes")
}
if .kem == dhKEMX25519 {
:= .Bytes()
[0] &= 248
[31] &= 127
[31] |= 64
return , nil
}
return .Bytes(), nil
}
func ( *dhKEMPrivateKey) () PublicKey {
return &dhKEMPublicKey{
kem: .kem,
pub: .priv.PublicKey(),
}
}
func ( *dhKEMPrivateKey) ( []byte) ([]byte, error) {
, := .priv.Curve().NewPublicKey()
if != nil {
return nil,
}
, := .priv.ECDH()
if != nil {
return nil,
}
:= append(slices.Clip(), .priv.PublicKey().Bytes()...)
return .kem.extractAndExpand(, )
}