Mailing List Archive

[PATCH] Add Streamlined NTRU Prime sntrup761.
Hi

See attached patch that adds sntrup761. What do you think?

My use case is to enable implementation of OpenSSH's
sntrup761x25519-sha512 in libssh/libssh2.

Specific open issues:

- Documentation

- Benchmarking self-test

- Self-tests that validate test vectors

Not trivial because the algorithm is randomized, so we would have to
use deterministic randomness somehow -- and to use an deterministic
algorithm for which there exists sntrup761 test vectors (DRBG-CTR is
the only one I am aware of, but far from ideal).

- API design

- Are gcry_kem_open/gcry_kem_close useful? They complicate
implementation for no gain for sntrup761, but could be useful for
other KEM's, OTOH they may just complicate it for all KEM's since I
believe the KEM APIs are fairly established these days.

- The pubkey parameter for KEM-Enc could be stored in the handle, as
could the seckey parameter for KEM-Dec. This would make the
gcry_kem_open/gcry_kem_close more useful, however it would mean
more memory zeroization issues.

- The #define's for output lengths could be functions, similar to
other libgcrypt APIs. This makes it harder to use statically
allocated buffers, so I think the current #define's are useful.

/Simon
Re: [PATCH] Add Streamlined NTRU Prime sntrup761. [ In reply to ]
Hi

Attached is a second version of the sntrup761 patch, this time using a
minimal API that would work for Kyber too (please confirm). Unless we
know complexity is required, I prefer to keep things minimal.

I've pushed it to:
https://gitlab.com/jas/libgcrypt/-/commits/jas/sntrup761v2

Below is the added API. Thoughts?

enum gcry_kem_algos
{
GCRY_KEM_SNTRUP761 = 761,
};

#define GCRY_KEM_SNTRUP761_SECRETKEY_SIZE 1763
#define GCRY_KEM_SNTRUP761_PUBLICKEY_SIZE 1158
#define GCRY_KEM_SNTRUP761_CIPHERTEXT_SIZE 1039
#define GCRY_KEM_SNTRUP761_SHAREDSECRET_SIZE 32

gcry_error_t gcry_kem_keypair (int algo,
void *pubkey,
void *seckey);

gcry_error_t gcry_kem_enc (int algo,
const void *pubkey,
void *ciphertext,
void *ss);

gcry_error_t gcry_kem_dec (int algo,
const void *ciphertext,
const void *seckey,
void *ss);

/Simon
Re: [PATCH] Add Streamlined NTRU Prime sntrup761. [ In reply to ]
Hi Simon,

Am 16.05.23 um 08:56 schrieb Simon Josefsson via Gcrypt-devel:
> Hi
>
> Attached is a second version of the sntrup761 patch, this time using a
> minimal API that would work for Kyber too (please confirm). Unless we
> know complexity is required, I prefer to keep things minimal.
>
> I've pushed it to:
> https://gitlab.com/jas/libgcrypt/-/commits/jas/sntrup761v2
>
> Below is the added API. Thoughts?
>
> enum gcry_kem_algos
> {
> GCRY_KEM_SNTRUP761 = 761,
> };
>
> #define GCRY_KEM_SNTRUP761_SECRETKEY_SIZE 1763
> #define GCRY_KEM_SNTRUP761_PUBLICKEY_SIZE 1158
> #define GCRY_KEM_SNTRUP761_CIPHERTEXT_SIZE 1039
> #define GCRY_KEM_SNTRUP761_SHAREDSECRET_SIZE 32
>
> gcry_error_t gcry_kem_keypair (int algo,
> void *pubkey,
> void *seckey);
>
> gcry_error_t gcry_kem_enc (int algo,
> const void *pubkey,
> void *ciphertext,
> void *ss);
>
> gcry_error_t gcry_kem_dec (int algo,
> const void *ciphertext,
> const void *seckey,
> void *ss);

I think this is already going into the right direction. However, I have
some proposals:

1. I would prefer a more type safe API: distinct public and private key
objects instead of void pointers, i.e gcry_kem_public_key_t and
gcry_kem_private_key_t. From your proposed API it does not become clear
if pubkey and seckey are objects or just byte arrays. Since
instantiating a key from a byte array may involve some precomputations
(imagine for instance instantiating a private key from a PRNG seed), for
efficiency reasons it is in my view necessary to have public and private
key objects.

2. Also the enum should by typedef'd and used with its type in the
function signature.

3. There is no need to provide algo again in the enc/dec functions. A
key object will know it's algorithm. (Probably this is due to key void
pointers meant as byte arrays)

4. All input/output byte arrays should be typed as uint8_t* and be
passed in with their lengths. If without lengths, client code will be
prone to memory access errors.

5. Then we will also need extra functions for serialization and
deserialization of keys.

- Falko

>
> /Simon
>
> _______________________________________________
> Gcrypt-devel mailing list
> Gcrypt-devel@gnupg.org
> https://lists.gnupg.org/mailman/listinfo/gcrypt-devel
--

*MTG AG*
Dr. Falko Strenzke
Executive System Architect

Phone: +49 6151 8000 24
E-Mail: falko.strenzke@mtg.de
Web: mtg.de <https://www.mtg.de>


*MTG Exhibitions – See you in 2023*

------------------------------------------------------------------------
<https://community.e-world-essen.com/institutions/allExhibitors?query=true&keywords=mtg>
<https://www.itsa365.de/de-de/companies/m/mtg-ag>

MTG AG - Dolivostr. 11 - 64293 Darmstadt, Germany
Commercial register: HRB 8901
Register Court: Amtsgericht Darmstadt
Management Board: Jürgen Ruf (CEO), Tamer Kemeröz
Chairman of the Supervisory Board: Dr. Thomas Milde

This email may contain confidential and/or privileged information. If
you are not the correct recipient or have received this email in error,
please inform the sender immediately and delete this email. Unauthorised
copying or distribution of this email is not permitted.

Data protection information: Privacy policy
<https://www.mtg.de/en/privacy-policy>
Re: [PATCH] Add Streamlined NTRU Prime sntrup761. [ In reply to ]
Am Dienstag, 16. Mai 2023, 08:56:08 CEST schrieb Simon Josefsson via Gcrypt-
devel:

Hi Simon,

> Hi
>
> Attached is a second version of the sntrup761 patch, this time using a
> minimal API that would work for Kyber too (please confirm). Unless we
> know complexity is required, I prefer to keep things minimal.
>
> I've pushed it to:
> https://gitlab.com/jas/libgcrypt/-/commits/jas/sntrup761v2
>
> Below is the added API. Thoughts?
>
> enum gcry_kem_algos
> {
> GCRY_KEM_SNTRUP761 = 761,
> };
>
> #define GCRY_KEM_SNTRUP761_SECRETKEY_SIZE 1763
> #define GCRY_KEM_SNTRUP761_PUBLICKEY_SIZE 1158
> #define GCRY_KEM_SNTRUP761_CIPHERTEXT_SIZE 1039
> #define GCRY_KEM_SNTRUP761_SHAREDSECRET_SIZE 32
>
> gcry_error_t gcry_kem_keypair (int algo,
> void *pubkey,
> void *seckey);
>
> gcry_error_t gcry_kem_enc (int algo,
> const void *pubkey,
> void *ciphertext,
> void *ss);

May I suggest to add another parameter: size_t ss_len which shall specify the
caller-requested size of ss?
>
> gcry_error_t gcry_kem_dec (int algo,
> const void *ciphertext,
> const void *seckey,
> void *ss);

Same here.

Kyber uses a KDF as the last step. I am aware of the fact that the Kyber
reference implementation returns 32 bytes statically. However, considering the
use of a true KDF which has the property of a pseudorandom behavior (either
SHAKE256 or AES-CTR is used), the KDF can produce arbitrary amounts of data.
By specifying an ss_len parameter, the caller can directly request the data
that may be needed as a key/IV/mac Key or similar for subsequent cipher
operations.

In [1] and [2], I use such an ss_len parameter which in turn serves me well
for production use cases.

[1] https://github.com/smuellerDD/leancrypto/blob/master/kem/api/
lc_kyber.h#L149

[2] https://github.com/smuellerDD/leancrypto/blob/master/kem/api/
lc_kyber.h#L167

Thanks a lot
Stephan



_______________________________________________
Gcrypt-devel mailing list
Gcrypt-devel@gnupg.org
https://lists.gnupg.org/mailman/listinfo/gcrypt-devel
Re: [PATCH] Add Streamlined NTRU Prime sntrup761. [ In reply to ]
Hi!

> My use case is to enable implementation of OpenSSH's
> sntrup761x25519-sha512 in libssh/libssh2.

Given that OpenSSH starts to move into that direction, it is a good idea
to add support to Libgcrypt. After all we want that gpg-agent can also
work with that algorithms.

> - Are gcry_kem_open/gcry_kem_close useful? They complicate
> implementation for no gain for sntrup761, but could be useful for
> other KEM's, OTOH they may just complicate it for all KEM's since I
> believe the KEM APIs are fairly established these days.

I have not yet anaylyzed your needs but I think that this new API is not
needed because we have KEM functions already implemented in the pubkey
API.

Instead of a new separate API it should be sufficient to make use of the
general idea of gcry_ctx_t. Right now we use such a context only for
KATs and to implement custom EC functions.

The context object was actually implemented to add state to the public
key functions and to allow the provisioning of larger parameters by
associating them with an s-expression. A context is also a way to
implement n-way processing within Libgcrypt.



Salam-Shalom,

Werner

--
The pioneers of a warless world are the youth that
refuse military service. - A. Einstein
Re: [PATCH] Add Streamlined NTRU Prime sntrup761. [ In reply to ]
Werner Koch via Gcrypt-devel <gcrypt-devel@gnupg.org> writes:

> I have not yet anaylyzed your needs but I think that this new API is not
> needed because we have KEM functions already implemented in the pubkey
> API.

Do you mean these?

-- Function: gcry_error_t gcry_pk_genkey (gcry_sexp_t *R_KEY,
gcry_sexp_t PARMS)
-- Function: gcry_error_t gcry_pk_encrypt (gcry_sexp_t *R_CIPH,
gcry_sexp_t DATA, gcry_sexp_t PKEY)
-- Function: gcry_error_t gcry_pk_decrypt (gcry_sexp_t *R_PLAIN,
gcry_sexp_t DATA, gcry_sexp_t SKEY)

I think these are poorly suited for modern KEM's like sntrup761. They
are all now byte-oriented, not MPI/sexp. KEM's use of public/private
keys are ephemeral, like diffie-hellman, so they are different than
long-term keys. I think this is comparable to the separate APIs
introduced for X25519:

-- Function: gpg_error_t gcry_ecc_mul_point (int CURVEID,
unsigned char *RESULT, const unsigned char *SCALAR,
const unsigned char *POINT)

Using MPI's to store byte-values lead to a security concern in RFC 8731,
since MPI's encode different byte-values in different length depending
on the content. I haven't checked if libgcrypt would be vulnerable to
the same problem, but type-overloading is not safe.

Maybe you could take a second look on the API I proposed below? It
matches the API that several modern KEM's uses. Yes this would make
KEM's a special animal that is not compatible with other
public/private-key stuff in libgcrypt, but I think that is actually a
good thing.

enum gcry_kem_algos
{
GCRY_KEM_SNTRUP761 = 761,
};

#define GCRY_KEM_SNTRUP761_SECRETKEY_SIZE 1763
#define GCRY_KEM_SNTRUP761_PUBLICKEY_SIZE 1158
#define GCRY_KEM_SNTRUP761_CIPHERTEXT_SIZE 1039
#define GCRY_KEM_SNTRUP761_SHAREDSECRET_SIZE 32

gcry_error_t gcry_kem_keypair (int algo,
void *pubkey,
void *seckey);
gcry_error_t gcry_kem_enc (int algo,
const void *pubkey,
void *ciphertext,
void *ss);
gcry_error_t gcry_kem_dec (int algo,
const void *ciphertext,
const void *seckey,
void *ss);

/Simon
Re: [PATCH] Add Streamlined NTRU Prime sntrup761. [ In reply to ]
Falko Strenzke <falko.strenzke@mtg.de> writes:

> I think this is already going into the right direction. However, I
> have some proposals:

Thank you for feedback!

> 1. I would prefer a more type safe API: distinct public and private
> key objects instead of void pointers, i.e gcry_kem_public_key_t and
> gcry_kem_private_key_t. From your proposed API it does not become
> clear if pubkey and seckey are objects or just byte arrays. Since
> instantiating a key from a byte array may involve some precomputations
> (imagine for instance instantiating a private key from a PRNG seed),
> for efficiency reasons it is in my view necessary to have public and
> private key objects.

This is a trade-off, and my rationale was that I prefer doing
byte-oriented APIs since that seems to what all modern KEM's are using
(including Kyber?). And for some reason byte-strings are passed as
'void*' in libgcrypt, so I followed that style. There should be
documentation explaining this.

I think the core decision should be to use 1) byte-oriented API or 2)
some higher-level representation like MPI/sexp. The API types follow
from that decision. I agree with you that 'void*' is not nice, but it
seems like the libgcrypt idiom.

However you make me believe we could use uint8_t here? My KEM API is
not similar to other parts of libgcrypt anyway, so we don't have to
repeat using 'void*' for data.

> 2. Also the enum should by typedef'd and used with its type in the
> function signature.

My use was modeled in existing uses like 'enum gcry_cipher_algos', 'enum
gcry_pk_algos' etc. I do agree with you, but I think consistency is
also important.

> 3. There is no need to provide algo again in the enc/dec functions. A
> key object will know it's algorithm. (Probably this is due to key void
> pointers meant as byte arrays)

I think either a context handle or algorithm identifier is needed. The
key parameter is just a opaque byte array, it doesn't know its
algorithm.

> 4. All input/output byte arrays should be typed as uint8_t* and be
> passed in with their lengths. If without lengths, client code will be
> prone to memory access errors.

That was my first version of the API. It felt useless to have all these
size_t lengths and checks for them since they were all fixed strings
anyway. Let's see where all other issues end up, if this is still
relevant.

> 5. Then we will also need extra functions for serialization and
> deserialization of keys.

My approach uses raw keys directly, so this is included.

/Simon
Re: [PATCH] Add Streamlined NTRU Prime sntrup761. [ In reply to ]
Stephan Mueller <smueller@chronox.de> writes:

>> gcry_error_t gcry_kem_enc (int algo,
>> const void *pubkey,
>> void *ciphertext,
>> void *ss);
>
> May I suggest to add another parameter: size_t ss_len which shall specify the
> caller-requested size of ss?

Is that to support variable-length outputs? Or just to indicate the
buffer size? Does kyber or some other popular KEM supports
variable-length outputs?

>> gcry_error_t gcry_kem_dec (int algo,
>> const void *ciphertext,
>> const void *seckey,
>> void *ss);
>
> Same here.
>
> Kyber uses a KDF as the last step. I am aware of the fact that the Kyber
> reference implementation returns 32 bytes statically. However, considering the
> use of a true KDF which has the property of a pseudorandom behavior (either
> SHAKE256 or AES-CTR is used), the KDF can produce arbitrary amounts of data.
> By specifying an ss_len parameter, the caller can directly request the data
> that may be needed as a key/IV/mac Key or similar for subsequent cipher
> operations.

What does the specification says? Is kyber specified as a
variable-length output, or output of 32 bytes?

One approach is to have another API for that use-case:

gcry_error_t gcry_kem_enc_kdf (int algo,
const void *pubkey,
void *ciphertext,
size_t sslen, void *ss);
gcry_error_t gcry_kem_dec_kdf (int algo,
const void *ciphertext,
const void *seckey,
size_t sslen, void *ss);

/Simon
Re: [PATCH] Add Streamlined NTRU Prime sntrup761. [ In reply to ]
Am Freitag, 19. Mai 2023, 23:52:00 CEST schrieb Simon Josefsson:

Hi Simon,

> Stephan Mueller <smueller@chronox.de> writes:
> >> gcry_error_t gcry_kem_enc (int algo,
> >>
> >> const void *pubkey,
> >> void *ciphertext,
> >> void *ss);
> >
> > May I suggest to add another parameter: size_t ss_len which shall specify
> > the caller-requested size of ss?
>
> Is that to support variable-length outputs? Or just to indicate the
> buffer size? Does kyber or some other popular KEM supports
> variable-length outputs?

Short answer: this is to indicate variable length outputs.

Long answer: Kyber KEM defines as its last step a KDF using either SHAKE256.
Both KDFs allow the caller to request an arbitrary output size. Yet, the
sample source code generates hard-coded 32 bytes.

To avoid waste of CPU cycles and considering that both KDF operations are
defined as pseudorandom operations (see SP800-185 for SHAKE), I personally
think that this KDF should be asked to generate exakt those number of bytes
that are needed. This implies that I added the ss_len parameter to my API set
to advertise that this helps preventing the waste of precious CPU cycles.
>
> >> gcry_error_t gcry_kem_dec (int algo,
> >>
> >> const void *ciphertext,
> >> const void *seckey,
> >> void *ss);
> >
> > Same here.
> >
> > Kyber uses a KDF as the last step. I am aware of the fact that the Kyber
> > reference implementation returns 32 bytes statically. However, considering
> > the use of a true KDF which has the property of a pseudorandom behavior
> > (either SHAKE256 or AES-CTR is used), the KDF can produce arbitrary
> > amounts of data. By specifying an ss_len parameter, the caller can
> > directly request the data that may be needed as a key/IV/mac Key or
> > similar for subsequent cipher operations.
>
> What does the specification says? Is kyber specified as a
> variable-length output, or output of 32 bytes?

The specification contains the following words regarding the KDF:

"""
As a modification in round-2, we decided to derive the final key using
SHAKE-256 instead of SHA3-256.
This is an advantage for protocols that need keys of more than 256 bits.
Instead of first requesting a 256-bit
key from Kyber and then expanding it, they can pass an additional key-length
parameter to Kyber and
obtain a key of the desired length. This feature is not supported by the NIST
API, so in our implementations
we set the keylength to a fixed length of 32 bytes in api.h.
"""

So, the authors deem it as acceptable to specify an ss_len.
>
> One approach is to have another API for that use-case:
>
> gcry_error_t gcry_kem_enc_kdf (int algo,
> const void *pubkey,
> void *ciphertext,
> size_t sslen, void *ss);
> gcry_error_t gcry_kem_dec_kdf (int algo,
> const void *ciphertext,
> const void *seckey,
> size_t sslen, void *ss);

Fine with me, too
>
> /Simon


Ciao
Stephan



_______________________________________________
Gcrypt-devel mailing list
Gcrypt-devel@gnupg.org
https://lists.gnupg.org/mailman/listinfo/gcrypt-devel