import (
"bytes"
+ "crypto/cipher"
"crypto/ecdh"
"crypto/hkdf"
"crypto/rand"
const (
FdPubR = 4
FdPrvR = 8
+
+ X25519KeyLen = 32
)
func blake2bHash() hash.Hash {
if kem.Encap == nil {
log.Fatalln("missing encap")
}
- if len(kem.Encap) != sntrup4591761.CiphertextSize+32 {
+ if len(kem.Encap) != sntrup4591761.CiphertextSize+X25519KeyLen {
log.Fatalln("invalid encap len")
}
for _, prv := range prvs {
if prv.A != sntrup4591761x25519.SNTRUP4591761X25519 {
continue
}
- if len(prv.V) != sntrup4591761.PrivateKeySize+32 {
+ if len(prv.V) != sntrup4591761.PrivateKeySize+X25519KeyLen {
log.Fatalln("invalid private keys len")
}
var ourSNTRUP sntrup4591761.PrivateKey
prk,
string(append(
[]byte(cmenc.SNTRUP4591761X25519Info),
- encrypted.Id[:]...,
- )),
+ encrypted.Id[:]...)),
chacha20poly1305.KeySize,
)
if err != nil {
if kem.Encap == nil {
log.Fatalln("missing encap")
}
- if len(kem.Encap) != mceliece6960119.CiphertextSize+32 {
+ if len(kem.Encap) != mceliece6960119.CiphertextSize+X25519KeyLen+chacha20poly1305.Overhead {
log.Fatalln("invalid encap len")
}
for _, prv := range prvs {
if prv.A != mceliece6960119x25519.ClassicMcEliece6960119X25519 {
continue
}
- if len(prv.V) != mceliece6960119.PrivateKeySize+32 {
+ if len(prv.V) != mceliece6960119.PrivateKeySize+X25519KeyLen {
log.Fatalln("invalid private keys len")
}
var ourMcEliece *mceliece6960119.PrivateKey
ourMcEliece, err = mceliece6960119.UnmarshalBinaryPrivateKey(
- prv.V[:len(prv.V)-32],
+ prv.V[:len(prv.V)-X25519KeyLen],
)
if err != nil {
log.Fatal(err)
}
x25519 := ecdh.X25519()
var ourX25519 *ecdh.PrivateKey
- ourX25519, err = x25519.NewPrivateKey(prv.V[len(prv.V)-32:])
+ ourX25519, err = x25519.NewPrivateKey(prv.V[len(prv.V)-X25519KeyLen:])
if err != nil {
log.Fatal(err)
}
- theirMcEliece := (kem.Encap)[:len(kem.Encap)-32]
+ theirMcEliece := (kem.Encap)[:len(kem.Encap)-X25519KeyLen-chacha20poly1305.Overhead]
+ theirX2559Encap := (kem.Encap)[len(kem.Encap)-X25519KeyLen-chacha20poly1305.Overhead:]
var keyMcEliece []byte
keyMcEliece, err = mceliece6960119.Decapsulate(
ourMcEliece, theirMcEliece,
log.Fatal(err)
}
var theirX25519 *ecdh.PublicKey
- theirX25519, err = x25519.NewPublicKey(
- (kem.Encap)[len(kem.Encap)-32:],
- )
- if err != nil {
- log.Fatal(err)
+ {
+ var decapKeymat []byte
+ decapKeymat, err = hkdf.Expand(
+ cmhash.NewSHAKE256,
+ keyMcEliece,
+ string(append(
+ []byte(cmenc.ClassicMcEliece6960119X25519DecapInfo),
+ encrypted.Id[:]...)),
+ chacha20poly1305.KeySize+chacha20poly1305.NonceSizeX,
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+ decapKey := decapKeymat[:chacha20poly1305.KeySize]
+ decapNonce := decapKeymat[chacha20poly1305.KeySize:]
+ var aead cipher.AEAD
+ aead, err = chacha20poly1305.NewX(decapKey)
+ if err != nil {
+ log.Fatal(err)
+ }
+ var theirX25519Raw []byte
+ theirX25519Raw, err = aead.Open(
+ nil,
+ decapNonce,
+ theirX2559Encap,
+ theirMcEliece)
+ if err != nil {
+ log.Fatal(err)
+ }
+ theirX25519, err = x25519.NewPublicKey(theirX25519Raw)
+ if err != nil {
+ log.Fatal(err)
+ }
}
var keyX25519 []byte
keyX25519, err = ourX25519.ECDH(theirX25519)
pkHash.Write(ourX25519.PublicKey().Bytes())
ikm := bytes.Join([][]byte{
keyMcEliece, keyX25519,
- sha3.SumSHAKE256(kem.Encap, 32),
+ sha3.SumSHAKE256(kem.Encap, X25519KeyLen),
pkHash.Sum(nil),
}, []byte{})
var prk []byte
prk,
string(append(
[]byte(cmenc.ClassicMcEliece6960119X25519Info),
- encrypted.Id[:]...,
- )),
+ encrypted.Id[:]...)),
chacha20poly1305.KeySize,
)
if err != nil {
for pubId, pub := range pubs {
switch pub.A {
case sntrup4591761x25519.SNTRUP4591761X25519:
- if len(pub.V) != sntrup4591761.PublicKeySize+32 {
+ if len(pub.V) != sntrup4591761.PublicKeySize+X25519KeyLen {
log.Fatalln("invalid public keys len")
}
var theirSNTRUP sntrup4591761.PublicKey
}
kems = append(kems, kem)
case mceliece6960119x25519.ClassicMcEliece6960119X25519:
- if len(pub.V) != mceliece6960119.PublicKeySize+32 {
+ if len(pub.V) != mceliece6960119.PublicKeySize+X25519KeyLen {
log.Fatalln("invalid public keys len")
}
var theirMcEliece *mceliece6960119.PublicKey
theirMcEliece, err = mceliece6960119.UnmarshalBinaryPublicKey(
- pub.V[:len(pub.V)-32],
+ pub.V[:len(pub.V)-X25519KeyLen],
)
if err != nil {
log.Fatal(err)
}
x25519 := ecdh.X25519()
var theirX25519 *ecdh.PublicKey
- theirX25519, err = x25519.NewPublicKey(pub.V[len(pub.V)-32:])
+ theirX25519, err = x25519.NewPublicKey(pub.V[len(pub.V)-X25519KeyLen:])
if err != nil {
log.Fatal(err)
}
log.Fatal(err)
}
ourPubX25519 := ourPrvX25519.PublicKey()
+ kem := cmenc.KEM{
+ A: mceliece6960119x25519.ClassicMcEliece6960119X25519HKDFSHAKE256,
+ }
+ {
+ var decapKeymat []byte
+ decapKeymat, err = hkdf.Expand(
+ cmhash.NewSHAKE256,
+ keyMcEliece,
+ string(append(
+ []byte(cmenc.ClassicMcEliece6960119X25519DecapInfo),
+ id[:]...)),
+ chacha20poly1305.KeySize+chacha20poly1305.NonceSizeX,
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+ decapKey := decapKeymat[:chacha20poly1305.KeySize]
+ decapNonce := decapKeymat[chacha20poly1305.KeySize:]
+ var aead cipher.AEAD
+ aead, err = chacha20poly1305.NewX(decapKey)
+ if err != nil {
+ log.Fatal(err)
+ }
+ kem.Encap = aead.Seal(
+ ciphertext[:],
+ decapNonce,
+ ourPubX25519.Bytes(),
+ ciphertext)
+ }
var keyX25519 []byte
keyX25519, err = ourPrvX25519.ECDH(theirX25519)
if err != nil {
log.Fatal(err)
}
- kem := cmenc.KEM{
- A: mceliece6960119x25519.ClassicMcEliece6960119X25519HKDFSHAKE256,
- Encap: append(ciphertext[:], ourPubX25519.Bytes()...),
- }
{
ikm := bytes.Join([][]byte{
keyMcEliece[:], keyX25519,
prk,
string(append(
[]byte(cmenc.ClassicMcEliece6960119X25519Info),
- id[:]...),
- ),
+ id[:]...)),
chacha20poly1305.KeySize,
)
if err != nil {
algorithm must be used. It should have "kem" key usage set.
"/kem/*/encap" field is a concatenation of 194 bytes of
-Classic McEliece 6960-119 ciphertext, containing ephemeral key,
-with 32 bytes of ephemeral X25519 public key.
+Classic McEliece 6960-119 ciphertext, with XChaCha20-Poly1305-encrypted
+32 bytes ephemeral X25519 public key:
-Recipient performs X25519 and Classic McEliece computations to
-derive/decapsulate two 32-byte shared keys. Then it combines
-them to get the KEK decryption key of the CEK.
+ mceliece-ciphertext || XChaCha20-Poly1305(
+ key=decapKey,
+ nonce=decapNonce,
+ data=e-x25519-sender-public-key,
+ ad=mceliece-ciphertext)
+
+Recipient performs Classic McEliece decapsulation, decrypts ephemeral
+X25519 public key, computes shared secrets, combines them and derives KEK.
H = SHAKE256
+ mceliece-ciphertext, mceliece-shared-key = KEM-Encap(mceliece-recipient-public-key)
+ mceliece-shared-key = KEM-Decap(mceliece-recipient-private-key, mceliece-ciphertext)
+
+ decapKey, decapNonce = HKDF-Expand(H, prk=mceliece-shared-key,
+ info="cm/encrypted/mceliece6960119-x25519-hkdf-shake256/decap" || /id)
+ es-x25519-shared-key = X25519(e-x25519-sender-private-key,
+ s-x25519-recipient-public-key)
PRK = HKDF-Extract(H, salt="", ikm=
- mceliece6960119-shared-key || es-x25519-shared-key ||
- H(mceliece6960119-sender-ciphertext || e-x25519-sender-public-key) ||
- H(mceliece6960119-recipient-public-key || s-x25519-recipient-public-key))
+ mceliece-shared-key || es-x25519-shared-key ||
+ H(mceliece-sender-ciphertext || e-x25519-sender-public-key) ||
+ H(mceliece-recipient-public-key || s-x25519-recipient-public-key))
if {specified sender}
+ ss-x25519-shared-key = X25519(s-x25519-sender-private-key,
+ s-x25519-recipient-public-key)
PRK = HKDF-Extract(H, salt=PRK, ikm=
- ss-x25519-shared-key ||
- s-x25519-sender-public-key ||
- s-x25519-recipient-public-key)
+ ss-x25519-shared-key || s-x25519-sender-public-key)
KEK = HKDF-Expand(H, prk=PRK,
info="cm/encrypted/mceliece6960119-x25519-hkdf-shake256" || /id)