/*- * Copyright 2016 Vsevolod Stakhov * Copyright (c) 2014 cforler * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "config.h" #include "catena.h" #include "../blake2/blake2.h" #if __BYTE_ORDER == __LITTLE_ENDIAN #define TO_LITTLE_ENDIAN_64(n) (n) #define TO_LITTLE_ENDIAN_32(n) (n) #else #define TO_LITTLE_ENDIAN_64 GUINT64_SWAP_LE_BE #define TO_LITTLE_ENDIAN_32 GUINT32_SWAP_LE_BE #endif /* Recommended default values */ #define H_LEN CATENA_HLEN #define KEY_LEN 16 const uint8_t VERSION_ID[] = "Butterfly-Full"; const uint8_t LAMBDA = 4; const uint8_t GARLIC = 16; const uint8_t MIN_GARLIC = 16; /* * Hash part */ static inline void __Hash1(const uint8_t *input, const uint32_t inputlen, uint8_t hash[H_LEN]) { blake2b_state ctx; blake2b_init (&ctx); blake2b_update (&ctx, input, inputlen); blake2b_final (&ctx, hash); } /***************************************************/ static inline void __Hash2(const uint8_t *i1, const uint8_t i1len, const uint8_t *i2, const uint8_t i2len, uint8_t hash[H_LEN]) { blake2b_state ctx; blake2b_init (&ctx); blake2b_update (&ctx, i1, i1len); blake2b_update (&ctx, i2, i2len); blake2b_final (&ctx, hash); } /***************************************************/ static inline void __Hash3(const uint8_t *i1, const uint8_t i1len, const uint8_t *i2, const uint8_t i2len, const uint8_t *i3, const uint8_t i3len, uint8_t hash[H_LEN]) { blake2b_state ctx; blake2b_init (&ctx); blake2b_update (&ctx, i1, i1len); blake2b_update (&ctx, i2, i2len); blake2b_update (&ctx, i3, i3len); blake2b_final (&ctx, hash); } /***************************************************/ static inline void __Hash4(const uint8_t *i1, const uint8_t i1len, const uint8_t *i2, const uint8_t i2len, const uint8_t *i3, const uint8_t i3len, const uint8_t *i4, const uint8_t i4len, uint8_t hash[H_LEN]) { blake2b_state ctx; blake2b_init (&ctx); blake2b_update (&ctx, i1, i1len); blake2b_update (&ctx, i2, i2len); blake2b_update (&ctx, i3, i3len); blake2b_update (&ctx, i4, i4len); blake2b_final (&ctx, hash); } /***************************************************/ static inline void __Hash5(const uint8_t *i1, const uint8_t i1len, const uint8_t *i2, const uint8_t i2len, const uint8_t *i3, const uint8_t i3len, const uint8_t *i4, const uint8_t i4len, const uint8_t *i5, const uint8_t i5len, uint8_t hash[H_LEN]) { blake2b_state ctx; blake2b_init (&ctx); blake2b_update (&ctx, i1, i1len); blake2b_update (&ctx, i2, i2len); blake2b_update (&ctx, i3, i3len); blake2b_update (&ctx, i4, i4len); blake2b_update (&ctx, i5, i5len); blake2b_final (&ctx, hash); } static inline void __HashFast(int vindex, const uint8_t* i1, const uint8_t* i2, uint8_t hash[H_LEN]) { __Hash2 (i1, H_LEN, i2, H_LEN, hash); } static void __ResetState(void) { } /* * Misc utils */ const uint8_t ZERO8[H_LEN] = {0}; /* see: http://en.wikipedia.org/wiki/Xorshift#Variations */ static int p; static uint64_t s[16]; static void initXSState (const uint8_t* a, const uint8_t* b) { p = 0; for (int i = 0; i < 8; i++) { s[i] = UINT64_C(0); s[i + 8] = UINT64_C(0); for (int j = 0; j < 8; j++) { s[i] |= ((uint64_t) a[i * 8 + j]) << j * 8; s[i + 8] |= ((uint64_t) b[i * 8 + j]) << j * 8; } } } static uint64_t xorshift1024star (void) { uint64_t s0 = s[p]; uint64_t s1 = s[p = (p + 1) & 15]; s1 ^= s1 << 31; s1 ^= s1 >> 11; s0 ^= s0 >> 30; return (s[p] = s0 ^ s1) * UINT64_C(1181783497276652981); } static void H_INIT (const uint8_t* x, const uint16_t xlen, uint8_t *vm1, uint8_t *vm2) { const uint8_t l = 2; uint8_t *tmp = (uint8_t*) g_malloc (l * H_LEN); for (uint8_t i = 0; i != l; ++i) { __Hash2 (&i, 1, x, xlen, tmp + i * H_LEN); } memcpy (vm1, tmp, H_LEN); memcpy (vm2, tmp+(l/2*H_LEN), H_LEN); g_free (tmp); } static void H_First (const uint8_t* i1, const uint8_t* i2, uint8_t* hash) { uint8_t i = 0; uint8_t *x = (uint8_t*) g_malloc (H_LEN); __ResetState (); __Hash2 (i1, H_LEN, i2, H_LEN, x); __Hash2 (&i, 1, x, H_LEN, hash); g_free (x); } static inline void initmem (const uint8_t x[H_LEN], const uint64_t c, uint8_t *r) { uint8_t *vm2 = (uint8_t*) g_malloc (H_LEN); uint8_t *vm1 = (uint8_t*) g_malloc (H_LEN); H_INIT (x, H_LEN, vm1, vm2); __ResetState (); __HashFast (0, vm1, vm2, r); __HashFast (1, r, vm1, r + H_LEN); /* Top row */ for (uint64_t i = 2; i < c; i++) { __HashFast (i, r + (i - 1) * H_LEN, r + (i - 2) * H_LEN, r + i * H_LEN); } g_free (vm2); g_free (vm1); } static inline void gamma (const uint8_t garlic, const uint8_t *salt, const uint8_t saltlen, uint8_t *r) { const uint64_t q = UINT64_C(1) << ((3 * garlic + 3) / 4); uint64_t i, j, j2; uint8_t *tmp = g_malloc (H_LEN); uint8_t *tmp2 = g_malloc (H_LEN); __Hash1 (salt, saltlen, tmp); __Hash1 (tmp, H_LEN, tmp2); initXSState (tmp, tmp2); __ResetState (); for (i = 0; i < q; i++) { j = xorshift1024star () >> (64 - garlic); j2 = xorshift1024star () >> (64 - garlic); __HashFast (i, r + j * H_LEN, r + j2 * H_LEN, r + j * H_LEN); } g_free (tmp); g_free (tmp2); } static void XOR (const uint8_t *input1, const uint8_t *input2, uint8_t *output) { uint32_t i; for(i = 0; i < H_LEN; i++) { output[i] = input1[i] ^ input2[i]; } } /* * Butterfly part */ /* * Sigma function that defines the diagonal connections of a DBG * diagonal front: flip the (g-i)th bit (Inverse Buttferly Graph) * diagonal back: flip the i-(g-1)th bit (Regular Butterfly Graph) */ static uint64_t sigma(const uint8_t g, const uint64_t i, const uint64_t j) { if (i < g) { return (j ^ (UINT64_C(1) << (g - 1 - i))); /* diagonal front */ } else { return (j ^ (UINT64_C(1) << (i - (g - 1)))); /* diagonal back */ } } /*calculate actual index from level and element index*/ static uint64_t idx(uint64_t i, uint64_t j, uint8_t co, uint64_t c, uint64_t m) { i += co; if (i % 3 == 0) { return j; } else if (i % 3 == 1) { if (j < m) { /* still fits in the array */ return j + c; } else { /* start overwriting elements at the beginning */ return j - m; } } /* i % 3 == 2 */ return j + m; } /* * Computes the hash of x using a Double Butterfly Graph, * that forms as (2^g,\lamba)-Superconcentrator */ static void Flap (const uint8_t x[H_LEN], const uint8_t lambda, const uint8_t garlic, const uint8_t *salt, const uint8_t saltlen, uint8_t h[H_LEN]) { const uint64_t c = UINT64_C(1) << garlic; const uint64_t m = UINT64_C(1) << (garlic - 1); /* 0.5 * 2^g */ const uint32_t l = 2 * garlic; uint8_t *r = g_malloc ((c + m) * H_LEN); uint8_t *tmp = g_malloc (H_LEN); uint64_t i, j; uint8_t k; uint8_t co = 0; /* carry over from last iteration */ /* Top row */ initmem (x, c, r); /*Gamma Function*/ gamma (garlic, salt, saltlen, r); /* DBH */ for (k = 0; k < lambda; k++) { for (i = 1; i < l; i++) { XOR (r + idx (i - 1, c - 1, co, c, m) * H_LEN, r + idx (i - 1, 0, co, c, m) * H_LEN, tmp); /* * r0 := H(tmp || vsigma(g,i-1,0) ) * __Hash2(tmp, H_LEN, r+idx(i-1,sigma(garlic,i-1,0),co,c,m) * H_LEN, H_LEN, * r+idx(i,0,co,c,m) *H_LEN); */ H_First (tmp, r + idx (i - 1, sigma (garlic, i - 1, 0), co, c, m) * H_LEN, r + idx (i, 0, co, c, m) * H_LEN); __ResetState (); /* vertices */ for (j = 1; j < c; j++) { /* tmp:= rj-1 XOR vj */ XOR (r + idx (i, j - 1, co, c, m) * H_LEN, r + idx (i - 1, j, co, c, m) * H_LEN, tmp); /* rj := H(tmp || vsigma(g,i-1,j)) */ __HashFast (j, tmp, r + idx (i - 1, sigma (garlic, i - 1, j), co, c, m) * H_LEN, r + idx (i, j, co, c, m) * H_LEN); } } co = (co + (i - 1)) % 3; } memcpy(h, r + idx(0,c-1,co,c,m) * H_LEN, H_LEN); g_free (r); g_free (tmp); } static int __Catena (const uint8_t *pwd, const uint32_t pwdlen, const uint8_t *salt, const uint8_t saltlen, const uint8_t *data, const uint32_t datalen, const uint8_t lambda, const uint8_t min_garlic, const uint8_t garlic, const uint8_t hashlen, const uint8_t client, const uint8_t tweak_id, uint8_t *hash) { uint8_t x[H_LEN]; uint8_t hv[H_LEN]; uint8_t t[4]; uint8_t c; if ((hashlen > H_LEN) || (garlic > 63) || (min_garlic > garlic) || (lambda == 0) || (min_garlic == 0)) { return -1; } /*Compute H(V)*/ __Hash1 (VERSION_ID, strlen ((char*) VERSION_ID), hv); /* Compute Tweak */ t[0] = tweak_id; t[1] = lambda; t[2] = hashlen; t[3] = saltlen; /* Compute H(AD) */ __Hash1 ((uint8_t *) data, datalen, x); /* Compute the initial value to hash */ __Hash5 (hv, H_LEN, t, 4, x, H_LEN, pwd, pwdlen, salt, saltlen, x); /*Overwrite Password if enabled*/ #ifdef OVERWRITE erasepwd(pwd,pwdlen); #endif Flap (x, lambda, (min_garlic + 1) / 2, salt, saltlen, x); for (c = min_garlic; c <= garlic; c++) { Flap (x, lambda, c, salt, saltlen, x); if ((c == garlic) && (client == CLIENT)) { memcpy (hash, x, H_LEN); return 0; } __Hash2 (&c, 1, x, H_LEN, x); memset (x + hashlen, 0, H_LEN - hashlen); } memcpy (hash, x, hashlen); return 0; } /***************************************************/ int catena (const uint8_t *pwd, const uint32_t pwdlen, const uint8_t *salt, const uint8_t saltlen, const uint8_t *data, const uint32_t datalen, const uint8_t lambda, const uint8_t min_garlic, const uint8_t garlic, const uint8_t hashlen, uint8_t *hash) { return __Catena (pwd, pwdlen, salt, saltlen, data, datalen, lambda, min_garlic, garlic, hashlen, REGULAR, PASSWORD_HASHING_MODE, hash); } int simple_catena (const uint8_t *pwd, const uint32_t pwdlen, const uint8_t *salt, const uint8_t saltlen, const uint8_t *data, const uint32_t datalen, uint8_t hash[H_LEN]) { return __Catena (pwd, pwdlen, salt, saltlen, data, datalen, LAMBDA, MIN_GARLIC, GARLIC, H_LEN, REGULAR, PASSWORD_HASHING_MODE, hash); } int catena_test (void) { /* From catena-v3.1 spec */ guint8 pw[] = {0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64}; guint8 salt[] = {0x73, 0x61, 0x6c, 0x74}; guint8 ad[] = {0x64, 0x61,0x74, 0x61}; guint8 expected[] = { 0x20, 0xc5, 0x91, 0x93, 0x8f, 0xc3, 0xaf, 0xcc, 0x3b, 0xba, 0x91, 0xd2, 0xfb, 0x84, 0xbf, 0x7b, 0x44, 0x04, 0xf9, 0x4c, 0x45, 0xed, 0x4d, 0x11, 0xa7, 0xe2, 0xb4, 0x12, 0x3e, 0xab, 0x0b, 0x77, 0x4a, 0x12, 0xb4, 0x22, 0xd0, 0xda, 0xb5, 0x25, 0x29, 0x02, 0xfc, 0x54, 0x47, 0xea, 0x82, 0x63, 0x8c, 0x1a, 0xfb, 0xa7, 0xa9, 0x94, 0x24, 0x13, 0x0e, 0x44, 0x36, 0x3b, 0x9d, 0x9f, 0xc9, 0x60 }; guint8 real[H_LEN]; if (catena (pw, sizeof (pw), salt, sizeof (salt), ad, sizeof (ad), 4, 10, 10, H_LEN, real) != 0) { return -1; } return memcmp (real, expected, H_LEN); }