"go.cypherpunks.su/keks"
ed25519blake2b "go.cypherpunks.su/keks/pki/ed25519-blake2b"
"go.cypherpunks.su/keks/pki/gost"
+ mceliece6960119x25519 "go.cypherpunks.su/keks/pki/mceliece6960119-x25519"
sntrup4591761x25519 "go.cypherpunks.su/keks/pki/sntrup4591761-x25519"
)
const (
- Ed25519BLAKE2b = ed25519blake2b.Ed25519BLAKE2b
- Ed25519PhBLAKE2b = ed25519blake2b.Ed25519PhBLAKE2b
- Ed25519PhBLAKE2bMerkle = ed25519blake2b.Ed25519PhBLAKE2bMerkle
- GOST3410256A = gost.GOST3410256A
- GOST3410256AMerkle = gost.GOST3410256AMerkle
- GOST3410512C = gost.GOST3410512C
- GOST3410512CMerkle = gost.GOST3410512CMerkle
- SNTRUP4591761X25519 = sntrup4591761x25519.SNTRUP4591761X25519
- SNTRUP4591761X25519HKDFBLAKE2b = sntrup4591761x25519.SNTRUP4591761X25519HKDFBLAKE2b
- BalloonBLAKE2bHKDF = "balloon-blake2b-hkdf"
- ChaCha20Poly1305 = "chacha20poly1305"
+ BalloonBLAKE2bHKDF = "balloon-blake2b-hkdf"
+ ChaCha20Poly1305 = "chacha20poly1305"
+ ClassicMcEliece6960119X25519 = mceliece6960119x25519.ClassicMcEliece6960119X25519
+ ClassicMcEliece6960119X25519HKDFSHAKE256 = mceliece6960119x25519.ClassicMcEliece6960119X25519HKDFSHAKE256
+ Ed25519BLAKE2b = ed25519blake2b.Ed25519BLAKE2b
+ Ed25519PhBLAKE2b = ed25519blake2b.Ed25519PhBLAKE2b
+ Ed25519PhBLAKE2bMerkle = ed25519blake2b.Ed25519PhBLAKE2bMerkle
+ GOST3410256A = gost.GOST3410256A
+ GOST3410256AMerkle = gost.GOST3410256AMerkle
+ GOST3410512C = gost.GOST3410512C
+ GOST3410512CMerkle = gost.GOST3410512CMerkle
+ SNTRUP4591761X25519 = sntrup4591761x25519.SNTRUP4591761X25519
+ SNTRUP4591761X25519HKDFBLAKE2b = sntrup4591761x25519.SNTRUP4591761X25519HKDFBLAKE2b
EncryptedMagic = keks.Magic("pki/encryptd")
HashedMagic = keks.Magic("pki/hashed")
hasher = pkihash.ByName(pkihash.BLAKE2b256)
case GOST3410256A, GOST3410512C:
hasher = pkihash.ByName(pkihash.Streebog256)
+ case ClassicMcEliece6960119X25519:
+ hasher = pkihash.ByName(pkihash.SHAKE128)
default:
id = uuid.Nil
return
"go.cypherpunks.su/keks/pki"
ed25519blake2b "go.cypherpunks.su/keks/pki/ed25519-blake2b"
"go.cypherpunks.su/keks/pki/gost"
+ mceliece6960119x25519 "go.cypherpunks.su/keks/pki/mceliece6960119-x25519"
"go.cypherpunks.su/keks/pki/sign"
sntrup4591761x25519 "go.cypherpunks.su/keks/pki/sntrup4591761-x25519"
"go.cypherpunks.su/keks/pki/utils"
pki.GOST3410256A,
pki.GOST3410512C,
pki.SNTRUP4591761X25519,
+ pki.ClassicMcEliece6960119X25519,
}
sort.Strings(algos)
for _, s := range algos {
_, prvRaw, pub, err = gost.NewKeypair(*algo)
case pki.SNTRUP4591761X25519:
prvRaw, pub, err = sntrup4591761x25519.NewKeypair()
+ case pki.ClassicMcEliece6960119X25519:
+ prvRaw, pub, err = mceliece6960119x25519.NewKeypair()
default:
err = errors.New("unknown -algo specified")
}
"log"
"os"
+ circlkem "github.com/cloudflare/circl/kem"
+ "github.com/cloudflare/circl/kem/mceliece/mceliece6960119"
"github.com/companyzero/sntrup4591761"
"github.com/google/uuid"
"go.cypherpunks.su/balloon/v3"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/hkdf"
+ "golang.org/x/crypto/sha3"
"golang.org/x/term"
"go.cypherpunks.su/keks"
)
const (
- BalloonSaltLen = 8
- BalloonHKDFSalt = "keks/pki/encrypted/balloon-blake2b-hkdf"
- SNTRUP4591761X25519Salt = "keks/pki/encrypted/sntrup4591761-x25519-hkdf-blake2b"
+ BalloonSaltLen = 8
+ BalloonHKDFSalt = "keks/pki/encrypted/balloon-blake2b-hkdf"
+ SNTRUP4591761X25519Salt = "keks/pki/encrypted/sntrup4591761-x25519-hkdf-blake2b"
+ ClassicMcEliece6960119X25519Salt = "keks/pki/encrypted/mceliece6960119-x25519-hkdf-shake256"
BindFdNum = 3 + 1
)
Cost *BalloonCost `keks:"cost,omitempty"`
Salt *[]byte `keks:"salt,omitempty"`
- // sntrup4591761-x25519-hkdf-blake2b related
Encap *[]byte `keks:"encap,omitempty"`
}
return h
}
+func shake256() hash.Hash {
+ return sha3.NewShake256()
+}
+
func readPasswd(prompt string) (passwd []byte) {
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
cek = cekp
}
}
+ case pki.ClassicMcEliece6960119X25519HKDFSHAKE256:
+ if len(prvs) == 0 {
+ log.Println(kemIdx, kem.A, "skipping because no -prv")
+ continue
+ }
+ if kem.Encap == nil {
+ log.Fatalln("missing encap")
+ }
+ scheme := mceliece6960119.Scheme()
+ if len(*kem.Encap) != scheme.CiphertextSize()+32 {
+ log.Fatalln("invalid encap len")
+ }
+ for _, prv := range prvs {
+ if len(prv.V) != scheme.PrivateKeySize()+32 {
+ log.Fatalln("invalid private keys len")
+ }
+ var ourMcEliece circlkem.PrivateKey
+ ourMcEliece, err = scheme.UnmarshalBinaryPrivateKey(
+ prv.V[:len(prv.V)-32],
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ x25519 := ecdh.X25519()
+ var ourX25519 *ecdh.PrivateKey
+ ourX25519, err = x25519.NewPrivateKey(prv.V[len(prv.V)-32:])
+ if err != nil {
+ log.Fatal(err)
+ }
+ theirMcEliece := (*kem.Encap)[:len(*kem.Encap)-32]
+ var keyMcEliece []byte
+ keyMcEliece, err = scheme.Decapsulate(ourMcEliece, theirMcEliece)
+ if err != nil {
+ log.Fatal(err)
+ }
+ var theirX25519 *ecdh.PublicKey
+ theirX25519, err = x25519.NewPublicKey(
+ (*kem.Encap)[len(*kem.Encap)-32:],
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+ var keyX25519 []byte
+ keyX25519, err = ourX25519.ECDH(theirX25519)
+ if err != nil {
+ log.Fatal(err)
+ }
+ {
+ ourMcEliecePub := ourMcEliece.Public()
+ var ourMcEliecePubRaw []byte
+ ourMcEliecePubRaw, err = ourMcEliecePub.MarshalBinary()
+ if err != nil {
+ log.Fatal(err)
+ }
+ pub := append(
+ ourMcEliecePubRaw,
+ ourX25519.PublicKey().Bytes()...,
+ )
+ ikm := bytes.Join([][]byte{
+ encrypted.Bind[:],
+ *kem.Encap, pub,
+ keyMcEliece, keyX25519,
+ }, []byte{})
+ kek := hkdf.Extract(shake256,
+ ikm, []byte(ClassicMcEliece6960119X25519Salt))
+ var cekp []byte
+ cekp, err = kemChaPolyOpen(
+ kek[:chacha20poly1305.KeySize],
+ kem.CEK,
+ chacha20poly1305.KeySize,
+ )
+ if err != nil {
+ log.Println(kemIdx, kem.A, err, ", skipping")
+ continue
+ }
+ cek = cekp
+ }
+ }
default:
log.Println("unsupported KEM:", kem.A)
continue
kem.To = &pub.Id
}
kems = append(kems, kem)
+ case pki.ClassicMcEliece6960119X25519:
+ scheme := mceliece6960119.Scheme()
+ if len(pub.V) != scheme.PublicKeySize()+32 {
+ log.Fatalln("invalid public keys len")
+ }
+ var theirMcEliece circlkem.PublicKey
+ theirMcEliece, err = scheme.UnmarshalBinaryPublicKey(
+ pub.V[:len(pub.V)-32],
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+ x25519 := ecdh.X25519()
+ var theirX25519 *ecdh.PublicKey
+ theirX25519, err = x25519.NewPublicKey(pub.V[len(pub.V)-32:])
+ if err != nil {
+ log.Fatal(err)
+ }
+ var ciphertext []byte
+ var keyMcEliece []byte
+ ciphertext, keyMcEliece, err = scheme.Encapsulate(theirMcEliece)
+ if err != nil {
+ log.Fatal(err)
+ }
+ var ourPrvX25519 *ecdh.PrivateKey
+ ourPrvX25519, err = ecdh.X25519().GenerateKey(rand.Reader)
+ if err != nil {
+ log.Fatal(err)
+ }
+ ourPubX25519 := ourPrvX25519.PublicKey()
+ var keyX25519 []byte
+ keyX25519, err = ourPrvX25519.ECDH(theirX25519)
+ if err != nil {
+ log.Fatal(err)
+ }
+ kem := KEM{A: pki.ClassicMcEliece6960119X25519HKDFSHAKE256}
+ encap := append(ciphertext[:], ourPubX25519.Bytes()...)
+ kem.Encap = &encap
+ {
+ ikm := bytes.Join([][]byte{
+ binding[:],
+ encap, pub.V,
+ keyMcEliece[:], keyX25519,
+ }, []byte{})
+ kek := hkdf.Extract(shake256,
+ ikm, []byte(ClassicMcEliece6960119X25519Salt))
+ kem.CEK, err = kemChaPolySeal(kek[:chacha20poly1305.KeySize], cek)
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+ if *includeTo {
+ kem.To = &pub.Id
+ }
+ kems = append(kems, kem)
default:
log.Fatalln("unsupported KEM:", pub.A)
}
"go.cypherpunks.su/gogost/v6/gost34112012256"
"go.cypherpunks.su/gogost/v6/gost34112012512"
"golang.org/x/crypto/blake2b"
+ "golang.org/x/crypto/sha3"
ed25519blake2b "go.cypherpunks.su/keks/pki/ed25519-blake2b"
"go.cypherpunks.su/keks/pki/gost"
panic(err)
}
return h
+ case SHAKE128:
+ return sha3.NewShake128()
+ case SHAKE256:
+ return sha3.NewShake256()
case SHAKE128Merkle:
return NewSHAKE128MerkleHasher(
merkle.DefaultChunkLen, runtime.NumCPU())
--- /dev/null
+https://github.com/cloudflare/circl/pull/378
+https://github.com/cloudflare/circl.git
+7dfc396c96830ed3601ace705e1612b9bcc447f9
--- /dev/null
+package mceliece6960119x25519
+
+const (
+ ClassicMcEliece6960119X25519 = "mceliece6960119-x25519"
+ ClassicMcEliece6960119X25519HKDFSHAKE256 = "mceliece6960119-x25519-hkdf-shake256"
+)
--- /dev/null
+// GoKEKS/PKI -- PKI-related capabilities based on KEKS encoded formats
+// Copyright (C) 2024-2025 Sergey Matveev <stargrave@stargrave.org>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package mceliece6960119x25519
+
+import (
+ "crypto/ecdh"
+ "crypto/rand"
+
+ "github.com/cloudflare/circl/kem"
+ "github.com/cloudflare/circl/kem/mceliece/mceliece6960119"
+)
+
+func NewKeypair() (prv, pub []byte, err error) {
+ var pubMcEliece kem.PublicKey
+ var prvMcEliece kem.PrivateKey
+ scheme := mceliece6960119.Scheme()
+ pubMcEliece, prvMcEliece, err = scheme.GenerateKeyPair()
+ if err != nil {
+ return
+ }
+ var prvX25519 *ecdh.PrivateKey
+ prvX25519, err = ecdh.X25519().GenerateKey(rand.Reader)
+ if err != nil {
+ return
+ }
+ pubX25519 := prvX25519.PublicKey()
+ raw, err := prvMcEliece.MarshalBinary()
+ if err != nil {
+ return
+ }
+ prv = append(raw, prvX25519.Bytes()...)
+ raw, err = pubMcEliece.MarshalBinary()
+ if err != nil {
+ return
+ }
+ pub = append(raw, pubX25519.Bytes()...)
+ return
+}
Algorithm identifiers for the public key: @code{ed25519ph-blake2b},
@node cer-sntrup4591761-x25519
-@subsection cer with SNTRUP4591761-X25519
+@subsection cer with SNTRUP4591761+X25519
Certificate with combined Streamlined NTRU Prime 4591^761 and Curve25519
public keys is used for KEM purposes, so should have "kem" key usage set.
Public key's identifier and @code{cid} should be calculated
using BLAKE2b hash with 128 or 256 bit output length specified.
+
+@node cer-mceliece6960119-x25519
+@subsection cer with Classic McEliece 6960119+x25519
+
+Certificate with combined Classic McEliece 6960119 and Curve25519
+public keys is used for KEM purposes, so should have "kem" key usage set.
+
+Its algorithm identifier is @code{mceliece6960119-x25519}. Its public key
+value is a concatenation of 1047319-byte mceliece6960119 public key and
+32-byte Curve25519 one.
+
+Public key's identifier and @code{cid} should be calculated
+using either SHAKE128 or SHAKE256 hash.
kem = kem-generic /
kem-balloon-blake2b-hkdf /
kem-gost3410-hkdf-kexp15 /
- kem-sntrup4591761-x25519-hkdf-blake2b
+ kem-sntrup4591761-x25519-hkdf-blake2b /
+ kem-mceliece6960119-x25519-hkdf-shake256
kem-generic = {
a: ai,
}
kem-sntrup4591761-x25519-hkdf-blake2b = {
- a: ai,
+ a: "sntrup4591761-x25519-hkdf-blake2b",
+ cek: bytes,
+ encap: bytes,
+ ? to: uuid,
+}
+
+kem-mceliece6960119-x25519-hkdf-shake256 = {
+ a: "mceliece6960119-x25519-hkdf-shake256 ",
cek: bytes,
encap: bytes,
? to: uuid,
@node pki-encrypted-chacha20poly1305
@subsection pki-encrypted with ChaCha20-Poly1305 DEM
-@code{/dem/a} equals to "chacha20poly1305". Data is split on 64 KiB
-chunks which are encrypted the following way:
+ @code{/dem/a} equals to "chacha20poly1305". Data is split on 64 KiB
+ chunks which are encrypted the following way:
@verbatim
ChaCha20-Poly1305(
data=16*0x00 || chunk, ad="")
@end verbatim
-where @code{counter} starts at zero and incremented with each chunk.
-@code{tail-flag} is a byte indicating if that is the last chunk in the
-payload. It equals to 0x01 for the last chunk and to 0x00 for other ones.
-Last chunk should be smaller than previous ones, maybe even empty.
+ where @code{counter} starts at zero and incremented with each chunk.
+ @code{tail-flag} is a byte indicating if that is the last chunk in the
+ payload. It equals to 0x01 for the last chunk and to 0x00 for other ones.
+ Last chunk should be smaller than previous ones, maybe even empty.
-@code{/ciphertext}'s chunk length equals to 16+64KiB+16 bytes.
+ @code{/ciphertext}'s chunk length equals to 16+64KiB+16 bytes.
@node pki-encrypted-kuznechik-ctracpkm-hmac-hkdf
@subsection pki-encrypted-kuznechik-ctracpkm-hmac-hkdf
-@code{/dem/a} equals to "kuznechik-ctracpkm-hmac-hkdf".
-@code{/dem/seed} contains 16 bytes for the HKDF invocation below.
-@code{/dem/iv} contains 8 bytes of initialisation vector.
+ @code{/dem/a} equals to "kuznechik-ctracpkm-hmac-hkdf".
+ @code{/dem/seed} contains 16 bytes for the HKDF invocation below.
+ @code{/dem/iv} contains 8 bytes of initialisation vector.
@verbatim
Kenc, Kauth = HKDF-Extract(Streebog-512,
secret=seed || CEK)
@end verbatim
-Encryption is performed with Kuznechik (ГОСТ Р 34.12-2015) block cipher
-in CTR-ACPKM mode of operation (Р 1323565.1.017) with 256KiB section
-size. Authentication of ciphertext is performed with Streebog-512 (ГОСТ
-Р 34.11-2012) in HMAC mode.
+ Encryption is performed with Kuznechik (ГОСТ Р 34.12-2015) block cipher
+ in CTR-ACPKM mode of operation (Р 1323565.1.017) with 256KiB section
+ size. Authentication of ciphertext is performed with Streebog-512 (ГОСТ
+ Р 34.11-2012) in HMAC mode.
-@code{/ciphertext}'s chunk length equals to 64KiB bytes.
+ @code{/ciphertext}'s chunk length equals to 64KiB bytes.
@node pki-encrypted-balloon-blake2b-hkdf
@subsection pki-encrypted with Balloon-BLAKE2b+HKDF-BLAKE2b KEM
-@code{/kem/*/a} equals to "balloon-blake2b-hkdf".
-Recipient map must also contain additional fields:
+ @code{/kem/*/a} equals to "balloon-blake2b-hkdf".
+ Recipient map must also contain additional fields:
-@table @code
-@item /to/*/cost/s: uint64
- Balloon's space cost (buffer size, number of hash-output sized blocks).
-@item /to/*/cost/t: uint64
- Balloon's time cost (number of rounds).
-@item /to/*/cost/p: uint64
- Balloon's parallel cost (number of threads).
-@item /to/*/salt: bin
- Salt.
-@end table
+ @table @code
+ @item /to/*/cost/s: uint64
+ Balloon's space cost (buffer size, number of hash-output sized blocks).
+ @item /to/*/cost/t: uint64
+ Balloon's time cost (number of rounds).
+ @item /to/*/cost/p: uint64
+ Balloon's parallel cost (number of threads).
+ @item /to/*/salt: bin
+ Salt.
+ @end table
-@url{https://crypto.stanford.edu/balloon/, Balloon} memory-hardened
-password hasher must be used with BLAKE2b-256 hash.
+ @url{https://crypto.stanford.edu/balloon/, Balloon} memory-hardened
+ password hasher must be used with BLAKE2b-256 hash.
-@code{/kem/*/cek} is encrypted the following way:
+ @code{/kem/*/cek} is encrypted the following way:
@verbatim
KEK = HKDF-Extract(BLAKE2b-256,
@node pki-encrypted-gost3410-hkdf-kexp15
@subsection pki-encrypted-gost3410-hkdf-kexp15
-@code{/kem/*/a} equals to "gost3410-hkdf-kexp15".
-Recipient map must also contain additional fields:
+ @code{/kem/*/a} equals to "gost3410-hkdf-kexp15".
+ Recipient map must also contain additional fields:
-@table @code
-@item /to/*/ukm: bytes
- Additional 16-bytes keying material.
-@item /to/*/pub: bytes
- Sender's ephemeral 512-bit public key.
-@item /to/*/iv: bytes
- 8-byte initialisation vector for KExp15.
-@end table
+ @table @code
+ @item /to/*/ukm: bytes
+ Additional 16-bytes keying material.
+ @item /to/*/pub: bytes
+ Sender's ephemeral 512-bit public key.
+ @item /to/*/iv: bytes
+ 8-byte initialisation vector for KExp15.
+ @end table
-ГОСТ Р 34.10-2012 VKO parameter set A/C ("gost3410-256A", "gost3410-512C")
-must be used for DH operation, with UKM taken from the structure. VKO's
-output is 512- or 1024-bit @code{BE(X)||BE(Y)} point. It is used in HKDF
-and KExp15 (Р 1323565.1.017) key wrapping algorithm:
+ ГОСТ Р 34.10-2012 VKO parameter set A/C ("gost3410-256A", "gost3410-512C")
+ must be used for DH operation, with UKM taken from the structure. VKO's
+ output is 512- or 1024-bit @code{BE(X)||BE(Y)} point. It is used in HKDF
+ and KExp15 (Р 1323565.1.017) key wrapping algorithm:
@verbatim
KEKenv, KEKauth = HKDF-Extract(Streebog-512,
@node pki-encrypted-sntrup4591761-x25519-hkdf-blake2b
@subsection pki-encrypted with SNTRUP4591761+x25519+HKDF-BLAKE2b KEM
-@code{/kem/*/a} equals to "sntrup4591761-x25519-hkdf-blake2b".
-Recipient certificate with
-@ref{cer-sntrup4591761-x25519, @code{sntrup4591761-x25519}} public key
-must be used. It should have "kem" key usage set.
+ @code{/kem/*/a} equals to "sntrup4591761-x25519-hkdf-blake2b".
+ Recipient certificate with
+ @ref{cer-sntrup4591761-x25519, @code{sntrup4591761-x25519}} public key
+ must be used. It should have "kem" key usage set.
-Recipient map must also contain additional field:
-@code{/kem/*/encap: bytes} -- concatenation of 1047 bytes of Streamlined
-NTRU Prime 4591^761's ciphertext with 32 bytes of ephemeral
-Curve25519 public key.
+ Recipient map must also contain additional field:
+ @code{/kem/*/encap: bytes} -- concatenation of 1047 bytes of Streamlined
+ NTRU Prime 4591^761's ciphertext with 32 bytes of ephemeral
+ Curve25519 public key.
-Recipient performs Curve25519 and SNTRUP computation to
-derive/decapsulate two 32-byte shared keys. Then it combines
-them to get the decryption key of the CEK.
-@code{/kem/*/cek} is encrypted the following way:
+ Recipient performs Curve25519 and SNTRUP computation to
+ derive/decapsulate two 32-byte shared keys. Then it combines
+ them to get the decryption key of the CEK.
+ @code{/kem/*/cek} is encrypted the following way:
@verbatim
KEK = HKDF-Extract(BLAKE2b-256,
x25519-shared-key)
ChaCha20-Poly1305(data=16*0x00 || CEK, key=KEK, nonce=12*0x00, ad="")
@end verbatim
+
+@node pki-encrypted-mceliece6960119-x25519-hkdf-shake256
+@subsection pki-encrypted with Classic McEliece 6960119+x25519+HKDF-SHAKE256 KEM
+
+ @code{/kem/*/a} equals to "mceliece6960119-x25519-hkdf-shake256".
+ Recipient certificate with
+ @ref{cer-mceliece6960119-x25519, @code{mceliece6960119-x25519}} public key
+ must be used. It should have "kem" key usage set.
+
+ Recipient map must also contain additional field:
+ @code{/kem/*/encap: bytes} -- concatenation of 194 bytes of
+ Classic McEliece 6960119 ciphertext with 32 bytes of ephemeral
+ Curve25519 public key.
+
+ Recipient performs Curve25519 and Classic McEliece computation to
+ derive/decapsulate two 32-byte shared keys. Then it combines
+ them to get the decryption key of the CEK.
+ @code{/kem/*/cek} is encrypted the following way:
+
+@verbatim
+KEK = HKDF-Extract(SHAKE256,
+ salt="keks/pki/encrypted/mceliece6960119-x25519-hkdf-shake256",
+ secret=bind ||
+ mceliece6960119-sender-ciphertext ||
+ x25519-sender-public-key ||
+ mceliece6960119-recipient-public-key ||
+ x25519-recipient-public-key ||
+ mceliece6960119-shared-key ||
+ x25519-shared-key)[:32]
+ChaCha20-Poly1305(data=16*0x00 || CEK, key=KEK, nonce=12*0x00, ad="")
+@end verbatim
@node Merkle hashing
@cindex Merkle tree
@cindex Merkle hashing
-@section Merkle-tree based hashing
+@subsection Merkle-tree based hashing
Merkle trees are very convenient way to parallelise data hashing.
@url{https://datatracker.ietf.org/doc/html/rfc9162, RFC 9162} is used as
@node private-key-gost3410
@subsection private-key with GOST R 34.10-2012
-Big-endian private key representation must be used.
+ Big-endian private key representation must be used.
-Following algorithm identifiers are used:
-@code{gost3410-256A}, @code{gost3410-512C}.
+ Following algorithm identifiers are used:
+ @code{gost3410-256A}, @code{gost3410-512C}.
@node private-key-ed25519-blake2b
@subsection private-key with Ed25519-BLAKE2b
-32-byte Ed25519 private key is used, as described in
-@url{https://datatracker.ietf.org/doc/html/rfc8032, EdDSA} RFC.
-In many libraries it is called "seed".
+ 32-byte Ed25519 private key is used, as described in
+ @url{https://datatracker.ietf.org/doc/html/rfc8032, EdDSA} RFC.
+ In many libraries it is called "seed".
-@code{ed25519-blake2b} algorithm identifier is used, however actually no
-hash is involved in private key storage.
+ @code{ed25519-blake2b} algorithm identifier is used, however actually no
+ hash is involved in private key storage.
@node private-key-sntrup4591761-x25519
-@subsection private-key with SNTRUP4591761-X25519
+@subsection private-key with SNTRUP4591761+X25519
-Concatenation of Streamlined NTRU Prime 4591^761's 1600-byte private key
-and Curve25519's 32-byte one.
+ Concatenation of Streamlined NTRU Prime 4591^761's 1600-byte private key
+ and Curve25519's 32-byte one.
-@code{sntrup4591761-x25519} algorithm identifier is used.
+ @code{sntrup4591761-x25519} algorithm identifier is used.
+
+@node private-key-mceliece6960119-x25519
+@subsection private-key with Classic McEliece 6960119+X25519
+
+ Concatenation of Classic McEliece 6960119 13948-byte private key
+ and Curve25519's 32-byte one.
+
+ @code{mceliece6960119-x25519} algorithm identifier is used.
@code{@ref{pki-encrypted-balloon-blake2b-hkdf}}
@item gost3410-hkdf-kexp15
@code{@ref{pki-encrypted-gost3410-hkdf-kexp15}}
+@item mceliece6960119-x25519-hkdf-shake256
+ @code{@ref{pki-encrypted-mceliece6960119-x25519-hkdf-shake256}}
@item mlkem768-x25519
@item sntrup761-x25519
@item sntrup4591761-x25519