diff --git a/Crypto/Error/Types.hs b/Crypto/Error/Types.hs index cb512d2..fd0e9c4 100644 --- a/Crypto/Error/Types.hs +++ b/Crypto/Error/Types.hs @@ -45,6 +45,10 @@ data CryptoError = | CryptoError_AuthenticationTagSizeInvalid -- Prime generation error | CryptoError_PrimeSizeInvalid + -- Parameter errors + | CryptoError_SaltTooSmall + | CryptoError_OutputLengthTooSmall + | CryptoError_OutputLengthTooBig deriving (Show,Eq,Enum,Data,Typeable) instance E.Exception CryptoError diff --git a/Crypto/KDF/Argon2.hs b/Crypto/KDF/Argon2.hs new file mode 100644 index 0000000..312ceaf --- /dev/null +++ b/Crypto/KDF/Argon2.hs @@ -0,0 +1,156 @@ +-- | +-- Module : Crypto.KDF.Argon2 +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : unknown +-- +-- Argon2 hashing function (P-H-C winner) +-- +-- Recommended to use this module qualified +-- +-- File started from Argon2.hs, from Oliver Charles +-- at https://github.com/ocharles/argon2 +-- +module Crypto.KDF.Argon2 + ( + Options(..) + , TimeCost + , MemoryCost + , Parallelism + , Variant(..) + , Version(..) + , defaultOptions + -- * Hashing function + , hash + ) where + +import Crypto.Internal.ByteArray (ScrubbedBytes, ByteArray, ByteArrayAccess) +import qualified Crypto.Internal.ByteArray as B +import Crypto.Error +import Control.Monad (when) +import Data.Word +import Data.Typeable +import Foreign.C +import Foreign.Ptr + +-- | Which variant of Argon2 to use. You should choose the variant that is most +-- applicable to your intention to hash inputs. +data Variant = + Argon2d -- ^ Argon2i uses data-independent memory access, which is preferred + -- for password hashing and password-based key derivation. Argon2i + -- is slower as it makes more passes over the memory to protect from + -- tradeoff attacks. + | Argon2i -- ^ Argon2d is faster and uses data-depending memory access, which + -- makes it suitable for cryptocurrencies and applications with no + -- threats from side-channel timing attacks. + | Argon2id -- ^ Argon2id is a hybrid of Argon2i and Argon2d, using a combination + -- of data-depending and data-independent memory accesses, which gives + -- some of Argon2i's resistance to side-channel cache timing attacks + -- and much of Argon2d's resistance to GPU cracking attacks + deriving (Eq,Ord,Read,Show,Enum,Bounded,Typeable) + +-- | Which version of Argon2 to use +data Version = Version10 | Version13 + deriving (Eq,Ord,Read,Show,Enum,Bounded,Typeable) + +-- | The time cost, which defines the amount of computation realized and therefore the execution time, given in number of iterations. +-- +-- 'FFI.ARGON2_MIN_TIME' <= 'hashIterations' <= 'FFI.ARGON2_MAX_TIME' +type TimeCost = Word32 + +-- | The memory cost, which defines the memory usage, given in kibibytes. +-- +-- max 'FFI.ARGON2_MIN_MEMORY' (8 * 'hashParallelism') <= 'hashMemory' <= 'FFI.ARGON2_MAX_MEMORY' +type MemoryCost = Word32 + +-- \ A parallelism degree, which defines the number of parallel threads. +-- +-- 'FFI.ARGON2_MIN_LANES' <= 'hashParallelism' <= 'FFI.ARGON2_MAX_LANES' && 'FFI.ARGON_MIN_THREADS' <= 'hashParallelism' <= 'FFI.ARGON2_MAX_THREADS' +type Parallelism = Word32 + +-- | Parameters that can be adjusted to change the runtime performance of the +-- hashing. +data Options = Options + { iterations :: !TimeCost + , memory :: !MemoryCost + , parallelism :: !Parallelism + , variant :: !Variant -- ^ Which variant of Argon2 to use. + , version :: !Version -- ^ Which version of Argon2 to use. + } + deriving (Eq,Ord,Read,Show,Typeable) + +saltMinLength :: Int +saltMinLength = 8 + +outputMinLength :: Int +outputMinLength = 4 + +outputMaxLength :: Int +outputMaxLength = 0xffffffff + +defaultOptions :: Options +defaultOptions = + Options { iterations = 1 + , memory = 2 ^ (17 :: Int) + , parallelism = 4 + , variant = Argon2i + , version = Version13 + } + +hash :: (ByteArrayAccess password, ByteArrayAccess salt, ByteArray out) + => Options + -> password + -> salt + -> Int + -> CryptoFailable out +hash options password salt outLen + | saltLen < saltMinLength = CryptoFailed CryptoError_SaltTooSmall + | outLen < outputMinLength = CryptoFailed CryptoError_OutputLengthTooSmall + | outLen > outputMaxLength = CryptoFailed CryptoError_OutputLengthTooBig + | otherwise = CryptoPassed $ B.allocAndFreeze outLen $ \out -> do + res <- B.withByteArray password $ \pPass -> + B.withByteArray salt $ \pSalt -> + argon2_hash (iterations options) + (memory options) + (parallelism options) + pPass + (csizeOfInt passwordLen) + pSalt + (csizeOfInt saltLen) + out + (csizeOfInt outLen) + (cOfVariant $ variant options) + (cOfVersion $ version options) + when (res /= 0) $ error "argon2: hash: internal error" + where + saltLen = B.length salt + passwordLen = B.length password + +data Pass +data Salt +data HashOut + +type CVariant = CInt -- valid value is 0 (Argon2d), 1 (Argon2i) and 2 (Argon2id) +type CVersion = CInt -- valid value is 0x10, 0x13 + +cOfVersion :: Version -> CVersion +cOfVersion Version10 = 0x10 +cOfVersion Version13 = 0x13 + +cOfVariant :: Variant -> CVariant +cOfVariant Argon2d = 0 +cOfVariant Argon2i = 1 +cOfVariant Argon2id = 2 + +csizeOfInt :: Int -> CSize +csizeOfInt = fromIntegral + +foreign import ccall unsafe "cryptonite_argon2_hash" + argon2_hash :: Word32 -> Word32 -> Word32 + -> Ptr Pass -> CSize + -> Ptr Salt -> CSize + -> Ptr HashOut -> CSize + -> CVariant + -> CVersion + -> IO CInt diff --git a/cbits/argon2/argon2.c b/cbits/argon2/argon2.c index 2bb3619..717522e 100644 --- a/cbits/argon2/argon2.c +++ b/cbits/argon2/argon2.c @@ -20,22 +20,8 @@ #include #include "argon2.h" -#include "encoding.c" #include "core.c" -const char *cryptonite_argon2_type2string(argon2_type type, int uppercase) { - switch (type) { - case Argon2_d: - return uppercase ? "Argon2d" : "argon2d"; - case Argon2_i: - return uppercase ? "Argon2i" : "argon2i"; - case Argon2_id: - return uppercase ? "Argon2id" : "argon2id"; - } - - return NULL; -} - int cryptonite_argon2_ctx(argon2_context *context, argon2_type type) { /* 1. Validate all inputs */ int result = validate_inputs(context); @@ -96,8 +82,7 @@ int cryptonite_argon2_ctx(argon2_context *context, argon2_type type) { int cryptonite_argon2_hash(const uint32_t t_cost, const uint32_t m_cost, const uint32_t parallelism, const void *pwd, const size_t pwdlen, const void *salt, const size_t saltlen, - void *hash, const size_t hashlen, char *encoded, - const size_t encodedlen, argon2_type type, + void *hash, const size_t hashlen, argon2_type type, const uint32_t version){ argon2_context context; @@ -149,288 +134,9 @@ int cryptonite_argon2_hash(const uint32_t t_cost, const uint32_t m_cost, memcpy(hash, out, hashlen); } - /* if encoding requested, write it */ - if (encoded && encodedlen) { - if (encode_string(encoded, encodedlen, &context, type) != ARGON2_OK) { - clear_internal_memory(out, hashlen); /* wipe buffers if error */ - clear_internal_memory(encoded, encodedlen); - free(out); - return ARGON2_ENCODING_FAIL; - } - } clear_internal_memory(out, hashlen); free(out); return ARGON2_OK; } -int cryptonite_argon2i_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, - const uint32_t parallelism, const void *pwd, - const size_t pwdlen, const void *salt, - const size_t saltlen, const size_t hashlen, - char *encoded, const size_t encodedlen) { - - return cryptonite_argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, - NULL, hashlen, encoded, encodedlen, Argon2_i, - ARGON2_VERSION_NUMBER); -} - -int cryptonite_argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost, - const uint32_t parallelism, const void *pwd, - const size_t pwdlen, const void *salt, - const size_t saltlen, void *hash, const size_t hashlen) { - - return cryptonite_argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, - hash, hashlen, NULL, 0, Argon2_i, ARGON2_VERSION_NUMBER); -} - -int cryptonite_argon2d_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, - const uint32_t parallelism, const void *pwd, - const size_t pwdlen, const void *salt, - const size_t saltlen, const size_t hashlen, - char *encoded, const size_t encodedlen) { - - return cryptonite_argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, - NULL, hashlen, encoded, encodedlen, Argon2_d, - ARGON2_VERSION_NUMBER); -} - -int cryptonite_argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost, - const uint32_t parallelism, const void *pwd, - const size_t pwdlen, const void *salt, - const size_t saltlen, void *hash, const size_t hashlen) { - - return cryptonite_argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, - hash, hashlen, NULL, 0, Argon2_d, ARGON2_VERSION_NUMBER); -} - -int cryptonite_argon2id_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, - const uint32_t parallelism, const void *pwd, - const size_t pwdlen, const void *salt, - const size_t saltlen, const size_t hashlen, - char *encoded, const size_t encodedlen) { - - return cryptonite_argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, - NULL, hashlen, encoded, encodedlen, Argon2_id, - ARGON2_VERSION_NUMBER); -} - -int cryptonite_argon2id_hash_raw(const uint32_t t_cost, const uint32_t m_cost, - const uint32_t parallelism, const void *pwd, - const size_t pwdlen, const void *salt, - const size_t saltlen, void *hash, const size_t hashlen) { - return cryptonite_argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, - hash, hashlen, NULL, 0, Argon2_id, - ARGON2_VERSION_NUMBER); -} - -static int cryptonite_argon2_compare(const uint8_t *b1, const uint8_t *b2, size_t len) { - size_t i; - uint8_t d = 0U; - - for (i = 0U; i < len; i++) { - d |= b1[i] ^ b2[i]; - } - return (int)((1 & ((d - 1) >> 8)) - 1); -} - -int cryptonite_argon2_verify(const char *encoded, const void *pwd, const size_t pwdlen, - argon2_type type) { - - argon2_context ctx; - uint8_t *desired_result = NULL; - - int ret = ARGON2_OK; - - size_t encoded_len; - uint32_t max_field_len; - - if (encoded == NULL) { - return ARGON2_DECODING_FAIL; - } - - encoded_len = strlen(encoded); - if (encoded_len > UINT32_MAX) { - return ARGON2_DECODING_FAIL; - } - - /* No field can be longer than the encoded length */ - max_field_len = (uint32_t)encoded_len; - - ctx.saltlen = max_field_len; - ctx.outlen = max_field_len; - - ctx.salt = malloc(ctx.saltlen); - ctx.out = malloc(ctx.outlen); - if (!ctx.salt || !ctx.out) { - ret = ARGON2_MEMORY_ALLOCATION_ERROR; - goto fail; - } - - ctx.pwd = (uint8_t *)pwd; - ctx.pwdlen = pwdlen; - - ret = decode_string(&ctx, encoded, type); - if (ret != ARGON2_OK) { - goto fail; - } - - /* Set aside the desired result, and get a new buffer. */ - desired_result = ctx.out; - ctx.out = malloc(ctx.outlen); - if (!ctx.out) { - ret = ARGON2_MEMORY_ALLOCATION_ERROR; - goto fail; - } - - ret = cryptonite_argon2_verify_ctx(&ctx, (char *)desired_result, type); - if (ret != ARGON2_OK) { - goto fail; - } - -fail: - free(ctx.salt); - free(ctx.out); - free(desired_result); - - return ret; -} - -int cryptonite_argon2i_verify(const char *encoded, const void *pwd, const size_t pwdlen) { - - return cryptonite_argon2_verify(encoded, pwd, pwdlen, Argon2_i); -} - -int cryptonite_argon2d_verify(const char *encoded, const void *pwd, const size_t pwdlen) { - - return cryptonite_argon2_verify(encoded, pwd, pwdlen, Argon2_d); -} - -int cryptonite_argon2id_verify(const char *encoded, const void *pwd, const size_t pwdlen) { - - return cryptonite_argon2_verify(encoded, pwd, pwdlen, Argon2_id); -} - -int cryptonite_argon2d_ctx(argon2_context *context) { - return cryptonite_argon2_ctx(context, Argon2_d); -} - -int cryptonite_argon2i_ctx(argon2_context *context) { - return cryptonite_argon2_ctx(context, Argon2_i); -} - -int cryptonite_argon2id_ctx(argon2_context *context) { - return cryptonite_argon2_ctx(context, Argon2_id); -} - -int cryptonite_argon2_verify_ctx(argon2_context *context, const char *hash, - argon2_type type) { - int ret = cryptonite_argon2_ctx(context, type); - if (ret != ARGON2_OK) { - return ret; - } - - if (cryptonite_argon2_compare((uint8_t *)hash, context->out, context->outlen)) { - return ARGON2_VERIFY_MISMATCH; - } - - return ARGON2_OK; -} - -int cryptonite_argon2d_verify_ctx(argon2_context *context, const char *hash) { - return cryptonite_argon2_verify_ctx(context, hash, Argon2_d); -} - -int cryptonite_argon2i_verify_ctx(argon2_context *context, const char *hash) { - return cryptonite_argon2_verify_ctx(context, hash, Argon2_i); -} - -int cryptonite_argon2id_verify_ctx(argon2_context *context, const char *hash) { - return cryptonite_argon2_verify_ctx(context, hash, Argon2_id); -} - -const char *cryptonite_argon2_error_message(int error_code) { - switch (error_code) { - case ARGON2_OK: - return "OK"; - case ARGON2_OUTPUT_PTR_NULL: - return "Output pointer is NULL"; - case ARGON2_OUTPUT_TOO_SHORT: - return "Output is too short"; - case ARGON2_OUTPUT_TOO_LONG: - return "Output is too long"; - case ARGON2_PWD_TOO_SHORT: - return "Password is too short"; - case ARGON2_PWD_TOO_LONG: - return "Password is too long"; - case ARGON2_SALT_TOO_SHORT: - return "Salt is too short"; - case ARGON2_SALT_TOO_LONG: - return "Salt is too long"; - case ARGON2_AD_TOO_SHORT: - return "Associated data is too short"; - case ARGON2_AD_TOO_LONG: - return "Associated data is too long"; - case ARGON2_SECRET_TOO_SHORT: - return "Secret is too short"; - case ARGON2_SECRET_TOO_LONG: - return "Secret is too long"; - case ARGON2_TIME_TOO_SMALL: - return "Time cost is too small"; - case ARGON2_TIME_TOO_LARGE: - return "Time cost is too large"; - case ARGON2_MEMORY_TOO_LITTLE: - return "Memory cost is too small"; - case ARGON2_MEMORY_TOO_MUCH: - return "Memory cost is too large"; - case ARGON2_LANES_TOO_FEW: - return "Too few lanes"; - case ARGON2_LANES_TOO_MANY: - return "Too many lanes"; - case ARGON2_PWD_PTR_MISMATCH: - return "Password pointer is NULL, but password length is not 0"; - case ARGON2_SALT_PTR_MISMATCH: - return "Salt pointer is NULL, but salt length is not 0"; - case ARGON2_SECRET_PTR_MISMATCH: - return "Secret pointer is NULL, but secret length is not 0"; - case ARGON2_AD_PTR_MISMATCH: - return "Associated data pointer is NULL, but ad length is not 0"; - case ARGON2_MEMORY_ALLOCATION_ERROR: - return "Memory allocation error"; - case ARGON2_FREE_MEMORY_CBK_NULL: - return "The free memory callback is NULL"; - case ARGON2_ALLOCATE_MEMORY_CBK_NULL: - return "The allocate memory callback is NULL"; - case ARGON2_INCORRECT_PARAMETER: - return "Argon2_Context context is NULL"; - case ARGON2_INCORRECT_TYPE: - return "There is no such version of Argon2"; - case ARGON2_OUT_PTR_MISMATCH: - return "Output pointer mismatch"; - case ARGON2_THREADS_TOO_FEW: - return "Not enough threads"; - case ARGON2_THREADS_TOO_MANY: - return "Too many threads"; - case ARGON2_MISSING_ARGS: - return "Missing arguments"; - case ARGON2_ENCODING_FAIL: - return "Encoding failed"; - case ARGON2_DECODING_FAIL: - return "Decoding failed"; - case ARGON2_THREAD_FAIL: - return "Threading failure"; - case ARGON2_DECODING_LENGTH_FAIL: - return "Some of encoded parameters are too long or too short"; - case ARGON2_VERIFY_MISMATCH: - return "The password does not match the supplied hash"; - default: - return "Unknown error code"; - } -} - -size_t cryptonite_argon2_encodedlen(uint32_t t_cost, uint32_t m_cost, uint32_t parallelism, - uint32_t saltlen, uint32_t hashlen, argon2_type type) { - return strlen("$$v=$m=,t=,p=$$") + strlen(cryptonite_argon2_type2string(type, 0)) + - numlen(t_cost) + numlen(m_cost) + numlen(parallelism) + - b64len(saltlen) + b64len(hashlen) + numlen(ARGON2_VERSION_NUMBER) + 1; -} diff --git a/cbits/argon2/argon2.h b/cbits/argon2/argon2.h index 595bfde..a4a088f 100644 --- a/cbits/argon2/argon2.h +++ b/cbits/argon2/argon2.h @@ -228,14 +228,6 @@ typedef enum Argon2_version { ARGON2_VERSION_NUMBER = ARGON2_VERSION_13 } argon2_version; -/* - * Function that gives the string representation of an argon2_type. - * @param type The argon2_type that we want the string for - * @param uppercase Whether the string should have the first letter uppercase - * @return NULL if invalid type, otherwise the string representation. - */ -ARGON2_PUBLIC const char *cryptonite_argon2_type2string(argon2_type type, int uppercase); - /* * Function that performs memory-hard hashing with certain degree of parallelism * @param context Pointer to the Argon2 internal structure @@ -243,29 +235,6 @@ ARGON2_PUBLIC const char *cryptonite_argon2_type2string(argon2_type type, int up */ ARGON2_PUBLIC int cryptonite_argon2_ctx(argon2_context *context, argon2_type type); -/** - * Hashes a password with Argon2i, producing an encoded hash - * @param t_cost Number of iterations - * @param m_cost Sets memory usage to m_cost kibibytes - * @param parallelism Number of threads and compute lanes - * @param pwd Pointer to password - * @param pwdlen Password size in bytes - * @param salt Pointer to salt - * @param saltlen Salt size in bytes - * @param hashlen Desired length of the hash in bytes - * @param encoded Buffer where to write the encoded hash - * @param encodedlen Size of the buffer (thus max size of the encoded hash) - * @pre Different parallelism levels will give different results - * @pre Returns ARGON2_OK if successful - */ -ARGON2_PUBLIC int cryptonite_argon2i_hash_encoded(const uint32_t t_cost, - const uint32_t m_cost, - const uint32_t parallelism, - const void *pwd, const size_t pwdlen, - const void *salt, const size_t saltlen, - const size_t hashlen, char *encoded, - const size_t encodedlen); - /** * Hashes a password with Argon2i, producing a raw hash by allocating memory at * @hash @@ -281,152 +250,15 @@ ARGON2_PUBLIC int cryptonite_argon2i_hash_encoded(const uint32_t t_cost, * @pre Different parallelism levels will give different results * @pre Returns ARGON2_OK if successful */ -ARGON2_PUBLIC int cryptonite_argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost, - const uint32_t parallelism, const void *pwd, - const size_t pwdlen, const void *salt, - const size_t saltlen, void *hash, - const size_t hashlen); - -ARGON2_PUBLIC int cryptonite_argon2d_hash_encoded(const uint32_t t_cost, - const uint32_t m_cost, - const uint32_t parallelism, - const void *pwd, const size_t pwdlen, - const void *salt, const size_t saltlen, - const size_t hashlen, char *encoded, - const size_t encodedlen); - -ARGON2_PUBLIC int cryptonite_argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost, - const uint32_t parallelism, const void *pwd, - const size_t pwdlen, const void *salt, - const size_t saltlen, void *hash, - const size_t hashlen); - -ARGON2_PUBLIC int cryptonite_argon2id_hash_encoded(const uint32_t t_cost, - const uint32_t m_cost, - const uint32_t parallelism, - const void *pwd, const size_t pwdlen, - const void *salt, const size_t saltlen, - const size_t hashlen, char *encoded, - const size_t encodedlen); - -ARGON2_PUBLIC int cryptonite_argon2id_hash_raw(const uint32_t t_cost, - const uint32_t m_cost, - const uint32_t parallelism, const void *pwd, - const size_t pwdlen, const void *salt, - const size_t saltlen, void *hash, - const size_t hashlen); /* generic function underlying the above ones */ ARGON2_PUBLIC int cryptonite_argon2_hash(const uint32_t t_cost, const uint32_t m_cost, const uint32_t parallelism, const void *pwd, const size_t pwdlen, const void *salt, const size_t saltlen, void *hash, - const size_t hashlen, char *encoded, - const size_t encodedlen, argon2_type type, + const size_t hashlen, argon2_type type, const uint32_t version); -/** - * Verifies a password against an encoded string - * Encoded string is restricted as in validate_inputs() - * @param encoded String encoding parameters, salt, hash - * @param pwd Pointer to password - * @pre Returns ARGON2_OK if successful - */ -ARGON2_PUBLIC int cryptonite_argon2i_verify(const char *encoded, const void *pwd, - const size_t pwdlen); - -ARGON2_PUBLIC int cryptonite_argon2d_verify(const char *encoded, const void *pwd, - const size_t pwdlen); - -ARGON2_PUBLIC int cryptonite_argon2id_verify(const char *encoded, const void *pwd, - const size_t pwdlen); - -/* generic function underlying the above ones */ -ARGON2_PUBLIC int cryptonite_argon2_verify(const char *encoded, const void *pwd, - const size_t pwdlen, argon2_type type); - -/** - * Argon2d: Version of Argon2 that picks memory blocks depending - * on the password and salt. Only for side-channel-free - * environment!! - ***** - * @param context Pointer to current Argon2 context - * @return Zero if successful, a non zero error code otherwise - */ -ARGON2_PUBLIC int cryptonite_argon2d_ctx(argon2_context *context); - -/** - * Argon2i: Version of Argon2 that picks memory blocks - * independent on the password and salt. Good for side-channels, - * but worse w.r.t. tradeoff attacks if only one pass is used. - ***** - * @param context Pointer to current Argon2 context - * @return Zero if successful, a non zero error code otherwise - */ -ARGON2_PUBLIC int cryptonite_argon2i_ctx(argon2_context *context); - -/** - * Argon2id: Version of Argon2 where the first half-pass over memory is - * password-independent, the rest are password-dependent (on the password and - * salt). OK against side channels (they reduce to 1/2-pass Argon2i), and - * better with w.r.t. tradeoff attacks (similar to Argon2d). - ***** - * @param context Pointer to current Argon2 context - * @return Zero if successful, a non zero error code otherwise - */ -ARGON2_PUBLIC int cryptonite_argon2id_ctx(argon2_context *context); - -/** - * Verify if a given password is correct for Argon2d hashing - * @param context Pointer to current Argon2 context - * @param hash The password hash to verify. The length of the hash is - * specified by the context outlen member - * @return Zero if successful, a non zero error code otherwise - */ -ARGON2_PUBLIC int cryptonite_argon2d_verify_ctx(argon2_context *context, const char *hash); - -/** - * Verify if a given password is correct for Argon2i hashing - * @param context Pointer to current Argon2 context - * @param hash The password hash to verify. The length of the hash is - * specified by the context outlen member - * @return Zero if successful, a non zero error code otherwise - */ -ARGON2_PUBLIC int cryptonite_argon2i_verify_ctx(argon2_context *context, const char *hash); - -/** - * Verify if a given password is correct for Argon2id hashing - * @param context Pointer to current Argon2 context - * @param hash The password hash to verify. The length of the hash is - * specified by the context outlen member - * @return Zero if successful, a non zero error code otherwise - */ -ARGON2_PUBLIC int cryptonite_argon2id_verify_ctx(argon2_context *context, - const char *hash); - -/* generic function underlying the above ones */ -ARGON2_PUBLIC int cryptonite_argon2_verify_ctx(argon2_context *context, const char *hash, - argon2_type type); - -/** - * Get the associated error message for given error code - * @return The error message associated with the given error code - */ -ARGON2_PUBLIC const char *cryptonite_argon2_error_message(int error_code); - -/** - * Returns the encoded hash length for the given input parameters - * @param t_cost Number of iterations - * @param m_cost Memory usage in kibibytes - * @param parallelism Number of threads; used to compute lanes - * @param saltlen Salt size in bytes - * @param hashlen Hash size in bytes - * @param type The argon2_type that we want the encoded length for - * @return The encoded hash length in bytes - */ -ARGON2_PUBLIC size_t cryptonite_argon2_encodedlen(uint32_t t_cost, uint32_t m_cost, - uint32_t parallelism, uint32_t saltlen, - uint32_t hashlen, argon2_type type); #if defined(__cplusplus) } diff --git a/cbits/argon2/bench.c b/cbits/argon2/bench.c deleted file mode 100644 index 9ec6a4a..0000000 --- a/cbits/argon2/bench.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Argon2 reference source code package - reference C implementations - * - * Copyright 2015 - * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves - * - * You may use this work under the terms of a Creative Commons CC0 1.0 - * License/Waiver or the Apache Public License 2.0, at your option. The terms of - * these licenses can be found at: - * - * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - * - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - * - * You should have received a copy of both of these licenses along with this - * software. If not, they may be obtained at the above URLs. - */ - -#include -#include -#include -#include -#include -#ifdef _MSC_VER -#include -#endif - -#include "argon2.h" - -static uint64_t rdtsc(void) { -#ifdef _MSC_VER - return __rdtsc(); -#else -#if defined(__amd64__) || defined(__x86_64__) - uint64_t rax, rdx; - __asm__ __volatile__("rdtsc" : "=a"(rax), "=d"(rdx) : :); - return (rdx << 32) | rax; -#elif defined(__i386__) || defined(__i386) || defined(__X86__) - uint64_t rax; - __asm__ __volatile__("rdtsc" : "=A"(rax) : :); - return rax; -#else -#error "Not implemented!" -#endif -#endif -} - -/* - * Benchmarks Argon2 with salt length 16, password length 16, t_cost 1, - and different m_cost and threads - */ -static void benchmark() { -#define BENCH_OUTLEN 16 -#define BENCH_INLEN 16 - const uint32_t inlen = BENCH_INLEN; - const unsigned outlen = BENCH_OUTLEN; - unsigned char out[BENCH_OUTLEN]; - unsigned char pwd_array[BENCH_INLEN]; - unsigned char salt_array[BENCH_INLEN]; -#undef BENCH_INLEN -#undef BENCH_OUTLEN - - uint32_t t_cost = 3; - uint32_t m_cost; - uint32_t thread_test[4] = {1, 2, 4, 8}; - argon2_type types[3] = {Argon2_i, Argon2_d, Argon2_id}; - - memset(pwd_array, 0, inlen); - memset(salt_array, 1, inlen); - - for (m_cost = (uint32_t)1 << 10; m_cost <= (uint32_t)1 << 22; m_cost *= 2) { - unsigned i; - for (i = 0; i < 4; ++i) { - double run_time = 0; - uint32_t thread_n = thread_test[i]; - - unsigned j; - for (j = 0; j < 3; ++j) { - clock_t start_time, stop_time; - uint64_t start_cycles, stop_cycles; - uint64_t delta; - double mcycles; - - argon2_type type = types[j]; - start_time = clock(); - start_cycles = rdtsc(); - - argon2_hash(t_cost, m_cost, thread_n, pwd_array, inlen, - salt_array, inlen, out, outlen, NULL, 0, type, - ARGON2_VERSION_NUMBER); - - stop_cycles = rdtsc(); - stop_time = clock(); - - delta = (stop_cycles - start_cycles) / (m_cost); - mcycles = (double)(stop_cycles - start_cycles) / (1UL << 20); - run_time += ((double)stop_time - start_time) / (CLOCKS_PER_SEC); - - printf("%s %d iterations %d MiB %d threads: %2.2f cpb %2.2f " - "Mcycles \n", argon2_type2string(type, 1), t_cost, - m_cost >> 10, thread_n, (float)delta / 1024, mcycles); - } - - printf("%2.4f seconds\n\n", run_time); - } - } -} - -int main() { - benchmark(); - return ARGON2_OK; -} diff --git a/cbits/argon2/core.c b/cbits/argon2/core.c index 1c6cad1..fb5233e 100644 --- a/cbits/argon2/core.c +++ b/cbits/argon2/core.c @@ -60,6 +60,66 @@ #define NOT_OPTIMIZED #endif +/* Argon2 Team - Begin Code */ +static int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) { + uint8_t *out = (uint8_t *)pout; + blake2b_state blake_state; + uint8_t outlen_bytes[sizeof(uint32_t)] = {0}; + int ret = -1; + + if (outlen > UINT32_MAX) { + goto fail; + } + + /* Ensure little-endian byte order! */ + store32(outlen_bytes, (uint32_t)outlen); + +#define TRY(statement) \ + do { \ + ret = statement; \ + if (ret < 0) { \ + goto fail; \ + } \ + } while ((void)0, 0) + + if (outlen <= BLAKE2B_OUTBYTES) { + TRY(blake2b_init(&blake_state, outlen)); + TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); + TRY(blake2b_update(&blake_state, in, inlen)); + TRY(blake2b_final(&blake_state, out, outlen)); + } else { + uint32_t toproduce; + uint8_t out_buffer[BLAKE2B_OUTBYTES]; + uint8_t in_buffer[BLAKE2B_OUTBYTES]; + TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES)); + TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); + TRY(blake2b_update(&blake_state, in, inlen)); + TRY(blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES)); + memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); + out += BLAKE2B_OUTBYTES / 2; + toproduce = (uint32_t)outlen - BLAKE2B_OUTBYTES / 2; + + while (toproduce > BLAKE2B_OUTBYTES) { + memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); + TRY(blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer, + BLAKE2B_OUTBYTES, NULL, 0)); + memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); + out += BLAKE2B_OUTBYTES / 2; + toproduce -= BLAKE2B_OUTBYTES / 2; + } + + memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); + TRY(blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL, + 0)); + memcpy(out, out_buffer, toproduce); + } +fail: + clear_internal_memory(&blake_state, sizeof(blake_state)); + return ret; +#undef TRY +} +/* Argon2 Team - End Code */ + /***************Instance and Position constructors**********/ static void init_block_value(block *b, uint8_t in) { memset(b->v, in, sizeof(b->v)); } diff --git a/cbits/argon2/encoding.c b/cbits/argon2/encoding.c deleted file mode 100644 index 44952a7..0000000 --- a/cbits/argon2/encoding.c +++ /dev/null @@ -1,450 +0,0 @@ -/* - * Argon2 reference source code package - reference C implementations - * - * Copyright 2015 - * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves - * - * You may use this work under the terms of a Creative Commons CC0 1.0 - * License/Waiver or the Apache Public License 2.0, at your option. The terms of - * these licenses can be found at: - * - * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - * - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - * - * You should have received a copy of both of these licenses along with this - * software. If not, they may be obtained at the above URLs. - */ - -#include -#include -#include -#include -#include "encoding.h" -#include "core.h" - -/* - * Example code for a decoder and encoder of "hash strings", with Argon2 - * parameters. - * - * This code comprises three sections: - * - * -- The first section contains generic Base64 encoding and decoding - * functions. It is conceptually applicable to any hash function - * implementation that uses Base64 to encode and decode parameters, - * salts and outputs. It could be made into a library, provided that - * the relevant functions are made public (non-static) and be given - * reasonable names to avoid collisions with other functions. - * - * -- The second section is specific to Argon2. It encodes and decodes - * the parameters, salts and outputs. It does not compute the hash - * itself. - * - * The code was originally written by Thomas Pornin , - * to whom comments and remarks may be sent. It is released under what - * should amount to Public Domain or its closest equivalent; the - * following mantra is supposed to incarnate that fact with all the - * proper legal rituals: - * - * --------------------------------------------------------------------- - * This file is provided under the terms of Creative Commons CC0 1.0 - * Public Domain Dedication. To the extent possible under law, the - * author (Thomas Pornin) has waived all copyright and related or - * neighboring rights to this file. This work is published from: Canada. - * --------------------------------------------------------------------- - * - * Copyright (c) 2015 Thomas Pornin - */ - -/* ==================================================================== */ -/* - * Common code; could be shared between different hash functions. - * - * Note: the Base64 functions below assume that uppercase letters (resp. - * lowercase letters) have consecutive numerical codes, that fit on 8 - * bits. All modern systems use ASCII-compatible charsets, where these - * properties are true. If you are stuck with a dinosaur of a system - * that still defaults to EBCDIC then you already have much bigger - * interoperability issues to deal with. - */ - -/* - * Some macros for constant-time comparisons. These work over values in - * the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true". - */ -#define EQ(x, y) ((((0U - ((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF) -#define GT(x, y) ((((unsigned)(y) - (unsigned)(x)) >> 8) & 0xFF) -#define GE(x, y) (GT(y, x) ^ 0xFF) -#define LT(x, y) GT(y, x) -#define LE(x, y) GE(y, x) - -/* - * Convert value x (0..63) to corresponding Base64 character. - */ -static int b64_byte_to_char(unsigned x) { - return (LT(x, 26) & (x + 'A')) | - (GE(x, 26) & LT(x, 52) & (x + ('a' - 26))) | - (GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '+') | - (EQ(x, 63) & '/'); -} - -/* - * Convert character c to the corresponding 6-bit value. If character c - * is not a Base64 character, then 0xFF (255) is returned. - */ -static unsigned b64_char_to_byte(int c) { - unsigned x; - - x = (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) | - (GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) | - (GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) | - (EQ(c, '/') & 63); - return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF)); -} - -/* - * Convert some bytes to Base64. 'dst_len' is the length (in characters) - * of the output buffer 'dst'; if that buffer is not large enough to - * receive the result (including the terminating 0), then (size_t)-1 - * is returned. Otherwise, the zero-terminated Base64 string is written - * in the buffer, and the output length (counted WITHOUT the terminating - * zero) is returned. - */ -static size_t to_base64(char *dst, size_t dst_len, const void *src, - size_t src_len) { - size_t olen; - const unsigned char *buf; - unsigned acc, acc_len; - - olen = (src_len / 3) << 2; - switch (src_len % 3) { - case 2: - olen++; - /* fall through */ - case 1: - olen += 2; - break; - } - if (dst_len <= olen) { - return (size_t)-1; - } - acc = 0; - acc_len = 0; - buf = (const unsigned char *)src; - while (src_len-- > 0) { - acc = (acc << 8) + (*buf++); - acc_len += 8; - while (acc_len >= 6) { - acc_len -= 6; - *dst++ = (char)b64_byte_to_char((acc >> acc_len) & 0x3F); - } - } - if (acc_len > 0) { - *dst++ = (char)b64_byte_to_char((acc << (6 - acc_len)) & 0x3F); - } - *dst++ = 0; - return olen; -} - -/* - * Decode Base64 chars into bytes. The '*dst_len' value must initially - * contain the length of the output buffer '*dst'; when the decoding - * ends, the actual number of decoded bytes is written back in - * '*dst_len'. - * - * Decoding stops when a non-Base64 character is encountered, or when - * the output buffer capacity is exceeded. If an error occurred (output - * buffer is too small, invalid last characters leading to unprocessed - * buffered bits), then NULL is returned; otherwise, the returned value - * points to the first non-Base64 character in the source stream, which - * may be the terminating zero. - */ -static const char *from_base64(void *dst, size_t *dst_len, const char *src) { - size_t len; - unsigned char *buf; - unsigned acc, acc_len; - - buf = (unsigned char *)dst; - len = 0; - acc = 0; - acc_len = 0; - for (;;) { - unsigned d; - - d = b64_char_to_byte(*src); - if (d == 0xFF) { - break; - } - src++; - acc = (acc << 6) + d; - acc_len += 6; - if (acc_len >= 8) { - acc_len -= 8; - if ((len++) >= *dst_len) { - return NULL; - } - *buf++ = (acc >> acc_len) & 0xFF; - } - } - - /* - * If the input length is equal to 1 modulo 4 (which is - * invalid), then there will remain 6 unprocessed bits; - * otherwise, only 0, 2 or 4 bits are buffered. The buffered - * bits must also all be zero. - */ - if (acc_len > 4 || (acc & (((unsigned)1 << acc_len) - 1)) != 0) { - return NULL; - } - *dst_len = len; - return src; -} - -/* - * Decode decimal integer from 'str'; the value is written in '*v'. - * Returned value is a pointer to the next non-decimal character in the - * string. If there is no digit at all, or the value encoding is not - * minimal (extra leading zeros), or the value does not fit in an - * 'unsigned long', then NULL is returned. - */ -static const char *decode_decimal(const char *str, unsigned long *v) { - const char *orig; - unsigned long acc; - - acc = 0; - for (orig = str;; str++) { - int c; - - c = *str; - if (c < '0' || c > '9') { - break; - } - c -= '0'; - if (acc > (ULONG_MAX / 10)) { - return NULL; - } - acc *= 10; - if ((unsigned long)c > (ULONG_MAX - acc)) { - return NULL; - } - acc += (unsigned long)c; - } - if (str == orig || (*orig == '0' && str != (orig + 1))) { - return NULL; - } - *v = acc; - return str; -} - -/* ==================================================================== */ -/* - * Code specific to Argon2. - * - * The code below applies the following format: - * - * $argon2[$v=]$m=,t=,p=$$ - * - * where is either 'd', 'id', or 'i', is a decimal integer (positive, - * fits in an 'unsigned long'), and is Base64-encoded data (no '=' padding - * characters, no newline or whitespace). - * - * The last two binary chunks (encoded in Base64) are, in that order, - * the salt and the output. Both are required. The binary salt length and the - * output length must be in the allowed ranges defined in argon2.h. - * - * The ctx struct must contain buffers large enough to hold the salt and pwd - * when it is fed into decode_string. - */ - -static int decode_string(argon2_context *ctx, const char *str, argon2_type type) { - -/* check for prefix */ -#define CC(prefix) \ - do { \ - size_t cc_len = strlen(prefix); \ - if (strncmp(str, prefix, cc_len) != 0) { \ - return ARGON2_DECODING_FAIL; \ - } \ - str += cc_len; \ - } while ((void)0, 0) - -/* optional prefix checking with supplied code */ -#define CC_opt(prefix, code) \ - do { \ - size_t cc_len = strlen(prefix); \ - if (strncmp(str, prefix, cc_len) == 0) { \ - str += cc_len; \ - { code; } \ - } \ - } while ((void)0, 0) - -/* Decoding prefix into decimal */ -#define DECIMAL(x) \ - do { \ - unsigned long dec_x; \ - str = decode_decimal(str, &dec_x); \ - if (str == NULL) { \ - return ARGON2_DECODING_FAIL; \ - } \ - (x) = dec_x; \ - } while ((void)0, 0) - -/* Decoding base64 into a binary buffer */ -#define BIN(buf, max_len, len) \ - do { \ - size_t bin_len = (max_len); \ - str = from_base64(buf, &bin_len, str); \ - if (str == NULL || bin_len > UINT32_MAX) { \ - return ARGON2_DECODING_FAIL; \ - } \ - (len) = (uint32_t)bin_len; \ - } while ((void)0, 0) - - size_t maxsaltlen = ctx->saltlen; - size_t maxoutlen = ctx->outlen; - int validation_result; - const char* type_string; - - /* We should start with the argon2_type we are using */ - type_string = cryptonite_argon2_type2string(type, 0); - if (!type_string) { - return ARGON2_INCORRECT_TYPE; - } - - CC("$"); - CC(type_string); - - /* Reading the version number if the default is suppressed */ - ctx->version = ARGON2_VERSION_10; - CC_opt("$v=", DECIMAL(ctx->version)); - - CC("$m="); - DECIMAL(ctx->m_cost); - CC(",t="); - DECIMAL(ctx->t_cost); - CC(",p="); - DECIMAL(ctx->lanes); - ctx->threads = ctx->lanes; - - CC("$"); - BIN(ctx->salt, maxsaltlen, ctx->saltlen); - CC("$"); - BIN(ctx->out, maxoutlen, ctx->outlen); - - /* The rest of the fields get the default values */ - ctx->secret = NULL; - ctx->secretlen = 0; - ctx->ad = NULL; - ctx->adlen = 0; - ctx->allocate_cbk = NULL; - ctx->free_cbk = NULL; - ctx->flags = ARGON2_DEFAULT_FLAGS; - - /* On return, must have valid context */ - validation_result = validate_inputs(ctx); - if (validation_result != ARGON2_OK) { - return validation_result; - } - - /* Can't have any additional characters */ - if (*str == 0) { - return ARGON2_OK; - } else { - return ARGON2_DECODING_FAIL; - } -#undef CC -#undef CC_opt -#undef DECIMAL -#undef BIN -} - -static int encode_string(char *dst, size_t dst_len, argon2_context *ctx, - argon2_type type) { -#define SS(str) \ - do { \ - size_t pp_len = strlen(str); \ - if (pp_len >= dst_len) { \ - return ARGON2_ENCODING_FAIL; \ - } \ - memcpy(dst, str, pp_len + 1); \ - dst += pp_len; \ - dst_len -= pp_len; \ - } while ((void)0, 0) - -#define SX(x) \ - do { \ - char tmp[30]; \ - sprintf(tmp, "%lu", (unsigned long)(x)); \ - SS(tmp); \ - } while ((void)0, 0) - -#define SB(buf, len) \ - do { \ - size_t sb_len = to_base64(dst, dst_len, buf, len); \ - if (sb_len == (size_t)-1) { \ - return ARGON2_ENCODING_FAIL; \ - } \ - dst += sb_len; \ - dst_len -= sb_len; \ - } while ((void)0, 0) - - const char* type_string = cryptonite_argon2_type2string(type, 0); - int validation_result = validate_inputs(ctx); - - if (!type_string) { - return ARGON2_ENCODING_FAIL; - } - - if (validation_result != ARGON2_OK) { - return validation_result; - } - - - SS("$"); - SS(type_string); - - SS("$v="); - SX(ctx->version); - - SS("$m="); - SX(ctx->m_cost); - SS(",t="); - SX(ctx->t_cost); - SS(",p="); - SX(ctx->lanes); - - SS("$"); - SB(ctx->salt, ctx->saltlen); - - SS("$"); - SB(ctx->out, ctx->outlen); - return ARGON2_OK; - -#undef SS -#undef SX -#undef SB -} - -static size_t b64len(uint32_t len) { - size_t olen = ((size_t)len / 3) << 2; - - switch (len % 3) { - case 2: - olen++; - /* fall through */ - case 1: - olen += 2; - break; - } - - return olen; -} - -static size_t numlen(uint32_t num) { - size_t len = 1; - while (num >= 10) { - ++len; - num = num / 10; - } - return len; -} - diff --git a/cbits/argon2/encoding.h b/cbits/argon2/encoding.h deleted file mode 100644 index 325a6e3..0000000 --- a/cbits/argon2/encoding.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Argon2 reference source code package - reference C implementations - * - * Copyright 2015 - * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves - * - * You may use this work under the terms of a Creative Commons CC0 1.0 - * License/Waiver or the Apache Public License 2.0, at your option. The terms of - * these licenses can be found at: - * - * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - * - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - * - * You should have received a copy of both of these licenses along with this - * software. If not, they may be obtained at the above URLs. - */ - -#ifndef ENCODING_H -#define ENCODING_H -#include "argon2.h" - -#define ARGON2_MAX_DECODED_LANES UINT32_C(255) -#define ARGON2_MIN_DECODED_SALT_LEN UINT32_C(8) -#define ARGON2_MIN_DECODED_OUT_LEN UINT32_C(12) - -/* -* encode an Argon2 hash string into the provided buffer. 'dst_len' -* contains the size, in characters, of the 'dst' buffer; if 'dst_len' -* is less than the number of required characters (including the -* terminating 0), then this function returns ARGON2_ENCODING_ERROR. -* -* on success, ARGON2_OK is returned. -*/ -static int encode_string(char *dst, size_t dst_len, argon2_context *ctx, - argon2_type type); - -/* -* Decodes an Argon2 hash string into the provided structure 'ctx'. -* The only fields that must be set prior to this call are ctx.saltlen and -* ctx.outlen (which must be the maximal salt and out length values that are -* allowed), ctx.salt and ctx.out (which must be buffers of the specified -* length), and ctx.pwd and ctx.pwdlen which must hold a valid password. -* -* Invalid input string causes an error. On success, the ctx is valid and all -* fields have been initialized. -* -* Returned value is ARGON2_OK on success, other ARGON2_ codes on error. -*/ -static int decode_string(argon2_context *ctx, const char *str, argon2_type type); - -/* Returns the length of the encoded byte stream with length len */ -static size_t b64len(uint32_t len); - -/* Returns the length of the encoded number num */ -static size_t numlen(uint32_t num); - -#endif diff --git a/cryptonite.cabal b/cryptonite.cabal index 90d9587..21c7dcd 100644 --- a/cryptonite.cabal +++ b/cryptonite.cabal @@ -125,6 +125,7 @@ Library Crypto.Number.Prime Crypto.Number.Serialize Crypto.Number.Serialize.Internal + Crypto.KDF.Argon2 Crypto.KDF.PBKDF2 Crypto.KDF.Scrypt Crypto.KDF.BCrypt @@ -340,6 +341,7 @@ Test-Suite test-cryptonite KAT_AES.KATXTS KAT_AES KAT_AFIS + KAT_Argon2 KAT_Blowfish KAT_Camellia KAT_Curve25519 diff --git a/tests/KAT_Argon2.hs b/tests/KAT_Argon2.hs new file mode 100644 index 0000000..aa73555 --- /dev/null +++ b/tests/KAT_Argon2.hs @@ -0,0 +1,42 @@ +{-# LANGUAGE OverloadedStrings #-} +module KAT_Argon2 (tests) where + +import Crypto.Error +import qualified Crypto.KDF.Argon2 as Argon2 +import qualified Data.ByteString as B +import Imports + +data KDFVector = KDFVector + { kdfPass :: ByteString + , kdfSalt :: ByteString + , kdfOptions :: Argon2.Options + , kdfResult :: ByteString + } + +argon2i_13 :: Argon2.TimeCost -> Argon2.MemoryCost -> Argon2.Options +argon2i_13 iters memory = Argon2.Options + { Argon2.iterations = iters + , Argon2.memory = memory + , Argon2.parallelism = 1 + , Argon2.variant = Argon2.Argon2i + , Argon2.version = Argon2.Version13 + } + +vectors = + [ KDFVector "password" "somesalt" (argon2i_13 2 65536) + "\xc1\x62\x88\x32\x14\x7d\x97\x20\xc5\xbd\x1c\xfd\x61\x36\x70\x78\x72\x9f\x6d\xfb\x6f\x8f\xea\x9f\xf9\x81\x58\xe0\xd7\x81\x6e\xd0" + ] + +kdfTests :: [TestTree] +kdfTests = map toKDFTest $ zip is vectors + where + toKDFTest (i, v) = + testCase (show i) + (CryptoPassed (kdfResult v) @=? Argon2.hash (kdfOptions v) (kdfPass v) (kdfSalt v) (B.length $ kdfResult v)) + + is :: [Int] + is = [1..] + +tests = testGroup "Argon2" + [ testGroup "KATs" kdfTests + ] diff --git a/tests/Tests.hs b/tests/Tests.hs index 37aad17..68d79f4 100644 --- a/tests/Tests.hs +++ b/tests/Tests.hs @@ -16,6 +16,7 @@ import qualified KAT_MiyaguchiPreneel import qualified KAT_CMAC import qualified KAT_HMAC import qualified KAT_HKDF +import qualified KAT_Argon2 import qualified KAT_PBKDF2 import qualified KAT_Curve25519 import qualified KAT_Curve448 @@ -57,6 +58,7 @@ tests = testGroup "cryptonite" , KAT_Scrypt.tests , BCrypt.tests , KAT_HKDF.tests + , KAT_Argon2.tests ] , testGroup "block-cipher" [ KAT_AES.tests