]> git.codecow.com Git - Monocypher.git/commitdiff
Reworked Argon2 API (draft)
authorLoup Vaillant <loup@loup-vaillant.fr>
Mon, 12 Dec 2022 21:21:23 +0000 (22:21 +0100)
committerLoup Vaillant <loup@loup-vaillant.fr>
Mon, 12 Dec 2022 22:12:18 +0000 (23:12 +0100)
This is a prelude to Argon2d and Argon2id support.  The rationale here
is that supporting both with the current API would require way too many
functions.  Using a structure also helps manage the ungodly amount of
arguments this function has.

A number of unresolved questions so far:

- Should we pass by value or by reference?
- Should we start the struct with a size field, Microsoft style?
- Should we add a version field?
- Should we keep the nb_lanes field?
- If so should we support more than one lane, even while staying single
  threaded?
- Should we provide structures with default values to begin with?

This is mostly an API/ABI compatibility question.  Personally I think we
should omit the size field and pass by value, it feels more convenient
in practice.

A version field would let us support future versions of Argon2 without
breaking users, but the specs are so stable nowadays that I'm not sure
this is worth the trouble.  We may add it if users don't need to know
it's there.

The nb_lanes field however might be required for compatibility with the
_current_ specs, so I'm inclined to keep it even if we delay multi-lane
support indefinitely.

Default values are a difficult problem.  The correct strength for
password hashing is highly context dependent: we almost always want to
chose the highest tolerable strength, and there is no one size fits all.
The current manual outlines a _process_ for finding the values that work
for any given situation.

If we don't provide defaults, users have to fill out the fields
themselves, including fields that won't change often (nb_iterations), or
aren't supported yet (nb_lanes if we keep it). If we do provide
defaults, we need to chose them very carefully, and risk quick
obsolescence.

Finally, it's not clear which field should be in the struct, and which
field should be a regular argument.  Right now I put fields that are
likely to stay identical from invocation to invocation in the struct.

Another possibility is to instead restrict ourselves to fields that have
a good default, which would likely demote the nb_blocks to being a
regular argument.  That way users will know what parameters should be
treated as strong recommendations, and which they're supposed to chose
themselves.

Prepares #243

src/monocypher.c
src/monocypher.h
tests/speed/speed.c
tests/test.c
tests/tis-ci.c

index 2864f1fbaf1e2ac7e11e67eb5c06213e36e9ccbb..7714709381d0b19c7722cecb8fae84d3d541049a 100644 (file)
@@ -848,35 +848,36 @@ static u32 gidx_next(gidx_ctx *ctx)
        return (u32)(ref < ctx->nb_blocks ? ref : ref - ctx->nb_blocks);
 }
 
+const crypto_argon2_settings crypto_argon2i_defaults = {
+               CRYPTO_ARGON2_I, // algorithm
+               100000, 3, 1,    // nb_blocks, nb_iterations, nb_lanes
+               16, 32,          // salt_size, hash_size
+               0, 0, 0, 0,      // no key, no ad
+};
+
 // Main algorithm
-void crypto_argon2i_general(u8       *hash,      u32 hash_size,
-                            void     *work_area, u32 nb_blocks,
-                            u32 nb_iterations,
-                            const u8 *password,  u32 password_size,
-                            const u8 *salt,      u32 salt_size,
-                            const u8 *key,       u32 key_size,
-                            const u8 *ad,        u32 ad_size)
+void crypto_argon2(u8 *hash, void *work_area, const u8 *password,
+                   u32 password_size, const u8 *salt, crypto_argon2_settings s)
 {
        // work area seen as blocks (must be suitably aligned)
        block *blocks = (block*)work_area;
        {
                crypto_blake2b_ctx ctx;
                crypto_blake2b_init(&ctx);
-
-               blake_update_32      (&ctx, 1            ); // p: number of threads
-               blake_update_32      (&ctx, hash_size    );
-               blake_update_32      (&ctx, nb_blocks    );
-               blake_update_32      (&ctx, nb_iterations);
-               blake_update_32      (&ctx, 0x13         ); // v: version number
-               blake_update_32      (&ctx, 1            ); // y: Argon2i
+               blake_update_32      (&ctx, s.nb_lanes     ); // p: number of "threads"
+               blake_update_32      (&ctx, s.hash_size    );
+               blake_update_32      (&ctx, s.nb_blocks    );
+               blake_update_32      (&ctx, s.nb_iterations);
+               blake_update_32      (&ctx, 0x13           ); // v: version number
+               blake_update_32      (&ctx, s.algorithm    ); // y: Argon2i, Argon2d...
                blake_update_32      (&ctx,           password_size);
                crypto_blake2b_update(&ctx, password, password_size);
-               blake_update_32      (&ctx,           salt_size);
-               crypto_blake2b_update(&ctx, salt,     salt_size);
-               blake_update_32      (&ctx,           key_size);
-               crypto_blake2b_update(&ctx, key,      key_size);
-               blake_update_32      (&ctx,           ad_size);
-               crypto_blake2b_update(&ctx, ad,       ad_size);
+               blake_update_32      (&ctx,           s.salt_size);
+               crypto_blake2b_update(&ctx, salt,     s.salt_size);
+               blake_update_32      (&ctx,           s.key_size);
+               crypto_blake2b_update(&ctx, s.key,    s.key_size);
+               blake_update_32      (&ctx,           s.ad_size);
+               crypto_blake2b_update(&ctx, s.ad,     s.ad_size);
 
                u8 initial_hash[72]; // 64 bytes plus 2 words for future hashes
                crypto_blake2b_final(&ctx, initial_hash);
@@ -896,18 +897,18 @@ void crypto_argon2i_general(u8       *hash,      u32 hash_size,
                WIPE_BUFFER(hash_area);
        }
 
-       // Actual number of blocks
-       nb_blocks -= nb_blocks & 3; // round down to 4 p (p == 1 thread)
+       // Actual number of blocks (must be a multiple of 4 p)
+       u32 nb_blocks = s.nb_blocks - s.nb_blocks % (4 * s.nb_lanes);
        const u32 segment_size = nb_blocks >> 2;
 
        // fill (then re-fill) the rest of the blocks
        block tmp;
        gidx_ctx ctx; // public information, no need to wipe
-       FOR_T (u32, pass_number, 0, nb_iterations) {
+       FOR_T (u32, pass_number, 0, s.nb_iterations) {
                int first_pass = pass_number == 0;
 
                FOR_T (u32, segment, 0, 4) {
-                       gidx_init(&ctx, pass_number, segment, nb_blocks, nb_iterations);
+                       gidx_init(&ctx, pass_number, segment, nb_blocks, s.nb_iterations);
 
                        // On the first segment of the first pass,
                        // blocks 0 and 1 are already filled.
@@ -942,19 +943,10 @@ void crypto_argon2i_general(u8       *hash,      u32 hash_size,
        ZERO(p, 128 * nb_blocks);
 
        // hash the very last block with H' into the output hash
-       extended_hash(hash, hash_size, final_block, 1024);
+       extended_hash(hash, s.hash_size, final_block, 1024);
        WIPE_BUFFER(final_block);
 }
 
-void crypto_argon2i(u8   *hash,      u32 hash_size,
-                    void *work_area, u32 nb_blocks, u32 nb_iterations,
-                    const u8 *password,  u32 password_size,
-                    const u8 *salt,      u32 salt_size)
-{
-       crypto_argon2i_general(hash, hash_size, work_area, nb_blocks, nb_iterations,
-                              password, password_size, salt , salt_size, 0,0,0,0);
-}
-
 ////////////////////////////////////
 /// Arithmetic modulo 2^255 - 19 ///
 ////////////////////////////////////
index 941fa0471292f079c361d0225b2547cfc61ce9ab..6dacc87e7e088448327a01944f0b64abb7f48a59 100644 (file)
@@ -142,19 +142,29 @@ void crypto_blake2b_general_init(crypto_blake2b_ctx *ctx, size_t hash_size,
 
 // Password key derivation (Argon2 i)
 // ----------------------------------
-void crypto_argon2i(uint8_t       *hash,      uint32_t hash_size,     // >= 4
-                    void          *work_area, uint32_t nb_blocks,     // >= 8
-                    uint32_t       nb_iterations,                     // >= 3
-                    const uint8_t *password,  uint32_t password_size,
-                    const uint8_t *salt,      uint32_t salt_size);    // >= 8
-
-void crypto_argon2i_general(uint8_t       *hash,      uint32_t hash_size,// >= 4
-                            void          *work_area, uint32_t nb_blocks,// >= 8
-                            uint32_t       nb_iterations,                // >= 3
-                            const uint8_t *password,  uint32_t password_size,
-                            const uint8_t *salt,      uint32_t salt_size,// >= 8
-                            const uint8_t *key,       uint32_t key_size,
-                            const uint8_t *ad,        uint32_t ad_size);
+
+typedef struct {
+       uint32_t algorithm;     // Argon2i, Argon2d, Argon2id
+       uint32_t nb_blocks;     // memory hardness, >= 8
+       uint32_t nb_iterations; // CPU hardness, >= 1 (>= 3 recommended for Argon2i)
+       uint32_t nb_lanes;      // parallelism level (single threaded anyway)
+       uint32_t salt_size;     // we recommend 16 bytes
+       uint32_t hash_size;     // we recommend 32 bytes per key
+       const uint8_t *key;     // pointers are aligned to 8 bytes
+       const uint8_t *ad;
+       uint32_t key_size;
+       uint32_t ad_size;
+} crypto_argon2_settings;
+
+#define CRYPTO_ARGON2_I  1
+
+extern const crypto_argon2_settings crypto_argon2i_defaults;
+
+void crypto_argon2(uint8_t       *hash,
+                   void          *work_area,
+                   const uint8_t *password,  uint32_t password_size,
+                   const uint8_t *salt,
+                   crypto_argon2_settings s);
 
 
 // Key exchange (X-25519)
index 0b73725e989ffc9d147f8b5e62b55770c7f366e1..5c10f79f24521fc0e6d2e582b43bb297b7b89649 100644 (file)
@@ -120,13 +120,14 @@ static u64 argon2i(void)
 {
        u64 work_area[SIZE / 8];
        u8  hash     [32];
-       u32 nb_blocks = (u32)(SIZE / 1024);
        RANDOM_INPUT(password,  16);
        RANDOM_INPUT(salt    ,  16);
 
+       crypto_argon2_settings s = crypto_argon2i_defaults;
+       s.nb_blocks = (u32)(SIZE / 1024);
+
        TIMING_START {
-               crypto_argon2i(hash, 32, work_area, nb_blocks, 3,
-                              password, 16, salt, 16);
+               crypto_argon2(hash, work_area, password, 16, salt, s);
        }
        TIMING_END;
 }
index b47038b479615ff753d6f2cf05bbb11bf44cb3d5..53c578c1544340e3180314dd14fd2a9f8729ec98 100644 (file)
@@ -539,12 +539,18 @@ static void argon2i(vector_reader *reader)
        vector ad            = next_input(reader);
        vector out           = next_output(reader);
        void  *work_area     = alloc(nb_blocks * 1024);
-       crypto_argon2i_general(out.buf, (u32)out.size,
-                              work_area, (u32)nb_blocks, (u32)nb_iterations,
-                              password.buf, (u32)password.size,
-                              salt    .buf, (u32)salt    .size,
-                              key     .buf, (u32)key     .size,
-                              ad      .buf, (u32)ad      .size);
+
+       crypto_argon2_settings s = crypto_argon2i_defaults;
+       s.nb_blocks     = nb_blocks;
+       s.nb_iterations = nb_iterations;
+       s.hash_size     = out.size;
+       s.salt_size     = salt.size;
+       s.key           = key.buf;
+       s.key_size      = key.size;
+       s.ad            = ad.buf;
+       s.ad_size       = ad.size;
+
+       crypto_argon2(out.buf, work_area, password.buf, password.size, salt.buf, s);
        free(work_area);
 }
 
@@ -552,20 +558,6 @@ static void test_argon2i()
 {
        VECTORS(argon2i);
 
-       printf("\tArgon2i (easy interface)\n");
-       {
-               void *work_area = alloc(8 * 1024);
-               RANDOM_INPUT(password , 32);
-               RANDOM_INPUT(salt     , 16);
-               u8 hash_general[32];
-               u8 hash_easy   [32];
-               crypto_argon2i_general(hash_general, 32, work_area, 8, 1,
-                                      password, 32, salt, 16, 0, 0, 0, 0);
-               crypto_argon2i(hash_easy, 32, work_area, 8, 1, password, 32, salt, 16);
-               ASSERT_EQUAL(hash_general, hash_easy, 32);
-               free(work_area);
-       }
-
        printf("\tArgon2i (overlapping i/o)\n");
        u8 *work_area       = (u8*)alloc(8 * 1024);
        u8 *clean_work_area = (u8*)alloc(8 * 1024);
@@ -583,13 +575,23 @@ static void test_argon2i()
                u8  key  [32];  FOR (j, 0, 32) { key [j] = work_area[j +  key_offset]; }
                u8  ad   [32];  FOR (j, 0, 32) { ad  [j] = work_area[j +   ad_offset]; }
 
-               crypto_argon2i_general(hash1, 32, clean_work_area, 8, 1,
-                                      pass, 16, salt, 16, key, 32, ad, 32);
-               crypto_argon2i_general(hash2, 32, work_area, 8, 1,
-                                      work_area + pass_offset, 16,
-                                      work_area + salt_offset, 16,
-                                      work_area +  key_offset, 32,
-                                      work_area +   ad_offset, 32);
+               // without overlap
+               crypto_argon2_settings s = crypto_argon2i_defaults;
+               s.nb_blocks     = 8;
+               s.nb_iterations = 1;
+               s.key           = key;
+               s.ad            = ad;
+               s.key_size      = 32;
+               s.ad_size       = 32;
+               crypto_argon2(hash1, clean_work_area, pass, 16, salt, s);
+
+               // with overlap
+               s.key = work_area + key_offset;
+               s.ad  = work_area +  ad_offset;
+               crypto_argon2(hash2, work_area,
+                             work_area + pass_offset, 16,
+                             work_area + salt_offset, s);
+
                ASSERT_EQUAL(hash1, hash2, 32);
        }
        free(work_area);
index 0fb8e29f012de6f1b0e7a4587bcb0f4cc35882ff..ce9dc94829b6439f643ee663c95250ba4fb8330c 100644 (file)
@@ -168,12 +168,18 @@ static void argon2i(vector_reader *reader)
        vector ad            = next_input(reader);
        vector out           = next_output(reader);
        void  *work_area     = alloc(nb_blocks * 1024);
-       crypto_argon2i_general(out.buf, (u32)out.size,
-                              work_area, (u32)nb_blocks, (u32)nb_iterations,
-                              password.buf, (u32)password.size,
-                              salt    .buf, (u32)salt    .size,
-                              key     .buf, (u32)key     .size,
-                              ad      .buf, (u32)ad      .size);
+
+       crypto_argon2_settings s = crypto_argon2i_defaults;
+       s.nb_blocks     = nb_blocks;
+       s.nb_iterations = nb_iterations;
+       s.hash_size     = out.size;
+       s.salt_size     = salt.size;
+       s.key           = key.buf;
+       s.key_size      = key.size;
+       s.ad            = ad.buf;
+       s.ad_size       = ad.size;
+
+       crypto_argon2(out.buf, work_area, password.buf, password.size, salt.buf, s);
        free(work_area);
 }