]> Nutra Git (v2) - gamesguru/feather.git/commitdiff
Vendor monero-seed
authortobtoht <thotbot@protonmail.com>
Mon, 7 Feb 2022 20:03:05 +0000 (21:03 +0100)
committertobtoht <thotbot@protonmail.com>
Mon, 7 Feb 2022 20:03:05 +0000 (21:03 +0100)
34 files changed:
CMakeLists.txt
Dockerfile.linux
Dockerfile.windows
contrib/monero-seed/CMakeLists.txt [new file with mode: 0644]
contrib/monero-seed/LICENSE.txt [new file with mode: 0644]
contrib/monero-seed/README.md [new file with mode: 0644]
contrib/monero-seed/include/monero_seed/galois_field.hpp [new file with mode: 0644]
contrib/monero-seed/include/monero_seed/gf_elem.hpp [new file with mode: 0644]
contrib/monero-seed/include/monero_seed/gf_poly.hpp [new file with mode: 0644]
contrib/monero-seed/include/monero_seed/monero_seed.hpp [new file with mode: 0644]
contrib/monero-seed/include/monero_seed/reed_solomon_code.hpp [new file with mode: 0644]
contrib/monero-seed/include/monero_seed/secure_random.hpp [new file with mode: 0644]
contrib/monero-seed/include/monero_seed/wordlist.hpp [new file with mode: 0644]
contrib/monero-seed/src/argon2/argon2.c [new file with mode: 0644]
contrib/monero-seed/src/argon2/argon2.h [new file with mode: 0644]
contrib/monero-seed/src/argon2/blake2/blake2-impl.h [new file with mode: 0644]
contrib/monero-seed/src/argon2/blake2/blake2.h [new file with mode: 0644]
contrib/monero-seed/src/argon2/blake2/blake2b.c [new file with mode: 0644]
contrib/monero-seed/src/argon2/blake2/blamka-round-ref.h [new file with mode: 0644]
contrib/monero-seed/src/argon2/core.c [new file with mode: 0644]
contrib/monero-seed/src/argon2/core.h [new file with mode: 0644]
contrib/monero-seed/src/argon2/ref.c [new file with mode: 0644]
contrib/monero-seed/src/galois_field.cpp [new file with mode: 0644]
contrib/monero-seed/src/gf_elem.cpp [new file with mode: 0644]
contrib/monero-seed/src/gf_poly.cpp [new file with mode: 0644]
contrib/monero-seed/src/main.cpp [new file with mode: 0644]
contrib/monero-seed/src/monero_seed.cpp [new file with mode: 0644]
contrib/monero-seed/src/pbkdf2.c [new file with mode: 0644]
contrib/monero-seed/src/pbkdf2.h [new file with mode: 0644]
contrib/monero-seed/src/reed_solomon_code.cpp [new file with mode: 0644]
contrib/monero-seed/src/secure_random.cpp [new file with mode: 0644]
contrib/monero-seed/src/sha256/hash_impl.h [new file with mode: 0644]
contrib/monero-seed/src/wordlist.cpp [new file with mode: 0644]
src/CMakeLists.txt

index 768f0d963b01c06ba2d83404966b99dfb536fd70..7b95e66847fd58555f19224e9117f1390e270011 100644 (file)
@@ -106,22 +106,7 @@ message(STATUS "libzbar: include dir at ${ZBAR_INCLUDE_DIR}")
 message(STATUS "libzbar: libraries at ${ZBAR_LIBRARIES}")
 
 # Tevador 14 word Monero seed
-find_package(monero-seed CONFIG)
-if(NOT monero-seed_FOUND)
-  if(FETCH_DEPS)
-    FetchContent_Declare(monero-seed
-      GIT_REPOSITORY https://github.com/feather-wallet/monero-seed.git)
-    FetchContent_GetProperties(monero-seed)
-    if(NOT monero-seed_POPULATED)
-      message(STATUS "Fetching monero-seed")
-      FetchContent_Populate(monero-seed)
-      add_subdirectory(${monero-seed_SOURCE_DIR} ${monero-seed_BINARY_DIR})
-    endif()
-    add_library(monero-seed::monero-seed ALIAS monero-seed)
-  else()
-    message(FATAL_ERROR "monero-seed was not installed and fetching deps is disabled")
-  endif()
-endif()
+add_subdirectory(contrib/monero-seed)
 
 # libzip
 find_package(zlib CONFIG)
index a91fe4d41a6e67ceba8020c58b2784bcfab1b2d6..e897c56021eabf2c6a3ed21e5ed178c2f44e89a0 100644 (file)
@@ -265,18 +265,6 @@ RUN git clone -b v4.1.1 --depth 1 https://github.com/fukuchi/libqrencode.git &&
     make -j$THREADS install && \
     rm -rf $(pwd)
 
-# monero-seed: Required for Feather
-# Tevador's 14 word seed library
-ADD contrib/monero-seed.patch .
-RUN git clone https://github.com/feather-wallet/feather.git && \
-    cd monero-seed && \
-    git reset --hard 4674ef09b6faa6fe602ab5ae0b9ca8e1fd7d5e1b && \
-    git apply /monero-seed.patch && \
-    cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && \
-    make -Cbuild -j$THREADS && \
-    make -Cbuild install && \
-    rm -rf $(pwd)
-
 # libzip: Required for Feather
 # Used to unzip updates downloaded by the built-in updater
 RUN git clone -b v1.7.3 --depth 1 https://github.com/nih-at/libzip.git && \
index 52e0b17716e85c7ffdbf974440bc68e757a293c7..6588bd8e8deae0f05104e9be2030e7b4893cacd0 100644 (file)
@@ -180,16 +180,6 @@ RUN git clone -b tor-$TOR_VERSION --depth 1 https://git.torproject.org/tor.git &
     rm -rf $(pwd) && \
     strip -s -D /usr/local/tor/bin/tor.exe
 
-RUN git clone https://github.com/feather-wallet/feather.git && \
-    cd monero-seed && \
-    git reset --hard 4674ef09b6faa6fe602ab5ae0b9ca8e1fd7d5e1b && \
-    cmake -DCMAKE_INSTALL_PREFIX=/depends/x86_64-w64-mingw32 \
-          -DCMAKE_BUILD_TYPE=Release \
-          -DCMAKE_TOOLCHAIN_FILE=/depends/x86_64-w64-mingw32/share/toolchain.cmake -Bbuild && \
-    make -Cbuild -j$THREADS && \
-    make -Cbuild install && \
-    rm -rf $(pwd)
-
 RUN git clone https://github.com/nih-at/libzip.git && \
     cd libzip && \
     git reset --hard 66e496489bdae81bfda8b0088172871d8fda0032 && \
diff --git a/contrib/monero-seed/CMakeLists.txt b/contrib/monero-seed/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2028ceb
--- /dev/null
@@ -0,0 +1,50 @@
+# Copyright (c) 2020, tevador <tevador@gmail.com>
+
+cmake_minimum_required(VERSION 3.13)
+
+option(MONERO_SEED_DEMO "Build a demo executable for monero-seed")
+
+if(NOT CMAKE_BUILD_TYPE)
+  set(CMAKE_BUILD_TYPE Release)
+  message(STATUS "Setting default build type: ${CMAKE_BUILD_TYPE}")
+endif()
+
+project(monero-seed)
+
+include(GNUInstallDirs)
+
+add_library(${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/monero_seed.cpp
+src/pbkdf2.c
+src/reed_solomon_code.cpp
+src/secure_random.cpp
+src/wordlist.cpp)
+
+set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 11)
+target_include_directories(${PROJECT_NAME} PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/monero_seed>)
+
+if(MONERO_SEED_DEMO)
+  add_executable(demo src/main.cpp)
+  set_property(TARGET demo PROPERTY CXX_STANDARD 11)
+  target_link_libraries(demo -Wl,--whole-archive ${PROJECT_NAME} -Wl,--no-whole-archive)
+endif()
+
+install(TARGETS ${PROJECT_NAME}
+  EXPORT ${PROJECT_NAME}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
+install(DIRECTORY include/
+  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+install(EXPORT ${PROJECT_NAME}
+  FILE ${PROJECT_NAME}Config.cmake
+  NAMESPACE ${PROJECT_NAME}::
+  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
diff --git a/contrib/monero-seed/LICENSE.txt b/contrib/monero-seed/LICENSE.txt
new file mode 100644 (file)
index 0000000..62df1b5
--- /dev/null
@@ -0,0 +1,19 @@
+Copyright (c) 2020 tevador <tevador@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/contrib/monero-seed/README.md b/contrib/monero-seed/README.md
new file mode 100644 (file)
index 0000000..d44196e
--- /dev/null
@@ -0,0 +1,108 @@
+## Build
+```
+git clone https://github.com/tevador/monero-seed.git
+cd monero-seed
+mkdir build && cd build
+cmake ..
+make
+```
+
+## Features
+
+* embedded wallet birthday to optimize restoring from the seed (only blocks after the wallet birthday have to be scanned for transactions)
+* 5 bits reserved for future updates
+* advanced checksum based on Reed-Solomon linear code, which allows certain types of errors to be detected without false positives and provides limited error correction capability
+* built-in way to make seeds incompatible between different coins, e.g. a seed for Aeon cannot be accidentally used to restore a Monero wallet
+
+## Usage
+
+### Create a new seed
+
+```
+> ./monero-seed --create [--date <yyyy-MM-dd>] [--coin <monero|aeon>]
+```
+
+Example:
+```
+> ./monero-seed --create --date 2100/03/14 --coin monero
+Mnemonic phrase: test park taste security oxygen decorate essence ridge ship fish vehicle dream fluid pattern
+- coin: monero
+- private key: 7b816d8134e29393b0333eed4b6ed6edf97c156ad139055a706a6fb9599dcf8c
+- created on or after: 02/Mar/2100
+```
+
+### Restore seed
+```
+./monero-seed --restore "<14-word seed>" [--coin <monero|aeon>]
+```
+
+Example:
+
+```
+> ./monero-seed --restore "test park taste security oxygen decorate essence ridge ship fish vehicle dream fluid pattern" --coin monero
+- coin: monero
+- private key: 7b816d8134e29393b0333eed4b6ed6edf97c156ad139055a706a6fb9599dcf8c
+- created on or after: 02/Mar/2100
+```
+
+Attempting to restore the same seed under a different coin will fail:
+```
+> ./monero-seed --restore "test park taste security oxygen decorate essence ridge ship fish vehicle dream fluid pattern" --coin aeon
+ERROR: phrase is invalid (checksum mismatch)
+```
+
+Restore has limited error correction capability, namely it can correct a single erasure (illegible word with a known location).
+This can be tested by replacing a word with `xxxx`:
+
+```
+> ./monero-seed --restore "test park xxxx security oxygen decorate essence ridge ship fish vehicle dream fluid pattern" --coin monero
+Warning: corrected erasure: xxxx -> taste
+- coin: monero
+- private key: 7b816d8134e29393b0333eed4b6ed6edf97c156ad139055a706a6fb9599dcf8c
+- created on or after: 02/Mar/2100
+```
+
+## Implementation details
+
+The mnemonic phrase contains 154 bits of data, which are used as follows:
+
+* 5 bits reserved for future use
+* 10 bits for approximate wallet birthday
+* 128 bits for the private key seed
+* 11 bits for checksum
+
+### Wordlist
+
+The mnemonic phrase uses the BIP-39 wordlist, which 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. The wordlist is available for 9 languages (this repository only uses the English list).
+
+### Reserved bits
+
+There are 5 reserved bits for future use. Possible use cases for the reserved bits include:
+
+* a flag to differentiate between normal and "short" address format (with view key equal to the spend key)
+* different KDF algorithms for generating the private key
+* seed encrypted with a passphrase
+
+Backwards compatibility is achieved under these two conditions:
+
+1. Reserved (unused) bits are required to be 0. The software should return an error otherwise.
+2. When defining a new feature bit, 0 should be the previous behavior.
+
+### Wallet birthday
+
+The mnemonic phrase stores the approximate date 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 birthday 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 derived from the 128-bit seed using PBKDF2-HMAC-SHA256 with 4096 iterations.The wallet birthday and the 5 reserved/feature bits are used as a salt. 128-bit seed provides the same level of security as the elliptic curve used by Monero.
+
+Future extensions may define other KDFs.
+
+### Checksum
+
+The mnemonic phrase can be treated as a polynomial over GF(2048), which allows us to use an efficient Reed-Solomon error correction code with one check word. All single-word errors can be detected and all single-word erasures can be corrected without false positives.
+
+To prevent the seed from being accidentally used with a different cryptocurrency, a coin-specific value is subtracted from the first data-word after the checksum is calculated. Checksum validation will fail unless the wallet software adds the same value back to the first data-word when restoring.
diff --git a/contrib/monero-seed/include/monero_seed/galois_field.hpp b/contrib/monero-seed/include/monero_seed/galois_field.hpp
new file mode 100644 (file)
index 0000000..20d9850
--- /dev/null
@@ -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>;
diff --git a/contrib/monero-seed/include/monero_seed/gf_elem.hpp b/contrib/monero-seed/include/monero_seed/gf_elem.hpp
new file mode 100644 (file)
index 0000000..af68197
--- /dev/null
@@ -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();
+    }
+    constexpr gf_elem() : value_(0)
+    {
+    }
+    constexpr 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_;
+};
diff --git a/contrib/monero-seed/include/monero_seed/gf_poly.hpp b/contrib/monero-seed/include/monero_seed/gf_poly.hpp
new file mode 100644 (file)
index 0000000..42caa9a
--- /dev/null
@@ -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_;
+};
diff --git a/contrib/monero-seed/include/monero_seed/monero_seed.hpp b/contrib/monero-seed/include/monero_seed/monero_seed.hpp
new file mode 100644 (file)
index 0000000..33c8d6a
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+       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, const std::string& coin);
+       monero_seed(std::time_t date_created, const std::string& coin);
+       std::time_t date() const {
+               return date_;
+       }
+       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 reserved_;
+       std::string correction_;
+       gf_poly message_;
+};
+
+std::ostream& operator<<(std::ostream& os, const monero_seed::secret_key& key);
diff --git a/contrib/monero-seed/include/monero_seed/reed_solomon_code.hpp b/contrib/monero-seed/include/monero_seed/reed_solomon_code.hpp
new file mode 100644 (file)
index 0000000..7ea3a18
--- /dev/null
@@ -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;
+};
diff --git a/contrib/monero-seed/include/monero_seed/secure_random.hpp b/contrib/monero-seed/include/monero_seed/secure_random.hpp
new file mode 100644 (file)
index 0000000..3039202
--- /dev/null
@@ -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);
+};
+
diff --git a/contrib/monero-seed/include/monero_seed/wordlist.hpp b/contrib/monero-seed/include/monero_seed/wordlist.hpp
new file mode 100644 (file)
index 0000000..8b622fe
--- /dev/null
@@ -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];
+};
diff --git a/contrib/monero-seed/src/argon2/argon2.c b/contrib/monero-seed/src/argon2/argon2.c
new file mode 100644 (file)
index 0000000..e9882b7
--- /dev/null
@@ -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";
+    }
+}
diff --git a/contrib/monero-seed/src/argon2/argon2.h b/contrib/monero-seed/src/argon2/argon2.h
new file mode 100644 (file)
index 0000000..1b471f6
--- /dev/null
@@ -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
diff --git a/contrib/monero-seed/src/argon2/blake2/blake2-impl.h b/contrib/monero-seed/src/argon2/blake2/blake2-impl.h
new file mode 100644 (file)
index 0000000..241f0be
--- /dev/null
@@ -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
diff --git a/contrib/monero-seed/src/argon2/blake2/blake2.h b/contrib/monero-seed/src/argon2/blake2/blake2.h
new file mode 100644 (file)
index 0000000..469e8fe
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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)
+};
+
+#define blake2b_init        moneroseed_blake2b_init
+#define blake2b_init_key    moneroseed_blake2b_init_key
+#define blake2b_init_param  moneroseed_blake2b_init_param
+#define blake2b_update      moneroseed_blake2b_update
+#define blake2b_final       moneroseed_blake2b_final
+#define blake2b             moneroseed_blake2b
+#define blake2b_long        moneroseed_blake2b_long
+
+/* 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
diff --git a/contrib/monero-seed/src/argon2/blake2/blake2b.c b/contrib/monero-seed/src/argon2/blake2/blake2b.c
new file mode 100644 (file)
index 0000000..ca05df5
--- /dev/null
@@ -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 */
diff --git a/contrib/monero-seed/src/argon2/blake2/blamka-round-ref.h b/contrib/monero-seed/src/argon2/blake2/blamka-round-ref.h
new file mode 100644 (file)
index 0000000..b8f2cf4
--- /dev/null
@@ -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
diff --git a/contrib/monero-seed/src/argon2/core.c b/contrib/monero-seed/src/argon2/core.c
new file mode 100644 (file)
index 0000000..5eafe08
--- /dev/null
@@ -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;
+}
diff --git a/contrib/monero-seed/src/argon2/core.h b/contrib/monero-seed/src/argon2/core.h
new file mode 100644 (file)
index 0000000..e569eb4
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * 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********************************/
+
+#define finalize           moneroseed_finalize
+#define initialize         moneroseed_initialize
+#define validate_inputs    moneroseed_validate_inputs
+#define fill_first_blocks  moneroseed_fill_first_blocks
+#define initial_hash       moneroseed_initial_hash
+#define fill_memory_blocks moneroseed_fill_memory_blocks
+
+/* 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
diff --git a/contrib/monero-seed/src/argon2/ref.c b/contrib/monero-seed/src/argon2/ref.c
new file mode 100644 (file)
index 0000000..ad1cf46
--- /dev/null
@@ -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);
+            }
+        }
+    }
+}
diff --git a/contrib/monero-seed/src/galois_field.cpp b/contrib/monero-seed/src/galois_field.cpp
new file mode 100644 (file)
index 0000000..675dd26
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+       Copyright (c) 2020 tevador <tevador@gmail.com>
+       All rights reserved.
+*/
+
+#include <monero_seed/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>;
diff --git a/contrib/monero-seed/src/gf_elem.cpp b/contrib/monero-seed/src/gf_elem.cpp
new file mode 100644 (file)
index 0000000..6f2a69b
--- /dev/null
@@ -0,0 +1,8 @@
+/*
+       Copyright (c) 2020 tevador <tevador@gmail.com>
+       All rights reserved.
+*/
+
+#include <monero_seed/gf_elem.hpp>
+
+const gf_2048 gf_elem::field;
diff --git a/contrib/monero-seed/src/gf_poly.cpp b/contrib/monero-seed/src/gf_poly.cpp
new file mode 100644 (file)
index 0000000..cc8af39
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+       Copyright (c) 2020 tevador <tevador@gmail.com>
+       All rights reserved.
+*/
+
+#include <monero_seed/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;
+}
diff --git a/contrib/monero-seed/src/main.cpp b/contrib/monero-seed/src/main.cpp
new file mode 100644 (file)
index 0000000..577b40c
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+       Copyright (c) 2020 tevador <tevador@gmail.com>
+       All rights reserved.
+*/
+
+#include <monero_seed/monero_seed.hpp>
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <stdexcept>
+#include <cstring>
+
+static inline void read_string_option(const char* option, int argc,
+       const char** argv, const char** out, const char* def_val = nullptr) {
+       for (int i = 0; i < argc - 1; ++i) {
+               if (strcmp(argv[i], option) == 0) {
+                       *out = argv[i + 1];
+                       return;
+               }
+       }
+       *out = def_val;
+}
+
+static inline void read_option(const char* option, int argc, const char** argv,
+       bool& out) {
+       for (int i = 0; i < argc; ++i) {
+               if (strcmp(argv[i], option) == 0) {
+                       out = true;
+                       return;
+               }
+       }
+       out = false;
+}
+
+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, const char* coin, 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 << "- coin: " << coin << 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, const char* argv[]) {
+       bool create;
+       const char* create_date;
+       const char* coin;
+       const char* restore;
+       read_option("--create", argc, argv, create);
+       read_string_option("--date", argc, argv, &create_date);
+       read_string_option("--coin", argc, argv, &coin, "monero");
+       read_string_option("--restore", argc, argv, &restore);
+
+       try {
+               if (create) {
+                       time_t time;
+                       if (create_date != nullptr) {
+                               time = parse_date(create_date);
+                       }
+                       else {
+                               time = std::time(nullptr);
+                       }
+                       monero_seed seed(time, coin);
+                       print_seed(seed, coin, true);
+               }
+               else if (restore != nullptr) {
+                       monero_seed seed(restore, coin);
+                       print_seed(seed, coin, false);
+               }
+               else {
+                       std::cout << "Monero 14-word mnemonic seed proof of concept" << std::endl;
+                       std::cout << "Usage: " << std::endl;
+                       std::cout << argv[0] << " --create [--date <yyyy-MM-dd>] [--coin <monero|aeon|wownero>]" << std::endl;
+                       std::cout << argv[0] << " --restore \"<14-word seed>\" [--coin <monero|aeon|wownero>]" << std::endl;
+               }
+       }
+       catch (const std::exception & ex) {
+               std::cout << "ERROR: " << ex.what() << std::endl;
+               return 1;
+       }
+       return 0;
+}
diff --git a/contrib/monero-seed/src/monero_seed.cpp b/contrib/monero-seed/src/monero_seed.cpp
new file mode 100644 (file)
index 0000000..720c06c
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+       Copyright (c) 2020 tevador <tevador@gmail.com>
+       All rights reserved.
+*/
+
+#include <monero_seed/monero_seed.hpp>
+#include <monero_seed/secure_random.hpp>
+#include <monero_seed/wordlist.hpp>
+#include <monero_seed/gf_poly.hpp>
+#include <monero_seed/reed_solomon_code.hpp>
+#include "argon2/argon2.h"
+#include "argon2/blake2/blake2-impl.h"
+#include "pbkdf2.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 reserved_bits = 5;
+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;
+constexpr int pbkdf2_iterations = 4096;
+
+static const std::string COIN_MONERO = "monero";
+static const std::string COIN_AEON = "aeon";
+static const std::string COIN_WOWNERO = "wownero";
+
+constexpr gf_elem monero_flag = gf_elem(0x539);
+constexpr gf_elem aeon_flag = gf_elem(0x201);
+constexpr gf_elem wownero_flag = gf_elem(0x1a4);
+
+static const char* KDF_PBKDF2 = "PBKDF2-HMAC-SHA256/4096";
+
+static_assert(total_bits
+       == reserved_bits + date_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 << digit_bits) - 1)) << rest_bits;
+       used_bits += digit_bits;
+       if (rest_bits > 0) {
+               read_data(poly, used_bits, value, rest_bits);
+       }
+}
+
+static gf_elem get_coin_flag(const std::string& coin) {
+       if (coin == COIN_MONERO) {
+               return monero_flag;
+       }
+       else if (coin == COIN_AEON) {
+               return aeon_flag;
+       }
+       else if (coin == COIN_WOWNERO) {
+               return wownero_flag;
+       }
+       else {
+               THROW_EXCEPTION("invalid coin");
+       }
+}
+
+static const reed_solomon_code rs(check_digits);
+
+monero_seed::monero_seed(std::time_t date_created, const std::string& coin) {
+       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;
+       gf_elem coin_flag = get_coin_flag(coin);
+       reserved_ = 0;
+       secure_random::gen_bytes(seed_.data(), seed_.size());
+       uint8_t salt[25] = "Monero 14-word seed";
+       salt[20] = reserved_;
+       store32(salt + 21, quantized_date);
+       //argon2id_hash_raw(argon_tcost, argon_mcost, 1, seed_.data(), seed_.size(), salt, sizeof(salt), key_.data(), key_.size());
+       pbkdf2_hmac_sha256(seed_.data(), seed_.size(), salt, sizeof(salt), pbkdf2_iterations, key_.data(), key_.size());
+       unsigned rem_bits = gf_elem::size();
+       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_);
+       message_[check_digits] -= coin_flag;
+}
+
+monero_seed::monero_seed(const std::string& phrase, const std::string& coin) {
+       gf_elem coin_flag = get_coin_flag(coin);
+       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("two 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;
+                       message_[check_digits] += coin_flag;
+                       if (rs.check(message_)) {
+                               correction_ = wordlist::english.get_word(i);
+                               break;
+                       }
+                       message_[check_digits] -= coin_flag;
+               }
+               assert(!correction_.empty());
+       }
+       else {
+               message_[check_digits] += coin_flag;
+               if (!rs.check(message_)) {
+                       THROW_EXCEPTION("phrase is invalid (checksum mismatch)");
+               }
+       }
+
+       unsigned used_bits = checksum_size;
+       unsigned quantized_date;
+       reserved_ = 0;
+       quantized_date = 0;
+       memset(seed_.data(), 0, seed_.size());
+
+       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);
+
+       if (reserved_ != 0) {
+               THROW_EXCEPTION("reserved bits must be zero");
+       }
+
+       date_ = epoch + quantized_date * time_step;
+
+       uint8_t salt[25] = "Monero 14-word seed";
+       salt[20] = reserved_;
+       store32(salt + 21, quantized_date);
+       //argon2id_hash_raw(argon_tcost, argon_mcost, 1, seed_.data(), seed_.size(), salt, sizeof(salt), key_.data(), key_.size());
+       pbkdf2_hmac_sha256(seed_.data(), seed_.size(), salt, sizeof(salt), pbkdf2_iterations, 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;
+}
diff --git a/contrib/monero-seed/src/pbkdf2.c b/contrib/monero-seed/src/pbkdf2.c
new file mode 100644 (file)
index 0000000..b21cdb6
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+       Copyright (c) 2020 tevador <tevador@gmail.com>
+       All rights reserved.
+*/
+
+#include "sha256/hash_impl.h"
+
+#include <string.h>
+#include <stdint.h>
+
+#define BLOCK_SIZE 32
+
+typedef struct pbkdf_state {
+       int iterations;
+       uint32_t block_count;
+       uint8_t block[BLOCK_SIZE];
+} pbkdf_state;
+
+static void pbkdf2_transform(const uint8_t* password, size_t pw_size,
+       const uint8_t* salt, size_t salt_size, pbkdf_state* state)
+{
+       hmac_sha256_state hash_state;
+       hmac_sha256_initialize(&hash_state, password, pw_size);
+       hmac_sha256_write(&hash_state, salt, salt_size);
+       uint8_t block_buff[4];
+       //big endian
+       block_buff[0] = state->block_count >> 24;
+       block_buff[1] = state->block_count >> 16;
+       block_buff[2] = state->block_count >> 8;
+       block_buff[3] = state->block_count;
+       hmac_sha256_write(&hash_state, block_buff, sizeof(block_buff));
+       hmac_sha256_finalize(&hash_state, state->block);
+       hmac_sha256_initialize(&hash_state, password, pw_size);
+
+       uint8_t temp[BLOCK_SIZE];
+       memcpy(temp, state->block, BLOCK_SIZE);
+
+       for (unsigned i = 2; i <= state->iterations; ++i) {
+               hmac_sha256_write(&hash_state, temp, sizeof(temp));
+               hmac_sha256_finalize(&hash_state, temp);
+               for (unsigned j = 0; j < BLOCK_SIZE; ++j) {
+                       state->block[j] ^= temp[j];
+               }
+               hmac_sha256_initialize(&hash_state, password, pw_size);
+       }
+
+       state->block_count++;
+}
+
+void pbkdf2_hmac_sha256(const uint8_t* password, size_t pw_size,
+       uint8_t* salt, size_t salt_size,
+       int iterations, uint8_t* key, size_t key_size)
+{
+       pbkdf_state state = {
+               .block_count = 1,
+               .iterations = iterations
+       };
+       while (key_size > 0) {
+               pbkdf2_transform(password, pw_size, salt, salt_size, &state);
+               size_t block_size = key_size > BLOCK_SIZE ? BLOCK_SIZE : key_size;
+               memcpy(key, state.block, block_size);
+               key += BLOCK_SIZE;
+               key_size -= BLOCK_SIZE;
+       }
+}
diff --git a/contrib/monero-seed/src/pbkdf2.h b/contrib/monero-seed/src/pbkdf2.h
new file mode 100644 (file)
index 0000000..a25dc6e
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+       Copyright (c) 2020 tevador <tevador@gmail.com>
+       All rights reserved.
+*/
+
+#pragma once
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void pbkdf2_hmac_sha256(const uint8_t* password, size_t pw_size,
+       const uint8_t* salt, size_t salt_size,
+       int iterations, uint8_t* key, size_t key_size);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/contrib/monero-seed/src/reed_solomon_code.cpp b/contrib/monero-seed/src/reed_solomon_code.cpp
new file mode 100644 (file)
index 0000000..4b80539
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+       Copyright (c) 2020 tevador <tevador@gmail.com>
+       All rights reserved.
+*/
+
+#include <monero_seed/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;
+}
diff --git a/contrib/monero-seed/src/secure_random.cpp b/contrib/monero-seed/src/secure_random.cpp
new file mode 100644 (file)
index 0000000..d4930c1
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+       Copyright (c) 2020 tevador <tevador@gmail.com>
+       All rights reserved.
+*/
+
+#include <monero_seed/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) {
+#ifndef __APPLE__
+                       if (errno != EINTR && errno != EAGAIN) {
+                               break;
+                       }
+#endif
+                       continue;
+               }
+               outptr += len;
+               size -= len;
+       }
+       close(fd);
+       if (size) {
+               throw std::runtime_error("Unable to read " RANDOM_FILE);
+       }
+#endif
+}
diff --git a/contrib/monero-seed/src/sha256/hash_impl.h b/contrib/monero-seed/src/sha256/hash_impl.h
new file mode 100644 (file)
index 0000000..0921a54
--- /dev/null
@@ -0,0 +1,216 @@
+/**********************************************************************
+ * Copyright (c) 2014 Pieter Wuille                                   *
+ * Distributed under the MIT software license, see the accompanying   *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_HASH_IMPL_H_
+#define _SECP256K1_HASH_IMPL_H_
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+typedef struct {
+    uint32_t s[8];
+    uint32_t buf[16]; /* In big endian */
+    size_t bytes;
+} sha256_state;
+
+static void sha256_initialize(sha256_state* hash);
+static void sha256_write(sha256_state* hash, const uint8_t* data, size_t size);
+static void sha256_finalize(sha256_state* hash, uint8_t* out32);
+
+typedef struct {
+    sha256_state inner, outer;
+} hmac_sha256_state;
+
+static void hmac_sha256_initialize(hmac_sha256_state* hash, const uint8_t* key, size_t size);
+static void hmac_sha256_write(hmac_sha256_state* hash, const uint8_t* data, size_t size);
+static void hmac_sha256_finalize(hmac_sha256_state* hash, uint8_t* out32);
+
+#define Ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z))))
+#define Maj(x,y,z) (((x) & (y)) | ((z) & ((x) | (y))))
+#define Sigma0(x) (((x) >> 2 | (x) << 30) ^ ((x) >> 13 | (x) << 19) ^ ((x) >> 22 | (x) << 10))
+#define Sigma1(x) (((x) >> 6 | (x) << 26) ^ ((x) >> 11 | (x) << 21) ^ ((x) >> 25 | (x) << 7))
+#define sigma0(x) (((x) >> 7 | (x) << 25) ^ ((x) >> 18 | (x) << 14) ^ ((x) >> 3))
+#define sigma1(x) (((x) >> 17 | (x) << 15) ^ ((x) >> 19 | (x) << 13) ^ ((x) >> 10))
+
+#define Round(a,b,c,d,e,f,g,h,k,w) do { \
+    uint32_t t1 = (h) + Sigma1(e) + Ch((e), (f), (g)) + (k) + (w); \
+    uint32_t t2 = Sigma0(a) + Maj((a), (b), (c)); \
+    (d) += t1; \
+    (h) = t1 + t2; \
+} while(0)
+
+#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24))
+
+static void sha256_initialize(sha256_state *hash) {
+    hash->s[0] = 0x6a09e667ul;
+    hash->s[1] = 0xbb67ae85ul;
+    hash->s[2] = 0x3c6ef372ul;
+    hash->s[3] = 0xa54ff53aul;
+    hash->s[4] = 0x510e527ful;
+    hash->s[5] = 0x9b05688cul;
+    hash->s[6] = 0x1f83d9abul;
+    hash->s[7] = 0x5be0cd19ul;
+    hash->bytes = 0;
+}
+
+/** Perform one SHA-256 transformation, processing 16 big endian 32-bit words. */
+static void sha256_transform(uint32_t* s, const uint32_t* chunk) {
+    uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7];
+    uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15;
+
+    Round(a, b, c, d, e, f, g, h, 0x428a2f98, w0 = BE32(chunk[0]));
+    Round(h, a, b, c, d, e, f, g, 0x71374491, w1 = BE32(chunk[1]));
+    Round(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w2 = BE32(chunk[2]));
+    Round(f, g, h, a, b, c, d, e, 0xe9b5dba5, w3 = BE32(chunk[3]));
+    Round(e, f, g, h, a, b, c, d, 0x3956c25b, w4 = BE32(chunk[4]));
+    Round(d, e, f, g, h, a, b, c, 0x59f111f1, w5 = BE32(chunk[5]));
+    Round(c, d, e, f, g, h, a, b, 0x923f82a4, w6 = BE32(chunk[6]));
+    Round(b, c, d, e, f, g, h, a, 0xab1c5ed5, w7 = BE32(chunk[7]));
+    Round(a, b, c, d, e, f, g, h, 0xd807aa98, w8 = BE32(chunk[8]));
+    Round(h, a, b, c, d, e, f, g, 0x12835b01, w9 = BE32(chunk[9]));
+    Round(g, h, a, b, c, d, e, f, 0x243185be, w10 = BE32(chunk[10]));
+    Round(f, g, h, a, b, c, d, e, 0x550c7dc3, w11 = BE32(chunk[11]));
+    Round(e, f, g, h, a, b, c, d, 0x72be5d74, w12 = BE32(chunk[12]));
+    Round(d, e, f, g, h, a, b, c, 0x80deb1fe, w13 = BE32(chunk[13]));
+    Round(c, d, e, f, g, h, a, b, 0x9bdc06a7, w14 = BE32(chunk[14]));
+    Round(b, c, d, e, f, g, h, a, 0xc19bf174, w15 = BE32(chunk[15]));
+
+    Round(a, b, c, d, e, f, g, h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1));
+    Round(h, a, b, c, d, e, f, g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2));
+    Round(g, h, a, b, c, d, e, f, 0x0fc19dc6, w2 += sigma1(w0) + w11 + sigma0(w3));
+    Round(f, g, h, a, b, c, d, e, 0x240ca1cc, w3 += sigma1(w1) + w12 + sigma0(w4));
+    Round(e, f, g, h, a, b, c, d, 0x2de92c6f, w4 += sigma1(w2) + w13 + sigma0(w5));
+    Round(d, e, f, g, h, a, b, c, 0x4a7484aa, w5 += sigma1(w3) + w14 + sigma0(w6));
+    Round(c, d, e, f, g, h, a, b, 0x5cb0a9dc, w6 += sigma1(w4) + w15 + sigma0(w7));
+    Round(b, c, d, e, f, g, h, a, 0x76f988da, w7 += sigma1(w5) + w0 + sigma0(w8));
+    Round(a, b, c, d, e, f, g, h, 0x983e5152, w8 += sigma1(w6) + w1 + sigma0(w9));
+    Round(h, a, b, c, d, e, f, g, 0xa831c66d, w9 += sigma1(w7) + w2 + sigma0(w10));
+    Round(g, h, a, b, c, d, e, f, 0xb00327c8, w10 += sigma1(w8) + w3 + sigma0(w11));
+    Round(f, g, h, a, b, c, d, e, 0xbf597fc7, w11 += sigma1(w9) + w4 + sigma0(w12));
+    Round(e, f, g, h, a, b, c, d, 0xc6e00bf3, w12 += sigma1(w10) + w5 + sigma0(w13));
+    Round(d, e, f, g, h, a, b, c, 0xd5a79147, w13 += sigma1(w11) + w6 + sigma0(w14));
+    Round(c, d, e, f, g, h, a, b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15));
+    Round(b, c, d, e, f, g, h, a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0(w0));
+
+    Round(a, b, c, d, e, f, g, h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0(w1));
+    Round(h, a, b, c, d, e, f, g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0(w2));
+    Round(g, h, a, b, c, d, e, f, 0x4d2c6dfc, w2 += sigma1(w0) + w11 + sigma0(w3));
+    Round(f, g, h, a, b, c, d, e, 0x53380d13, w3 += sigma1(w1) + w12 + sigma0(w4));
+    Round(e, f, g, h, a, b, c, d, 0x650a7354, w4 += sigma1(w2) + w13 + sigma0(w5));
+    Round(d, e, f, g, h, a, b, c, 0x766a0abb, w5 += sigma1(w3) + w14 + sigma0(w6));
+    Round(c, d, e, f, g, h, a, b, 0x81c2c92e, w6 += sigma1(w4) + w15 + sigma0(w7));
+    Round(b, c, d, e, f, g, h, a, 0x92722c85, w7 += sigma1(w5) + w0 + sigma0(w8));
+    Round(a, b, c, d, e, f, g, h, 0xa2bfe8a1, w8 += sigma1(w6) + w1 + sigma0(w9));
+    Round(h, a, b, c, d, e, f, g, 0xa81a664b, w9 += sigma1(w7) + w2 + sigma0(w10));
+    Round(g, h, a, b, c, d, e, f, 0xc24b8b70, w10 += sigma1(w8) + w3 + sigma0(w11));
+    Round(f, g, h, a, b, c, d, e, 0xc76c51a3, w11 += sigma1(w9) + w4 + sigma0(w12));
+    Round(e, f, g, h, a, b, c, d, 0xd192e819, w12 += sigma1(w10) + w5 + sigma0(w13));
+    Round(d, e, f, g, h, a, b, c, 0xd6990624, w13 += sigma1(w11) + w6 + sigma0(w14));
+    Round(c, d, e, f, g, h, a, b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15));
+    Round(b, c, d, e, f, g, h, a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0(w0));
+
+    Round(a, b, c, d, e, f, g, h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0(w1));
+    Round(h, a, b, c, d, e, f, g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0(w2));
+    Round(g, h, a, b, c, d, e, f, 0x2748774c, w2 += sigma1(w0) + w11 + sigma0(w3));
+    Round(f, g, h, a, b, c, d, e, 0x34b0bcb5, w3 += sigma1(w1) + w12 + sigma0(w4));
+    Round(e, f, g, h, a, b, c, d, 0x391c0cb3, w4 += sigma1(w2) + w13 + sigma0(w5));
+    Round(d, e, f, g, h, a, b, c, 0x4ed8aa4a, w5 += sigma1(w3) + w14 + sigma0(w6));
+    Round(c, d, e, f, g, h, a, b, 0x5b9cca4f, w6 += sigma1(w4) + w15 + sigma0(w7));
+    Round(b, c, d, e, f, g, h, a, 0x682e6ff3, w7 += sigma1(w5) + w0 + sigma0(w8));
+    Round(a, b, c, d, e, f, g, h, 0x748f82ee, w8 += sigma1(w6) + w1 + sigma0(w9));
+    Round(h, a, b, c, d, e, f, g, 0x78a5636f, w9 += sigma1(w7) + w2 + sigma0(w10));
+    Round(g, h, a, b, c, d, e, f, 0x84c87814, w10 += sigma1(w8) + w3 + sigma0(w11));
+    Round(f, g, h, a, b, c, d, e, 0x8cc70208, w11 += sigma1(w9) + w4 + sigma0(w12));
+    Round(e, f, g, h, a, b, c, d, 0x90befffa, w12 += sigma1(w10) + w5 + sigma0(w13));
+    Round(d, e, f, g, h, a, b, c, 0xa4506ceb, w13 += sigma1(w11) + w6 + sigma0(w14));
+    Round(c, d, e, f, g, h, a, b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15));
+    Round(b, c, d, e, f, g, h, a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0(w0));
+
+    s[0] += a;
+    s[1] += b;
+    s[2] += c;
+    s[3] += d;
+    s[4] += e;
+    s[5] += f;
+    s[6] += g;
+    s[7] += h;
+}
+
+static void sha256_write(sha256_state *hash, const uint8_t *data, size_t len) {
+    size_t bufsize = hash->bytes & 0x3F;
+    hash->bytes += len;
+    while (bufsize + len >= 64) {
+        /* Fill the buffer, and process it. */
+        memcpy(((uint8_t*)hash->buf) + bufsize, data, 64 - bufsize);
+        data += 64 - bufsize;
+        len -= 64 - bufsize;
+        sha256_transform(hash->s, hash->buf);
+        bufsize = 0;
+    }
+    if (len) {
+        /* Fill the buffer with what remains. */
+        memcpy(((uint8_t*)hash->buf) + bufsize, data, len);
+    }
+}
+
+static void sha256_finalize(sha256_state *hash, uint8_t *out32) {
+    static const uint8_t pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    uint32_t sizedesc[2];
+    uint32_t out[8];
+    int i = 0;
+    sizedesc[0] = BE32(hash->bytes >> 29);
+    sizedesc[1] = BE32(hash->bytes << 3);
+    sha256_write(hash, pad, 1 + ((119 - (hash->bytes % 64)) % 64));
+    sha256_write(hash, (const uint8_t*)sizedesc, 8);
+    for (i = 0; i < 8; i++) {
+        out[i] = BE32(hash->s[i]);
+        hash->s[i] = 0;
+    }
+    memcpy(out32, (const uint8_t*)out, 32);
+}
+
+static void hmac_sha256_initialize(hmac_sha256_state *hash, const uint8_t *key, size_t keylen) {
+    int n;
+    uint8_t rkey[64];
+    if (keylen <= 64) {
+        memcpy(rkey, key, keylen);
+        memset(rkey + keylen, 0, 64 - keylen);
+    } else {
+        sha256_state sha256;
+        sha256_initialize(&sha256);
+        sha256_write(&sha256, key, keylen);
+        sha256_finalize(&sha256, rkey);
+        memset(rkey + 32, 0, 32);
+    }
+
+    sha256_initialize(&hash->outer);
+    for (n = 0; n < 64; n++) {
+        rkey[n] ^= 0x5c;
+    }
+    sha256_write(&hash->outer, rkey, 64);
+
+    sha256_initialize(&hash->inner);
+    for (n = 0; n < 64; n++) {
+        rkey[n] ^= 0x5c ^ 0x36;
+    }
+    sha256_write(&hash->inner, rkey, 64);
+    memset(rkey, 0, 64);
+}
+
+static void hmac_sha256_write(hmac_sha256_state *hash, const uint8_t *data, size_t size) {
+    sha256_write(&hash->inner, data, size);
+}
+
+static void hmac_sha256_finalize(hmac_sha256_state *hash, uint8_t *out32) {
+    uint8_t temp[32];
+    sha256_finalize(&hash->inner, temp);
+    sha256_write(&hash->outer, temp, 32);
+    memset(temp, 0, 32);
+    sha256_finalize(&hash->outer, out32);
+}
+
+#endif
diff --git a/contrib/monero-seed/src/wordlist.cpp b/contrib/monero-seed/src/wordlist.cpp
new file mode 100644 (file)
index 0000000..dc1cd1f
--- /dev/null
@@ -0,0 +1,2081 @@
+/*
+       Copyright (c) 2020 tevador <tevador@gmail.com>
+       All rights reserved.
+*/
+
+#include <monero_seed/wordlist.hpp>
+#include <algorithm>
+
+const static std::string english_words[] = {
+       "abandon",
+       "ability",
+       "able",
+       "about",
+       "above",
+       "absent",
+       "absorb",
+       "abstract",
+       "absurd",
+       "abuse",
+       "access",
+       "accident",
+       "account",
+       "accuse",
+       "achieve",
+       "acid",
+       "acoustic",
+       "acquire",
+       "across",
+       "act",
+       "action",
+       "actor",
+       "actress",
+       "actual",
+       "adapt",
+       "add",
+       "addict",
+       "address",
+       "adjust",
+       "admit",
+       "adult",
+       "advance",
+       "advice",
+       "aerobic",
+       "affair",
+       "afford",
+       "afraid",
+       "again",
+       "age",
+       "agent",
+       "agree",
+       "ahead",
+       "aim",
+       "air",
+       "airport",
+       "aisle",
+       "alarm",
+       "album",
+       "alcohol",
+       "alert",
+       "alien",
+       "all",
+       "alley",
+       "allow",
+       "almost",
+       "alone",
+       "alpha",
+       "already",
+       "also",
+       "alter",
+       "always",
+       "amateur",
+       "amazing",
+       "among",
+       "amount",
+       "amused",
+       "analyst",
+       "anchor",
+       "ancient",
+       "anger",
+       "angle",
+       "angry",
+       "animal",
+       "ankle",
+       "announce",
+       "annual",
+       "another",
+       "answer",
+       "antenna",
+       "antique",
+       "anxiety",
+       "any",
+       "apart",
+       "apology",
+       "appear",
+       "apple",
+       "approve",
+       "april",
+       "arch",
+       "arctic",
+       "area",
+       "arena",
+       "argue",
+       "arm",
+       "armed",
+       "armor",
+       "army",
+       "around",
+       "arrange",
+       "arrest",
+       "arrive",
+       "arrow",
+       "art",
+       "artefact",
+       "artist",
+       "artwork",
+       "ask",
+       "aspect",
+       "assault",
+       "asset",
+       "assist",
+       "assume",
+       "asthma",
+       "athlete",
+       "atom",
+       "attack",
+       "attend",
+       "attitude",
+       "attract",
+       "auction",
+       "audit",
+       "august",
+       "aunt",
+       "author",
+       "auto",
+       "autumn",
+       "average",
+       "avocado",
+       "avoid",
+       "awake",
+       "aware",
+       "away",
+       "awesome",
+       "awful",
+       "awkward",
+       "axis",
+       "baby",
+       "bachelor",
+       "bacon",
+       "badge",
+       "bag",
+       "balance",
+       "balcony",
+       "ball",
+       "bamboo",
+       "banana",
+       "banner",
+       "bar",
+       "barely",
+       "bargain",
+       "barrel",
+       "base",
+       "basic",
+       "basket",
+       "battle",
+       "beach",
+       "bean",
+       "beauty",
+       "because",
+       "become",
+       "beef",
+       "before",
+       "begin",
+       "behave",
+       "behind",
+       "believe",
+       "below",
+       "belt",
+       "bench",
+       "benefit",
+       "best",
+       "betray",
+       "better",
+       "between",
+       "beyond",
+       "bicycle",
+       "bid",
+       "bike",
+       "bind",
+       "biology",
+       "bird",
+       "birth",
+       "bitter",
+       "black",
+       "blade",
+       "blame",
+       "blanket",
+       "blast",
+       "bleak",
+       "bless",
+       "blind",
+       "blood",
+       "blossom",
+       "blouse",
+       "blue",
+       "blur",
+       "blush",
+       "board",
+       "boat",
+       "body",
+       "boil",
+       "bomb",
+       "bone",
+       "bonus",
+       "book",
+       "boost",
+       "border",
+       "boring",
+       "borrow",
+       "boss",
+       "bottom",
+       "bounce",
+       "box",
+       "boy",
+       "bracket",
+       "brain",
+       "brand",
+       "brass",
+       "brave",
+       "bread",
+       "breeze",
+       "brick",
+       "bridge",
+       "brief",
+       "bright",
+       "bring",
+       "brisk",
+       "broccoli",
+       "broken",
+       "bronze",
+       "broom",
+       "brother",
+       "brown",
+       "brush",
+       "bubble",
+       "buddy",
+       "budget",
+       "buffalo",
+       "build",
+       "bulb",
+       "bulk",
+       "bullet",
+       "bundle",
+       "bunker",
+       "burden",
+       "burger",
+       "burst",
+       "bus",
+       "business",
+       "busy",
+       "butter",
+       "buyer",
+       "buzz",
+       "cabbage",
+       "cabin",
+       "cable",
+       "cactus",
+       "cage",
+       "cake",
+       "call",
+       "calm",
+       "camera",
+       "camp",
+       "can",
+       "canal",
+       "cancel",
+       "candy",
+       "cannon",
+       "canoe",
+       "canvas",
+       "canyon",
+       "capable",
+       "capital",
+       "captain",
+       "car",
+       "carbon",
+       "card",
+       "cargo",
+       "carpet",
+       "carry",
+       "cart",
+       "case",
+       "cash",
+       "casino",
+       "castle",
+       "casual",
+       "cat",
+       "catalog",
+       "catch",
+       "category",
+       "cattle",
+       "caught",
+       "cause",
+       "caution",
+       "cave",
+       "ceiling",
+       "celery",
+       "cement",
+       "census",
+       "century",
+       "cereal",
+       "certain",
+       "chair",
+       "chalk",
+       "champion",
+       "change",
+       "chaos",
+       "chapter",
+       "charge",
+       "chase",
+       "chat",
+       "cheap",
+       "check",
+       "cheese",
+       "chef",
+       "cherry",
+       "chest",
+       "chicken",
+       "chief",
+       "child",
+       "chimney",
+       "choice",
+       "choose",
+       "chronic",
+       "chuckle",
+       "chunk",
+       "churn",
+       "cigar",
+       "cinnamon",
+       "circle",
+       "citizen",
+       "city",
+       "civil",
+       "claim",
+       "clap",
+       "clarify",
+       "claw",
+       "clay",
+       "clean",
+       "clerk",
+       "clever",
+       "click",
+       "client",
+       "cliff",
+       "climb",
+       "clinic",
+       "clip",
+       "clock",
+       "clog",
+       "close",
+       "cloth",
+       "cloud",
+       "clown",
+       "club",
+       "clump",
+       "cluster",
+       "clutch",
+       "coach",
+       "coast",
+       "coconut",
+       "code",
+       "coffee",
+       "coil",
+       "coin",
+       "collect",
+       "color",
+       "column",
+       "combine",
+       "come",
+       "comfort",
+       "comic",
+       "common",
+       "company",
+       "concert",
+       "conduct",
+       "confirm",
+       "congress",
+       "connect",
+       "consider",
+       "control",
+       "convince",
+       "cook",
+       "cool",
+       "copper",
+       "copy",
+       "coral",
+       "core",
+       "corn",
+       "correct",
+       "cost",
+       "cotton",
+       "couch",
+       "country",
+       "couple",
+       "course",
+       "cousin",
+       "cover",
+       "coyote",
+       "crack",
+       "cradle",
+       "craft",
+       "cram",
+       "crane",
+       "crash",
+       "crater",
+       "crawl",
+       "crazy",
+       "cream",
+       "credit",
+       "creek",
+       "crew",
+       "cricket",
+       "crime",
+       "crisp",
+       "critic",
+       "crop",
+       "cross",
+       "crouch",
+       "crowd",
+       "crucial",
+       "cruel",
+       "cruise",
+       "crumble",
+       "crunch",
+       "crush",
+       "cry",
+       "crystal",
+       "cube",
+       "culture",
+       "cup",
+       "cupboard",
+       "curious",
+       "current",
+       "curtain",
+       "curve",
+       "cushion",
+       "custom",
+       "cute",
+       "cycle",
+       "dad",
+       "damage",
+       "damp",
+       "dance",
+       "danger",
+       "daring",
+       "dash",
+       "daughter",
+       "dawn",
+       "day",
+       "deal",
+       "debate",
+       "debris",
+       "decade",
+       "december",
+       "decide",
+       "decline",
+       "decorate",
+       "decrease",
+       "deer",
+       "defense",
+       "define",
+       "defy",
+       "degree",
+       "delay",
+       "deliver",
+       "demand",
+       "demise",
+       "denial",
+       "dentist",
+       "deny",
+       "depart",
+       "depend",
+       "deposit",
+       "depth",
+       "deputy",
+       "derive",
+       "describe",
+       "desert",
+       "design",
+       "desk",
+       "despair",
+       "destroy",
+       "detail",
+       "detect",
+       "develop",
+       "device",
+       "devote",
+       "diagram",
+       "dial",
+       "diamond",
+       "diary",
+       "dice",
+       "diesel",
+       "diet",
+       "differ",
+       "digital",
+       "dignity",
+       "dilemma",
+       "dinner",
+       "dinosaur",
+       "direct",
+       "dirt",
+       "disagree",
+       "discover",
+       "disease",
+       "dish",
+       "dismiss",
+       "disorder",
+       "display",
+       "distance",
+       "divert",
+       "divide",
+       "divorce",
+       "dizzy",
+       "doctor",
+       "document",
+       "dog",
+       "doll",
+       "dolphin",
+       "domain",
+       "donate",
+       "donkey",
+       "donor",
+       "door",
+       "dose",
+       "double",
+       "dove",
+       "draft",
+       "dragon",
+       "drama",
+       "drastic",
+       "draw",
+       "dream",
+       "dress",
+       "drift",
+       "drill",
+       "drink",
+       "drip",
+       "drive",
+       "drop",
+       "drum",
+       "dry",
+       "duck",
+       "dumb",
+       "dune",
+       "during",
+       "dust",
+       "dutch",
+       "duty",
+       "dwarf",
+       "dynamic",
+       "eager",
+       "eagle",
+       "early",
+       "earn",
+       "earth",
+       "easily",
+       "east",
+       "easy",
+       "echo",
+       "ecology",
+       "economy",
+       "edge",
+       "edit",
+       "educate",
+       "effort",
+       "egg",
+       "eight",
+       "either",
+       "elbow",
+       "elder",
+       "electric",
+       "elegant",
+       "element",
+       "elephant",
+       "elevator",
+       "elite",
+       "else",
+       "embark",
+       "embody",
+       "embrace",
+       "emerge",
+       "emotion",
+       "employ",
+       "empower",
+       "empty",
+       "enable",
+       "enact",
+       "end",
+       "endless",
+       "endorse",
+       "enemy",
+       "energy",
+       "enforce",
+       "engage",
+       "engine",
+       "enhance",
+       "enjoy",
+       "enlist",
+       "enough",
+       "enrich",
+       "enroll",
+       "ensure",
+       "enter",
+       "entire",
+       "entry",
+       "envelope",
+       "episode",
+       "equal",
+       "equip",
+       "era",
+       "erase",
+       "erode",
+       "erosion",
+       "error",
+       "erupt",
+       "escape",
+       "essay",
+       "essence",
+       "estate",
+       "eternal",
+       "ethics",
+       "evidence",
+       "evil",
+       "evoke",
+       "evolve",
+       "exact",
+       "example",
+       "excess",
+       "exchange",
+       "excite",
+       "exclude",
+       "excuse",
+       "execute",
+       "exercise",
+       "exhaust",
+       "exhibit",
+       "exile",
+       "exist",
+       "exit",
+       "exotic",
+       "expand",
+       "expect",
+       "expire",
+       "explain",
+       "expose",
+       "express",
+       "extend",
+       "extra",
+       "eye",
+       "eyebrow",
+       "fabric",
+       "face",
+       "faculty",
+       "fade",
+       "faint",
+       "faith",
+       "fall",
+       "false",
+       "fame",
+       "family",
+       "famous",
+       "fan",
+       "fancy",
+       "fantasy",
+       "farm",
+       "fashion",
+       "fat",
+       "fatal",
+       "father",
+       "fatigue",
+       "fault",
+       "favorite",
+       "feature",
+       "february",
+       "federal",
+       "fee",
+       "feed",
+       "feel",
+       "female",
+       "fence",
+       "festival",
+       "fetch",
+       "fever",
+       "few",
+       "fiber",
+       "fiction",
+       "field",
+       "figure",
+       "file",
+       "film",
+       "filter",
+       "final",
+       "find",
+       "fine",
+       "finger",
+       "finish",
+       "fire",
+       "firm",
+       "first",
+       "fiscal",
+       "fish",
+       "fit",
+       "fitness",
+       "fix",
+       "flag",
+       "flame",
+       "flash",
+       "flat",
+       "flavor",
+       "flee",
+       "flight",
+       "flip",
+       "float",
+       "flock",
+       "floor",
+       "flower",
+       "fluid",
+       "flush",
+       "fly",
+       "foam",
+       "focus",
+       "fog",
+       "foil",
+       "fold",
+       "follow",
+       "food",
+       "foot",
+       "force",
+       "forest",
+       "forget",
+       "fork",
+       "fortune",
+       "forum",
+       "forward",
+       "fossil",
+       "foster",
+       "found",
+       "fox",
+       "fragile",
+       "frame",
+       "frequent",
+       "fresh",
+       "friend",
+       "fringe",
+       "frog",
+       "front",
+       "frost",
+       "frown",
+       "frozen",
+       "fruit",
+       "fuel",
+       "fun",
+       "funny",
+       "furnace",
+       "fury",
+       "future",
+       "gadget",
+       "gain",
+       "galaxy",
+       "gallery",
+       "game",
+       "gap",
+       "garage",
+       "garbage",
+       "garden",
+       "garlic",
+       "garment",
+       "gas",
+       "gasp",
+       "gate",
+       "gather",
+       "gauge",
+       "gaze",
+       "general",
+       "genius",
+       "genre",
+       "gentle",
+       "genuine",
+       "gesture",
+       "ghost",
+       "giant",
+       "gift",
+       "giggle",
+       "ginger",
+       "giraffe",
+       "girl",
+       "give",
+       "glad",
+       "glance",
+       "glare",
+       "glass",
+       "glide",
+       "glimpse",
+       "globe",
+       "gloom",
+       "glory",
+       "glove",
+       "glow",
+       "glue",
+       "goat",
+       "goddess",
+       "gold",
+       "good",
+       "goose",
+       "gorilla",
+       "gospel",
+       "gossip",
+       "govern",
+       "gown",
+       "grab",
+       "grace",
+       "grain",
+       "grant",
+       "grape",
+       "grass",
+       "gravity",
+       "great",
+       "green",
+       "grid",
+       "grief",
+       "grit",
+       "grocery",
+       "group",
+       "grow",
+       "grunt",
+       "guard",
+       "guess",
+       "guide",
+       "guilt",
+       "guitar",
+       "gun",
+       "gym",
+       "habit",
+       "hair",
+       "half",
+       "hammer",
+       "hamster",
+       "hand",
+       "happy",
+       "harbor",
+       "hard",
+       "harsh",
+       "harvest",
+       "hat",
+       "have",
+       "hawk",
+       "hazard",
+       "head",
+       "health",
+       "heart",
+       "heavy",
+       "hedgehog",
+       "height",
+       "hello",
+       "helmet",
+       "help",
+       "hen",
+       "hero",
+       "hidden",
+       "high",
+       "hill",
+       "hint",
+       "hip",
+       "hire",
+       "history",
+       "hobby",
+       "hockey",
+       "hold",
+       "hole",
+       "holiday",
+       "hollow",
+       "home",
+       "honey",
+       "hood",
+       "hope",
+       "horn",
+       "horror",
+       "horse",
+       "hospital",
+       "host",
+       "hotel",
+       "hour",
+       "hover",
+       "hub",
+       "huge",
+       "human",
+       "humble",
+       "humor",
+       "hundred",
+       "hungry",
+       "hunt",
+       "hurdle",
+       "hurry",
+       "hurt",
+       "husband",
+       "hybrid",
+       "ice",
+       "icon",
+       "idea",
+       "identify",
+       "idle",
+       "ignore",
+       "ill",
+       "illegal",
+       "illness",
+       "image",
+       "imitate",
+       "immense",
+       "immune",
+       "impact",
+       "impose",
+       "improve",
+       "impulse",
+       "inch",
+       "include",
+       "income",
+       "increase",
+       "index",
+       "indicate",
+       "indoor",
+       "industry",
+       "infant",
+       "inflict",
+       "inform",
+       "inhale",
+       "inherit",
+       "initial",
+       "inject",
+       "injury",
+       "inmate",
+       "inner",
+       "innocent",
+       "input",
+       "inquiry",
+       "insane",
+       "insect",
+       "inside",
+       "inspire",
+       "install",
+       "intact",
+       "interest",
+       "into",
+       "invest",
+       "invite",
+       "involve",
+       "iron",
+       "island",
+       "isolate",
+       "issue",
+       "item",
+       "ivory",
+       "jacket",
+       "jaguar",
+       "jar",
+       "jazz",
+       "jealous",
+       "jeans",
+       "jelly",
+       "jewel",
+       "job",
+       "join",
+       "joke",
+       "journey",
+       "joy",
+       "judge",
+       "juice",
+       "jump",
+       "jungle",
+       "junior",
+       "junk",
+       "just",
+       "kangaroo",
+       "keen",
+       "keep",
+       "ketchup",
+       "key",
+       "kick",
+       "kid",
+       "kidney",
+       "kind",
+       "kingdom",
+       "kiss",
+       "kit",
+       "kitchen",
+       "kite",
+       "kitten",
+       "kiwi",
+       "knee",
+       "knife",
+       "knock",
+       "know",
+       "lab",
+       "label",
+       "labor",
+       "ladder",
+       "lady",
+       "lake",
+       "lamp",
+       "language",
+       "laptop",
+       "large",
+       "later",
+       "latin",
+       "laugh",
+       "laundry",
+       "lava",
+       "law",
+       "lawn",
+       "lawsuit",
+       "layer",
+       "lazy",
+       "leader",
+       "leaf",
+       "learn",
+       "leave",
+       "lecture",
+       "left",
+       "leg",
+       "legal",
+       "legend",
+       "leisure",
+       "lemon",
+       "lend",
+       "length",
+       "lens",
+       "leopard",
+       "lesson",
+       "letter",
+       "level",
+       "liar",
+       "liberty",
+       "library",
+       "license",
+       "life",
+       "lift",
+       "light",
+       "like",
+       "limb",
+       "limit",
+       "link",
+       "lion",
+       "liquid",
+       "list",
+       "little",
+       "live",
+       "lizard",
+       "load",
+       "loan",
+       "lobster",
+       "local",
+       "lock",
+       "logic",
+       "lonely",
+       "long",
+       "loop",
+       "lottery",
+       "loud",
+       "lounge",
+       "love",
+       "loyal",
+       "lucky",
+       "luggage",
+       "lumber",
+       "lunar",
+       "lunch",
+       "luxury",
+       "lyrics",
+       "machine",
+       "mad",
+       "magic",
+       "magnet",
+       "maid",
+       "mail",
+       "main",
+       "major",
+       "make",
+       "mammal",
+       "man",
+       "manage",
+       "mandate",
+       "mango",
+       "mansion",
+       "manual",
+       "maple",
+       "marble",
+       "march",
+       "margin",
+       "marine",
+       "market",
+       "marriage",
+       "mask",
+       "mass",
+       "master",
+       "match",
+       "material",
+       "math",
+       "matrix",
+       "matter",
+       "maximum",
+       "maze",
+       "meadow",
+       "mean",
+       "measure",
+       "meat",
+       "mechanic",
+       "medal",
+       "media",
+       "melody",
+       "melt",
+       "member",
+       "memory",
+       "mention",
+       "menu",
+       "mercy",
+       "merge",
+       "merit",
+       "merry",
+       "mesh",
+       "message",
+       "metal",
+       "method",
+       "middle",
+       "midnight",
+       "milk",
+       "million",
+       "mimic",
+       "mind",
+       "minimum",
+       "minor",
+       "minute",
+       "miracle",
+       "mirror",
+       "misery",
+       "miss",
+       "mistake",
+       "mix",
+       "mixed",
+       "mixture",
+       "mobile",
+       "model",
+       "modify",
+       "mom",
+       "moment",
+       "monitor",
+       "monkey",
+       "monster",
+       "month",
+       "moon",
+       "moral",
+       "more",
+       "morning",
+       "mosquito",
+       "mother",
+       "motion",
+       "motor",
+       "mountain",
+       "mouse",
+       "move",
+       "movie",
+       "much",
+       "muffin",
+       "mule",
+       "multiply",
+       "muscle",
+       "museum",
+       "mushroom",
+       "music",
+       "must",
+       "mutual",
+       "myself",
+       "mystery",
+       "myth",
+       "naive",
+       "name",
+       "napkin",
+       "narrow",
+       "nasty",
+       "nation",
+       "nature",
+       "near",
+       "neck",
+       "need",
+       "negative",
+       "neglect",
+       "neither",
+       "nephew",
+       "nerve",
+       "nest",
+       "net",
+       "network",
+       "neutral",
+       "never",
+       "news",
+       "next",
+       "nice",
+       "night",
+       "noble",
+       "noise",
+       "nominee",
+       "noodle",
+       "normal",
+       "north",
+       "nose",
+       "notable",
+       "note",
+       "nothing",
+       "notice",
+       "novel",
+       "now",
+       "nuclear",
+       "number",
+       "nurse",
+       "nut",
+       "oak",
+       "obey",
+       "object",
+       "oblige",
+       "obscure",
+       "observe",
+       "obtain",
+       "obvious",
+       "occur",
+       "ocean",
+       "october",
+       "odor",
+       "off",
+       "offer",
+       "office",
+       "often",
+       "oil",
+       "okay",
+       "old",
+       "olive",
+       "olympic",
+       "omit",
+       "once",
+       "one",
+       "onion",
+       "online",
+       "only",
+       "open",
+       "opera",
+       "opinion",
+       "oppose",
+       "option",
+       "orange",
+       "orbit",
+       "orchard",
+       "order",
+       "ordinary",
+       "organ",
+       "orient",
+       "original",
+       "orphan",
+       "ostrich",
+       "other",
+       "outdoor",
+       "outer",
+       "output",
+       "outside",
+       "oval",
+       "oven",
+       "over",
+       "own",
+       "owner",
+       "oxygen",
+       "oyster",
+       "ozone",
+       "pact",
+       "paddle",
+       "page",
+       "pair",
+       "palace",
+       "palm",
+       "panda",
+       "panel",
+       "panic",
+       "panther",
+       "paper",
+       "parade",
+       "parent",
+       "park",
+       "parrot",
+       "party",
+       "pass",
+       "patch",
+       "path",
+       "patient",
+       "patrol",
+       "pattern",
+       "pause",
+       "pave",
+       "payment",
+       "peace",
+       "peanut",
+       "pear",
+       "peasant",
+       "pelican",
+       "pen",
+       "penalty",
+       "pencil",
+       "people",
+       "pepper",
+       "perfect",
+       "permit",
+       "person",
+       "pet",
+       "phone",
+       "photo",
+       "phrase",
+       "physical",
+       "piano",
+       "picnic",
+       "picture",
+       "piece",
+       "pig",
+       "pigeon",
+       "pill",
+       "pilot",
+       "pink",
+       "pioneer",
+       "pipe",
+       "pistol",
+       "pitch",
+       "pizza",
+       "place",
+       "planet",
+       "plastic",
+       "plate",
+       "play",
+       "please",
+       "pledge",
+       "pluck",
+       "plug",
+       "plunge",
+       "poem",
+       "poet",
+       "point",
+       "polar",
+       "pole",
+       "police",
+       "pond",
+       "pony",
+       "pool",
+       "popular",
+       "portion",
+       "position",
+       "possible",
+       "post",
+       "potato",
+       "pottery",
+       "poverty",
+       "powder",
+       "power",
+       "practice",
+       "praise",
+       "predict",
+       "prefer",
+       "prepare",
+       "present",
+       "pretty",
+       "prevent",
+       "price",
+       "pride",
+       "primary",
+       "print",
+       "priority",
+       "prison",
+       "private",
+       "prize",
+       "problem",
+       "process",
+       "produce",
+       "profit",
+       "program",
+       "project",
+       "promote",
+       "proof",
+       "property",
+       "prosper",
+       "protect",
+       "proud",
+       "provide",
+       "public",
+       "pudding",
+       "pull",
+       "pulp",
+       "pulse",
+       "pumpkin",
+       "punch",
+       "pupil",
+       "puppy",
+       "purchase",
+       "purity",
+       "purpose",
+       "purse",
+       "push",
+       "put",
+       "puzzle",
+       "pyramid",
+       "quality",
+       "quantum",
+       "quarter",
+       "question",
+       "quick",
+       "quit",
+       "quiz",
+       "quote",
+       "rabbit",
+       "raccoon",
+       "race",
+       "rack",
+       "radar",
+       "radio",
+       "rail",
+       "rain",
+       "raise",
+       "rally",
+       "ramp",
+       "ranch",
+       "random",
+       "range",
+       "rapid",
+       "rare",
+       "rate",
+       "rather",
+       "raven",
+       "raw",
+       "razor",
+       "ready",
+       "real",
+       "reason",
+       "rebel",
+       "rebuild",
+       "recall",
+       "receive",
+       "recipe",
+       "record",
+       "recycle",
+       "reduce",
+       "reflect",
+       "reform",
+       "refuse",
+       "region",
+       "regret",
+       "regular",
+       "reject",
+       "relax",
+       "release",
+       "relief",
+       "rely",
+       "remain",
+       "remember",
+       "remind",
+       "remove",
+       "render",
+       "renew",
+       "rent",
+       "reopen",
+       "repair",
+       "repeat",
+       "replace",
+       "report",
+       "require",
+       "rescue",
+       "resemble",
+       "resist",
+       "resource",
+       "response",
+       "result",
+       "retire",
+       "retreat",
+       "return",
+       "reunion",
+       "reveal",
+       "review",
+       "reward",
+       "rhythm",
+       "rib",
+       "ribbon",
+       "rice",
+       "rich",
+       "ride",
+       "ridge",
+       "rifle",
+       "right",
+       "rigid",
+       "ring",
+       "riot",
+       "ripple",
+       "risk",
+       "ritual",
+       "rival",
+       "river",
+       "road",
+       "roast",
+       "robot",
+       "robust",
+       "rocket",
+       "romance",
+       "roof",
+       "rookie",
+       "room",
+       "rose",
+       "rotate",
+       "rough",
+       "round",
+       "route",
+       "royal",
+       "rubber",
+       "rude",
+       "rug",
+       "rule",
+       "run",
+       "runway",
+       "rural",
+       "sad",
+       "saddle",
+       "sadness",
+       "safe",
+       "sail",
+       "salad",
+       "salmon",
+       "salon",
+       "salt",
+       "salute",
+       "same",
+       "sample",
+       "sand",
+       "satisfy",
+       "satoshi",
+       "sauce",
+       "sausage",
+       "save",
+       "say",
+       "scale",
+       "scan",
+       "scare",
+       "scatter",
+       "scene",
+       "scheme",
+       "school",
+       "science",
+       "scissors",
+       "scorpion",
+       "scout",
+       "scrap",
+       "screen",
+       "script",
+       "scrub",
+       "sea",
+       "search",
+       "season",
+       "seat",
+       "second",
+       "secret",
+       "section",
+       "security",
+       "seed",
+       "seek",
+       "segment",
+       "select",
+       "sell",
+       "seminar",
+       "senior",
+       "sense",
+       "sentence",
+       "series",
+       "service",
+       "session",
+       "settle",
+       "setup",
+       "seven",
+       "shadow",
+       "shaft",
+       "shallow",
+       "share",
+       "shed",
+       "shell",
+       "sheriff",
+       "shield",
+       "shift",
+       "shine",
+       "ship",
+       "shiver",
+       "shock",
+       "shoe",
+       "shoot",
+       "shop",
+       "short",
+       "shoulder",
+       "shove",
+       "shrimp",
+       "shrug",
+       "shuffle",
+       "shy",
+       "sibling",
+       "sick",
+       "side",
+       "siege",
+       "sight",
+       "sign",
+       "silent",
+       "silk",
+       "silly",
+       "silver",
+       "similar",
+       "simple",
+       "since",
+       "sing",
+       "siren",
+       "sister",
+       "situate",
+       "six",
+       "size",
+       "skate",
+       "sketch",
+       "ski",
+       "skill",
+       "skin",
+       "skirt",
+       "skull",
+       "slab",
+       "slam",
+       "sleep",
+       "slender",
+       "slice",
+       "slide",
+       "slight",
+       "slim",
+       "slogan",
+       "slot",
+       "slow",
+       "slush",
+       "small",
+       "smart",
+       "smile",
+       "smoke",
+       "smooth",
+       "snack",
+       "snake",
+       "snap",
+       "sniff",
+       "snow",
+       "soap",
+       "soccer",
+       "social",
+       "sock",
+       "soda",
+       "soft",
+       "solar",
+       "soldier",
+       "solid",
+       "solution",
+       "solve",
+       "someone",
+       "song",
+       "soon",
+       "sorry",
+       "sort",
+       "soul",
+       "sound",
+       "soup",
+       "source",
+       "south",
+       "space",
+       "spare",
+       "spatial",
+       "spawn",
+       "speak",
+       "special",
+       "speed",
+       "spell",
+       "spend",
+       "sphere",
+       "spice",
+       "spider",
+       "spike",
+       "spin",
+       "spirit",
+       "split",
+       "spoil",
+       "sponsor",
+       "spoon",
+       "sport",
+       "spot",
+       "spray",
+       "spread",
+       "spring",
+       "spy",
+       "square",
+       "squeeze",
+       "squirrel",
+       "stable",
+       "stadium",
+       "staff",
+       "stage",
+       "stairs",
+       "stamp",
+       "stand",
+       "start",
+       "state",
+       "stay",
+       "steak",
+       "steel",
+       "stem",
+       "step",
+       "stereo",
+       "stick",
+       "still",
+       "sting",
+       "stock",
+       "stomach",
+       "stone",
+       "stool",
+       "story",
+       "stove",
+       "strategy",
+       "street",
+       "strike",
+       "strong",
+       "struggle",
+       "student",
+       "stuff",
+       "stumble",
+       "style",
+       "subject",
+       "submit",
+       "subway",
+       "success",
+       "such",
+       "sudden",
+       "suffer",
+       "sugar",
+       "suggest",
+       "suit",
+       "summer",
+       "sun",
+       "sunny",
+       "sunset",
+       "super",
+       "supply",
+       "supreme",
+       "sure",
+       "surface",
+       "surge",
+       "surprise",
+       "surround",
+       "survey",
+       "suspect",
+       "sustain",
+       "swallow",
+       "swamp",
+       "swap",
+       "swarm",
+       "swear",
+       "sweet",
+       "swift",
+       "swim",
+       "swing",
+       "switch",
+       "sword",
+       "symbol",
+       "symptom",
+       "syrup",
+       "system",
+       "table",
+       "tackle",
+       "tag",
+       "tail",
+       "talent",
+       "talk",
+       "tank",
+       "tape",
+       "target",
+       "task",
+       "taste",
+       "tattoo",
+       "taxi",
+       "teach",
+       "team",
+       "tell",
+       "ten",
+       "tenant",
+       "tennis",
+       "tent",
+       "term",
+       "test",
+       "text",
+       "thank",
+       "that",
+       "theme",
+       "then",
+       "theory",
+       "there",
+       "they",
+       "thing",
+       "this",
+       "thought",
+       "three",
+       "thrive",
+       "throw",
+       "thumb",
+       "thunder",
+       "ticket",
+       "tide",
+       "tiger",
+       "tilt",
+       "timber",
+       "time",
+       "tiny",
+       "tip",
+       "tired",
+       "tissue",
+       "title",
+       "toast",
+       "tobacco",
+       "today",
+       "toddler",
+       "toe",
+       "together",
+       "toilet",
+       "token",
+       "tomato",
+       "tomorrow",
+       "tone",
+       "tongue",
+       "tonight",
+       "tool",
+       "tooth",
+       "top",
+       "topic",
+       "topple",
+       "torch",
+       "tornado",
+       "tortoise",
+       "toss",
+       "total",
+       "tourist",
+       "toward",
+       "tower",
+       "town",
+       "toy",
+       "track",
+       "trade",
+       "traffic",
+       "tragic",
+       "train",
+       "transfer",
+       "trap",
+       "trash",
+       "travel",
+       "tray",
+       "treat",
+       "tree",
+       "trend",
+       "trial",
+       "tribe",
+       "trick",
+       "trigger",
+       "trim",
+       "trip",
+       "trophy",
+       "trouble",
+       "truck",
+       "true",
+       "truly",
+       "trumpet",
+       "trust",
+       "truth",
+       "try",
+       "tube",
+       "tuition",
+       "tumble",
+       "tuna",
+       "tunnel",
+       "turkey",
+       "turn",
+       "turtle",
+       "twelve",
+       "twenty",
+       "twice",
+       "twin",
+       "twist",
+       "two",
+       "type",
+       "typical",
+       "ugly",
+       "umbrella",
+       "unable",
+       "unaware",
+       "uncle",
+       "uncover",
+       "under",
+       "undo",
+       "unfair",
+       "unfold",
+       "unhappy",
+       "uniform",
+       "unique",
+       "unit",
+       "universe",
+       "unknown",
+       "unlock",
+       "until",
+       "unusual",
+       "unveil",
+       "update",
+       "upgrade",
+       "uphold",
+       "upon",
+       "upper",
+       "upset",
+       "urban",
+       "urge",
+       "usage",
+       "use",
+       "used",
+       "useful",
+       "useless",
+       "usual",
+       "utility",
+       "vacant",
+       "vacuum",
+       "vague",
+       "valid",
+       "valley",
+       "valve",
+       "van",
+       "vanish",
+       "vapor",
+       "various",
+       "vast",
+       "vault",
+       "vehicle",
+       "velvet",
+       "vendor",
+       "venture",
+       "venue",
+       "verb",
+       "verify",
+       "version",
+       "very",
+       "vessel",
+       "veteran",
+       "viable",
+       "vibrant",
+       "vicious",
+       "victory",
+       "video",
+       "view",
+       "village",
+       "vintage",
+       "violin",
+       "virtual",
+       "virus",
+       "visa",
+       "visit",
+       "visual",
+       "vital",
+       "vivid",
+       "vocal",
+       "voice",
+       "void",
+       "volcano",
+       "volume",
+       "vote",
+       "voyage",
+       "wage",
+       "wagon",
+       "wait",
+       "walk",
+       "wall",
+       "walnut",
+       "want",
+       "warfare",
+       "warm",
+       "warrior",
+       "wash",
+       "wasp",
+       "waste",
+       "water",
+       "wave",
+       "way",
+       "wealth",
+       "weapon",
+       "wear",
+       "weasel",
+       "weather",
+       "web",
+       "wedding",
+       "weekend",
+       "weird",
+       "welcome",
+       "west",
+       "wet",
+       "whale",
+       "what",
+       "wheat",
+       "wheel",
+       "when",
+       "where",
+       "whip",
+       "whisper",
+       "wide",
+       "width",
+       "wife",
+       "wild",
+       "will",
+       "win",
+       "window",
+       "wine",
+       "wing",
+       "wink",
+       "winner",
+       "winter",
+       "wire",
+       "wisdom",
+       "wise",
+       "wish",
+       "witness",
+       "wolf",
+       "woman",
+       "wonder",
+       "wood",
+       "wool",
+       "word",
+       "work",
+       "world",
+       "worry",
+       "worth",
+       "wrap",
+       "wreck",
+       "wrestle",
+       "wrist",
+       "write",
+       "wrong",
+       "yard",
+       "year",
+       "yellow",
+       "you",
+       "young",
+       "youth",
+       "zebra",
+       "zero",
+       "zone",
+       "zoo",
+};
+
+const wordlist wordlist::english = wordlist(english_words);
+
+template<class iter, class T>
+iter binary_find(iter begin, iter end, const T& val) {
+       iter i = std::lower_bound(begin, end, val);
+       if (i != end && !(val < *i)) {
+               return i;
+       }
+       else {
+               return end;
+       }
+}
+
+int wordlist::parse(const std::string& word) const {
+       auto begin = std::begin(values_);
+       auto end = std::end(values_);
+       auto iter = binary_find(begin, end, word);
+       if (iter != end) {
+               return iter - begin;
+       }
+       return -1;
+}
index 6dc11ced588c47f3cc1bc4e47d8985df9b574db6..78fab9eb6c9da11a58d81af0a96ef0b326005531 100644 (file)
@@ -223,9 +223,9 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
 if(UNIX AND NOT APPLE)
     # https://stackoverflow.com/questions/57766620/cmake-add-library-doesnt-initialize-static-global-variable
     # so that contrib/monero-seed/src/gf_elem.cpp properly initializes. A better solution is welcome.
-    target_link_libraries(feather -Wl,--whole-archive monero-seed::monero-seed -Wl,--no-whole-archive)
+    target_link_libraries(feather -Wl,--whole-archive monero-seed -Wl,--no-whole-archive)
 else()
-    target_link_libraries(feather monero-seed::monero-seed)
+    target_link_libraries(feather monero-seed)
 endif()
 
 target_link_libraries(feather