Hello Niels,

Thanks for your feedback!

Le 2021-02-03 à 03 h 47, Niels Möller a écrit :

Thanks. Can you give a bit more details on the usecase? I've understood
that it's part of web-related specs, but do you know any examples of
protocols or applicatinos using it, and how?

The AES Key Wrap is an encryption scheme designed to encrypt/decrypt a cryptographic key, the RFC introduction says:

   This key wrap algorithm needs to provide ample security to protect
   keys in the context of prudently designed key management
   architecture.

The AES key wrap is used in the JOSE standard to protect the encryption key used in some JWE (JSON Web Encryption) key management algorithms [1].

Basically, a JWE is an encrypted token. The payload encryption uses AES CBC|GCM with 128/192/256 bits key length. The AES key used to encrypt the payload is included encrypted in the token.

Concerning the algorithms used to encrypt the AES key, AES Key Wrap is required in 9 out of 17 possible algorithms.

I'm the author of the SSO Server Glewlwyd [2], Glewlwyd implements OpenID Connect, which uses JWT (JSON Web Tokens) via one of my library called Rhonabwy [3]. A JWT is a token signed (JWS) and/or encrypted (JWE). The encrypted tokens can be used for requests or response in lots of cases, to increase the security level of the application when it's required.

Currently Glewlwyd supports all signing algorithms specified, but not all encryption algorithm are supported yet. I intend to support as much possible encryption algorithm. And AES key Wrap is required in 9/17 specified encryption algorithms [4]


Should it really be AES only? It could be generalized to arbitrary block
ciphers (as long as block size is an even number of bytes), or more
easily to any 16-byte block cipher. I think the spec
https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38F.pdf
mentioned a variant for "TDEA" (I guess that's triple-DES?), which
doesn't seem that relevant.

It was designed to wrap key data, but not necessarily AES only. The kek must be an AES key though. The key data to wrap can be any data, as long as it's a set of 64 bits blocks.

Some comments on the code:

* Nettle usually doesn't return any status codes based on validation of
   inputs. The key wrap function should not have a return value. It can
   assume passed in pointers are valid, and use assert to check that
   lengths are valid. The unwrap function should obviously have a return
   value, but it doesn't need to check conditions that are trivial to
   check for the caller (not an obvious call if invalid ciphertext length
   for unwrap should trigger an assert or give a zero return value;
   whatever choice is made it needs to be clearly documented).

Noted, I'll remove the return statement for the key_wrap function, I'll also remove the input validation

* Try to avoid conditions on algorithm type. The common style in nettle
   is to have a general function taking an opaque context and needed
   function pointers as input, and then small convenience wrappers for
   algorithms of interest. So for the key wrap function, the cipher could
   be represented as a const void * for a cipher context already
   initialized with the "kek" key, and a nettle_cipher_func for invoking
   the cipher. If we want to support arbitrary block size (which is a bit
   questionable), it would also need a block size parametert.

Good point, instead of passing the kek in the input params, it's better to pass a struct aes_ctx which is supposed to be already initialized.

* If we restrict the implementation to 16-byte block size, it would be
   good to use wider types than uint8_t for the internal data shuffling.
   I think most or all internal state could be represented as union
   nettle_block16 or uint64_t. Movement (and xor) is independent of
   endianness. Easiest way to test for big-endian is likely to target
   mips, using cross compiler and qemu (like the mips builds in the
   gitlab ci). The

         I[7] ^= (n*j)+(i+1);
 >
   line would differ depending on endianness, if we want to do on a
   64-bit state variable. (Staying with bytes and memcpy may be fine if
   you think it makes the code simpler and there's no significant
   performance difference).

Good point too. Endianess is my kryptonite so my code would not be valid on all architectures...

* For the final memcmp in the unwrap function, you could use memeql_sec,
   to make the comparison side-channel silent (i.e., not leak information
   about the first differing byte).

noted too

* Since relation bewteen input and output size is very simple, we don't
   need both arguments. Just document the relation and provide
   appropriate #defined constants. The convention for similar functions
   is that the single length argument specifies the size of the *output*
   buffer.

So to be concrete on the interface comments, I'd suggest something like

void
nist_keywrap16(const void *ctx, nettle_cipher_func *encrypt,
                const uint8_t *iv, size_t ciphertext_length,
                uint8_t *ciphertext, cosnt uint8_t *cleartext);

int
nist_keyunwrap16(const void *ctx, nettle_cipher_func *decrypt,
                  const uint8_t *iv, size_t cleartext_length,
                  uint8_t *cleartext, const uint8_t *ciphertext);

We can also replace the uint8_t with uint64_t as well in the input values?

Not clear if default iv should be handled at this level. What are the
usecases for specifying a different iv?

It's up to the calling algorithm.
The IV is used for key data integrity [5].
Concerning using different IVs, the paragraph 2.2.3.2 mentions that alternative IVs can be used as part of larger key management protocols if the key data is not just an AES key, it may not always be a multiple of 64 bits.

Also, setting the default IV as a 'const uint8_t default_iv' in the function code isn't the best move, I totally agree.

Perhaps if instead of using uint8_t[8] I use uint64_t, then the default IV can be a classic #define DEFAULT_IV A6A6A6A6A6A6A6A6

I can add tests based on the tests vectors in the RFC [2], but I'm not
sure how the test suites are build, should I need to create test_wrap
functions like in aes-test.c or something else?

I think it would make sense to put tests in a new file. test_main should
contain one fucntion call per test case, calling whatever helper
functions are needed. It's probably sufficient to test
algorithm-specific convenience functions.

I'll get back to the tests when the function code will be completed then.

Thanks for all the feedback and the help!

/Nicolas

[1] https://tools.ietf.org/html/rfc7518#section-4
[2] https://github.com/babelouest/glewlwyd
[3] https://github.com/babelouest/rhonabwy
[4] https://tools.ietf.org/html/rfc7518#section-4.1
[5] https://tools.ietf.org/html/rfc3394#section-2.2.3

Attachment: OpenPGP_signature
Description: OpenPGP digital signature

_______________________________________________
nettle-bugs mailing list
[email protected]
http://lists.lysator.liu.se/mailman/listinfo/nettle-bugs

Reply via email to