mirror of
https://git.wownero.com/wowlet/wownero-seed.git
synced 2024-12-21 23:38:52 +00:00
initial commit
This commit is contained in:
commit
afb3912f51
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
*.user
|
||||||
|
*.suo
|
||||||
|
.vs
|
||||||
|
x64/
|
||||||
|
Release/
|
||||||
|
Debug/
|
||||||
|
build/
|
26
CMakeLists.txt
Normal file
26
CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Copyright (c) 2020, tevador <tevador@gmail.com>
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 2.8.7)
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
|
message(STATUS "Setting default build type: ${CMAKE_BUILD_TYPE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
project(monero-seed)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME}
|
||||||
|
src/argon2/blake2/blake2b.c
|
||||||
|
src/argon2/argon2.c
|
||||||
|
src/argon2/core.c
|
||||||
|
src/argon2/ref.c
|
||||||
|
src/galois_field.cpp
|
||||||
|
src/gf_elem.cpp
|
||||||
|
src/gf_poly.cpp
|
||||||
|
src/main.cpp
|
||||||
|
src/monero_seed.cpp
|
||||||
|
src/reed_solomon_code.cpp
|
||||||
|
src/secure_random.cpp
|
||||||
|
src/wordlist.cpp)
|
||||||
|
|
||||||
|
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 11)
|
70
README.md
Normal file
70
README.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
## Build
|
||||||
|
```
|
||||||
|
git clone https://github.com/tevador/monero-seed.git
|
||||||
|
cd monero-seed
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Create a new seed
|
||||||
|
|
||||||
|
The sole argument is the wallet creation date in `yyyy/MM/dd` format.
|
||||||
|
```
|
||||||
|
> ./monero-seed --create 2100/03/14
|
||||||
|
Mnemonic phrase: pumpkin alter spice lend position sentence surface snow atom lobster exotic robot profit chase
|
||||||
|
- version: 1
|
||||||
|
- private key: a147f59c9b80e77824ba2e44241598b3b9ddf2e6458e9970352894216b9cbbba
|
||||||
|
- created on or after: 02/Mar/2100
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore seed
|
||||||
|
|
||||||
|
```
|
||||||
|
> ./monero-seed --restore "pumpkin alter spice lend position sentence surface snow atom lobster exotic robot profit chase"
|
||||||
|
- version: 1
|
||||||
|
- private key: a147f59c9b80e77824ba2e44241598b3b9ddf2e6458e9970352894216b9cbbba
|
||||||
|
- created on or after: 02/Mar/2100
|
||||||
|
```
|
||||||
|
|
||||||
|
Restore has limited error correction capability, namely it can correct a single erasure (illegible symbol with a known location).
|
||||||
|
This can be tested by replacing a word with `xxxx`:
|
||||||
|
|
||||||
|
```
|
||||||
|
> ./monero-seed --restore "pumpkin alter xxxx lend position sentence surface snow atom lobster exotic robot profit chase"
|
||||||
|
Warning: corrected erasure: xxxx -> spice
|
||||||
|
- version: 1
|
||||||
|
- private key: a147f59c9b80e77824ba2e44241598b3b9ddf2e6458e9970352894216b9cbbba
|
||||||
|
- created on or after: 02/Mar/2100
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation details
|
||||||
|
|
||||||
|
The mnemonic phrase contains 154 bits of data, which are used as follows:
|
||||||
|
|
||||||
|
* 3 bits for version (this allows the format to be updated up to 7 times)
|
||||||
|
* 2 bits reserved for future use
|
||||||
|
* 10 bits for approximate wallet creation date
|
||||||
|
* 128 bits for the private key seed
|
||||||
|
* 11 bits for error detection/correction
|
||||||
|
|
||||||
|
### Wordlist
|
||||||
|
|
||||||
|
Uses the wordlist from BIP-39. It has 2048 words, allowing 11 bits to be stored in each word. It has some additional useful properties,
|
||||||
|
for example each word can be uniquly identified by its first 4 characters.
|
||||||
|
|
||||||
|
### Wallet creation date
|
||||||
|
|
||||||
|
The mnemonic phrase doesn't store block height, but the time when the wallet was created. This allows the seed to be generated
|
||||||
|
offline without access to the blockchain. Wallet software can easily convert a date to the corresponding block height when restoring a seed.
|
||||||
|
The wallet creation date has a resolution of 2629746 seconds (1/12 of the average Gregorian year). All dates between June 2020
|
||||||
|
and September 2105 can be represented.
|
||||||
|
|
||||||
|
### Private key seed
|
||||||
|
|
||||||
|
The private key is generated by wallet software from the 128-bit seed included in the mnemonic phrase. Argon2id is used as KDF (memory = 256 MB). The wallet creation date is used as a salt. 128-bit seed provides the same level of security as the elliptic curve used by Monero.
|
||||||
|
|
||||||
|
### Error detection/correction
|
||||||
|
|
||||||
|
The mnemonic phrase can be treated as a polynomial over GF(2048), which allows us to use an efficient Reed-Solomon ECC with one check word. All single-word errors can be detected and all single-word erasures can be corrected.
|
354
src/argon2/argon2.c
Normal file
354
src/argon2/argon2.c
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
/*
|
||||||
|
* 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 <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "argon2.h"
|
||||||
|
#include "core.h"
|
||||||
|
|
||||||
|
const char *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 argon2_ctx(argon2_context *context, argon2_type type) {
|
||||||
|
/* 1. Validate all inputs */
|
||||||
|
int result = validate_inputs(context);
|
||||||
|
uint32_t memory_blocks, segment_length;
|
||||||
|
argon2_instance_t instance;
|
||||||
|
|
||||||
|
if (ARGON2_OK != result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Argon2_d != type && Argon2_i != type && Argon2_id != type) {
|
||||||
|
return ARGON2_INCORRECT_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Align memory size */
|
||||||
|
/* Minimum memory_blocks = 8L blocks, where L is the number of lanes */
|
||||||
|
memory_blocks = context->m_cost;
|
||||||
|
|
||||||
|
if (memory_blocks < 2 * ARGON2_SYNC_POINTS * context->lanes) {
|
||||||
|
memory_blocks = 2 * ARGON2_SYNC_POINTS * context->lanes;
|
||||||
|
}
|
||||||
|
|
||||||
|
segment_length = memory_blocks / (context->lanes * ARGON2_SYNC_POINTS);
|
||||||
|
/* Ensure that all segments have equal length */
|
||||||
|
memory_blocks = segment_length * (context->lanes * ARGON2_SYNC_POINTS);
|
||||||
|
|
||||||
|
instance.version = context->version;
|
||||||
|
instance.memory = NULL;
|
||||||
|
instance.passes = context->t_cost;
|
||||||
|
instance.memory_blocks = memory_blocks;
|
||||||
|
instance.segment_length = segment_length;
|
||||||
|
instance.lane_length = segment_length * ARGON2_SYNC_POINTS;
|
||||||
|
instance.lanes = context->lanes;
|
||||||
|
instance.threads = context->threads;
|
||||||
|
instance.type = type;
|
||||||
|
|
||||||
|
if (instance.threads > instance.lanes) {
|
||||||
|
instance.threads = instance.lanes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. Initialization: Hashing inputs, allocating memory, filling first
|
||||||
|
* blocks
|
||||||
|
*/
|
||||||
|
result = initialize(&instance, context);
|
||||||
|
|
||||||
|
if (ARGON2_OK != result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4. Filling memory */
|
||||||
|
result = fill_memory_blocks(&instance);
|
||||||
|
|
||||||
|
if (ARGON2_OK != result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/* 5. Finalization */
|
||||||
|
finalize(context, &instance);
|
||||||
|
|
||||||
|
return ARGON2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int 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 uint32_t version){
|
||||||
|
|
||||||
|
argon2_context context;
|
||||||
|
int result;
|
||||||
|
uint8_t *out;
|
||||||
|
|
||||||
|
if (pwdlen > ARGON2_MAX_PWD_LENGTH) {
|
||||||
|
return ARGON2_PWD_TOO_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (saltlen > ARGON2_MAX_SALT_LENGTH) {
|
||||||
|
return ARGON2_SALT_TOO_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hashlen > ARGON2_MAX_OUTLEN) {
|
||||||
|
return ARGON2_OUTPUT_TOO_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hashlen < ARGON2_MIN_OUTLEN) {
|
||||||
|
return ARGON2_OUTPUT_TOO_SHORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
out = malloc(hashlen);
|
||||||
|
if (!out) {
|
||||||
|
return ARGON2_MEMORY_ALLOCATION_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.out = (uint8_t *)out;
|
||||||
|
context.outlen = (uint32_t)hashlen;
|
||||||
|
context.pwd = CONST_CAST(uint8_t *)pwd;
|
||||||
|
context.pwdlen = (uint32_t)pwdlen;
|
||||||
|
context.salt = CONST_CAST(uint8_t *)salt;
|
||||||
|
context.saltlen = (uint32_t)saltlen;
|
||||||
|
context.secret = NULL;
|
||||||
|
context.secretlen = 0;
|
||||||
|
context.ad = NULL;
|
||||||
|
context.adlen = 0;
|
||||||
|
context.t_cost = t_cost;
|
||||||
|
context.m_cost = m_cost;
|
||||||
|
context.lanes = parallelism;
|
||||||
|
context.threads = parallelism;
|
||||||
|
context.allocate_cbk = NULL;
|
||||||
|
context.free_cbk = NULL;
|
||||||
|
context.flags = ARGON2_DEFAULT_FLAGS;
|
||||||
|
context.version = version;
|
||||||
|
|
||||||
|
result = argon2_ctx(&context, type);
|
||||||
|
|
||||||
|
if (result != ARGON2_OK) {
|
||||||
|
clear_internal_memory(out, hashlen);
|
||||||
|
free(out);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if raw hash requested, write it */
|
||||||
|
if (hash) {
|
||||||
|
memcpy(hash, out, hashlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_internal_memory(out, hashlen);
|
||||||
|
free(out);
|
||||||
|
|
||||||
|
return ARGON2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int 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 argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
|
||||||
|
NULL, hashlen, encoded, encodedlen, Argon2_i,
|
||||||
|
ARGON2_VERSION_NUMBER);
|
||||||
|
}
|
||||||
|
|
||||||
|
int 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 argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
|
||||||
|
hash, hashlen, NULL, 0, Argon2_i, ARGON2_VERSION_NUMBER);
|
||||||
|
}
|
||||||
|
|
||||||
|
int 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 argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
|
||||||
|
NULL, hashlen, encoded, encodedlen, Argon2_d,
|
||||||
|
ARGON2_VERSION_NUMBER);
|
||||||
|
}
|
||||||
|
|
||||||
|
int 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 argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
|
||||||
|
hash, hashlen, NULL, 0, Argon2_d, ARGON2_VERSION_NUMBER);
|
||||||
|
}
|
||||||
|
|
||||||
|
int 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 argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
|
||||||
|
NULL, hashlen, encoded, encodedlen, Argon2_id,
|
||||||
|
ARGON2_VERSION_NUMBER);
|
||||||
|
}
|
||||||
|
|
||||||
|
int 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 argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
|
||||||
|
hash, hashlen, NULL, 0, Argon2_id,
|
||||||
|
ARGON2_VERSION_NUMBER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int 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 argon2d_ctx(argon2_context *context) {
|
||||||
|
return argon2_ctx(context, Argon2_d);
|
||||||
|
}
|
||||||
|
|
||||||
|
int argon2i_ctx(argon2_context *context) {
|
||||||
|
return argon2_ctx(context, Argon2_i);
|
||||||
|
}
|
||||||
|
|
||||||
|
int argon2id_ctx(argon2_context *context) {
|
||||||
|
return argon2_ctx(context, Argon2_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
int argon2_verify_ctx(argon2_context *context, const char *hash,
|
||||||
|
argon2_type type) {
|
||||||
|
int ret = argon2_ctx(context, type);
|
||||||
|
if (ret != ARGON2_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argon2_compare((uint8_t *)hash, context->out, context->outlen)) {
|
||||||
|
return ARGON2_VERIFY_MISMATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ARGON2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int argon2d_verify_ctx(argon2_context *context, const char *hash) {
|
||||||
|
return argon2_verify_ctx(context, hash, Argon2_d);
|
||||||
|
}
|
||||||
|
|
||||||
|
int argon2i_verify_ctx(argon2_context *context, const char *hash) {
|
||||||
|
return argon2_verify_ctx(context, hash, Argon2_i);
|
||||||
|
}
|
||||||
|
|
||||||
|
int argon2id_verify_ctx(argon2_context *context, const char *hash) {
|
||||||
|
return argon2_verify_ctx(context, hash, Argon2_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *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";
|
||||||
|
}
|
||||||
|
}
|
437
src/argon2/argon2.h
Normal file
437
src/argon2/argon2.h
Normal file
@ -0,0 +1,437 @@
|
|||||||
|
/*
|
||||||
|
* 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 ARGON2_H
|
||||||
|
#define ARGON2_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Symbols visibility control */
|
||||||
|
#ifdef A2_VISCTL
|
||||||
|
#define ARGON2_PUBLIC __attribute__((visibility("default")))
|
||||||
|
#define ARGON2_LOCAL __attribute__ ((visibility ("hidden")))
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define ARGON2_PUBLIC __declspec(dllexport)
|
||||||
|
#define ARGON2_LOCAL
|
||||||
|
#else
|
||||||
|
#define ARGON2_PUBLIC
|
||||||
|
#define ARGON2_LOCAL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Argon2 input parameter restrictions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Minimum and maximum number of lanes (degree of parallelism) */
|
||||||
|
#define ARGON2_MIN_LANES UINT32_C(1)
|
||||||
|
#define ARGON2_MAX_LANES UINT32_C(0xFFFFFF)
|
||||||
|
|
||||||
|
/* Minimum and maximum number of threads */
|
||||||
|
#define ARGON2_MIN_THREADS UINT32_C(1)
|
||||||
|
#define ARGON2_MAX_THREADS UINT32_C(0xFFFFFF)
|
||||||
|
|
||||||
|
/* Number of synchronization points between lanes per pass */
|
||||||
|
#define ARGON2_SYNC_POINTS UINT32_C(4)
|
||||||
|
|
||||||
|
/* Minimum and maximum digest size in bytes */
|
||||||
|
#define ARGON2_MIN_OUTLEN UINT32_C(4)
|
||||||
|
#define ARGON2_MAX_OUTLEN UINT32_C(0xFFFFFFFF)
|
||||||
|
|
||||||
|
/* Minimum and maximum number of memory blocks (each of BLOCK_SIZE bytes) */
|
||||||
|
#define ARGON2_MIN_MEMORY (2 * ARGON2_SYNC_POINTS) /* 2 blocks per slice */
|
||||||
|
|
||||||
|
#define ARGON2_MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
/* Max memory size is addressing-space/2, topping at 2^32 blocks (4 TB) */
|
||||||
|
#define ARGON2_MAX_MEMORY_BITS \
|
||||||
|
ARGON2_MIN(UINT32_C(32), (sizeof(void *) * CHAR_BIT - 10 - 1))
|
||||||
|
#define ARGON2_MAX_MEMORY \
|
||||||
|
ARGON2_MIN(UINT32_C(0xFFFFFFFF), UINT64_C(1) << ARGON2_MAX_MEMORY_BITS)
|
||||||
|
|
||||||
|
/* Minimum and maximum number of passes */
|
||||||
|
#define ARGON2_MIN_TIME UINT32_C(1)
|
||||||
|
#define ARGON2_MAX_TIME UINT32_C(0xFFFFFFFF)
|
||||||
|
|
||||||
|
/* Minimum and maximum password length in bytes */
|
||||||
|
#define ARGON2_MIN_PWD_LENGTH UINT32_C(0)
|
||||||
|
#define ARGON2_MAX_PWD_LENGTH UINT32_C(0xFFFFFFFF)
|
||||||
|
|
||||||
|
/* Minimum and maximum associated data length in bytes */
|
||||||
|
#define ARGON2_MIN_AD_LENGTH UINT32_C(0)
|
||||||
|
#define ARGON2_MAX_AD_LENGTH UINT32_C(0xFFFFFFFF)
|
||||||
|
|
||||||
|
/* Minimum and maximum salt length in bytes */
|
||||||
|
#define ARGON2_MIN_SALT_LENGTH UINT32_C(8)
|
||||||
|
#define ARGON2_MAX_SALT_LENGTH UINT32_C(0xFFFFFFFF)
|
||||||
|
|
||||||
|
/* Minimum and maximum key length in bytes */
|
||||||
|
#define ARGON2_MIN_SECRET UINT32_C(0)
|
||||||
|
#define ARGON2_MAX_SECRET UINT32_C(0xFFFFFFFF)
|
||||||
|
|
||||||
|
/* Flags to determine which fields are securely wiped (default = no wipe). */
|
||||||
|
#define ARGON2_DEFAULT_FLAGS UINT32_C(0)
|
||||||
|
#define ARGON2_FLAG_CLEAR_PASSWORD (UINT32_C(1) << 0)
|
||||||
|
#define ARGON2_FLAG_CLEAR_SECRET (UINT32_C(1) << 1)
|
||||||
|
|
||||||
|
/* Global flag to determine if we are wiping internal memory buffers. This flag
|
||||||
|
* is defined in core.c and defaults to 1 (wipe internal memory). */
|
||||||
|
extern int FLAG_clear_internal_memory;
|
||||||
|
|
||||||
|
/* Error codes */
|
||||||
|
typedef enum Argon2_ErrorCodes {
|
||||||
|
ARGON2_OK = 0,
|
||||||
|
|
||||||
|
ARGON2_OUTPUT_PTR_NULL = -1,
|
||||||
|
|
||||||
|
ARGON2_OUTPUT_TOO_SHORT = -2,
|
||||||
|
ARGON2_OUTPUT_TOO_LONG = -3,
|
||||||
|
|
||||||
|
ARGON2_PWD_TOO_SHORT = -4,
|
||||||
|
ARGON2_PWD_TOO_LONG = -5,
|
||||||
|
|
||||||
|
ARGON2_SALT_TOO_SHORT = -6,
|
||||||
|
ARGON2_SALT_TOO_LONG = -7,
|
||||||
|
|
||||||
|
ARGON2_AD_TOO_SHORT = -8,
|
||||||
|
ARGON2_AD_TOO_LONG = -9,
|
||||||
|
|
||||||
|
ARGON2_SECRET_TOO_SHORT = -10,
|
||||||
|
ARGON2_SECRET_TOO_LONG = -11,
|
||||||
|
|
||||||
|
ARGON2_TIME_TOO_SMALL = -12,
|
||||||
|
ARGON2_TIME_TOO_LARGE = -13,
|
||||||
|
|
||||||
|
ARGON2_MEMORY_TOO_LITTLE = -14,
|
||||||
|
ARGON2_MEMORY_TOO_MUCH = -15,
|
||||||
|
|
||||||
|
ARGON2_LANES_TOO_FEW = -16,
|
||||||
|
ARGON2_LANES_TOO_MANY = -17,
|
||||||
|
|
||||||
|
ARGON2_PWD_PTR_MISMATCH = -18, /* NULL ptr with non-zero length */
|
||||||
|
ARGON2_SALT_PTR_MISMATCH = -19, /* NULL ptr with non-zero length */
|
||||||
|
ARGON2_SECRET_PTR_MISMATCH = -20, /* NULL ptr with non-zero length */
|
||||||
|
ARGON2_AD_PTR_MISMATCH = -21, /* NULL ptr with non-zero length */
|
||||||
|
|
||||||
|
ARGON2_MEMORY_ALLOCATION_ERROR = -22,
|
||||||
|
|
||||||
|
ARGON2_FREE_MEMORY_CBK_NULL = -23,
|
||||||
|
ARGON2_ALLOCATE_MEMORY_CBK_NULL = -24,
|
||||||
|
|
||||||
|
ARGON2_INCORRECT_PARAMETER = -25,
|
||||||
|
ARGON2_INCORRECT_TYPE = -26,
|
||||||
|
|
||||||
|
ARGON2_OUT_PTR_MISMATCH = -27,
|
||||||
|
|
||||||
|
ARGON2_THREADS_TOO_FEW = -28,
|
||||||
|
ARGON2_THREADS_TOO_MANY = -29,
|
||||||
|
|
||||||
|
ARGON2_MISSING_ARGS = -30,
|
||||||
|
|
||||||
|
ARGON2_ENCODING_FAIL = -31,
|
||||||
|
|
||||||
|
ARGON2_DECODING_FAIL = -32,
|
||||||
|
|
||||||
|
ARGON2_THREAD_FAIL = -33,
|
||||||
|
|
||||||
|
ARGON2_DECODING_LENGTH_FAIL = -34,
|
||||||
|
|
||||||
|
ARGON2_VERIFY_MISMATCH = -35
|
||||||
|
} argon2_error_codes;
|
||||||
|
|
||||||
|
/* Memory allocator types --- for external allocation */
|
||||||
|
typedef int (*allocate_fptr)(uint8_t **memory, size_t bytes_to_allocate);
|
||||||
|
typedef void (*deallocate_fptr)(uint8_t *memory, size_t bytes_to_allocate);
|
||||||
|
|
||||||
|
/* Argon2 external data structures */
|
||||||
|
|
||||||
|
/*
|
||||||
|
*****
|
||||||
|
* Context: structure to hold Argon2 inputs:
|
||||||
|
* output array and its length,
|
||||||
|
* password and its length,
|
||||||
|
* salt and its length,
|
||||||
|
* secret and its length,
|
||||||
|
* associated data and its length,
|
||||||
|
* number of passes, amount of used memory (in KBytes, can be rounded up a bit)
|
||||||
|
* number of parallel threads that will be run.
|
||||||
|
* All the parameters above affect the output hash value.
|
||||||
|
* Additionally, two function pointers can be provided to allocate and
|
||||||
|
* deallocate the memory (if NULL, memory will be allocated internally).
|
||||||
|
* Also, three flags indicate whether to erase password, secret as soon as they
|
||||||
|
* are pre-hashed (and thus not needed anymore), and the entire memory
|
||||||
|
*****
|
||||||
|
* Simplest situation: you have output array out[8], password is stored in
|
||||||
|
* pwd[32], salt is stored in salt[16], you do not have keys nor associated
|
||||||
|
* data. You need to spend 1 GB of RAM and you run 5 passes of Argon2d with
|
||||||
|
* 4 parallel lanes.
|
||||||
|
* You want to erase the password, but you're OK with last pass not being
|
||||||
|
* erased. You want to use the default memory allocator.
|
||||||
|
* Then you initialize:
|
||||||
|
Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false)
|
||||||
|
*/
|
||||||
|
typedef struct Argon2_Context {
|
||||||
|
uint8_t *out; /* output array */
|
||||||
|
uint32_t outlen; /* digest length */
|
||||||
|
|
||||||
|
uint8_t *pwd; /* password array */
|
||||||
|
uint32_t pwdlen; /* password length */
|
||||||
|
|
||||||
|
uint8_t *salt; /* salt array */
|
||||||
|
uint32_t saltlen; /* salt length */
|
||||||
|
|
||||||
|
uint8_t *secret; /* key array */
|
||||||
|
uint32_t secretlen; /* key length */
|
||||||
|
|
||||||
|
uint8_t *ad; /* associated data array */
|
||||||
|
uint32_t adlen; /* associated data length */
|
||||||
|
|
||||||
|
uint32_t t_cost; /* number of passes */
|
||||||
|
uint32_t m_cost; /* amount of memory requested (KB) */
|
||||||
|
uint32_t lanes; /* number of lanes */
|
||||||
|
uint32_t threads; /* maximum number of threads */
|
||||||
|
|
||||||
|
uint32_t version; /* version number */
|
||||||
|
|
||||||
|
allocate_fptr allocate_cbk; /* pointer to memory allocator */
|
||||||
|
deallocate_fptr free_cbk; /* pointer to memory deallocator */
|
||||||
|
|
||||||
|
uint32_t flags; /* array of bool options */
|
||||||
|
} argon2_context;
|
||||||
|
|
||||||
|
/* Argon2 primitive type */
|
||||||
|
typedef enum Argon2_type {
|
||||||
|
Argon2_d = 0,
|
||||||
|
Argon2_i = 1,
|
||||||
|
Argon2_id = 2
|
||||||
|
} argon2_type;
|
||||||
|
|
||||||
|
/* Version of the algorithm */
|
||||||
|
typedef enum Argon2_version {
|
||||||
|
ARGON2_VERSION_10 = 0x10,
|
||||||
|
ARGON2_VERSION_13 = 0x13,
|
||||||
|
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 *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
|
||||||
|
* @return Error code if smth is wrong, ARGON2_OK otherwise
|
||||||
|
*/
|
||||||
|
ARGON2_PUBLIC int 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 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 at @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 hash Buffer where to write the raw hash - updated by the function
|
||||||
|
* @param hashlen Desired length of the hash in bytes
|
||||||
|
* @pre Different parallelism levels will give different results
|
||||||
|
* @pre Returns ARGON2_OK if successful
|
||||||
|
*/
|
||||||
|
ARGON2_PUBLIC int 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 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 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 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 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 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 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 argon2i_verify(const char *encoded, const void *pwd,
|
||||||
|
const size_t pwdlen);
|
||||||
|
|
||||||
|
ARGON2_PUBLIC int argon2d_verify(const char *encoded, const void *pwd,
|
||||||
|
const size_t pwdlen);
|
||||||
|
|
||||||
|
ARGON2_PUBLIC int argon2id_verify(const char *encoded, const void *pwd,
|
||||||
|
const size_t pwdlen);
|
||||||
|
|
||||||
|
/* generic function underlying the above ones */
|
||||||
|
ARGON2_PUBLIC int 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 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 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 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 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 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 argon2id_verify_ctx(argon2_context *context,
|
||||||
|
const char *hash);
|
||||||
|
|
||||||
|
/* generic function underlying the above ones */
|
||||||
|
ARGON2_PUBLIC int 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 *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 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)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
156
src/argon2/blake2/blake2-impl.h
Normal file
156
src/argon2/blake2/blake2-impl.h
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* 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 PORTABLE_BLAKE2_IMPL_H
|
||||||
|
#define PORTABLE_BLAKE2_IMPL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define BLAKE2_INLINE __inline
|
||||||
|
#elif defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define BLAKE2_INLINE __inline__
|
||||||
|
#else
|
||||||
|
#define BLAKE2_INLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Argon2 Team - Begin Code */
|
||||||
|
/*
|
||||||
|
Not an exhaustive list, but should cover the majority of modern platforms
|
||||||
|
Additionally, the code will always be correct---this is only a performance
|
||||||
|
tweak.
|
||||||
|
*/
|
||||||
|
#if (defined(__BYTE_ORDER__) && \
|
||||||
|
(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \
|
||||||
|
defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \
|
||||||
|
defined(__AARCH64EL__) || defined(__amd64__) || defined(__i386__) || \
|
||||||
|
defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || \
|
||||||
|
defined(_M_ARM)
|
||||||
|
#define NATIVE_LITTLE_ENDIAN
|
||||||
|
#endif
|
||||||
|
/* Argon2 Team - End Code */
|
||||||
|
|
||||||
|
static BLAKE2_INLINE uint32_t load32(const void *src) {
|
||||||
|
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||||
|
uint32_t w;
|
||||||
|
memcpy(&w, src, sizeof w);
|
||||||
|
return w;
|
||||||
|
#else
|
||||||
|
const uint8_t *p = (const uint8_t *)src;
|
||||||
|
uint32_t w = *p++;
|
||||||
|
w |= (uint32_t)(*p++) << 8;
|
||||||
|
w |= (uint32_t)(*p++) << 16;
|
||||||
|
w |= (uint32_t)(*p++) << 24;
|
||||||
|
return w;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLAKE2_INLINE uint64_t load64(const void *src) {
|
||||||
|
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||||
|
uint64_t w;
|
||||||
|
memcpy(&w, src, sizeof w);
|
||||||
|
return w;
|
||||||
|
#else
|
||||||
|
const uint8_t *p = (const uint8_t *)src;
|
||||||
|
uint64_t w = *p++;
|
||||||
|
w |= (uint64_t)(*p++) << 8;
|
||||||
|
w |= (uint64_t)(*p++) << 16;
|
||||||
|
w |= (uint64_t)(*p++) << 24;
|
||||||
|
w |= (uint64_t)(*p++) << 32;
|
||||||
|
w |= (uint64_t)(*p++) << 40;
|
||||||
|
w |= (uint64_t)(*p++) << 48;
|
||||||
|
w |= (uint64_t)(*p++) << 56;
|
||||||
|
return w;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLAKE2_INLINE void store32(void *dst, uint32_t w) {
|
||||||
|
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||||
|
memcpy(dst, &w, sizeof w);
|
||||||
|
#else
|
||||||
|
uint8_t *p = (uint8_t *)dst;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLAKE2_INLINE void store64(void *dst, uint64_t w) {
|
||||||
|
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||||
|
memcpy(dst, &w, sizeof w);
|
||||||
|
#else
|
||||||
|
uint8_t *p = (uint8_t *)dst;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLAKE2_INLINE uint64_t load48(const void *src) {
|
||||||
|
const uint8_t *p = (const uint8_t *)src;
|
||||||
|
uint64_t w = *p++;
|
||||||
|
w |= (uint64_t)(*p++) << 8;
|
||||||
|
w |= (uint64_t)(*p++) << 16;
|
||||||
|
w |= (uint64_t)(*p++) << 24;
|
||||||
|
w |= (uint64_t)(*p++) << 32;
|
||||||
|
w |= (uint64_t)(*p++) << 40;
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLAKE2_INLINE void store48(void *dst, uint64_t w) {
|
||||||
|
uint8_t *p = (uint8_t *)dst;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
w >>= 8;
|
||||||
|
*p++ = (uint8_t)w;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLAKE2_INLINE uint32_t rotr32(const uint32_t w, const unsigned c) {
|
||||||
|
return (w >> c) | (w << (32 - c));
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLAKE2_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) {
|
||||||
|
return (w >> c) | (w << (64 - c));
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_internal_memory(void *v, size_t n);
|
||||||
|
|
||||||
|
#endif
|
89
src/argon2/blake2/blake2.h
Normal file
89
src/argon2/blake2/blake2.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* 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 PORTABLE_BLAKE2_H
|
||||||
|
#define PORTABLE_BLAKE2_H
|
||||||
|
|
||||||
|
#include "../argon2.h"
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum blake2b_constant {
|
||||||
|
BLAKE2B_BLOCKBYTES = 128,
|
||||||
|
BLAKE2B_OUTBYTES = 64,
|
||||||
|
BLAKE2B_KEYBYTES = 64,
|
||||||
|
BLAKE2B_SALTBYTES = 16,
|
||||||
|
BLAKE2B_PERSONALBYTES = 16
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct __blake2b_param {
|
||||||
|
uint8_t digest_length; /* 1 */
|
||||||
|
uint8_t key_length; /* 2 */
|
||||||
|
uint8_t fanout; /* 3 */
|
||||||
|
uint8_t depth; /* 4 */
|
||||||
|
uint32_t leaf_length; /* 8 */
|
||||||
|
uint64_t node_offset; /* 16 */
|
||||||
|
uint8_t node_depth; /* 17 */
|
||||||
|
uint8_t inner_length; /* 18 */
|
||||||
|
uint8_t reserved[14]; /* 32 */
|
||||||
|
uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */
|
||||||
|
uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */
|
||||||
|
} blake2b_param;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
typedef struct __blake2b_state {
|
||||||
|
uint64_t h[8];
|
||||||
|
uint64_t t[2];
|
||||||
|
uint64_t f[2];
|
||||||
|
uint8_t buf[BLAKE2B_BLOCKBYTES];
|
||||||
|
unsigned buflen;
|
||||||
|
unsigned outlen;
|
||||||
|
uint8_t last_node;
|
||||||
|
} blake2b_state;
|
||||||
|
|
||||||
|
/* Ensure param structs have not been wrongly padded */
|
||||||
|
/* Poor man's static_assert */
|
||||||
|
enum {
|
||||||
|
blake2_size_check_0 = 1 / !!(CHAR_BIT == 8),
|
||||||
|
blake2_size_check_2 =
|
||||||
|
1 / !!(sizeof(blake2b_param) == sizeof(uint64_t) * CHAR_BIT)
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Streaming API */
|
||||||
|
ARGON2_LOCAL int blake2b_init(blake2b_state *S, size_t outlen);
|
||||||
|
ARGON2_LOCAL int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key,
|
||||||
|
size_t keylen);
|
||||||
|
ARGON2_LOCAL int blake2b_init_param(blake2b_state *S, const blake2b_param *P);
|
||||||
|
ARGON2_LOCAL int blake2b_update(blake2b_state *S, const void *in, size_t inlen);
|
||||||
|
ARGON2_LOCAL int blake2b_final(blake2b_state *S, void *out, size_t outlen);
|
||||||
|
|
||||||
|
/* Simple API */
|
||||||
|
ARGON2_LOCAL int blake2b(void *out, size_t outlen, const void *in, size_t inlen,
|
||||||
|
const void *key, size_t keylen);
|
||||||
|
|
||||||
|
/* Argon2 Team - Begin Code */
|
||||||
|
ARGON2_LOCAL int blake2b_long(void *out, size_t outlen, const void *in, size_t inlen);
|
||||||
|
/* Argon2 Team - End Code */
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
390
src/argon2/blake2/blake2b.c
Normal file
390
src/argon2/blake2/blake2b.c
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
/*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "blake2.h"
|
||||||
|
#include "blake2-impl.h"
|
||||||
|
|
||||||
|
static const uint64_t blake2b_IV[8] = {
|
||||||
|
UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b),
|
||||||
|
UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1),
|
||||||
|
UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f),
|
||||||
|
UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179)};
|
||||||
|
|
||||||
|
static const unsigned int blake2b_sigma[12][16] = {
|
||||||
|
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||||
|
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
|
||||||
|
{11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
|
||||||
|
{7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
|
||||||
|
{9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
|
||||||
|
{2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
|
||||||
|
{12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
|
||||||
|
{13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
|
||||||
|
{6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
|
||||||
|
{10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0},
|
||||||
|
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||||
|
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
|
||||||
|
};
|
||||||
|
|
||||||
|
static BLAKE2_INLINE void blake2b_set_lastnode(blake2b_state *S) {
|
||||||
|
S->f[1] = (uint64_t)-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLAKE2_INLINE void blake2b_set_lastblock(blake2b_state *S) {
|
||||||
|
if (S->last_node) {
|
||||||
|
blake2b_set_lastnode(S);
|
||||||
|
}
|
||||||
|
S->f[0] = (uint64_t)-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLAKE2_INLINE void blake2b_increment_counter(blake2b_state *S,
|
||||||
|
uint64_t inc) {
|
||||||
|
S->t[0] += inc;
|
||||||
|
S->t[1] += (S->t[0] < inc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLAKE2_INLINE void blake2b_invalidate_state(blake2b_state *S) {
|
||||||
|
clear_internal_memory(S, sizeof(*S)); /* wipe */
|
||||||
|
blake2b_set_lastblock(S); /* invalidate for further use */
|
||||||
|
}
|
||||||
|
|
||||||
|
static BLAKE2_INLINE void blake2b_init0(blake2b_state *S) {
|
||||||
|
memset(S, 0, sizeof(*S));
|
||||||
|
memcpy(S->h, blake2b_IV, sizeof(S->h));
|
||||||
|
}
|
||||||
|
|
||||||
|
int blake2b_init_param(blake2b_state *S, const blake2b_param *P) {
|
||||||
|
const unsigned char *p = (const unsigned char *)P;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if (NULL == P || NULL == S) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
blake2b_init0(S);
|
||||||
|
/* IV XOR Parameter Block */
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
S->h[i] ^= load64(&p[i * sizeof(S->h[i])]);
|
||||||
|
}
|
||||||
|
S->outlen = P->digest_length;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sequential blake2b initialization */
|
||||||
|
int blake2b_init(blake2b_state *S, size_t outlen) {
|
||||||
|
blake2b_param P;
|
||||||
|
|
||||||
|
if (S == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) {
|
||||||
|
blake2b_invalidate_state(S);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup Parameter Block for unkeyed BLAKE2 */
|
||||||
|
P.digest_length = (uint8_t)outlen;
|
||||||
|
P.key_length = 0;
|
||||||
|
P.fanout = 1;
|
||||||
|
P.depth = 1;
|
||||||
|
P.leaf_length = 0;
|
||||||
|
P.node_offset = 0;
|
||||||
|
P.node_depth = 0;
|
||||||
|
P.inner_length = 0;
|
||||||
|
memset(P.reserved, 0, sizeof(P.reserved));
|
||||||
|
memset(P.salt, 0, sizeof(P.salt));
|
||||||
|
memset(P.personal, 0, sizeof(P.personal));
|
||||||
|
|
||||||
|
return blake2b_init_param(S, &P);
|
||||||
|
}
|
||||||
|
|
||||||
|
int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key,
|
||||||
|
size_t keylen) {
|
||||||
|
blake2b_param P;
|
||||||
|
|
||||||
|
if (S == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) {
|
||||||
|
blake2b_invalidate_state(S);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((key == 0) || (keylen == 0) || (keylen > BLAKE2B_KEYBYTES)) {
|
||||||
|
blake2b_invalidate_state(S);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup Parameter Block for keyed BLAKE2 */
|
||||||
|
P.digest_length = (uint8_t)outlen;
|
||||||
|
P.key_length = (uint8_t)keylen;
|
||||||
|
P.fanout = 1;
|
||||||
|
P.depth = 1;
|
||||||
|
P.leaf_length = 0;
|
||||||
|
P.node_offset = 0;
|
||||||
|
P.node_depth = 0;
|
||||||
|
P.inner_length = 0;
|
||||||
|
memset(P.reserved, 0, sizeof(P.reserved));
|
||||||
|
memset(P.salt, 0, sizeof(P.salt));
|
||||||
|
memset(P.personal, 0, sizeof(P.personal));
|
||||||
|
|
||||||
|
if (blake2b_init_param(S, &P) < 0) {
|
||||||
|
blake2b_invalidate_state(S);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uint8_t block[BLAKE2B_BLOCKBYTES];
|
||||||
|
memset(block, 0, BLAKE2B_BLOCKBYTES);
|
||||||
|
memcpy(block, key, keylen);
|
||||||
|
blake2b_update(S, block, BLAKE2B_BLOCKBYTES);
|
||||||
|
/* Burn the key from stack */
|
||||||
|
clear_internal_memory(block, BLAKE2B_BLOCKBYTES);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void blake2b_compress(blake2b_state *S, const uint8_t *block) {
|
||||||
|
uint64_t m[16];
|
||||||
|
uint64_t v[16];
|
||||||
|
unsigned int i, r;
|
||||||
|
|
||||||
|
for (i = 0; i < 16; ++i) {
|
||||||
|
m[i] = load64(block + i * sizeof(m[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
v[i] = S->h[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
v[8] = blake2b_IV[0];
|
||||||
|
v[9] = blake2b_IV[1];
|
||||||
|
v[10] = blake2b_IV[2];
|
||||||
|
v[11] = blake2b_IV[3];
|
||||||
|
v[12] = blake2b_IV[4] ^ S->t[0];
|
||||||
|
v[13] = blake2b_IV[5] ^ S->t[1];
|
||||||
|
v[14] = blake2b_IV[6] ^ S->f[0];
|
||||||
|
v[15] = blake2b_IV[7] ^ S->f[1];
|
||||||
|
|
||||||
|
#define G(r, i, a, b, c, d) \
|
||||||
|
do { \
|
||||||
|
a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \
|
||||||
|
d = rotr64(d ^ a, 32); \
|
||||||
|
c = c + d; \
|
||||||
|
b = rotr64(b ^ c, 24); \
|
||||||
|
a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \
|
||||||
|
d = rotr64(d ^ a, 16); \
|
||||||
|
c = c + d; \
|
||||||
|
b = rotr64(b ^ c, 63); \
|
||||||
|
} while ((void)0, 0)
|
||||||
|
|
||||||
|
#define ROUND(r) \
|
||||||
|
do { \
|
||||||
|
G(r, 0, v[0], v[4], v[8], v[12]); \
|
||||||
|
G(r, 1, v[1], v[5], v[9], v[13]); \
|
||||||
|
G(r, 2, v[2], v[6], v[10], v[14]); \
|
||||||
|
G(r, 3, v[3], v[7], v[11], v[15]); \
|
||||||
|
G(r, 4, v[0], v[5], v[10], v[15]); \
|
||||||
|
G(r, 5, v[1], v[6], v[11], v[12]); \
|
||||||
|
G(r, 6, v[2], v[7], v[8], v[13]); \
|
||||||
|
G(r, 7, v[3], v[4], v[9], v[14]); \
|
||||||
|
} while ((void)0, 0)
|
||||||
|
|
||||||
|
for (r = 0; r < 12; ++r) {
|
||||||
|
ROUND(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef G
|
||||||
|
#undef ROUND
|
||||||
|
}
|
||||||
|
|
||||||
|
int blake2b_update(blake2b_state *S, const void *in, size_t inlen) {
|
||||||
|
const uint8_t *pin = (const uint8_t *)in;
|
||||||
|
|
||||||
|
if (inlen == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sanity check */
|
||||||
|
if (S == NULL || in == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Is this a reused state? */
|
||||||
|
if (S->f[0] != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S->buflen + inlen > BLAKE2B_BLOCKBYTES) {
|
||||||
|
/* Complete current block */
|
||||||
|
size_t left = S->buflen;
|
||||||
|
size_t fill = BLAKE2B_BLOCKBYTES - left;
|
||||||
|
memcpy(&S->buf[left], pin, fill);
|
||||||
|
blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
|
||||||
|
blake2b_compress(S, S->buf);
|
||||||
|
S->buflen = 0;
|
||||||
|
inlen -= fill;
|
||||||
|
pin += fill;
|
||||||
|
/* Avoid buffer copies when possible */
|
||||||
|
while (inlen > BLAKE2B_BLOCKBYTES) {
|
||||||
|
blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
|
||||||
|
blake2b_compress(S, pin);
|
||||||
|
inlen -= BLAKE2B_BLOCKBYTES;
|
||||||
|
pin += BLAKE2B_BLOCKBYTES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memcpy(&S->buf[S->buflen], pin, inlen);
|
||||||
|
S->buflen += (unsigned int)inlen;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blake2b_final(blake2b_state *S, void *out, size_t outlen) {
|
||||||
|
uint8_t buffer[BLAKE2B_OUTBYTES] = {0};
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
/* Sanity checks */
|
||||||
|
if (S == NULL || out == NULL || outlen < S->outlen) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Is this a reused state? */
|
||||||
|
if (S->f[0] != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
blake2b_increment_counter(S, S->buflen);
|
||||||
|
blake2b_set_lastblock(S);
|
||||||
|
memset(&S->buf[S->buflen], 0, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */
|
||||||
|
blake2b_compress(S, S->buf);
|
||||||
|
|
||||||
|
for (i = 0; i < 8; ++i) { /* Output full hash to temp buffer */
|
||||||
|
store64(buffer + sizeof(S->h[i]) * i, S->h[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(out, buffer, S->outlen);
|
||||||
|
clear_internal_memory(buffer, sizeof(buffer));
|
||||||
|
clear_internal_memory(S->buf, sizeof(S->buf));
|
||||||
|
clear_internal_memory(S->h, sizeof(S->h));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blake2b(void *out, size_t outlen, const void *in, size_t inlen,
|
||||||
|
const void *key, size_t keylen) {
|
||||||
|
blake2b_state S;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
/* Verify parameters */
|
||||||
|
if (NULL == in && inlen > 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NULL == out || outlen == 0 || outlen > BLAKE2B_OUTBYTES) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((NULL == key && keylen > 0) || keylen > BLAKE2B_KEYBYTES) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keylen > 0) {
|
||||||
|
if (blake2b_init_key(&S, outlen, key, keylen) < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (blake2b_init(&S, outlen) < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blake2b_update(&S, in, inlen) < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
ret = blake2b_final(&S, out, outlen);
|
||||||
|
|
||||||
|
fail:
|
||||||
|
clear_internal_memory(&S, sizeof(S));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Argon2 Team - Begin Code */
|
||||||
|
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 */
|
56
src/argon2/blake2/blamka-round-ref.h
Normal file
56
src/argon2/blake2/blamka-round-ref.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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 BLAKE_ROUND_MKA_H
|
||||||
|
#define BLAKE_ROUND_MKA_H
|
||||||
|
|
||||||
|
#include "blake2.h"
|
||||||
|
#include "blake2-impl.h"
|
||||||
|
|
||||||
|
/* designed by the Lyra PHC team */
|
||||||
|
static BLAKE2_INLINE uint64_t fBlaMka(uint64_t x, uint64_t y) {
|
||||||
|
const uint64_t m = UINT64_C(0xFFFFFFFF);
|
||||||
|
const uint64_t xy = (x & m) * (y & m);
|
||||||
|
return x + y + 2 * xy;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define G(a, b, c, d) \
|
||||||
|
do { \
|
||||||
|
a = fBlaMka(a, b); \
|
||||||
|
d = rotr64(d ^ a, 32); \
|
||||||
|
c = fBlaMka(c, d); \
|
||||||
|
b = rotr64(b ^ c, 24); \
|
||||||
|
a = fBlaMka(a, b); \
|
||||||
|
d = rotr64(d ^ a, 16); \
|
||||||
|
c = fBlaMka(c, d); \
|
||||||
|
b = rotr64(b ^ c, 63); \
|
||||||
|
} while ((void)0, 0)
|
||||||
|
|
||||||
|
#define BLAKE2_ROUND_NOMSG(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, \
|
||||||
|
v12, v13, v14, v15) \
|
||||||
|
do { \
|
||||||
|
G(v0, v4, v8, v12); \
|
||||||
|
G(v1, v5, v9, v13); \
|
||||||
|
G(v2, v6, v10, v14); \
|
||||||
|
G(v3, v7, v11, v15); \
|
||||||
|
G(v0, v5, v10, v15); \
|
||||||
|
G(v1, v6, v11, v12); \
|
||||||
|
G(v2, v7, v8, v13); \
|
||||||
|
G(v3, v4, v9, v14); \
|
||||||
|
} while ((void)0, 0)
|
||||||
|
|
||||||
|
#endif
|
543
src/argon2/core.c
Normal file
543
src/argon2/core.c
Normal file
@ -0,0 +1,543 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*For memory wiping*/
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winbase.h> /* For SecureZeroMemory */
|
||||||
|
#endif
|
||||||
|
#if defined __STDC_LIB_EXT1__
|
||||||
|
#define __STDC_WANT_LIB_EXT1__ 1
|
||||||
|
#endif
|
||||||
|
#define VC_GE_2005(version) (version >= 1400)
|
||||||
|
|
||||||
|
/* for explicit_bzero() on glibc */
|
||||||
|
#define _DEFAULT_SOURCE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "core.h"
|
||||||
|
#include "blake2/blake2.h"
|
||||||
|
#include "blake2/blake2-impl.h"
|
||||||
|
|
||||||
|
#ifdef GENKAT
|
||||||
|
#include "genkat.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#if __has_attribute(optnone)
|
||||||
|
#define NOT_OPTIMIZED __attribute__((optnone))
|
||||||
|
#endif
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#define GCC_VERSION \
|
||||||
|
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
|
||||||
|
#if GCC_VERSION >= 40400
|
||||||
|
#define NOT_OPTIMIZED __attribute__((optimize("O0")))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#ifndef NOT_OPTIMIZED
|
||||||
|
#define NOT_OPTIMIZED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/***************Instance and Position constructors**********/
|
||||||
|
void init_block_value(block *b, uint8_t in) { memset(b->v, in, sizeof(b->v)); }
|
||||||
|
|
||||||
|
void copy_block(block *dst, const block *src) {
|
||||||
|
memcpy(dst->v, src->v, sizeof(uint64_t) * ARGON2_QWORDS_IN_BLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void xor_block(block *dst, const block *src) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
|
||||||
|
dst->v[i] ^= src->v[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_block(block *dst, const void *input) {
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
|
||||||
|
dst->v[i] = load64((const uint8_t *)input + i * sizeof(dst->v[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void store_block(void *output, const block *src) {
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
|
||||||
|
store64((uint8_t *)output + i * sizeof(src->v[i]), src->v[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************Memory functions*****************/
|
||||||
|
|
||||||
|
int allocate_memory(const argon2_context *context, uint8_t **memory,
|
||||||
|
size_t num, size_t size) {
|
||||||
|
size_t memory_size = num*size;
|
||||||
|
if (memory == NULL) {
|
||||||
|
return ARGON2_MEMORY_ALLOCATION_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1. Check for multiplication overflow */
|
||||||
|
if (size != 0 && memory_size / size != num) {
|
||||||
|
return ARGON2_MEMORY_ALLOCATION_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Try to allocate with appropriate allocator */
|
||||||
|
if (context->allocate_cbk) {
|
||||||
|
(context->allocate_cbk)(memory, memory_size);
|
||||||
|
} else {
|
||||||
|
*memory = malloc(memory_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*memory == NULL) {
|
||||||
|
return ARGON2_MEMORY_ALLOCATION_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ARGON2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_memory(const argon2_context *context, uint8_t *memory,
|
||||||
|
size_t num, size_t size) {
|
||||||
|
size_t memory_size = num*size;
|
||||||
|
clear_internal_memory(memory, memory_size);
|
||||||
|
if (context->free_cbk) {
|
||||||
|
(context->free_cbk)(memory, memory_size);
|
||||||
|
} else {
|
||||||
|
free(memory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__OpenBSD__)
|
||||||
|
#define HAVE_EXPLICIT_BZERO 1
|
||||||
|
#elif defined(__GLIBC__) && defined(__GLIBC_PREREQ)
|
||||||
|
#if __GLIBC_PREREQ(2,25)
|
||||||
|
#define HAVE_EXPLICIT_BZERO 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void NOT_OPTIMIZED secure_wipe_memory(void *v, size_t n) {
|
||||||
|
#if defined(_MSC_VER) && VC_GE_2005(_MSC_VER)
|
||||||
|
SecureZeroMemory(v, n);
|
||||||
|
#elif defined memset_s
|
||||||
|
memset_s(v, n, 0, n);
|
||||||
|
#elif defined(HAVE_EXPLICIT_BZERO)
|
||||||
|
explicit_bzero(v, n);
|
||||||
|
#else
|
||||||
|
static void *(*const volatile memset_sec)(void *, int, size_t) = &memset;
|
||||||
|
memset_sec(v, 0, n);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Memory clear flag defaults to true. */
|
||||||
|
int FLAG_clear_internal_memory = 1;
|
||||||
|
void clear_internal_memory(void *v, size_t n) {
|
||||||
|
if (FLAG_clear_internal_memory && v) {
|
||||||
|
secure_wipe_memory(v, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void finalize(const argon2_context *context, argon2_instance_t *instance) {
|
||||||
|
if (context != NULL && instance != NULL) {
|
||||||
|
block blockhash;
|
||||||
|
uint32_t l;
|
||||||
|
|
||||||
|
copy_block(&blockhash, instance->memory + instance->lane_length - 1);
|
||||||
|
|
||||||
|
/* XOR the last blocks */
|
||||||
|
for (l = 1; l < instance->lanes; ++l) {
|
||||||
|
uint32_t last_block_in_lane =
|
||||||
|
l * instance->lane_length + (instance->lane_length - 1);
|
||||||
|
xor_block(&blockhash, instance->memory + last_block_in_lane);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hash the result */
|
||||||
|
{
|
||||||
|
uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
|
||||||
|
store_block(blockhash_bytes, &blockhash);
|
||||||
|
blake2b_long(context->out, context->outlen, blockhash_bytes,
|
||||||
|
ARGON2_BLOCK_SIZE);
|
||||||
|
/* clear blockhash and blockhash_bytes */
|
||||||
|
clear_internal_memory(blockhash.v, ARGON2_BLOCK_SIZE);
|
||||||
|
clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef GENKAT
|
||||||
|
print_tag(context->out, context->outlen);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
free_memory(context, (uint8_t *)instance->memory,
|
||||||
|
instance->memory_blocks, sizeof(block));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t index_alpha(const argon2_instance_t *instance,
|
||||||
|
const argon2_position_t *position, uint32_t pseudo_rand,
|
||||||
|
int same_lane) {
|
||||||
|
/*
|
||||||
|
* Pass 0:
|
||||||
|
* This lane : all already finished segments plus already constructed
|
||||||
|
* blocks in this segment
|
||||||
|
* Other lanes : all already finished segments
|
||||||
|
* Pass 1+:
|
||||||
|
* This lane : (SYNC_POINTS - 1) last segments plus already constructed
|
||||||
|
* blocks in this segment
|
||||||
|
* Other lanes : (SYNC_POINTS - 1) last segments
|
||||||
|
*/
|
||||||
|
uint32_t reference_area_size;
|
||||||
|
uint64_t relative_position;
|
||||||
|
uint32_t start_position, absolute_position;
|
||||||
|
|
||||||
|
if (0 == position->pass) {
|
||||||
|
/* First pass */
|
||||||
|
if (0 == position->slice) {
|
||||||
|
/* First slice */
|
||||||
|
reference_area_size =
|
||||||
|
position->index - 1; /* all but the previous */
|
||||||
|
} else {
|
||||||
|
if (same_lane) {
|
||||||
|
/* The same lane => add current segment */
|
||||||
|
reference_area_size =
|
||||||
|
position->slice * instance->segment_length +
|
||||||
|
position->index - 1;
|
||||||
|
} else {
|
||||||
|
reference_area_size =
|
||||||
|
position->slice * instance->segment_length +
|
||||||
|
((position->index == 0) ? (-1) : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Second pass */
|
||||||
|
if (same_lane) {
|
||||||
|
reference_area_size = instance->lane_length -
|
||||||
|
instance->segment_length + position->index -
|
||||||
|
1;
|
||||||
|
} else {
|
||||||
|
reference_area_size = instance->lane_length -
|
||||||
|
instance->segment_length +
|
||||||
|
((position->index == 0) ? (-1) : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.2.4. Mapping pseudo_rand to 0..<reference_area_size-1> and produce
|
||||||
|
* relative position */
|
||||||
|
relative_position = pseudo_rand;
|
||||||
|
relative_position = relative_position * relative_position >> 32;
|
||||||
|
relative_position = reference_area_size - 1 -
|
||||||
|
(reference_area_size * relative_position >> 32);
|
||||||
|
|
||||||
|
/* 1.2.5 Computing starting position */
|
||||||
|
start_position = 0;
|
||||||
|
|
||||||
|
if (0 != position->pass) {
|
||||||
|
start_position = (position->slice == ARGON2_SYNC_POINTS - 1)
|
||||||
|
? 0
|
||||||
|
: (position->slice + 1) * instance->segment_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.2.6. Computing absolute position */
|
||||||
|
absolute_position = (start_position + relative_position) %
|
||||||
|
instance->lane_length; /* absolute position */
|
||||||
|
return absolute_position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Single-threaded version for p=1 case */
|
||||||
|
static int fill_memory_blocks_st(argon2_instance_t *instance) {
|
||||||
|
uint32_t r, s, l;
|
||||||
|
|
||||||
|
for (r = 0; r < instance->passes; ++r) {
|
||||||
|
for (s = 0; s < ARGON2_SYNC_POINTS; ++s) {
|
||||||
|
for (l = 0; l < instance->lanes; ++l) {
|
||||||
|
argon2_position_t position = {r, l, (uint8_t)s, 0};
|
||||||
|
fill_segment(instance, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef GENKAT
|
||||||
|
internal_kat(instance, r); /* Print all memory blocks */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return ARGON2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fill_memory_blocks(argon2_instance_t *instance) {
|
||||||
|
if (instance == NULL || instance->lanes == 0) {
|
||||||
|
return ARGON2_INCORRECT_PARAMETER;
|
||||||
|
}
|
||||||
|
return fill_memory_blocks_st(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
int validate_inputs(const argon2_context *context) {
|
||||||
|
if (NULL == context) {
|
||||||
|
return ARGON2_INCORRECT_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NULL == context->out) {
|
||||||
|
return ARGON2_OUTPUT_PTR_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate output length */
|
||||||
|
if (ARGON2_MIN_OUTLEN > context->outlen) {
|
||||||
|
return ARGON2_OUTPUT_TOO_SHORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGON2_MAX_OUTLEN < context->outlen) {
|
||||||
|
return ARGON2_OUTPUT_TOO_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate password (required param) */
|
||||||
|
if (NULL == context->pwd) {
|
||||||
|
if (0 != context->pwdlen) {
|
||||||
|
return ARGON2_PWD_PTR_MISMATCH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGON2_MIN_PWD_LENGTH > context->pwdlen) {
|
||||||
|
return ARGON2_PWD_TOO_SHORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGON2_MAX_PWD_LENGTH < context->pwdlen) {
|
||||||
|
return ARGON2_PWD_TOO_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate salt (required param) */
|
||||||
|
if (NULL == context->salt) {
|
||||||
|
if (0 != context->saltlen) {
|
||||||
|
return ARGON2_SALT_PTR_MISMATCH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGON2_MIN_SALT_LENGTH > context->saltlen) {
|
||||||
|
return ARGON2_SALT_TOO_SHORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGON2_MAX_SALT_LENGTH < context->saltlen) {
|
||||||
|
return ARGON2_SALT_TOO_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate secret (optional param) */
|
||||||
|
if (NULL == context->secret) {
|
||||||
|
if (0 != context->secretlen) {
|
||||||
|
return ARGON2_SECRET_PTR_MISMATCH;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ARGON2_MIN_SECRET > context->secretlen) {
|
||||||
|
return ARGON2_SECRET_TOO_SHORT;
|
||||||
|
}
|
||||||
|
if (ARGON2_MAX_SECRET < context->secretlen) {
|
||||||
|
return ARGON2_SECRET_TOO_LONG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate associated data (optional param) */
|
||||||
|
if (NULL == context->ad) {
|
||||||
|
if (0 != context->adlen) {
|
||||||
|
return ARGON2_AD_PTR_MISMATCH;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ARGON2_MIN_AD_LENGTH > context->adlen) {
|
||||||
|
return ARGON2_AD_TOO_SHORT;
|
||||||
|
}
|
||||||
|
if (ARGON2_MAX_AD_LENGTH < context->adlen) {
|
||||||
|
return ARGON2_AD_TOO_LONG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate memory cost */
|
||||||
|
if (ARGON2_MIN_MEMORY > context->m_cost) {
|
||||||
|
return ARGON2_MEMORY_TOO_LITTLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGON2_MAX_MEMORY < context->m_cost) {
|
||||||
|
return ARGON2_MEMORY_TOO_MUCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context->m_cost < 8 * context->lanes) {
|
||||||
|
return ARGON2_MEMORY_TOO_LITTLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate time cost */
|
||||||
|
if (ARGON2_MIN_TIME > context->t_cost) {
|
||||||
|
return ARGON2_TIME_TOO_SMALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGON2_MAX_TIME < context->t_cost) {
|
||||||
|
return ARGON2_TIME_TOO_LARGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate lanes */
|
||||||
|
if (ARGON2_MIN_LANES > context->lanes) {
|
||||||
|
return ARGON2_LANES_TOO_FEW;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGON2_MAX_LANES < context->lanes) {
|
||||||
|
return ARGON2_LANES_TOO_MANY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate threads */
|
||||||
|
if (ARGON2_MIN_THREADS > context->threads) {
|
||||||
|
return ARGON2_THREADS_TOO_FEW;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGON2_MAX_THREADS < context->threads) {
|
||||||
|
return ARGON2_THREADS_TOO_MANY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NULL != context->allocate_cbk && NULL == context->free_cbk) {
|
||||||
|
return ARGON2_FREE_MEMORY_CBK_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NULL == context->allocate_cbk && NULL != context->free_cbk) {
|
||||||
|
return ARGON2_ALLOCATE_MEMORY_CBK_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ARGON2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) {
|
||||||
|
uint32_t l;
|
||||||
|
/* Make the first and second block in each lane as G(H0||0||i) or
|
||||||
|
G(H0||1||i) */
|
||||||
|
uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
|
||||||
|
for (l = 0; l < instance->lanes; ++l) {
|
||||||
|
|
||||||
|
store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 0);
|
||||||
|
store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH + 4, l);
|
||||||
|
blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash,
|
||||||
|
ARGON2_PREHASH_SEED_LENGTH);
|
||||||
|
load_block(&instance->memory[l * instance->lane_length + 0],
|
||||||
|
blockhash_bytes);
|
||||||
|
|
||||||
|
store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 1);
|
||||||
|
blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash,
|
||||||
|
ARGON2_PREHASH_SEED_LENGTH);
|
||||||
|
load_block(&instance->memory[l * instance->lane_length + 1],
|
||||||
|
blockhash_bytes);
|
||||||
|
}
|
||||||
|
clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initial_hash(uint8_t *blockhash, argon2_context *context,
|
||||||
|
argon2_type type) {
|
||||||
|
blake2b_state BlakeHash;
|
||||||
|
uint8_t value[sizeof(uint32_t)];
|
||||||
|
|
||||||
|
if (NULL == context || NULL == blockhash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH);
|
||||||
|
|
||||||
|
store32(&value, context->lanes);
|
||||||
|
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||||
|
|
||||||
|
store32(&value, context->outlen);
|
||||||
|
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||||
|
|
||||||
|
store32(&value, context->m_cost);
|
||||||
|
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||||
|
|
||||||
|
store32(&value, context->t_cost);
|
||||||
|
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||||
|
|
||||||
|
store32(&value, context->version);
|
||||||
|
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||||
|
|
||||||
|
store32(&value, (uint32_t)type);
|
||||||
|
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||||
|
|
||||||
|
store32(&value, context->pwdlen);
|
||||||
|
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||||
|
|
||||||
|
if (context->pwd != NULL) {
|
||||||
|
blake2b_update(&BlakeHash, (const uint8_t *)context->pwd,
|
||||||
|
context->pwdlen);
|
||||||
|
|
||||||
|
if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) {
|
||||||
|
secure_wipe_memory(context->pwd, context->pwdlen);
|
||||||
|
context->pwdlen = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
store32(&value, context->saltlen);
|
||||||
|
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||||
|
|
||||||
|
if (context->salt != NULL) {
|
||||||
|
blake2b_update(&BlakeHash, (const uint8_t *)context->salt,
|
||||||
|
context->saltlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
store32(&value, context->secretlen);
|
||||||
|
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||||
|
|
||||||
|
if (context->secret != NULL) {
|
||||||
|
blake2b_update(&BlakeHash, (const uint8_t *)context->secret,
|
||||||
|
context->secretlen);
|
||||||
|
|
||||||
|
if (context->flags & ARGON2_FLAG_CLEAR_SECRET) {
|
||||||
|
secure_wipe_memory(context->secret, context->secretlen);
|
||||||
|
context->secretlen = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
store32(&value, context->adlen);
|
||||||
|
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
|
||||||
|
|
||||||
|
if (context->ad != NULL) {
|
||||||
|
blake2b_update(&BlakeHash, (const uint8_t *)context->ad,
|
||||||
|
context->adlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
int initialize(argon2_instance_t *instance, argon2_context *context) {
|
||||||
|
uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH];
|
||||||
|
int result = ARGON2_OK;
|
||||||
|
|
||||||
|
if (instance == NULL || context == NULL)
|
||||||
|
return ARGON2_INCORRECT_PARAMETER;
|
||||||
|
instance->context_ptr = context;
|
||||||
|
|
||||||
|
/* 1. Memory allocation */
|
||||||
|
result = allocate_memory(context, (uint8_t **)&(instance->memory),
|
||||||
|
instance->memory_blocks, sizeof(block));
|
||||||
|
if (result != ARGON2_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Initial hashing */
|
||||||
|
/* H_0 + 8 extra bytes to produce the first blocks */
|
||||||
|
/* uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; */
|
||||||
|
/* Hashing all inputs */
|
||||||
|
initial_hash(blockhash, context, instance->type);
|
||||||
|
/* Zeroing 8 extra bytes */
|
||||||
|
clear_internal_memory(blockhash + ARGON2_PREHASH_DIGEST_LENGTH,
|
||||||
|
ARGON2_PREHASH_SEED_LENGTH -
|
||||||
|
ARGON2_PREHASH_DIGEST_LENGTH);
|
||||||
|
|
||||||
|
#ifdef GENKAT
|
||||||
|
initial_kat(blockhash, context, instance->type);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 3. Creating first blocks, we always have at least two blocks in a slice
|
||||||
|
*/
|
||||||
|
fill_first_blocks(blockhash, instance);
|
||||||
|
/* Clearing the hash */
|
||||||
|
clear_internal_memory(blockhash, ARGON2_PREHASH_SEED_LENGTH);
|
||||||
|
|
||||||
|
return ARGON2_OK;
|
||||||
|
}
|
228
src/argon2/core.h
Normal file
228
src/argon2/core.h
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
/*
|
||||||
|
* 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 ARGON2_CORE_H
|
||||||
|
#define ARGON2_CORE_H
|
||||||
|
|
||||||
|
#include "argon2.h"
|
||||||
|
|
||||||
|
#define CONST_CAST(x) (x)(uintptr_t)
|
||||||
|
|
||||||
|
/**********************Argon2 internal constants*******************************/
|
||||||
|
|
||||||
|
enum argon2_core_constants {
|
||||||
|
/* Memory block size in bytes */
|
||||||
|
ARGON2_BLOCK_SIZE = 1024,
|
||||||
|
ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 8,
|
||||||
|
ARGON2_OWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 16,
|
||||||
|
ARGON2_HWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 32,
|
||||||
|
ARGON2_512BIT_WORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 64,
|
||||||
|
|
||||||
|
/* Number of pseudo-random values generated by one call to Blake in Argon2i
|
||||||
|
to
|
||||||
|
generate reference block positions */
|
||||||
|
ARGON2_ADDRESSES_IN_BLOCK = 128,
|
||||||
|
|
||||||
|
/* Pre-hashing digest length and its extension*/
|
||||||
|
ARGON2_PREHASH_DIGEST_LENGTH = 64,
|
||||||
|
ARGON2_PREHASH_SEED_LENGTH = 72
|
||||||
|
};
|
||||||
|
|
||||||
|
/*************************Argon2 internal data types***********************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Structure for the (1KB) memory block implemented as 128 64-bit words.
|
||||||
|
* Memory blocks can be copied, XORed. Internal words can be accessed by [] (no
|
||||||
|
* bounds checking).
|
||||||
|
*/
|
||||||
|
typedef struct block_ { uint64_t v[ARGON2_QWORDS_IN_BLOCK]; } block;
|
||||||
|
|
||||||
|
/*****************Functions that work with the block******************/
|
||||||
|
|
||||||
|
/* Initialize each byte of the block with @in */
|
||||||
|
void init_block_value(block *b, uint8_t in);
|
||||||
|
|
||||||
|
/* Copy block @src to block @dst */
|
||||||
|
void copy_block(block *dst, const block *src);
|
||||||
|
|
||||||
|
/* XOR @src onto @dst bytewise */
|
||||||
|
void xor_block(block *dst, const block *src);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Argon2 instance: memory pointer, number of passes, amount of memory, type,
|
||||||
|
* and derived values.
|
||||||
|
* Used to evaluate the number and location of blocks to construct in each
|
||||||
|
* thread
|
||||||
|
*/
|
||||||
|
typedef struct Argon2_instance_t {
|
||||||
|
block *memory; /* Memory pointer */
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t passes; /* Number of passes */
|
||||||
|
uint32_t memory_blocks; /* Number of blocks in memory */
|
||||||
|
uint32_t segment_length;
|
||||||
|
uint32_t lane_length;
|
||||||
|
uint32_t lanes;
|
||||||
|
uint32_t threads;
|
||||||
|
argon2_type type;
|
||||||
|
int print_internals; /* whether to print the memory blocks */
|
||||||
|
argon2_context *context_ptr; /* points back to original context */
|
||||||
|
} argon2_instance_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Argon2 position: where we construct the block right now. Used to distribute
|
||||||
|
* work between threads.
|
||||||
|
*/
|
||||||
|
typedef struct Argon2_position_t {
|
||||||
|
uint32_t pass;
|
||||||
|
uint32_t lane;
|
||||||
|
uint8_t slice;
|
||||||
|
uint32_t index;
|
||||||
|
} argon2_position_t;
|
||||||
|
|
||||||
|
/*Struct that holds the inputs for thread handling FillSegment*/
|
||||||
|
typedef struct Argon2_thread_data {
|
||||||
|
argon2_instance_t *instance_ptr;
|
||||||
|
argon2_position_t pos;
|
||||||
|
} argon2_thread_data;
|
||||||
|
|
||||||
|
/*************************Argon2 core functions********************************/
|
||||||
|
|
||||||
|
/* Allocates memory to the given pointer, uses the appropriate allocator as
|
||||||
|
* specified in the context. Total allocated memory is num*size.
|
||||||
|
* @param context argon2_context which specifies the allocator
|
||||||
|
* @param memory pointer to the pointer to the memory
|
||||||
|
* @param size the size in bytes for each element to be allocated
|
||||||
|
* @param num the number of elements to be allocated
|
||||||
|
* @return ARGON2_OK if @memory is a valid pointer and memory is allocated
|
||||||
|
*/
|
||||||
|
int allocate_memory(const argon2_context *context, uint8_t **memory,
|
||||||
|
size_t num, size_t size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Frees memory at the given pointer, uses the appropriate deallocator as
|
||||||
|
* specified in the context. Also cleans the memory using clear_internal_memory.
|
||||||
|
* @param context argon2_context which specifies the deallocator
|
||||||
|
* @param memory pointer to buffer to be freed
|
||||||
|
* @param size the size in bytes for each element to be deallocated
|
||||||
|
* @param num the number of elements to be deallocated
|
||||||
|
*/
|
||||||
|
void free_memory(const argon2_context *context, uint8_t *memory,
|
||||||
|
size_t num, size_t size);
|
||||||
|
|
||||||
|
/* Function that securely cleans the memory. This ignores any flags set
|
||||||
|
* regarding clearing memory. Usually one just calls clear_internal_memory.
|
||||||
|
* @param mem Pointer to the memory
|
||||||
|
* @param s Memory size in bytes
|
||||||
|
*/
|
||||||
|
void secure_wipe_memory(void *v, size_t n);
|
||||||
|
|
||||||
|
/* Function that securely clears the memory if FLAG_clear_internal_memory is
|
||||||
|
* set. If the flag isn't set, this function does nothing.
|
||||||
|
* @param mem Pointer to the memory
|
||||||
|
* @param s Memory size in bytes
|
||||||
|
*/
|
||||||
|
void clear_internal_memory(void *v, size_t n);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Computes absolute position of reference block in the lane following a skewed
|
||||||
|
* distribution and using a pseudo-random value as input
|
||||||
|
* @param instance Pointer to the current instance
|
||||||
|
* @param position Pointer to the current position
|
||||||
|
* @param pseudo_rand 32-bit pseudo-random value used to determine the position
|
||||||
|
* @param same_lane Indicates if the block will be taken from the current lane.
|
||||||
|
* If so we can reference the current segment
|
||||||
|
* @pre All pointers must be valid
|
||||||
|
*/
|
||||||
|
uint32_t index_alpha(const argon2_instance_t *instance,
|
||||||
|
const argon2_position_t *position, uint32_t pseudo_rand,
|
||||||
|
int same_lane);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function that validates all inputs against predefined restrictions and return
|
||||||
|
* an error code
|
||||||
|
* @param context Pointer to current Argon2 context
|
||||||
|
* @return ARGON2_OK if everything is all right, otherwise one of error codes
|
||||||
|
* (all defined in <argon2.h>
|
||||||
|
*/
|
||||||
|
int validate_inputs(const argon2_context *context);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears
|
||||||
|
* password and secret if needed
|
||||||
|
* @param context Pointer to the Argon2 internal structure containing memory
|
||||||
|
* pointer, and parameters for time and space requirements.
|
||||||
|
* @param blockhash Buffer for pre-hashing digest
|
||||||
|
* @param type Argon2 type
|
||||||
|
* @pre @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes
|
||||||
|
* allocated
|
||||||
|
*/
|
||||||
|
void initial_hash(uint8_t *blockhash, argon2_context *context,
|
||||||
|
argon2_type type);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function creates first 2 blocks per lane
|
||||||
|
* @param instance Pointer to the current instance
|
||||||
|
* @param blockhash Pointer to the pre-hashing digest
|
||||||
|
* @pre blockhash must point to @a PREHASH_SEED_LENGTH allocated values
|
||||||
|
*/
|
||||||
|
void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function allocates memory, hashes the inputs with Blake, and creates first
|
||||||
|
* two blocks. Returns the pointer to the main memory with 2 blocks per lane
|
||||||
|
* initialized
|
||||||
|
* @param context Pointer to the Argon2 internal structure containing memory
|
||||||
|
* pointer, and parameters for time and space requirements.
|
||||||
|
* @param instance Current Argon2 instance
|
||||||
|
* @return Zero if successful, -1 if memory failed to allocate. @context->state
|
||||||
|
* will be modified if successful.
|
||||||
|
*/
|
||||||
|
int initialize(argon2_instance_t *instance, argon2_context *context);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XORing the last block of each lane, hashing it, making the tag. Deallocates
|
||||||
|
* the memory.
|
||||||
|
* @param context Pointer to current Argon2 context (use only the out parameters
|
||||||
|
* from it)
|
||||||
|
* @param instance Pointer to current instance of Argon2
|
||||||
|
* @pre instance->state must point to necessary amount of memory
|
||||||
|
* @pre context->out must point to outlen bytes of memory
|
||||||
|
* @pre if context->free_cbk is not NULL, it should point to a function that
|
||||||
|
* deallocates memory
|
||||||
|
*/
|
||||||
|
void finalize(const argon2_context *context, argon2_instance_t *instance);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function that fills the segment using previous segments also from other
|
||||||
|
* threads
|
||||||
|
* @param context current context
|
||||||
|
* @param instance Pointer to the current instance
|
||||||
|
* @param position Current position
|
||||||
|
* @pre all block pointers must be valid
|
||||||
|
*/
|
||||||
|
void fill_segment(const argon2_instance_t *instance,
|
||||||
|
argon2_position_t position);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function that fills the entire memory t_cost times based on the first two
|
||||||
|
* blocks in each lane
|
||||||
|
* @param instance Pointer to the current instance
|
||||||
|
* @return ARGON2_OK if successful, @context->state
|
||||||
|
*/
|
||||||
|
int fill_memory_blocks(argon2_instance_t *instance);
|
||||||
|
|
||||||
|
#endif
|
194
src/argon2/ref.c
Normal file
194
src/argon2/ref.c
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "argon2.h"
|
||||||
|
#include "core.h"
|
||||||
|
|
||||||
|
#include "blake2/blamka-round-ref.h"
|
||||||
|
#include "blake2/blake2-impl.h"
|
||||||
|
#include "blake2/blake2.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function fills a new memory block and optionally XORs the old block over the new one.
|
||||||
|
* @next_block must be initialized.
|
||||||
|
* @param prev_block Pointer to the previous block
|
||||||
|
* @param ref_block Pointer to the reference block
|
||||||
|
* @param next_block Pointer to the block to be constructed
|
||||||
|
* @param with_xor Whether to XOR into the new block (1) or just overwrite (0)
|
||||||
|
* @pre all block pointers must be valid
|
||||||
|
*/
|
||||||
|
static void fill_block(const block *prev_block, const block *ref_block,
|
||||||
|
block *next_block, int with_xor) {
|
||||||
|
block blockR, block_tmp;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
copy_block(&blockR, ref_block);
|
||||||
|
xor_block(&blockR, prev_block);
|
||||||
|
copy_block(&block_tmp, &blockR);
|
||||||
|
/* Now blockR = ref_block + prev_block and block_tmp = ref_block + prev_block */
|
||||||
|
if (with_xor) {
|
||||||
|
/* Saving the next block contents for XOR over: */
|
||||||
|
xor_block(&block_tmp, next_block);
|
||||||
|
/* Now blockR = ref_block + prev_block and
|
||||||
|
block_tmp = ref_block + prev_block + next_block */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then
|
||||||
|
(16,17,..31)... finally (112,113,...127) */
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
BLAKE2_ROUND_NOMSG(
|
||||||
|
blockR.v[16 * i], blockR.v[16 * i + 1], blockR.v[16 * i + 2],
|
||||||
|
blockR.v[16 * i + 3], blockR.v[16 * i + 4], blockR.v[16 * i + 5],
|
||||||
|
blockR.v[16 * i + 6], blockR.v[16 * i + 7], blockR.v[16 * i + 8],
|
||||||
|
blockR.v[16 * i + 9], blockR.v[16 * i + 10], blockR.v[16 * i + 11],
|
||||||
|
blockR.v[16 * i + 12], blockR.v[16 * i + 13], blockR.v[16 * i + 14],
|
||||||
|
blockR.v[16 * i + 15]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then
|
||||||
|
(2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) */
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
BLAKE2_ROUND_NOMSG(
|
||||||
|
blockR.v[2 * i], blockR.v[2 * i + 1], blockR.v[2 * i + 16],
|
||||||
|
blockR.v[2 * i + 17], blockR.v[2 * i + 32], blockR.v[2 * i + 33],
|
||||||
|
blockR.v[2 * i + 48], blockR.v[2 * i + 49], blockR.v[2 * i + 64],
|
||||||
|
blockR.v[2 * i + 65], blockR.v[2 * i + 80], blockR.v[2 * i + 81],
|
||||||
|
blockR.v[2 * i + 96], blockR.v[2 * i + 97], blockR.v[2 * i + 112],
|
||||||
|
blockR.v[2 * i + 113]);
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_block(next_block, &block_tmp);
|
||||||
|
xor_block(next_block, &blockR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void next_addresses(block *address_block, block *input_block,
|
||||||
|
const block *zero_block) {
|
||||||
|
input_block->v[6]++;
|
||||||
|
fill_block(zero_block, input_block, address_block, 0);
|
||||||
|
fill_block(zero_block, address_block, address_block, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_segment(const argon2_instance_t *instance,
|
||||||
|
argon2_position_t position) {
|
||||||
|
block *ref_block = NULL, *curr_block = NULL;
|
||||||
|
block address_block, input_block, zero_block;
|
||||||
|
uint64_t pseudo_rand, ref_index, ref_lane;
|
||||||
|
uint32_t prev_offset, curr_offset;
|
||||||
|
uint32_t starting_index;
|
||||||
|
uint32_t i;
|
||||||
|
int data_independent_addressing;
|
||||||
|
|
||||||
|
if (instance == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_independent_addressing =
|
||||||
|
(instance->type == Argon2_i) ||
|
||||||
|
(instance->type == Argon2_id && (position.pass == 0) &&
|
||||||
|
(position.slice < ARGON2_SYNC_POINTS / 2));
|
||||||
|
|
||||||
|
if (data_independent_addressing) {
|
||||||
|
init_block_value(&zero_block, 0);
|
||||||
|
init_block_value(&input_block, 0);
|
||||||
|
|
||||||
|
input_block.v[0] = position.pass;
|
||||||
|
input_block.v[1] = position.lane;
|
||||||
|
input_block.v[2] = position.slice;
|
||||||
|
input_block.v[3] = instance->memory_blocks;
|
||||||
|
input_block.v[4] = instance->passes;
|
||||||
|
input_block.v[5] = instance->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
starting_index = 0;
|
||||||
|
|
||||||
|
if ((0 == position.pass) && (0 == position.slice)) {
|
||||||
|
starting_index = 2; /* we have already generated the first two blocks */
|
||||||
|
|
||||||
|
/* Don't forget to generate the first block of addresses: */
|
||||||
|
if (data_independent_addressing) {
|
||||||
|
next_addresses(&address_block, &input_block, &zero_block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Offset of the current block */
|
||||||
|
curr_offset = position.lane * instance->lane_length +
|
||||||
|
position.slice * instance->segment_length + starting_index;
|
||||||
|
|
||||||
|
if (0 == curr_offset % instance->lane_length) {
|
||||||
|
/* Last block in this lane */
|
||||||
|
prev_offset = curr_offset + instance->lane_length - 1;
|
||||||
|
} else {
|
||||||
|
/* Previous block */
|
||||||
|
prev_offset = curr_offset - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = starting_index; i < instance->segment_length;
|
||||||
|
++i, ++curr_offset, ++prev_offset) {
|
||||||
|
/*1.1 Rotating prev_offset if needed */
|
||||||
|
if (curr_offset % instance->lane_length == 1) {
|
||||||
|
prev_offset = curr_offset - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.2 Computing the index of the reference block */
|
||||||
|
/* 1.2.1 Taking pseudo-random value from the previous block */
|
||||||
|
if (data_independent_addressing) {
|
||||||
|
if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) {
|
||||||
|
next_addresses(&address_block, &input_block, &zero_block);
|
||||||
|
}
|
||||||
|
pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK];
|
||||||
|
} else {
|
||||||
|
pseudo_rand = instance->memory[prev_offset].v[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.2.2 Computing the lane of the reference block */
|
||||||
|
ref_lane = ((pseudo_rand >> 32)) % instance->lanes;
|
||||||
|
|
||||||
|
if ((position.pass == 0) && (position.slice == 0)) {
|
||||||
|
/* Can not reference other lanes yet */
|
||||||
|
ref_lane = position.lane;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1.2.3 Computing the number of possible reference block within the
|
||||||
|
* lane.
|
||||||
|
*/
|
||||||
|
position.index = i;
|
||||||
|
ref_index = index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF,
|
||||||
|
ref_lane == position.lane);
|
||||||
|
|
||||||
|
/* 2 Creating a new block */
|
||||||
|
ref_block =
|
||||||
|
instance->memory + instance->lane_length * ref_lane + ref_index;
|
||||||
|
curr_block = instance->memory + curr_offset;
|
||||||
|
if (ARGON2_VERSION_10 == instance->version) {
|
||||||
|
/* version 1.2.1 and earlier: overwrite, not XOR */
|
||||||
|
fill_block(instance->memory + prev_offset, ref_block, curr_block, 0);
|
||||||
|
} else {
|
||||||
|
if(0 == position.pass) {
|
||||||
|
fill_block(instance->memory + prev_offset, ref_block,
|
||||||
|
curr_block, 0);
|
||||||
|
} else {
|
||||||
|
fill_block(instance->memory + prev_offset, ref_block,
|
||||||
|
curr_block, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
src/galois_field.cpp
Normal file
32
src/galois_field.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020 tevador <tevador@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "galois_field.hpp"
|
||||||
|
|
||||||
|
template<unsigned bits, gf_item primitive>
|
||||||
|
galois_field<bits, primitive>::galois_field() {
|
||||||
|
gf_item b = 1;
|
||||||
|
for (gf_item i = 0; i < size_; ++i) {
|
||||||
|
log_table[b] = i;
|
||||||
|
exp_table[i] = b;
|
||||||
|
b <<= 1;
|
||||||
|
if ((b & size_) != 0)
|
||||||
|
b ^= primitive;
|
||||||
|
}
|
||||||
|
for (auto i = size_ - 1; i < (2 * size_); ++i) {
|
||||||
|
exp_table[i] = exp_table[i - (size_ - 1)];
|
||||||
|
}
|
||||||
|
for (gf_item i = 2; i < size_; i++) {
|
||||||
|
for (gf_item j = 2; j < size_; j++) {
|
||||||
|
auto p = mult(i, j);
|
||||||
|
if (p == 1) {
|
||||||
|
inv_table[i] = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template class galois_field<11, 2053>;
|
47
src/galois_field.hpp
Normal file
47
src/galois_field.hpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020 tevador <tevador@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
typedef uint_least16_t gf_storage;
|
||||||
|
typedef uint_fast16_t gf_item;
|
||||||
|
|
||||||
|
template<unsigned bits, gf_item primitive>
|
||||||
|
class galois_field {
|
||||||
|
public:
|
||||||
|
galois_field();
|
||||||
|
gf_item inverse(gf_item i) const {
|
||||||
|
assert(i != 0);
|
||||||
|
return i > 1 ? inv_table[i] : 1;
|
||||||
|
}
|
||||||
|
gf_item mult(gf_item a, gf_item b) const {
|
||||||
|
if (b == 0 || a == 0)
|
||||||
|
return 0;
|
||||||
|
if (b == 1)
|
||||||
|
return a;
|
||||||
|
if (a == 1)
|
||||||
|
return b;
|
||||||
|
return exp_table[log_table[a] + log_table[b]];
|
||||||
|
}
|
||||||
|
gf_item exp(gf_item i) const {
|
||||||
|
return exp_table[i];
|
||||||
|
}
|
||||||
|
static constexpr unsigned size() {
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
static constexpr unsigned elements() {
|
||||||
|
return size_;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
static_assert(bits <= 14, "field is too large");
|
||||||
|
static constexpr gf_item size_ = 1u << bits;
|
||||||
|
gf_storage log_table[size_];
|
||||||
|
gf_storage exp_table[2 * size_];
|
||||||
|
gf_storage inv_table[size_];
|
||||||
|
};
|
||||||
|
|
||||||
|
using gf_2048 = galois_field<11, 2053>;
|
8
src/gf_elem.cpp
Normal file
8
src/gf_elem.cpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020 tevador <tevador@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gf_elem.hpp"
|
||||||
|
|
||||||
|
const gf_2048 gf_elem::field;
|
68
src/gf_elem.hpp
Normal file
68
src/gf_elem.hpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020 tevador <tevador@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "galois_field.hpp"
|
||||||
|
|
||||||
|
class gf_elem {
|
||||||
|
public:
|
||||||
|
static constexpr gf_item size() {
|
||||||
|
return gf_2048::size();
|
||||||
|
}
|
||||||
|
gf_elem() : value_(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
gf_elem(gf_item value) : value_(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
gf_elem& operator+=(gf_elem x) {
|
||||||
|
value_ ^= x.value_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
gf_elem& operator|=(gf_elem x) {
|
||||||
|
value_ |= x.value_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
gf_elem& operator-=(gf_elem x) {
|
||||||
|
value_ ^= x.value_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
gf_elem& operator*=(gf_elem x) {
|
||||||
|
value_ = field.mult(value_, x.value_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
friend gf_elem operator+(gf_elem left, gf_elem right) {
|
||||||
|
left += right;
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
friend gf_elem operator-(gf_elem left, gf_elem right) {
|
||||||
|
left -= right;
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
friend gf_elem operator*(gf_elem left, gf_elem right) {
|
||||||
|
left *= right;
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
friend bool operator==(gf_elem lhs, gf_elem rhs) {
|
||||||
|
return lhs.value_ == rhs.value_;
|
||||||
|
}
|
||||||
|
friend bool operator!=(gf_elem lhs, gf_elem rhs) {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
gf_elem& inverse() {
|
||||||
|
value_ = field.inverse(value_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
gf_elem& exp() {
|
||||||
|
value_ = field.exp(value_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
gf_item value() const {
|
||||||
|
return value_;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
static const gf_2048 field;
|
||||||
|
gf_item value_;
|
||||||
|
};
|
135
src/gf_poly.cpp
Normal file
135
src/gf_poly.cpp
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020 tevador <tevador@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gf_poly.hpp"
|
||||||
|
|
||||||
|
gf_poly::gf_poly(gf_elem coeff, unsigned degree) : degree_(0) {
|
||||||
|
coeff_[degree] = coeff;
|
||||||
|
if (coeff != 0) {
|
||||||
|
degree_ = degree;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gf_poly::gf_poly(gf_elem coeff[], unsigned degree) : degree_(0) {
|
||||||
|
assert(degree <= max_degree);
|
||||||
|
for (unsigned i = 0; i <= degree; ++i) {
|
||||||
|
coeff_[i] = coeff[i];
|
||||||
|
if (coeff_[i] != 0) {
|
||||||
|
degree_ = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gf_poly::set_degree() {
|
||||||
|
degree_ = 0;
|
||||||
|
for (unsigned i = 0; i <= max_degree; ++i) {
|
||||||
|
if (coeff_[i] != 0) {
|
||||||
|
degree_ = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gf_poly& gf_poly::operator+=(const gf_poly& x) {
|
||||||
|
auto degree = std::max(degree_, x.degree_);
|
||||||
|
degree_ = 0;
|
||||||
|
for (unsigned i = 0; i <= degree; ++i) {
|
||||||
|
coeff_[i] += x[i];
|
||||||
|
if (coeff_[i] != 0) {
|
||||||
|
degree_ = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
gf_poly& gf_poly::operator-=(const gf_poly& x) {
|
||||||
|
auto degree = std::max(degree_, x.degree_);
|
||||||
|
degree_ = 0;
|
||||||
|
for (unsigned i = 0; i <= degree; ++i) {
|
||||||
|
coeff_[i] -= x[i];
|
||||||
|
if (coeff_[i] != 0) {
|
||||||
|
degree_ = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
gf_poly& gf_poly::operator*=(gf_elem x) {
|
||||||
|
auto degree = degree_;
|
||||||
|
for (unsigned i = 0; i <= degree; ++i) {
|
||||||
|
coeff_[i] *= x;
|
||||||
|
if (coeff_[i] != 0) {
|
||||||
|
degree_ = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
gf_poly& gf_poly::operator*=(const gf_poly& x) {
|
||||||
|
gf_poly result;
|
||||||
|
for (unsigned i = 0; i <= degree_; ++i) {
|
||||||
|
for (unsigned j = 0; j <= x.degree_; ++j) {
|
||||||
|
result.coeff_[i + j] += coeff_[i] * x[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (unsigned i = 0; i <= degree_ + x.degree_; ++i) {
|
||||||
|
if (result.coeff_[i] != 0) {
|
||||||
|
result.degree_ = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
gf_elem gf_poly::operator()(gf_elem x) const {
|
||||||
|
if (x == 0) {
|
||||||
|
return coeff_[0];
|
||||||
|
}
|
||||||
|
//Horner's method
|
||||||
|
auto result = coeff_[degree_];
|
||||||
|
for (unsigned i = degree_ - 1; i < degree_; --i) {
|
||||||
|
result = result * x + coeff_[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
gf_poly gf_poly::div_rem(const gf_poly& nom, const gf_poly& x, gf_poly& rem) {
|
||||||
|
assert(!x.is_zero());
|
||||||
|
gf_poly quotient;
|
||||||
|
rem = gf_poly(nom);
|
||||||
|
gf_elem divisor_term = x[x.degree_];
|
||||||
|
divisor_term.inverse();
|
||||||
|
while (rem.degree_ >= x.degree_ && !rem.is_zero()) {
|
||||||
|
auto degree_diff = rem.degree_ - x.degree_;
|
||||||
|
auto digit = rem[rem.degree_] * divisor_term;
|
||||||
|
gf_poly mono(digit, degree_diff);
|
||||||
|
gf_poly term = x * mono;
|
||||||
|
quotient += mono;
|
||||||
|
rem -= term;
|
||||||
|
}
|
||||||
|
|
||||||
|
return quotient;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const gf_poly& poly) {
|
||||||
|
bool term = false;
|
||||||
|
for (unsigned i = poly.degree_; i <= poly.degree_; --i) {
|
||||||
|
if (poly[i] != 0 || i == 0) {
|
||||||
|
if (term)
|
||||||
|
std::cout << " + ";
|
||||||
|
if (i == 0 || poly[i] != 1) {
|
||||||
|
std::cout << poly[i].value();
|
||||||
|
}
|
||||||
|
if (i != 0) {
|
||||||
|
if (i == 1)
|
||||||
|
std::cout << "x";
|
||||||
|
else
|
||||||
|
std::cout << "x**" << i;
|
||||||
|
}
|
||||||
|
term = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
57
src/gf_poly.hpp
Normal file
57
src/gf_poly.hpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020 tevador <tevador@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "gf_elem.hpp"
|
||||||
|
#include <cassert>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class gf_poly {
|
||||||
|
public:
|
||||||
|
static constexpr size_t max_degree = 13;
|
||||||
|
gf_poly() : degree_(0) //zero polynomial
|
||||||
|
{
|
||||||
|
}
|
||||||
|
gf_poly(gf_elem coeff, unsigned degree); //monomial
|
||||||
|
gf_poly(unsigned degree) : gf_poly(1, degree)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
gf_poly(gf_elem coeff[], unsigned degree);
|
||||||
|
unsigned degree() const {
|
||||||
|
return degree_;
|
||||||
|
}
|
||||||
|
void set_degree();
|
||||||
|
void set_degree(unsigned degree) {
|
||||||
|
degree_ = degree;
|
||||||
|
}
|
||||||
|
bool is_zero() const {
|
||||||
|
return degree_ == 0 && coeff_[0] == 0;
|
||||||
|
}
|
||||||
|
gf_elem operator[](unsigned i) const {
|
||||||
|
return coeff_[i];
|
||||||
|
}
|
||||||
|
gf_elem& operator[](unsigned i) {
|
||||||
|
return coeff_[i];
|
||||||
|
}
|
||||||
|
gf_elem operator()(gf_elem x) const; //evaluate at point x
|
||||||
|
gf_poly& operator+=(const gf_poly& x);
|
||||||
|
gf_poly& operator-=(const gf_poly& x);
|
||||||
|
gf_poly& operator*=(gf_elem x);
|
||||||
|
gf_poly& operator*=(const gf_poly& x);
|
||||||
|
friend gf_poly operator*(const gf_poly& lhs, const gf_poly& rhs) {
|
||||||
|
gf_poly result(lhs);
|
||||||
|
return result *= rhs;
|
||||||
|
}
|
||||||
|
friend gf_poly operator+(const gf_poly& lhs, const gf_poly& rhs) {
|
||||||
|
gf_poly result(lhs);
|
||||||
|
return result += rhs;
|
||||||
|
}
|
||||||
|
static gf_poly div_rem(const gf_poly& nom, const gf_poly& x, gf_poly& rem);
|
||||||
|
friend std::ostream& operator<<(std::ostream& os, const gf_poly& poly);
|
||||||
|
private:
|
||||||
|
gf_elem coeff_[2 * (max_degree + 1)] = { };
|
||||||
|
unsigned degree_;
|
||||||
|
};
|
83
src/main.cpp
Normal file
83
src/main.cpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020 tevador <tevador@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "monero_seed.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
static inline void read_string_option(const char* option, int argc, char** argv, char** out) {
|
||||||
|
for (int i = 0; i < argc - 1; ++i) {
|
||||||
|
if (strcmp(argv[i], option) == 0) {
|
||||||
|
*out = argv[i + 1];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*out = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static time_t parse_date(const char* s) {
|
||||||
|
std::istringstream iss(s);
|
||||||
|
char delimiter;
|
||||||
|
int day, month, year;
|
||||||
|
if (iss >> year >> delimiter >> month >> delimiter >> day) {
|
||||||
|
struct tm t = { 0 };
|
||||||
|
t.tm_mday = day;
|
||||||
|
t.tm_mon = month - 1;
|
||||||
|
t.tm_year = year - 1900;
|
||||||
|
t.tm_isdst = -1;
|
||||||
|
|
||||||
|
time_t dt = mktime(&t);
|
||||||
|
if (dt != -1) {
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::runtime_error("invalid date");
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_seed(const monero_seed& seed, bool phrase) {
|
||||||
|
if (!seed.correction().empty()) {
|
||||||
|
std::cout << "Warning: corrected erasure: " << monero_seed::erasure << " -> " << seed.correction() << std::endl;
|
||||||
|
}
|
||||||
|
if (phrase) {
|
||||||
|
std::cout << "Mnemonic phrase: " << seed << std::endl;
|
||||||
|
}
|
||||||
|
std::cout << "- version: " << seed.version() << std::endl;
|
||||||
|
std::cout << "- private key: " << seed.key() << std::endl;
|
||||||
|
auto created_on = seed.date();
|
||||||
|
std::tm tm = *std::localtime(&created_on);
|
||||||
|
std::cout << "- created on or after: " << std::put_time(&tm, "%d/%b/%Y") << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
char* create;
|
||||||
|
char* restore;
|
||||||
|
read_string_option("--create", argc, argv, &create);
|
||||||
|
read_string_option("--restore", argc, argv, &restore);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (create != NULL) {
|
||||||
|
monero_seed seed(parse_date(create));
|
||||||
|
print_seed(seed, true);
|
||||||
|
}
|
||||||
|
else if (restore != NULL) {
|
||||||
|
monero_seed seed(restore);
|
||||||
|
print_seed(seed, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cout << "Monero 14-word mnemonic seed proof of concept" << std::endl;
|
||||||
|
std::cout << "Usage: " << std::endl;
|
||||||
|
std::cout << argv[0] << " --create <yyyy-MM-dd>" << std::endl;
|
||||||
|
std::cout << argv[0] << " --restore <14-word seed>" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception & ex) {
|
||||||
|
std::cout << "ERROR: " << ex.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
212
src/monero_seed.cpp
Normal file
212
src/monero_seed.cpp
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020 tevador <tevador@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "monero_seed.hpp"
|
||||||
|
#include "secure_random.hpp"
|
||||||
|
#include "wordlist.hpp"
|
||||||
|
#include "gf_poly.hpp"
|
||||||
|
#include "reed_solomon_code.hpp"
|
||||||
|
#include "argon2/argon2.h"
|
||||||
|
#include "argon2/blake2/blake2-impl.h"
|
||||||
|
#include <chrono>
|
||||||
|
#include <cassert>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <climits>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
const std::string monero_seed::erasure = "xxxx";
|
||||||
|
|
||||||
|
class monero_seed_exception : public std::exception {
|
||||||
|
public:
|
||||||
|
monero_seed_exception(const std::string& msg)
|
||||||
|
: msg_(msg)
|
||||||
|
{ }
|
||||||
|
~monero_seed_exception() throw() {}
|
||||||
|
|
||||||
|
const char* what() const throw() override {
|
||||||
|
return msg_.c_str();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string msg_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define THROW_EXCEPTION(message) do { \
|
||||||
|
std::ostringstream oss; \
|
||||||
|
oss << message; \
|
||||||
|
throw monero_seed_exception(oss.str()); } \
|
||||||
|
while(false)
|
||||||
|
|
||||||
|
constexpr std::time_t epoch = 1590969600; //1st June 2020
|
||||||
|
constexpr std::time_t time_step = 2629746; //30.436875 days = 1/12 of the Gregorian year
|
||||||
|
|
||||||
|
constexpr unsigned date_bits = 10;
|
||||||
|
constexpr unsigned date_mask = (1u << date_bits) - 1;
|
||||||
|
constexpr unsigned version_bits = 3;
|
||||||
|
constexpr unsigned version_mask = (1u << version_bits) - 1;
|
||||||
|
constexpr unsigned reserved_bits = 2;
|
||||||
|
constexpr unsigned reserved_mask = (1u << reserved_bits) - 1;
|
||||||
|
constexpr unsigned check_digits = 1;
|
||||||
|
constexpr unsigned checksum_size = gf_elem::size() * check_digits;
|
||||||
|
constexpr unsigned phrase_words = gf_poly::max_degree + 1;
|
||||||
|
constexpr unsigned total_bits = gf_elem::size() * phrase_words;
|
||||||
|
constexpr uint32_t argon_tcost = 3;
|
||||||
|
constexpr uint32_t argon_mcost = 256 * 1024;
|
||||||
|
|
||||||
|
static_assert(total_bits
|
||||||
|
== version_bits + date_bits + reserved_bits + checksum_size +
|
||||||
|
sizeof(monero_seed::secret_seed) * CHAR_BIT,
|
||||||
|
"Invalid mnemonic seed size");
|
||||||
|
|
||||||
|
static void write_data(gf_poly& poly, unsigned& rem_bits, unsigned value, unsigned bits) {
|
||||||
|
if (rem_bits == 0) {
|
||||||
|
poly.set_degree(poly.degree() + 1);
|
||||||
|
rem_bits = gf_elem::size();
|
||||||
|
}
|
||||||
|
unsigned digit_bits = std::min(rem_bits, bits);
|
||||||
|
unsigned rest_bits = bits - digit_bits;
|
||||||
|
rem_bits -= digit_bits;
|
||||||
|
poly[poly.degree()] |= ((value >> rest_bits) & ((1u << digit_bits) - 1)) << rem_bits;
|
||||||
|
if (rest_bits > 0) {
|
||||||
|
write_data(poly, rem_bits, value & ((1u << rest_bits) - 1), rest_bits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static void read_data(gf_poly& poly, unsigned& used_bits, T& value, unsigned bits) {
|
||||||
|
unsigned coeff_index = used_bits / gf_elem::size();
|
||||||
|
unsigned bit_index = used_bits % gf_elem::size();
|
||||||
|
unsigned digit_bits = std::min((unsigned)gf_elem::size() - bit_index, bits);
|
||||||
|
unsigned rem_bits = gf_elem::size() - bit_index - digit_bits;
|
||||||
|
unsigned rest_bits = bits - digit_bits;
|
||||||
|
value |= ((poly[coeff_index].value() >> rem_bits) & ((1u << bits) - 1)) << rest_bits;
|
||||||
|
used_bits += digit_bits;
|
||||||
|
if (rest_bits > 0) {
|
||||||
|
read_data(poly, used_bits, value, rest_bits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const reed_solomon_code rs(check_digits);
|
||||||
|
|
||||||
|
monero_seed::monero_seed(std::time_t date_created) {
|
||||||
|
if (date_created < epoch) {
|
||||||
|
THROW_EXCEPTION("date_created must not be before 1st June 2020");
|
||||||
|
}
|
||||||
|
unsigned quantized_date = ((date_created - epoch) / time_step) & date_mask;
|
||||||
|
date_ = epoch + quantized_date * time_step;
|
||||||
|
version_ = 0;
|
||||||
|
reserved_ = 0;
|
||||||
|
secure_random::gen_bytes(seed_.data(), seed_.size());
|
||||||
|
|
||||||
|
char salt[25] = "Monero 14-word seed";
|
||||||
|
salt[20] = version_;
|
||||||
|
store32(salt + 21, quantized_date);
|
||||||
|
argon2id_hash_raw(argon_tcost, argon_mcost, 1, seed_.data(), seed_.size(), salt, sizeof(salt), key_.data(), key_.size());
|
||||||
|
|
||||||
|
unsigned rem_bits = gf_elem::size();
|
||||||
|
write_data(message_, rem_bits, version_, version_bits);
|
||||||
|
write_data(message_, rem_bits, reserved_, reserved_bits);
|
||||||
|
write_data(message_, rem_bits, quantized_date, date_bits);
|
||||||
|
for (auto byte : seed_) {
|
||||||
|
write_data(message_, rem_bits, byte, CHAR_BIT);
|
||||||
|
}
|
||||||
|
assert(rem_bits == 0);
|
||||||
|
rs.encode(message_);
|
||||||
|
}
|
||||||
|
|
||||||
|
monero_seed::monero_seed(const std::string& phrase) {
|
||||||
|
int word_count = 0;
|
||||||
|
size_t offset = 0;
|
||||||
|
int error = -1;
|
||||||
|
std::string words[phrase_words];
|
||||||
|
do {
|
||||||
|
size_t delim = phrase.find(' ', offset);
|
||||||
|
if (delim == std::string::npos) {
|
||||||
|
delim = phrase.size();
|
||||||
|
}
|
||||||
|
words[word_count] = phrase.substr(offset, delim - offset);
|
||||||
|
auto index = wordlist::english.parse(words[word_count]);
|
||||||
|
if (index == -1) {
|
||||||
|
if (words[word_count] != erasure) {
|
||||||
|
THROW_EXCEPTION("unrecognized word: '" << words[word_count] << "'");
|
||||||
|
}
|
||||||
|
if (error >= 0) {
|
||||||
|
THROW_EXCEPTION("teo or more erasures cannot be corrected");
|
||||||
|
}
|
||||||
|
error = word_count;
|
||||||
|
}
|
||||||
|
message_[word_count] = index;
|
||||||
|
word_count++;
|
||||||
|
offset = delim + 1;
|
||||||
|
} while (offset < phrase.size());
|
||||||
|
|
||||||
|
if (word_count != phrase_words) {
|
||||||
|
THROW_EXCEPTION("the mnemonic phrase must consist of " << phrase_words << " words");
|
||||||
|
}
|
||||||
|
|
||||||
|
message_.set_degree();
|
||||||
|
|
||||||
|
if (error >= 0) {
|
||||||
|
for (unsigned i = 0; i < gf_2048::elements(); ++i) {
|
||||||
|
message_[error] = i;
|
||||||
|
if (rs.check(message_)) {
|
||||||
|
correction_ = wordlist::english.get_word(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(!correction_.empty());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!rs.check(message_)) {
|
||||||
|
THROW_EXCEPTION("phrase is invalid (checksum mismatch)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned used_bits = checksum_size;
|
||||||
|
unsigned quantized_date;
|
||||||
|
|
||||||
|
version_ = 0;
|
||||||
|
reserved_ = 0;
|
||||||
|
quantized_date = 0;
|
||||||
|
memset(seed_.data(), 0, seed_.size());
|
||||||
|
|
||||||
|
read_data(message_, used_bits, version_, version_bits);
|
||||||
|
read_data(message_, used_bits, reserved_, reserved_bits);
|
||||||
|
read_data(message_, used_bits, quantized_date, date_bits);
|
||||||
|
|
||||||
|
for (uint8_t& byte : seed_) {
|
||||||
|
read_data(message_, used_bits, byte, CHAR_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(used_bits == total_bits);
|
||||||
|
|
||||||
|
date_ = epoch + quantized_date * time_step;
|
||||||
|
|
||||||
|
char salt[25] = "Monero 14-word seed";
|
||||||
|
salt[20] = version_;
|
||||||
|
store32(salt + 21, quantized_date);
|
||||||
|
argon2id_hash_raw(argon_tcost, argon_mcost, 1, seed_.data(), seed_.size(), salt, sizeof(salt), key_.data(), key_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const monero_seed& seed) {
|
||||||
|
for (int i = 0; i <= seed.message_.degree(); ++i) {
|
||||||
|
if (i > 0) {
|
||||||
|
os << " ";
|
||||||
|
}
|
||||||
|
os << wordlist::english.get_word(seed.message_[i].value());
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const monero_seed::secret_key& key) {
|
||||||
|
os << std::hex;
|
||||||
|
for (int i = 0; i < key.size(); ++i) {
|
||||||
|
os << std::setw(2) << std::setfill('0') << (unsigned)key[i];
|
||||||
|
}
|
||||||
|
os << std::dec;
|
||||||
|
return os;
|
||||||
|
}
|
46
src/monero_seed.hpp
Normal file
46
src/monero_seed.hpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020 tevador <tevador@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <ctime>
|
||||||
|
#include "gf_poly.hpp"
|
||||||
|
|
||||||
|
class monero_seed {
|
||||||
|
public:
|
||||||
|
static const std::string erasure;
|
||||||
|
static constexpr size_t size = 16;
|
||||||
|
static constexpr size_t key_size = 32;
|
||||||
|
using secret_key = std::array<uint8_t, key_size>;
|
||||||
|
using secret_seed = std::array<uint8_t, size>;
|
||||||
|
monero_seed(const std::string& phrase);
|
||||||
|
monero_seed(std::time_t date_created);
|
||||||
|
std::time_t date() const {
|
||||||
|
return date_;
|
||||||
|
}
|
||||||
|
unsigned version() const {
|
||||||
|
return version_ + 1;
|
||||||
|
}
|
||||||
|
const std::string& correction() const {
|
||||||
|
return correction_;
|
||||||
|
}
|
||||||
|
const secret_key& key() const {
|
||||||
|
return key_;
|
||||||
|
}
|
||||||
|
friend std::ostream& operator<<(std::ostream& os, const monero_seed& seed);
|
||||||
|
private:
|
||||||
|
secret_seed seed_;
|
||||||
|
secret_key key_;
|
||||||
|
std::time_t date_;
|
||||||
|
unsigned version_;
|
||||||
|
unsigned reserved_;
|
||||||
|
std::string correction_;
|
||||||
|
gf_poly message_;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const monero_seed::secret_key& key);
|
36
src/reed_solomon_code.cpp
Normal file
36
src/reed_solomon_code.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020 tevador <tevador@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "reed_solomon_code.hpp"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
reed_solomon_code::reed_solomon_code(unsigned check_digits) : generator(1, 0) {
|
||||||
|
for (unsigned i = 0; i < check_digits; ++i) {
|
||||||
|
gf_poly binom = gf_poly(1, 1);
|
||||||
|
binom[0] = gf_elem(i + 1).exp();
|
||||||
|
generator *= binom;
|
||||||
|
}
|
||||||
|
assert(generator.degree() == check_digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reed_solomon_code::encode(gf_poly& data) const {
|
||||||
|
data *= gf_poly(generator.degree());
|
||||||
|
gf_poly rem;
|
||||||
|
gf_poly::div_rem(data, generator, rem);
|
||||||
|
data -= rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reed_solomon_code::check(const gf_poly& message) const {
|
||||||
|
auto syndrome = get_syndrome(message);
|
||||||
|
return syndrome.is_zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
gf_poly reed_solomon_code::get_syndrome(const gf_poly& message) const {
|
||||||
|
gf_poly syndrome;
|
||||||
|
for (unsigned i = 1; i <= generator.degree(); ++i) {
|
||||||
|
syndrome[i - 1] = message(gf_elem(i).exp());
|
||||||
|
}
|
||||||
|
return syndrome;
|
||||||
|
}
|
17
src/reed_solomon_code.hpp
Normal file
17
src/reed_solomon_code.hpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020 tevador <tevador@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "gf_poly.hpp"
|
||||||
|
|
||||||
|
class reed_solomon_code {
|
||||||
|
public:
|
||||||
|
reed_solomon_code(unsigned check_digits);
|
||||||
|
void encode(gf_poly& data) const;
|
||||||
|
bool check(const gf_poly& message) const;
|
||||||
|
private:
|
||||||
|
gf_poly get_syndrome(const gf_poly& message) const;
|
||||||
|
gf_poly generator;
|
||||||
|
};
|
82
src/secure_random.cpp
Normal file
82
src/secure_random.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020 tevador <tevador@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "secure_random.hpp"
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||||
|
#define WINAPI
|
||||||
|
#include <windows.h>
|
||||||
|
#include <ntsecapi.h>
|
||||||
|
#elif defined __linux__ && defined __GLIBC__
|
||||||
|
#define STRINGIFY(x) #x
|
||||||
|
#define STR(x) STRINGIFY(x)
|
||||||
|
#if __GLIBC__ > 2 || __GLIBC_MINOR__ > 24
|
||||||
|
#define LINUX_GETENTROPY
|
||||||
|
#include <sys/random.h>
|
||||||
|
#else
|
||||||
|
#pragma message("Warning: getentropy is not available in GLIBC " \
|
||||||
|
STR(__GLIBC__) "." STR(__GLIBC_MINOR__))
|
||||||
|
#define UNIX_FALLBACK
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#if defined(SYS_getrandom)
|
||||||
|
#define LINUX_TRY_SYSCALL
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#else
|
||||||
|
#pragma message("Warning: Kernel doesn't support SYS_getrandom")
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define UNIX_FALLBACK
|
||||||
|
#endif
|
||||||
|
#if defined(UNIX_FALLBACK)
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#define RANDOM_FILE "/dev/urandom"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void secure_random::gen_bytes(void* output, size_t size) {
|
||||||
|
#if defined(WINAPI)
|
||||||
|
if (!RtlGenRandom(output, size)) {
|
||||||
|
throw std::runtime_error("RtlGenRandom failed");
|
||||||
|
}
|
||||||
|
#elif defined(LINUX_GETENTROPY)
|
||||||
|
if (-1 == getentropy(output, size)) {
|
||||||
|
throw std::runtime_error("getentropy failed");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#if defined(LINUX_TRY_SYSCALL)
|
||||||
|
if (size <= 256) {
|
||||||
|
if (0 == syscall(SYS_getrandom, output, size, 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
int fd = open(RANDOM_FILE, O_RDONLY);
|
||||||
|
if (fd == -1) {
|
||||||
|
throw std::runtime_error("Unable to open " RANDOM_FILE);
|
||||||
|
}
|
||||||
|
char* outptr = (char*)output;
|
||||||
|
while (size) {
|
||||||
|
ssize_t len = read(fd, outptr, size);
|
||||||
|
if (len < 0) {
|
||||||
|
if (errno != EINTR && errno != EAGAIN) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
outptr += len;
|
||||||
|
size -= len;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
if (size) {
|
||||||
|
throw std::runtime_error("Unable to read " RANDOM_FILE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
14
src/secure_random.hpp
Normal file
14
src/secure_random.hpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020 tevador <tevador@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class secure_random {
|
||||||
|
public:
|
||||||
|
static void gen_bytes(void* output, size_t size);
|
||||||
|
};
|
||||||
|
|
2081
src/wordlist.cpp
Normal file
2081
src/wordlist.cpp
Normal file
File diff suppressed because it is too large
Load Diff
25
src/wordlist.hpp
Normal file
25
src/wordlist.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020 tevador <tevador@gmail.com>
|
||||||
|
All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
class wordlist {
|
||||||
|
public:
|
||||||
|
static constexpr size_t size = 2048;
|
||||||
|
static const wordlist english;
|
||||||
|
const std::string& get_word(unsigned i) const {
|
||||||
|
assert(i < size);
|
||||||
|
return values_[i];
|
||||||
|
}
|
||||||
|
int parse(const std::string& word) const;
|
||||||
|
private:
|
||||||
|
wordlist(const std::string(&values)[size]) : values_(values)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
const std::string(&values_)[size];
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user