Daniel Kahn Gillmor dkg@fifthhorseman.net writes:
I seem to have a few options:
- Crypt::Nettle could write unique interfaces to those ciphers and
expose them to the user of the perl module as (for example) Crypt::Nettle::Cipher::3DES and Crypt::Nettle::Cipher::Blowfish . this breaks symmetry with the rest of the interface, though.
- Crypt::Nettle could create its own struct nettle_cipher objects for
these ciphers, wrapping the weak key checking in some code of that belongs to the perl module
- I could propose that nettle to create struct nettle_cipher objects
for these ciphers directly.
I recommend (1). The way this is done in the Pike bindings (the implementation is maybe a bit too complicated for its own good), I use a struct pike_cipher very similar to nettle_cipher,
/* Calls Pike_error on errors */ typedef void (*pike_nettle_set_key_func)(void *ctx, ptrdiff_t length, const char *key, /* Force means to use key even if it is weak */ int force);
struct pike_cipher { const char *name;
unsigned context_size;
unsigned block_size;
/* Suggested key size; other sizes are sometimes possible. */ unsigned key_size;
pike_nettle_set_key_func set_encrypt_key; pike_nettle_set_key_func set_decrypt_key;
nettle_crypt_func encrypt; nettle_crypt_func decrypt; };
Here, the pike_nettle_set_key_func differs from nettle_set_key_func is two ways, related to error handling:
1. It checks if the key size is appropriate for the algorithm, and raises an exception if not (in contrast, a bad key size passed to the nettle set_key function would abort the process with an assertion failure).
2. The behaviour for weak keys. If the force argument is zero (for Pike calls, it's an optional argument and omitting it also means zero), a weak key results in an exception. If the force argument is non-zero, a weak key is not not considered an error.
In these bindings, unlike yours, each cipher like AES is a single class, with multiple supported key sizes. So all ciphers need their own set_key wrapper for proper error checking.
In your case, where you have one separate class per possible key size, I think you could do something similar and still use the new enumeration interface for the "normal" algorithms.
If you're fine with either having weak keys always raise an exception or always be accepted, you could write set_key wrappers for the affected ciphers which do precisely that and which adhere to the nettle_set_key_func interface (note that des_set_key and des3_set_key don't have a key size argument so they need wrappers also for that reason). If you want it to be configurable, things get a bit more complicated and you may need your own struct perl_cipher to extend struct nettle_cipher (you could still enumerate the available nettle_cipher and convert each to a corresponding perl_cipher). Or you could just define separate classes with and without weak key checking.
There will be a little code duplication. But there ought to be code *somewhere* to implements the language-specific pieces of the interface, such as exception based error handling, and new features, like, e.g., the optional force argument above.
Or, am i barking up the wrong tree entirely? I'm imagining (for example) a user who has in their possession a symmetrically-encrypted message that they happen to know the key for. The cipher used was one of the "weak-key" ciphers, and it's even possible that the key is in fact a weak key. The user should still be able to decrypt the message using Crypt::Nettle (or any other binding).
I agree that there are certainly cases where you don't want to treat weak keys as errors. Even though I think it makes sense to have a default behaviour which treats weak keys as errors.
Regards, /Niels