From: Sergey Matveev Date: Mon, 21 Apr 2025 07:25:07 +0000 (+0300) Subject: Public-key authenticated encryption, multi-recipient safe X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=f130cf4b9c68f0d480792714fa35f78f7badb2779913bfae91d58ceec4394f96;p=keks.git Public-key authenticated encryption, multi-recipient safe --- diff --git a/spec/cm/authcrypt.texi b/spec/cm/authcrypt.texi new file mode 100644 index 0000000..f6de895 --- /dev/null +++ b/spec/cm/authcrypt.texi @@ -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 index 0000000..989a5d3 --- /dev/null +++ b/spec/cm/dem-xchapoly-krmr.texi @@ -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}. diff --git a/spec/cm/encrypted.texi b/spec/cm/encrypted.texi index 31acc3f..465cc76 100644 --- a/spec/cm/encrypted.texi +++ b/spec/cm/encrypted.texi @@ -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 diff --git a/spec/cm/kem-gost3410-hkdf.texi b/spec/cm/kem-gost3410-hkdf.texi index 7f77fb3..d8cbaa3 100644 --- a/spec/cm/kem-gost3410-hkdf.texi +++ b/spec/cm/kem-gost3410-hkdf.texi @@ -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 diff --git a/spec/cm/kem-mceliece6960119-x25519-hkdf-shake256.texi b/spec/cm/kem-mceliece6960119-x25519-hkdf-shake256.texi index db507dd..c57c860 100644 --- a/spec/cm/kem-mceliece6960119-x25519-hkdf-shake256.texi +++ b/spec/cm/kem-mceliece6960119-x25519-hkdf-shake256.texi @@ -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 diff --git a/spec/cm/kem-sntrup4591761-x25519-hkdf-blake2b.texi b/spec/cm/kem-sntrup4591761-x25519-hkdf-blake2b.texi index 3d5e0d4..b5434d6 100644 --- a/spec/cm/kem-sntrup4591761-x25519-hkdf-blake2b.texi +++ b/spec/cm/kem-sntrup4591761-x25519-hkdf-blake2b.texi @@ -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 diff --git a/tcl/schemas/encrypted.tcl b/tcl/schemas/encrypted.tcl index d4833e5..15d87f1 100644 --- a/tcl/schemas/encrypted.tcl +++ b/tcl/schemas/encrypted.tcl @@ -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} } diff --git a/tcl/schemas/kem-with-encap.tcl b/tcl/schemas/kem-with-encap.tcl index 157c23b..618dcfa 100644 --- a/tcl/schemas/kem-with-encap.tcl +++ b/tcl/schemas/kem-with-encap.tcl @@ -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} }