]> Cypherpunks repositories - keks.git/commitdiff
Public-key authenticated encryption, multi-recipient safe
authorSergey Matveev <stargrave@stargrave.org>
Mon, 21 Apr 2025 07:25:07 +0000 (10:25 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Mon, 21 Apr 2025 14:13:27 +0000 (17:13 +0300)
spec/cm/authcrypt.texi [new file with mode: 0644]
spec/cm/dem-xchapoly-krmr.texi [new file with mode: 0644]
spec/cm/encrypted.texi
spec/cm/kem-gost3410-hkdf.texi
spec/cm/kem-mceliece6960119-x25519-hkdf-shake256.texi
spec/cm/kem-sntrup4591761-x25519-hkdf-blake2b.texi
tcl/schemas/encrypted.tcl
tcl/schemas/kem-with-encap.tcl

diff --git a/spec/cm/authcrypt.texi b/spec/cm/authcrypt.texi
new file mode 100644 (file)
index 0000000..f6de895
--- /dev/null
@@ -0,0 +1,21 @@
+@node Authcrypt
+@cindex authenticated public-key encryption
+@nodedescription Authenticated public-key encryption
+@subsection Authenticated public-key encryption
+
+Public-key based @ref{KEM, KEMs} provides sender authentication
+@strong{only} if @code{/kem/*/from} field is specified. It should
+contain public key's @code{/load/v/id}, but may be equal to 256-bit
+zeros, to explicitly specify that sender's public key is used, but it is
+anonymous and hidden. It is not specified how recipient should find
+corresponding sender's key that way -- implementation/protocol specific.
+
+Optional @code{/pubs} is a list public keys, which may be used to supply
+sender's public key(s). Public keys may be encrypted, to hide the actual
+deanonymisation contents.
+
+It is @strong{highly} recommended to use multi-recipient safe DEM when
+encrypting to multiple recipients. For example
+@code{@ref{dem-xchapoly-krmr, dem-xchapoly-krmr}} instead of
+@code{@ref{dem-xchapoly-krkc, dem-xchapoly-krkc}}, but unfortunately
+with the price of more expensive double pass authentication scheme.
diff --git a/spec/cm/dem-xchapoly-krmr.texi b/spec/cm/dem-xchapoly-krmr.texi
new file mode 100644 (file)
index 0000000..989a5d3
--- /dev/null
@@ -0,0 +1,38 @@
+@node dem-xchapoly-krmr
+@cindex dem-xchapoly-krmr
+@nodedescription XChaCha20-Poly1305 with key ratcheting and multi-recipient DEM
+@subsubsection XChaCha20-Poly1305 with key ratcheting and multi-recipient DEM
+
+@code{cm/encrypted}'s @code{/dem/a} equals to "xchapoly-krmr".
+
+CEK consists of common 64 bytes long part equal in all KEMs (@code{CEK} itself),
+and 64 bytes long per-KEM/per-recipient random MAC key (@code{prMAC}).
+
+Data is split on 128 KiB chunks, each of which is encrypted the following way:
+
+@verbatim
+H = BLAKE2b
+CK0 = CEK
+CKi = HKDF-Extract(H, salt="", ikm=CK{i-1})
+KEY = HKDF-Expand(H, prk=CKi, info="cm/encrypted/xchapoly-krmr/key")
+IV = HKDF-Expand(H, prk=CKi, info="cm/encrypted/xchapoly-krmr/iv", len=24)
+if last chunk { IV[23] |= 0x01 } else { IV[23] &= 0xFE }
+CIPHERTEXT || TAG = XChaCha20-Poly1305(key=KEY, ad="", nonce=IV, data=chunk)
+MACi = BLAKE2b-256-MAC(key=prMACi, H(CIPHERTEXT || TAG))
+CIPHERTEXT || TAG || MAC0 [|| MAC1 ...]
+@end verbatim
+
+Chaining key (CK) advances with every chunk. 256-bit encryption key and
+randomised 192-bit nonce (initialisation vector) are derived from it.
+
+Nonce's lowest bit is set only if this is the last chunk we encrypting.
+
+@code{/payload}'s chunk length equals to 128KiB+16+32*recipients bytes.
+
+HKDF is KDF algorithm,
+@url{https://datatracker.ietf.org/doc/html/rfc5869.html, RFC 5869}.
+@url{https://www.blake2.net/, BLAKE2b} is hashing algorithm,
+@url{https://datatracker.ietf.org/doc/html/rfc7693.html, RFC 7693}.
+@url{https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha, XChaCha20-Poly1305}
+is an authenticated encryption algorithm, extended nonce version of ChaCha20-Poly1305,
+@url{https://datatracker.ietf.org/doc/html/rfc8439.html, RFC 8439}.
index 31acc3fedd067b985e213bb0df96d743434bdd149c941c9164953b0c1ed0b9a2..465cc768b83be2068e901ce89a0bf6317931172dfd94341abb80906b5d7035e7 100644 (file)
@@ -12,17 +12,15 @@ Encrypted container, some kind of analogue to ASN.1-based
 @item Ability to use multiple recipients
 @item Either passphrase- or public-key based KEMs
 @item Hybrid PQ/T KEMs
-@item Optionally anonymous recipients
+@item Ability to authenticate the sender
+@item Optionally anonymous recipients and sender
 @item Streaming friendly
 @item Ability to parallelise encryption/decryption procedures
 @item Current DEM schemes do explicit key commitment
 @item Current DEM schemes use key ratcheting and rotation
+@item Ability to safely encrypt to multiple recipients
 @end itemize
 
-Currently there is @strong{no} sender authentication! But remember to
-include recipient's public key fingerprint in encrypted signed document
-if you use it.
-
 Stored in a file, it should begin with "cm/encrypted" @ref{MAGIC, magic}.
 
 @verbatiminclude ../tcl/schemas/encrypted.tcl
@@ -38,19 +36,20 @@ means, for example just by following the @code{cm/encrypted} structure.
 It is recommended to encode it as a BLOB, which chunk's length depends
 on DEM algorithm.
 
-CEK is encapsulated in @code{/kem/*} entries (key encapsulation
-mechanism), using @code{/kem/*/a} algorithm. @code{/kem/*/cek} field
-contains an encrypted CEK.
+CEK is encapsulated in @code{/kem/*/cek} entries (key encapsulation
+mechanisms), using @code{/kem/*/a} algorithm.
 
-If KEM uses public-key based cryptography, then recipient's
-@ref{cm-pub, public key}(s) should be provided, which may lack the
-signatures at all. Optional @code{/kem/*/to}, public key's fingerprint,
-may provide a hint to quickly search for the key on the recipient's side.
+If KEM uses public-key based cryptography, then recipient's @ref{cm-pub,
+public key}(s) must be provided. Optional @code{/kem/*/to}, public key's
+fingerprint, may provide a hint to quickly search for the key on the
+recipient's side.
 
 Optional @code{/id} is used in KEMs for domain separation and envelope
 identification. UUIDv4 is recommended. If absent, then null UUID is used
 in KDF.
 
+@include cm/authcrypt.texi
+
 @node Key wrapping
 @cindex key wrapping
 @nodedescription Key wrapping mechanisms
@@ -65,6 +64,7 @@ in KDF.
 @subsection Data encapsulation mechanisms
 
 @include cm/dem-xchapoly-krkc.texi
+@include cm/dem-xchapoly-krmr.texi
 @include cm/dem-kuznechik-ctr-hmac-kr.texi
 
 @node KEM
index 7f77fb393618f8c31e0c1a6d41084a70bdbe4c3b2033b3ec5c89eb63ff835d97..d8cbaa38b5a03f8fd88b5131ff72745f1299d100963ebd69e8addd6979391193 100644 (file)
@@ -11,7 +11,10 @@ output is 512- or 1024-bit @code{BE(X)||BE(Y)} point, used in HKDF below:
 
 @verbatim
 H = Streebog-512
-PRK = HKDF-Extract(H, salt="", ikm=VKO(..., ukm=UKM))
+DH(sk, pk) = GOSTR3410-VKO(prv=sk, pub=pk, ukm=UKM)
+PRK = HKDF-Extract(H, salt="", ikm=DH(e, s))
+if specified(sender):
+    PRK = HKDF-Extract(H, salt=PRK, ikm=DH(s, s))
 KEK = HKDF-Expand(H, prk=PRK, info="cm/encrypted/gost3410-hkdf" || /id)
 @end verbatim
 
index db507dd1eb972bcb2cb350253029785c29ae4deb240935ec084ddfaec369052e..c57c8601f883323d3c0352509cab87329f6d8dc6ca4022b51c92d98907f3c03d 100644 (file)
@@ -11,8 +11,8 @@ Recipient public key with
 algorithm must be used. It should have "kem" key usage set.
 
 Recipient's map @code{/kem/*/encap} field is a concatenation of
-194 bytes of Classic McEliece 6960-119 ciphertext with 32 bytes of
-ephemeral X25519 public key.
+194 bytes of Classic McEliece 6960-119 ciphertext, containing
+ephemeral key, with 32 bytes of ephemeral X25519 public key.
 
 Recipient performs X25519 and Classic McEliece computations to
 derive/decapsulate two 32-byte shared keys. Then it combines
@@ -21,9 +21,14 @@ them to get the KEK decryption key of the CEK.
 @verbatim
 H = SHAKE256
 PRK = HKDF-Extract(H, salt="", ikm=
-    mceliece6960119-shared-key || x25519-shared-key ||
-    H(mceliece6960119-sender-ciphertext || x25519-sender-public-key) ||
-    H(mceliece6960119-recipient-public-key || x25519-recipient-public-key))
+    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))
+if specified(sender):
+    PRK = HKDF-Extract(H, salt=PRK, ikm=
+        ss-x25519-shared-key ||
+        s-x25519-sender-public-key ||
+        s-x25519-recipient-public-key)
 KEK = HKDF-Expand(H, prk=PRK,
     info="cm/encrypted/mceliece6960119-x25519-hkdf-shake256" || /salt)
 @end verbatim
index 3d5e0d4b63c6e5c615cd149ad30411aee6ca14c779fa682b5526779055bca83d..b5434d6ae0c70f81e6f5e667cf34797d94737f9ee5593ec9130ef4a223cb1ec4 100644 (file)
@@ -11,8 +11,8 @@ Recipient public key with @ref{cm-pub-sntrup4591761-x25519,
 "kem" key usage set.
 
 Recipient's map @code{/kem/*/encap} field is a concatenation of 1047
-bytes of Streamlined NTRU Prime 4591^761's ciphertext with 32 bytes of
-ephemeral X25519 public key.
+bytes of Streamlined NTRU Prime 4591^761's ciphertext, containing
+ephemeral key, with 32 bytes of ephemeral X25519 public key.
 
 Recipient performs X25519 and SNTRUP computations to derive/decapsulate
 two 32-byte shared keys. Then it combines them to get the KEK decryption
@@ -21,9 +21,14 @@ key of the CEK.
 @verbatim
 H = BLAKE2b
 PRK = HKDF-Extract(H, salt="", ikm=
-    sntrup4591761-shared-key || x25519-shared-key ||
-    H(sntrup4591761-sender-ciphertext || x25519-sender-public-key) ||
-    H(sntrup4591761-recipient-public-key || x25519-recipient-public-key))
+    sntrup4591761-shared-key || es-x25519-shared-key ||
+    H(sntrup4591761-sender-ciphertext || e-x25519-sender-public-key) ||
+    H(sntrup4591761-recipient-public-key || s-x25519-recipient-public-key))
+if specified(sender):
+    PRK = HKDF-Extract(H, salt=PRK, ikm=
+        ss-x25519-shared-key ||
+        s-x25519-sender-public-key ||
+        s-x25519-recipient-public-key)
 KEK = HKDF-Expand(H, prk=PRK, info="cm/encrypted/sntrup4591761-x25519-hkdf-blake2b" || /id)
 @end verbatim
 
index d4833e5d08b68ef3b3ed50081bd65121120a689038c241e92021e8ec5f3379ba..15d87f10ba2fb9df677861a5dd3f80b1524820ce7cc2aae63b5824bc02cf6db7 100644 (file)
@@ -3,11 +3,13 @@ encrypted {
     {field kem {list} {of kem} >0}
     {field id {hexlet} optional}
     {field payload {bin} optional}
+    {field pubs {list} {of map} optional >0} {# attached public keys}
 }
 
 dem {
     {field . {map}}
     {field a {str} >0} {# xchapoly-krkc}
+                       {# xchapoly-krmr}
                        {# kuznechik-ctr-hmac-kr}
 }
 
index 157c23b3acc75c9631db4bf38eb6c220775097c83eff6630e8d9e609fbfd4a57..618dcfaca83a231d057f13edeb3544c9db1dc574d696cea08cf9a29140a66c96 100644 (file)
@@ -4,4 +4,5 @@ kem-with-encap {
     {field cek {bin} >0} {# wrapped CEK}
     {field encap {bin} >0}
     {field to {with fpr} optional} {# recipient's public key}
+    {field from {with fpr} optional} {# sender's public key}
 }