991 lines
22 KiB
Go
991 lines
22 KiB
Go
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...)
|
|
}
|