]> Cypherpunks repositories - keks.git/commitdiff
mceliece6960119-x25519
authorSergey Matveev <stargrave@stargrave.org>
Tue, 11 Feb 2025 16:04:54 +0000 (19:04 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Tue, 11 Feb 2025 16:04:54 +0000 (19:04 +0300)
14 files changed:
go/pki/algo.go
go/pki/av.go
go/pki/cmd/certool/main.go
go/pki/cmd/enctool/main.go
go/pki/hash/algo.go
go/pki/mceliece6960119-x25519/SOURCE [new file with mode: 0644]
go/pki/mceliece6960119-x25519/algo.go [new file with mode: 0644]
go/pki/mceliece6960119-x25519/kp.go [new file with mode: 0644]
spec/format/cer.texi
spec/format/encrypted.cddl
spec/format/encrypted.texi
spec/format/hashed.texi
spec/format/private-key.texi
spec/format/registry.texi

index 69b769aca3bf40b2dd2bfa8df08a8efb73fadf8c2a804cffa26c3d04aaea7e75..4b5f209734b5518a650b2169e088d0e5ca4090369736c7b26c2e43a751ed34f4 100644 (file)
@@ -4,21 +4,24 @@ import (
        "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")
index e9cb679adf3faf72e09ebfbc2c0428a30868bcff93c46a9f1f6be393a3fd0e5e..b267941823ba4515405c5d37c3978cbcd23be5c30fd8788981c020f8a5c44821 100644 (file)
@@ -41,6 +41,8 @@ func (av *AV) Id() (id uuid.UUID) {
                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
index c437c73dc415d40260c2f141d9669239c059a0396fb83db0063c6901c8c5130d..ccd11ddcbf7100213693340763f6842b68922748efd003c1505562d71e39d4b9 100644 (file)
@@ -30,6 +30,7 @@ import (
        "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"
@@ -89,6 +90,7 @@ func main() {
                        pki.GOST3410256A,
                        pki.GOST3410512C,
                        pki.SNTRUP4591761X25519,
+                       pki.ClassicMcEliece6960119X25519,
                }
                sort.Strings(algos)
                for _, s := range algos {
@@ -170,6 +172,8 @@ func main() {
                        _, 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")
                }
index 0d6d7dae4ddaad5b27f97506d34f4698c9fbc34fe29b3df2216c587ca052d048..ca1ec8fad9484dd81fd84cdcb120a977a96f048d35d1ed45766c5b3b0a0257a4 100644 (file)
@@ -27,12 +27,15 @@ import (
        "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"
@@ -42,9 +45,10 @@ import (
 )
 
 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
 )
@@ -64,7 +68,6 @@ type KEM struct {
        Cost *BalloonCost `keks:"cost,omitempty"`
        Salt *[]byte      `keks:"salt,omitempty"`
 
-       // sntrup4591761-x25519-hkdf-blake2b related
        Encap *[]byte `keks:"encap,omitempty"`
 }
 
@@ -88,6 +91,10 @@ func blake2b256() hash.Hash {
        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 {
@@ -286,6 +293,85 @@ func main() {
                                                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
@@ -410,6 +496,61 @@ func main() {
                                        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)
                        }
index 8fb809fa24f014f43fc141e9d895a2a2adae27ba95fc2bc4ab31b02e000af50f..b2e4cbab618d8deb65fafa8249020f8aaba0f91399ddbf222f0ebdb343743de3 100644 (file)
@@ -22,6 +22,7 @@ import (
        "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"
@@ -71,6 +72,10 @@ func ByName(name string) hash.Hash {
                        panic(err)
                }
                return h
+       case SHAKE128:
+               return sha3.NewShake128()
+       case SHAKE256:
+               return sha3.NewShake256()
        case SHAKE128Merkle:
                return NewSHAKE128MerkleHasher(
                        merkle.DefaultChunkLen, runtime.NumCPU())
diff --git a/go/pki/mceliece6960119-x25519/SOURCE b/go/pki/mceliece6960119-x25519/SOURCE
new file mode 100644 (file)
index 0000000..4fa5970
--- /dev/null
@@ -0,0 +1,3 @@
+https://github.com/cloudflare/circl/pull/378
+https://github.com/cloudflare/circl.git
+7dfc396c96830ed3601ace705e1612b9bcc447f9
diff --git a/go/pki/mceliece6960119-x25519/algo.go b/go/pki/mceliece6960119-x25519/algo.go
new file mode 100644 (file)
index 0000000..852bb58
--- /dev/null
@@ -0,0 +1,6 @@
+package mceliece6960119x25519
+
+const (
+       ClassicMcEliece6960119X25519             = "mceliece6960119-x25519"
+       ClassicMcEliece6960119X25519HKDFSHAKE256 = "mceliece6960119-x25519-hkdf-shake256"
+)
diff --git a/go/pki/mceliece6960119-x25519/kp.go b/go/pki/mceliece6960119-x25519/kp.go
new file mode 100644 (file)
index 0000000..9c311b3
--- /dev/null
@@ -0,0 +1,51 @@
+// 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
+}
index a34f2442929e7ea40b41bc13dc415bf4324851385e3abdd2c904bfccf68ede3a..8bbde027ef77ac6f873da04465b7742340837fa34cd65ed88f866ae41e593dd9 100644 (file)
@@ -116,7 +116,7 @@ using BLAKE2b hash with 128 or 256 bit output length specified.
 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.
@@ -127,3 +127,16 @@ value is a concatenation of 1218-byte SNTRUP4591761 public key and
 
 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.
index 0122491e1643476aea23a14980f48e21e3d7cd9b48afa4a09b93e290a84834be..c575450d00e8769c832b4af3c9308692b4ce2d31ae92c193e79b9681f0e9eb70 100644 (file)
@@ -20,7 +20,8 @@ dem-kuznechik-ctracpkm-hmac-hkdf = {
 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,
@@ -49,7 +50,14 @@ kem-gost3410-hkdf-kexp15 = {
 }
 
 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,
index db0bd0c1f60621dce0a957e4fa6433dc1a3b99dc5a8c09a39baa917ace57b004..aca04bf28cf1472495f3d89cbd6f56c1dfaeba216361112d06a38b3371acaa9d 100644 (file)
@@ -33,8 +33,8 @@ Either UUIDv4 or UUIDv7 are recommended.
 @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(
@@ -42,19 +42,19 @@ 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,
@@ -62,34 +62,34 @@ 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,
@@ -101,22 +101,22 @@ ChaCha20-Poly1305(data=16*0x00 || CEK, key=KEK, nonce=12*0x00, ad="")
 @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,
@@ -129,20 +129,20 @@ KExp15(KEKenc, KEKauth, IV, CEK):
 @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,
@@ -156,3 +156,34 @@ 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
index ab81187f51fc48506ccd0d29f0bbd973662b6a8fd94b7d278a94617aa4a4024f..1a616d1843be62700e5b99dbe56130201385873b8c08e8938861411cccef024b 100644 (file)
@@ -22,7 +22,7 @@ algorithms.
 @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
index d41ce92ffd1ac581c1d12096b34271cb43708d3d684885c3a8964c32cd5968bf..80c6ddc4a3762f6eda2d6ea9a199b6648391b98a99a56c89168c63cb8bd019a3 100644 (file)
@@ -9,25 +9,33 @@ Stored in a file, it should begin with "pki/prvkey" @ref{Magic, magic}.
 @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.
index 66f793b4bb6d91b6fe2c277730403a0be2114ce90454bb56ac6c8f16d3fe8391..9b9d3377c871f8496654e1557efc0617bae6e61642f836ac14917e35981fb861 100644 (file)
@@ -64,6 +64,8 @@ There is example registry of known algorithm identifiers.
     @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