1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-02-16 17:42:02 +00:00

Merge remote-tracking branch 'core-local/andrewkozlik/slip0039' into andrewkozlik/slip0039

This commit is contained in:
Andrew Kozlik 2019-04-17 10:53:00 +02:00
commit d2a399debf
10 changed files with 2473 additions and 0 deletions

View File

@ -39,6 +39,7 @@ CPPDEFINES_MOD += [
SOURCE_MOD += [
'embed/extmod/modtrezorcrypto/modtrezorcrypto.c',
'embed/extmod/modtrezorcrypto/crc.c',
'embed/extmod/modtrezorcrypto/shamir.c',
'embed/extmod/modtrezorcrypto/rand.c',
'vendor/trezor-crypto/address.c',
'vendor/trezor-crypto/aes/aescrypt.c',

View File

@ -37,6 +37,7 @@ CPPDEFINES_MOD += [
SOURCE_MOD += [
'embed/extmod/modtrezorcrypto/modtrezorcrypto.c',
'embed/extmod/modtrezorcrypto/crc.c',
'embed/extmod/modtrezorcrypto/shamir.c',
'vendor/trezor-crypto/address.c',
'vendor/trezor-crypto/aes/aescrypt.c',
'vendor/trezor-crypto/aes/aeskey.c',

View File

@ -0,0 +1,88 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "py/obj.h"
#include "embed/extmod/trezorobj.h"
#include "shamir.h"
#define MAX_SHARE_COUNT 16
/// def interpolate(shares, x) -> bytes:
/// '''
/// Returns f(x) given the Shamir shares (x_1, f(x_1)), ... , (x_k, f(x_k)).
/// :param shares: The Shamir shares.
/// :type shares: A list of pairs (x_i, y_i), where x_i is an integer and
/// y_i is an array of bytes representing the evaluations of the
/// polynomials in x_i.
/// :param int x: The x coordinate of the result.
/// :return: Evaluations of the polynomials in x.
/// :rtype: Array of bytes.
/// '''
mp_obj_t mod_trezorcrypto_shamir_interpolate(mp_obj_t shares, mp_obj_t x) {
size_t share_count;
mp_obj_t *share_items;
mp_obj_get_array(shares, &share_count, &share_items);
if (share_count < 1 || share_count > MAX_SHARE_COUNT) {
mp_raise_ValueError("Invalid number of shares.");
}
uint8_t x_uint8 = trezor_obj_get_uint8(x);
uint8_t share_indices[MAX_SHARE_COUNT];
const uint8_t *share_values[MAX_SHARE_COUNT];
size_t value_len = 0;
for (int i = 0; i < share_count; ++i) {
mp_obj_t *share;
mp_obj_get_array_fixed_n(share_items[i], 2, &share);
share_indices[i] = trezor_obj_get_uint8(share[0]);
mp_buffer_info_t value;
mp_get_buffer_raise(share[1], &value, MP_BUFFER_READ);
if (value_len == 0) {
value_len = value.len;
if (value_len > SHAMIR_MAX_LEN) {
mp_raise_ValueError("Share value exceeds maximum supported length.");
}
}
if (value.len != value_len) {
mp_raise_ValueError("All shares must have the same length.");
}
share_values[i] = value.buf;
}
vstr_t vstr;
vstr_init_len(&vstr, value_len);
shamir_interpolate((uint8_t *)vstr.buf, x_uint8, share_indices, share_values,
share_count, value_len);
vstr_cut_tail_bytes(&vstr, vstr_len(&vstr) - value_len);
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_shamir_interpolate_obj,
mod_trezorcrypto_shamir_interpolate);
STATIC const mp_rom_map_elem_t mod_trezorcrypto_shamir_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_shamir)},
{MP_ROM_QSTR(MP_QSTR_interpolate),
MP_ROM_PTR(&mod_trezorcrypto_shamir_interpolate_obj)},
};
STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_shamir_globals,
mod_trezorcrypto_shamir_globals_table);
STATIC const mp_obj_module_t mod_trezorcrypto_shamir_module = {
.base = {&mp_type_module},
.globals = (mp_obj_dict_t *)&mod_trezorcrypto_shamir_globals,
};

View File

@ -50,6 +50,7 @@
#include "modtrezorcrypto-sha3-256.h"
#include "modtrezorcrypto-sha3-512.h"
#include "modtrezorcrypto-sha512.h"
#include "modtrezorcrypto-shamir.h"
STATIC const mp_rom_map_elem_t mp_module_trezorcrypto_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorcrypto)},
@ -89,6 +90,7 @@ STATIC const mp_rom_map_elem_t mp_module_trezorcrypto_globals_table[] = {
MP_ROM_PTR(&mod_trezorcrypto_Sha3_256_type)},
{MP_ROM_QSTR(MP_QSTR_sha3_512),
MP_ROM_PTR(&mod_trezorcrypto_Sha3_512_type)},
{MP_ROM_QSTR(MP_QSTR_shamir), MP_ROM_PTR(&mod_trezorcrypto_shamir_module)},
};
STATIC MP_DEFINE_CONST_DICT(mp_module_trezorcrypto_globals,
mp_module_trezorcrypto_globals_table);

View File

@ -0,0 +1,299 @@
/*
* Implementation of the hazardous parts of the SSS library
*
* Copyright (c) 2017 Daan Sprenkels <hello@dsprenkels.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.
*
* This code contains the actual Shamir secret sharing functionality. The
* implementation of this code is based on the idea that the user likes to
* generate/combine 32 shares (in GF(2^8)) at the same time, because a 256 bit
* key will be exactly 32 bytes. Therefore we bitslice all the input and
* unbitslice the output right before returning.
*
* This bitslice approach optimizes natively on all architectures that are 32
* bit or more. Care is taken to use not too many registers, to ensure that no
* values have to be leaked to the stack.
*
* All functions in this module are implemented constant time and constant
* lookup operations, as all proper crypto code must be.
*/
#include "shamir.h"
#include <stdio.h>
#include <string.h>
static void bitslice(uint32_t r[8], const uint8_t *x, size_t len) {
size_t bit_idx, arr_idx;
uint32_t cur;
memset(r, 0, sizeof(uint32_t[8]));
for (arr_idx = 0; arr_idx < len; arr_idx++) {
cur = (uint32_t)x[arr_idx];
for (bit_idx = 0; bit_idx < 8; bit_idx++) {
r[bit_idx] |= ((cur & (1 << bit_idx)) >> bit_idx) << arr_idx;
}
}
}
static void unbitslice(uint8_t *r, const uint32_t x[8], size_t len) {
size_t bit_idx, arr_idx;
uint32_t cur;
memset(r, 0, sizeof(uint8_t) * len);
for (bit_idx = 0; bit_idx < 8; bit_idx++) {
cur = (uint32_t)x[bit_idx];
for (arr_idx = 0; arr_idx < len; arr_idx++) {
r[arr_idx] |= ((cur & (1 << arr_idx)) >> arr_idx) << bit_idx;
}
}
}
static void bitslice_setall(uint32_t r[8], const uint8_t x) {
size_t idx;
for (idx = 0; idx < 8; idx++) {
r[idx] = ((int32_t)((x & (1 << idx)) << (31 - idx))) >> 31;
}
}
/*
* Add (XOR) `r` with `x` and store the result in `r`.
*/
static void gf256_add(uint32_t r[8], const uint32_t x[8]) {
size_t idx;
for (idx = 0; idx < 8; idx++) r[idx] ^= x[idx];
}
/*
* Safely multiply two bitsliced polynomials in GF(2^8) reduced by
* x^8 + x^4 + x^3 + x + 1. `r` and `a` may overlap, but overlapping of `r`
* and `b` will produce an incorrect result! If you need to square a polynomial
* use `gf256_square` instead.
*/
static void gf256_mul(uint32_t r[8], const uint32_t a[8], const uint32_t b[8]) {
/* This function implements Russian Peasant multiplication on two
* bitsliced polynomials.
*
* I personally think that these kinds of long lists of operations
* are often a bit ugly. A double for loop would be nicer and would
* take up a lot less lines of code.
* However, some compilers seem to fail in optimizing these kinds of
* loops. So we will just have to do this by hand.
*/
uint32_t a2[8];
memcpy(a2, a, sizeof(uint32_t[8]));
r[0] = a2[0] & b[0]; /* add (assignment, because r is 0) */
r[1] = a2[1] & b[0];
r[2] = a2[2] & b[0];
r[3] = a2[3] & b[0];
r[4] = a2[4] & b[0];
r[5] = a2[5] & b[0];
r[6] = a2[6] & b[0];
r[7] = a2[7] & b[0];
a2[0] ^= a2[7]; /* reduce */
a2[2] ^= a2[7];
a2[3] ^= a2[7];
r[0] ^= a2[7] & b[1]; /* add */
r[1] ^= a2[0] & b[1];
r[2] ^= a2[1] & b[1];
r[3] ^= a2[2] & b[1];
r[4] ^= a2[3] & b[1];
r[5] ^= a2[4] & b[1];
r[6] ^= a2[5] & b[1];
r[7] ^= a2[6] & b[1];
a2[7] ^= a2[6]; /* reduce */
a2[1] ^= a2[6];
a2[2] ^= a2[6];
r[0] ^= a2[6] & b[2]; /* add */
r[1] ^= a2[7] & b[2];
r[2] ^= a2[0] & b[2];
r[3] ^= a2[1] & b[2];
r[4] ^= a2[2] & b[2];
r[5] ^= a2[3] & b[2];
r[6] ^= a2[4] & b[2];
r[7] ^= a2[5] & b[2];
a2[6] ^= a2[5]; /* reduce */
a2[0] ^= a2[5];
a2[1] ^= a2[5];
r[0] ^= a2[5] & b[3]; /* add */
r[1] ^= a2[6] & b[3];
r[2] ^= a2[7] & b[3];
r[3] ^= a2[0] & b[3];
r[4] ^= a2[1] & b[3];
r[5] ^= a2[2] & b[3];
r[6] ^= a2[3] & b[3];
r[7] ^= a2[4] & b[3];
a2[5] ^= a2[4]; /* reduce */
a2[7] ^= a2[4];
a2[0] ^= a2[4];
r[0] ^= a2[4] & b[4]; /* add */
r[1] ^= a2[5] & b[4];
r[2] ^= a2[6] & b[4];
r[3] ^= a2[7] & b[4];
r[4] ^= a2[0] & b[4];
r[5] ^= a2[1] & b[4];
r[6] ^= a2[2] & b[4];
r[7] ^= a2[3] & b[4];
a2[4] ^= a2[3]; /* reduce */
a2[6] ^= a2[3];
a2[7] ^= a2[3];
r[0] ^= a2[3] & b[5]; /* add */
r[1] ^= a2[4] & b[5];
r[2] ^= a2[5] & b[5];
r[3] ^= a2[6] & b[5];
r[4] ^= a2[7] & b[5];
r[5] ^= a2[0] & b[5];
r[6] ^= a2[1] & b[5];
r[7] ^= a2[2] & b[5];
a2[3] ^= a2[2]; /* reduce */
a2[5] ^= a2[2];
a2[6] ^= a2[2];
r[0] ^= a2[2] & b[6]; /* add */
r[1] ^= a2[3] & b[6];
r[2] ^= a2[4] & b[6];
r[3] ^= a2[5] & b[6];
r[4] ^= a2[6] & b[6];
r[5] ^= a2[7] & b[6];
r[6] ^= a2[0] & b[6];
r[7] ^= a2[1] & b[6];
a2[2] ^= a2[1]; /* reduce */
a2[4] ^= a2[1];
a2[5] ^= a2[1];
r[0] ^= a2[1] & b[7]; /* add */
r[1] ^= a2[2] & b[7];
r[2] ^= a2[3] & b[7];
r[3] ^= a2[4] & b[7];
r[4] ^= a2[5] & b[7];
r[5] ^= a2[6] & b[7];
r[6] ^= a2[7] & b[7];
r[7] ^= a2[0] & b[7];
}
/*
* Square `x` in GF(2^8) and write the result to `r`. `r` and `x` may overlap.
*/
static void gf256_square(uint32_t r[8], const uint32_t x[8]) {
uint32_t r8, r10, r12, r14;
/* Use the Freshman's Dream rule to square the polynomial
* Assignments are done from 7 downto 0, because this allows the user
* to execute this function in-place (e.g. `gf256_square(r, r);`).
*/
r14 = x[7];
r12 = x[6];
r10 = x[5];
r8 = x[4];
r[6] = x[3];
r[4] = x[2];
r[2] = x[1];
r[0] = x[0];
/* Reduce with x^8 + x^4 + x^3 + x + 1 until order is less than 8 */
r[7] = r14; /* r[7] was 0 */
r[6] ^= r14;
r10 ^= r14;
/* Skip, because r13 is always 0 */
r[4] ^= r12;
r[5] = r12; /* r[5] was 0 */
r[7] ^= r12;
r8 ^= r12;
/* Skip, because r11 is always 0 */
r[2] ^= r10;
r[3] = r10; /* r[3] was 0 */
r[5] ^= r10;
r[6] ^= r10;
r[1] = r14; /* r[1] was 0 */
r[2] ^= r14; /* Substitute r9 by r14 because they will always be equal*/
r[4] ^= r14;
r[5] ^= r14;
r[0] ^= r8;
r[1] ^= r8;
r[3] ^= r8;
r[4] ^= r8;
}
/*
* Invert `x` in GF(2^8) and write the result to `r`
*/
static void gf256_inv(uint32_t r[8], uint32_t x[8]) {
uint32_t y[8], z[8];
gf256_square(y, x); // y = x^2
gf256_square(y, y); // y = x^4
gf256_square(r, y); // r = x^8
gf256_mul(z, r, x); // z = x^9
gf256_square(r, r); // r = x^16
gf256_mul(r, r, z); // r = x^25
gf256_square(r, r); // r = x^50
gf256_square(z, r); // z = x^100
gf256_square(z, z); // z = x^200
gf256_mul(r, r, z); // r = x^250
gf256_mul(r, r, y); // r = x^254
}
void shamir_interpolate(uint8_t *result, uint8_t result_index,
const uint8_t *share_indices,
const uint8_t **share_values, uint8_t share_count,
size_t len) {
size_t i, j;
uint32_t xs[share_count][8], ys[share_count][8];
uint32_t x[8];
uint32_t denom[8], tmp[8];
uint32_t num[8] = {~0}; /* num is the numerator (=1) */
uint32_t secret[8] = {0};
if (len > SHAMIR_MAX_LEN) return;
/* Collect the x and y values */
for (i = 0; i < share_count; i++) {
bitslice_setall(xs[i], share_indices[i]);
bitslice(ys[i], share_values[i], len);
}
bitslice_setall(x, result_index);
for (i = 0; i < share_count; i++) {
memcpy(tmp, x, sizeof(uint32_t[8]));
gf256_add(tmp, xs[i]);
gf256_mul(num, num, tmp);
}
/* Use Lagrange basis polynomials to calculate the secret coefficient */
for (i = 0; i < share_count; i++) {
memcpy(denom, x, sizeof(denom));
gf256_add(denom, xs[i]);
for (j = 0; j < share_count; j++) {
if (i == j) continue;
memcpy(tmp, xs[i], sizeof(uint32_t[8]));
gf256_add(tmp, xs[j]);
gf256_mul(denom, denom, tmp);
}
gf256_inv(tmp, denom); /* inverted denominator */
gf256_mul(tmp, tmp, num); /* basis polynomial */
gf256_mul(tmp, tmp, ys[i]); /* scaled coefficient */
gf256_add(secret, tmp);
}
unbitslice(result, secret, len);
}

View File

@ -0,0 +1,66 @@
/*
* Low level API for Daan Sprenkels' Shamir secret sharing library
* Copyright (c) 2017 Daan Sprenkels <hello@dsprenkels.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.
*
* Usage of this API is hazardous and is only reserved for beings with a
* good understanding of the Shamir secret sharing scheme and who know how
* crypto code is implemented. If you are unsure about this, use the
* intermediate level API. You have been warned!
*/
#ifndef __SHAMIR_H__
#define __SHAMIR_H__
#include <stddef.h>
#include <stdint.h>
#define SHAMIR_MAX_LEN 32
/*
* Computes f(x) given the Shamir shares (x_1, f(x_1)), ... , (x_m, f(x_m)).
* result: Array of length len where the evaluations of the polynomials in x
* will be written.
* result_index: The x coordinate of the result.
* share_indices: Points to an array of integers x_1, ... , x_m.
* share_values: Points to an array of y_1, ... , y_m, where each y_i is an
* array of bytes of length len representing the evaluations of the
* polynomials in x_i.
* share_count: The number of shares m.
* len: The length of the result array and each of the y_1, ... , y_m arrays.
* The number of shares used to compute the result may be larger than the
* required threshold.
*
* This function does *not* do *any* checking for integrity. If any of the
* shares are not original, this will result in an invalid restored value.
* All values written to `result` should be treated as secret. Even if some of
* the shares that were provided as input were incorrect, the result *still*
* allows an attacker to gain information about the correct result.
*
* This function treats `shares_values`, `share_indices` and `result` as secret
* values. `share_count` is treated as a public value (for performance reasons).
*/
void shamir_interpolate(uint8_t *result, uint8_t result_index,
const uint8_t *share_indices,
const uint8_t **share_values, uint8_t share_count,
size_t len);
#endif /* __SHAMIR_H__ */

View File

@ -0,0 +1,592 @@
# Copyright (c) 2018 Andrew R. Kozlik
#
# 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.
#
import math
from trezor.crypto import hashlib, hmac, pbkdf2, random
from trezor.crypto.slip39_wordlist import wordlist
from trezorcrypto import shamir
class ConfigurationError(Exception):
pass
class MnemonicError(Exception):
pass
RADIX_BITS = 10
"""The length of the radix in bits."""
RADIX = 2 ** RADIX_BITS
"""The number of words in the wordlist."""
ID_LENGTH_BITS = 15
"""The length of the random identifier in bits."""
ITERATION_EXP_LENGTH_BITS = 5
"""The length of the iteration exponent in bits."""
ID_EXP_LENGTH_WORDS = (ID_LENGTH_BITS + ITERATION_EXP_LENGTH_BITS) // RADIX_BITS
"""The length of the random identifier and iteration exponent in words."""
MAX_SHARE_COUNT = 16
"""The maximum number of shares that can be created."""
CHECKSUM_LENGTH_WORDS = 3
"""The length of the RS1024 checksum in words."""
DIGEST_LENGTH_BYTES = 4
"""The length of the digest of the shared secret in bytes."""
CUSTOMIZATION_STRING = b"shamir"
"""The customization string used in the RS1024 checksum and in the PBKDF2 salt."""
METADATA_LENGTH_WORDS = ID_EXP_LENGTH_WORDS + 2 + CHECKSUM_LENGTH_WORDS
"""The length of the mnemonic in words without the share value."""
MIN_STRENGTH_BITS = 128
"""The minimum allowed entropy of the master secret."""
MIN_MNEMONIC_LENGTH_WORDS = METADATA_LENGTH_WORDS + math.ceil(MIN_STRENGTH_BITS / 10)
"""The minimum allowed length of the mnemonic in words."""
MIN_ITERATION_COUNT = 10000
"""The minimum number of iterations to use in PBKDF2."""
ROUND_COUNT = 4
"""The number of rounds to use in the Feistel cipher."""
SECRET_INDEX = 255
"""The index of the share containing the shared secret."""
DIGEST_INDEX = 254
"""The index of the share containing the digest of the shared secret."""
def word_index(word):
lo = 0
hi = len(wordlist)
while hi - lo > 1:
mid = (hi + lo) // 2
if wordlist[mid] > word:
hi = mid
else:
lo = mid
if not wordlist[lo].startswith(word):
raise MnemonicError('Invalid mnemonic word "{}".'.format(word))
return lo
def _rs1024_polymod(values):
GEN = (
0xE0E040,
0x1C1C080,
0x3838100,
0x7070200,
0xE0E0009,
0x1C0C2412,
0x38086C24,
0x3090FC48,
0x21B1F890,
0x3F3F120,
)
chk = 1
for v in values:
b = chk >> 20
chk = (chk & 0xFFFFF) << 10 ^ v
for i in range(10):
chk ^= GEN[i] if ((b >> i) & 1) else 0
return chk
def rs1024_create_checksum(data):
values = tuple(CUSTOMIZATION_STRING) + data + CHECKSUM_LENGTH_WORDS * (0,)
polymod = _rs1024_polymod(values) ^ 1
return tuple(
(polymod >> 10 * i) & 1023 for i in reversed(range(CHECKSUM_LENGTH_WORDS))
)
def rs1024_verify_checksum(data):
return _rs1024_polymod(tuple(CUSTOMIZATION_STRING) + data) == 1
def xor(a, b):
return bytes(x ^ y for x, y in zip(a, b))
def _int_from_indices(indices):
"""Converts a list of base 1024 indices in big endian order to an integer value."""
value = 0
for index in indices:
value = (value << RADIX_BITS) + index
return value
def _int_to_indices(value, length, bits):
"""Converts an integer value to indices in big endian order."""
mask = (1 << bits) - 1
return ((value >> (i * bits)) & mask for i in reversed(range(length)))
def mnemonic_from_indices(indices):
return " ".join(wordlist[i] for i in indices)
def mnemonic_to_indices(mnemonic):
return (word_index(word.lower()) for word in mnemonic.split())
def _round_function(i, passphrase, e, salt, r):
"""The round function used internally by the Feistel cipher."""
return pbkdf2(
pbkdf2.HMAC_SHA256,
bytes([i]) + passphrase,
salt + r,
(MIN_ITERATION_COUNT << e) // ROUND_COUNT,
).key()[: len(r)]
def _get_salt(identifier):
return CUSTOMIZATION_STRING + identifier.to_bytes(
math.ceil(ID_LENGTH_BITS / 8), "big"
)
def _encrypt(master_secret, passphrase, iteration_exponent, identifier):
l = master_secret[: len(master_secret) // 2]
r = master_secret[len(master_secret) // 2 :]
salt = _get_salt(identifier)
for i in range(ROUND_COUNT):
(l, r) = (
r,
xor(l, _round_function(i, passphrase, iteration_exponent, salt, r)),
)
return r + l
def decrypt(identifier, iteration_exponent, encrypted_master_secret, passphrase):
l = encrypted_master_secret[: len(encrypted_master_secret) // 2]
r = encrypted_master_secret[len(encrypted_master_secret) // 2 :]
salt = _get_salt(identifier)
for i in reversed(range(ROUND_COUNT)):
(l, r) = (
r,
xor(l, _round_function(i, passphrase, iteration_exponent, salt, r)),
)
return r + l
def _create_digest(random_data, shared_secret):
return hmac.new(random_data, shared_secret, hashlib.sha256).digest()[
:DIGEST_LENGTH_BYTES
]
def _split_secret(threshold, share_count, shared_secret):
assert 0 < threshold <= share_count <= MAX_SHARE_COUNT
# If the threshold is 1, then the digest of the shared secret is not used.
if threshold == 1:
return [(i, shared_secret) for i in range(share_count)]
random_share_count = threshold - 2
if share_count > MAX_SHARE_COUNT:
raise ValueError(
"The requested number of shares ({}) must not exceed {}.".format(
share_count, MAX_SHARE_COUNT
)
)
shares = [(i, random.bytes(len(shared_secret))) for i in range(random_share_count)]
random_part = random.bytes(len(shared_secret) - DIGEST_LENGTH_BYTES)
digest = _create_digest(random_part, shared_secret)
base_shares = shares + [
(DIGEST_INDEX, digest + random_part),
(SECRET_INDEX, shared_secret),
]
for i in range(random_share_count, share_count):
shares.append((i, shamir.interpolate(base_shares, i)))
return shares
def _recover_secret(threshold, shares):
shared_secret = shamir.interpolate(shares, SECRET_INDEX)
# If the threshold is 1, then the digest of the shared secret is not used.
if threshold != 1:
digest_share = shamir.interpolate(shares, DIGEST_INDEX)
digest = digest_share[:DIGEST_LENGTH_BYTES]
random_part = digest_share[DIGEST_LENGTH_BYTES:]
if digest != _create_digest(random_part, shared_secret):
raise MnemonicError("Invalid digest of the shared secret.")
return shared_secret
def _group_prefix(
identifier, iteration_exponent, group_index, group_threshold, group_count
):
id_exp_int = (identifier << ITERATION_EXP_LENGTH_BITS) + iteration_exponent
return tuple(_int_to_indices(id_exp_int, ID_EXP_LENGTH_WORDS, RADIX_BITS)) + (
(group_index << 6) + ((group_threshold - 1) << 2) + ((group_count - 1) >> 2),
)
def encode_mnemonic(
identifier,
iteration_exponent,
group_index,
group_threshold,
group_count,
member_index,
member_threshold,
value,
):
"""
Converts share data to a share mnemonic.
:param int identifier: The random identifier.
:param int iteration_exponent: The iteration exponent.
:param int group_index: The x coordinate of the group share.
:param int group_threshold: The number of group shares needed to reconstruct the encrypted master secret.
:param int group_count: The total number of groups in existence.
:param int member_index: The x coordinate of the member share in the given group.
:param int member_threshold: The number of member shares needed to reconstruct the group share.
:param value: The share value representing the y coordinates of the share.
:type value: Array of bytes.
:return: The share mnemonic.
:rtype: Array of bytes.
"""
# Convert the share value from bytes to wordlist indices.
value_word_count = math.ceil(len(value) * 8 / RADIX_BITS)
value_int = int.from_bytes(value, "big")
share_data = (
_group_prefix(
identifier, iteration_exponent, group_index, group_threshold, group_count
)
+ (
(((group_count - 1) & 3) << 8)
+ (member_index << 4)
+ (member_threshold - 1),
)
+ tuple(_int_to_indices(value_int, value_word_count, RADIX_BITS))
)
checksum = rs1024_create_checksum(share_data)
return mnemonic_from_indices(share_data + checksum)
def decode_mnemonic(mnemonic):
"""Converts a share mnemonic to share data."""
mnemonic_data = tuple(mnemonic_to_indices(mnemonic))
if len(mnemonic_data) < MIN_MNEMONIC_LENGTH_WORDS:
raise MnemonicError(
"Invalid mnemonic length. The length of each mnemonic must be at least {} words.".format(
MIN_MNEMONIC_LENGTH_WORDS
)
)
padding_len = (10 * (len(mnemonic_data) - METADATA_LENGTH_WORDS)) % 16
if padding_len > 8:
raise MnemonicError("Invalid mnemonic length.")
if not rs1024_verify_checksum(mnemonic_data):
raise MnemonicError(
'Invalid mnemonic checksum for "{} ...".'.format(
" ".join(mnemonic.split()[: ID_EXP_LENGTH_WORDS + 2])
)
)
id_exp_int = _int_from_indices(mnemonic_data[:ID_EXP_LENGTH_WORDS])
identifier = id_exp_int >> ITERATION_EXP_LENGTH_BITS
iteration_exponent = id_exp_int & ((1 << ITERATION_EXP_LENGTH_BITS) - 1)
tmp = _int_from_indices(
mnemonic_data[ID_EXP_LENGTH_WORDS : ID_EXP_LENGTH_WORDS + 2]
)
group_index, group_threshold, group_count, member_index, member_threshold = _int_to_indices(
tmp, 5, 4
)
value_data = mnemonic_data[ID_EXP_LENGTH_WORDS + 2 : -CHECKSUM_LENGTH_WORDS]
if group_count < group_threshold:
raise MnemonicError(
'Invalid mnemonic "{} ...". Group threshold cannot be greater than group count.'.format(
" ".join(mnemonic.split()[: ID_EXP_LENGTH_WORDS + 2])
)
)
value_byte_count = (10 * len(value_data) - padding_len) // 8
value_int = _int_from_indices(value_data)
if value_data[0] >= 1 << (10 - padding_len):
raise MnemonicError(
'Invalid mnemonic padding for "{} ...".'.format(
" ".join(mnemonic.split()[: ID_EXP_LENGTH_WORDS + 2])
)
)
value = value_int.to_bytes(value_byte_count, "big")
return (
identifier,
iteration_exponent,
group_index,
group_threshold + 1,
group_count + 1,
member_index,
member_threshold + 1,
value,
)
def _decode_mnemonics(mnemonics):
identifiers = set()
iteration_exponents = set()
group_thresholds = set()
group_counts = set()
groups = {} # { group_index : [member_threshold, set_of_member_shares] }
for mnemonic in mnemonics:
identifier, iteration_exponent, group_index, group_threshold, group_count, member_index, member_threshold, share_value = decode_mnemonic(
mnemonic
)
identifiers.add(identifier)
iteration_exponents.add(iteration_exponent)
group_thresholds.add(group_threshold)
group_counts.add(group_count)
group = groups.setdefault(group_index, [member_threshold, set()])
if group[0] != member_threshold:
raise MnemonicError(
"Invalid set of mnemonics. All mnemonics in a group must have the same member threshold."
)
group[1].add((member_index, share_value))
if len(identifiers) != 1 or len(iteration_exponents) != 1:
raise MnemonicError(
"Invalid set of mnemonics. All mnemonics must begin with the same {} words.".format(
ID_EXP_LENGTH_WORDS
)
)
if len(group_thresholds) != 1:
raise MnemonicError(
"Invalid set of mnemonics. All mnemonics must have the same group threshold."
)
if len(group_counts) != 1:
raise MnemonicError(
"Invalid set of mnemonics. All mnemonics must have the same group count."
)
for group_index, group in groups.items():
if len(set(share[0] for share in group[1])) != len(group[1]):
raise MnemonicError(
"Invalid set of shares. Member indices in each group must be unique."
)
return (
identifiers.pop(),
iteration_exponents.pop(),
group_thresholds.pop(),
group_counts.pop(),
groups,
)
def _generate_random_identifier():
"""Returns a randomly generated integer in the range 0, ... , 2**ID_LENGTH_BITS - 1."""
identifier = int.from_bytes(random.bytes(math.ceil(ID_LENGTH_BITS / 8)), "big")
return identifier & ((1 << ID_LENGTH_BITS) - 1)
def generate_mnemonics(
group_threshold, groups, master_secret, passphrase=b"", iteration_exponent=0
):
"""
Splits a master secret into mnemonic shares using Shamir's secret sharing scheme.
:param int group_threshold: The number of groups required to reconstruct the master secret.
:param groups: A list of (member_threshold, member_count) pairs for each group, where member_count
is the number of shares to generate for the group and member_threshold is the number of members required to
reconstruct the group secret.
:type groups: List of pairs of integers.
:param master_secret: The master secret to split.
:type master_secret: Array of bytes.
:param passphrase: The passphrase used to encrypt the master secret.
:type passphrase: Array of bytes.
:param int iteration_exponent: The iteration exponent.
:return: List of mnemonics.
:rtype: List of byte arrays.
"""
identifier = _generate_random_identifier()
if len(master_secret) * 8 < MIN_STRENGTH_BITS:
raise ValueError(
"The length of the master secret ({} bytes) must be at least {} bytes.".format(
len(master_secret), math.ceil(MIN_STRENGTH_BITS / 8)
)
)
if len(master_secret) % 2 != 0:
raise ValueError(
"The length of the master secret in bytes must be an even number."
)
if group_threshold > len(groups):
raise ValueError(
"The requested group threshold ({}) must not exceed the number of groups ({}).".format(
group_threshold, len(groups)
)
)
encrypted_master_secret = _encrypt(
master_secret, passphrase, iteration_exponent, identifier
)
group_shares = _split_secret(group_threshold, len(groups), encrypted_master_secret)
return [
[
encode_mnemonic(
identifier,
iteration_exponent,
group_index,
group_threshold,
len(groups),
member_index,
member_threshold,
value,
)
for member_index, value in _split_secret(
member_threshold, member_count, group_secret
)
]
for (member_threshold, member_count), (group_index, group_secret) in zip(
groups, group_shares
)
]
def generate_mnemonics_random(
group_threshold, groups, strength_bits=128, passphrase=b"", iteration_exponent=0
):
"""
Generates a random master secret and splits it into mnemonic shares using Shamir's secret
sharing scheme.
:param int group_threshold: The number of groups required to reconstruct the master secret.
:param groups: A list of (member_threshold, member_count) pairs for each group, where member_count
is the number of shares to generate for the group and member_threshold is the number of members required to
reconstruct the group secret.
:type groups: List of pairs of integers.
:param int strength_bits: The entropy of the randomly generated master secret in bits.
:param passphrase: The passphrase used to encrypt the master secret.
:type passphrase: Array of bytes.
:param int iteration_exponent: The iteration exponent.
:return: List of mnemonics.
:rtype: List of byte arrays.
"""
if strength_bits < MIN_STRENGTH_BITS:
raise ValueError(
"The requested strength of the master secret ({} bits) must be at least {} bits.".format(
strength_bits, MIN_STRENGTH_BITS
)
)
if strength_bits % 16 != 0:
raise ValueError(
"The requested strength of the master secret ({} bits) must be a multiple of 16 bits.".format(
strength_bits
)
)
return generate_mnemonics(
group_threshold,
groups,
random.bytes(strength_bits // 8),
passphrase,
iteration_exponent,
)
def combine_mnemonics(mnemonics):
"""
Combines mnemonic shares to obtain the master secret which was previously split using
Shamir's secret sharing scheme.
:param mnemonics: List of mnemonics.
:type mnemonics: List of byte arrays.
:return: Identifier, iteration exponent, the encrypted master secret.
:rtype: Integer, integer, array of bytes.
"""
if not mnemonics:
raise MnemonicError("The list of mnemonics is empty.")
identifier, iteration_exponent, group_threshold, group_count, groups = _decode_mnemonics(
mnemonics
)
if len(groups) < group_threshold:
raise MnemonicError(
"Insufficient number of mnemonic groups ({}). The required number of groups is {}.".format(
len(groups), group_threshold
)
)
# Remove the groups, where the number of shares is below the member threshold.
bad_groups = {
group_index: group
for group_index, group in groups.items()
if len(group[1]) < group[0]
}
for group_index in bad_groups:
groups.pop(group_index)
if len(groups) < group_threshold:
group_index, group = next(iter(bad_groups.items()))
prefix = _group_prefix(
identifier, iteration_exponent, group_index, group_threshold, group_count
)
raise MnemonicError(
'Insufficient number of mnemonics. At least {} mnemonics starting with "{} ..." are required.'.format(
group[0], mnemonic_from_indices(prefix)
)
)
group_shares = [
(group_index, _recover_secret(group[0], list(group[1])))
for group_index, group in groups.items()
]
return (
identifier,
iteration_exponent,
_recover_secret(group_threshold, group_shares),
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,320 @@
vectors = [
[
[
"duckling enlarge academic academic agency result length solution fridge kidney coal piece deal husband erode duke ajar critical decision keyboard"
],
"bb54aac4b89dc868ba37d9cc21b2cece"
],
[
[
"duckling enlarge academic academic agency result length solution fridge kidney coal piece deal husband erode duke ajar critical decision kidney"
],
""
],
[
[
"duckling enlarge academic academic email result length solution fridge kidney coal piece deal husband erode duke ajar music cargo fitness"
],
""
],
[
[
"shadow pistol academic always adequate wildlife fancy gross oasis cylinder mustang wrist rescue view short owner flip making coding armed",
"shadow pistol academic acid actress prayer class unknown daughter sweater depict flip twice unkind craft early superior advocate guest smoking",
"shadow pistol academic agency acid license obtain preach firefly permit flavor library learn tension flea unusual nylon terminal exercise become"
],
"b43ceb7e57a0ea8766221624d01b0864"
],
[
[
"shadow pistol academic always adequate wildlife fancy gross oasis cylinder mustang wrist rescue view short owner flip making coding armed",
"shadow pistol academic agency acid license obtain preach firefly permit flavor library learn tension flea unusual nylon terminal exercise become"
],
"b43ceb7e57a0ea8766221624d01b0864"
],
[
[
"shadow pistol academic always adequate wildlife fancy gross oasis cylinder mustang wrist rescue view short owner flip making coding armed"
],
""
],
[
[
"numb stay academic acid aide deny bulge romp kernel rumor flavor estate traffic video email living grief move corner laundry",
"numb smoking academic agency cleanup false acne luxury withdraw swing ceramic adjust forget editor remind bedroom practice ting fancy theory"
],
""
],
[
[
"enlarge pants academic acid alien become warmth exclude unfold yield square unusual increase chest wireless rainbow duration cause wrote taste",
"enlarge painting academic agency anatomy infant carve platform nail already flavor mailman iris video beam rapids smoking teacher gross failure"
],
""
],
[
[
"script smoking academic enlarge diet theater twin guard already image increase trash lend pink general crowd chest remind faint literary",
"script smoking beard echo blimp diminish pencil curious vampire devote valuable skin mama starting tension explain elegant surface venture judicial",
"script smoking beard email charity clock join peasant acquire tidy dress drove width depend smart trust guitar bulb echo duration"
],
""
],
[
[
"scroll leader academic leaf become alcohol adapt tactics born orange eraser mobile fragment playoff easel nail edge sympathy liberty closet",
"scroll leader academic agency alcohol rhyme excuse image wolf sidewalk index yoga upgrade flip inmate blue relate twin mental corner"
],
""
],
[
[
"client isolate acrobat acid chew both garlic holy artist gums already package spray story remind tricycle texture trouble amazing evil",
"client isolate acrobat agency daisy retreat slush genuine trip overall burden beyond enforce shaft decrease loud crazy exact round answer",
"client isolate beard academic campus verdict wine lunch rapids born warn spend patent surface elevator satoshi upgrade general echo legs"
],
""
],
[
[
"involve deal academic always boundary formal answer genius science civil envy thunder duckling trip tracks exceed response grin humidity wolf",
"involve deal academic agency civil revenue program sharp merchant false ending credit wrote slush simple gather fiber justice emphasis hand",
"involve deal academic always display memory damage center promise rumor drove subject large payment spelling alpha twin browser dilemma revenue"
],
""
],
[
[
"patrol necklace academic academic dream empty advance artist hush prayer galaxy adequate camera presence patrol entrance excuse quantity gesture cradle",
"patrol necklace academic agency duckling kidney prize desire forecast imply moisture envy express beyond that profile involve dance much auction"
],
""
],
[
[
"drift deal academic acid acquire busy elephant crucial thumb jewelry lift agency amazing laden isolate negative smoking campus march teaspoon",
"drift deal academic agency adequate pants adjust cards champion aunt strategy fatigue observe aircraft valuable primary dominant webcam bike wits"
],
""
],
[
[
"memory stay beard romp depend idle unfold stilt thorn obesity alarm slap thumb says python ordinary mineral insect square cards"
],
""
],
[
[
"memory stay decision spew duckling carve single profile frozen result forbid pecan game greatest shaped advance craft golden visitor fangs",
"memory stay decision roster deliver organize olympic quantity clogs gather goat energy literary library pupal exotic season index yelp vexed"
],
""
],
[
[
"memory stay decision shadow always sheriff rich wine wrote raspy vintage boring careful recover overall reaction bishop writing blessing corner",
"memory stay beard romp depend idle unfold stilt thorn obesity alarm slap thumb says python ordinary mineral insect square cards"
],
""
],
[
[
"memory stay decision smug acid device switch likely fatal starting cluster fiction puny view trust program painting paper recover luxury",
"memory stay ceramic snake clothes loan company credit phantom blessing jewelry welcome smith legs unknown paid moisture unwrap become lobe",
"memory stay decision spew duckling carve single profile frozen result forbid pecan game greatest shaped advance craft golden visitor fangs",
"memory stay ceramic shaft cause receiver geology space ancient shelter tension universe radar learn round shrimp deal fragment desire hormone",
"memory stay ceramic round duration snapshot orbit cage debris script herald item have election adequate pink artist profile born eclipse"
],
"97a292a5941682d7a4ae2d898d11a519"
],
[
[
"memory stay beard romp depend idle unfold stilt thorn obesity alarm slap thumb says python ordinary mineral insect square cards",
"memory stay acrobat romp dilemma echo ruler process vintage leaves detailed speak tolerate timber lunch damage strategy junior element prevent"
],
"97a292a5941682d7a4ae2d898d11a519"
],
[
[
"memory stay ceramic snake clothes loan company credit phantom blessing jewelry welcome smith legs unknown paid moisture unwrap become lobe",
"memory stay decision scared aquatic receiver mason package corner animal cage receiver hazard beyond merit tension exotic reaction pulse deadline",
"memory stay acrobat romp dilemma echo ruler process vintage leaves detailed speak tolerate timber lunch damage strategy junior element prevent",
"memory stay beard romp depend idle unfold stilt thorn obesity alarm slap thumb says python ordinary mineral insect square cards",
"memory stay ceramic round duration snapshot orbit cage debris script herald item have election adequate pink artist profile born eclipse",
"memory stay decision spew duckling carve single profile frozen result forbid pecan game greatest shaped advance craft golden visitor fangs",
"memory stay decision roster deliver organize olympic quantity clogs gather goat energy literary library pupal exotic season index yelp vexed",
"memory stay decision sister devote theory promise staff venture symbolic mailman timely valid counter retreat building order dominant forward verify",
"memory stay ceramic skin crowd building glimpse space angry early language mortgage burden grumpy class deadline wildlife unhappy gums thorn",
"memory stay decision smug acid device switch likely fatal starting cluster fiction puny view trust program painting paper recover luxury",
"memory stay decision shadow always sheriff rich wine wrote raspy vintage boring careful recover overall reaction bishop writing blessing corner",
"memory stay ceramic shaft cause receiver geology space ancient shelter tension universe radar learn round shrimp deal fragment desire hormone",
"memory stay ceramic scatter detailed knit oral cage decent debut upstairs adequate science petition temple exchange scroll camera evaluate shrimp"
],
"97a292a5941682d7a4ae2d898d11a519"
],
[
[
"spark leader academic academic artwork crazy pacific friar holy preach trust empty remember syndrome mineral aviation medical dive cluster scroll organize painting helpful critical spend smug tackle cowboy identify pitch unfair flavor percent"
],
"33dfd8cc75e42477025dce88ae83e75a230086a0e00e9271f4c938b319067687"
],
[
[
"spark leader academic academic artwork crazy pacific friar holy preach trust empty remember syndrome mineral aviation medical dive cluster scroll organize painting helpful critical spend smug tackle cowboy identify pitch unfair flavor pencil"
],
""
],
[
[
"spark leader academic academic capacity crazy pacific friar holy preach trust empty remember syndrome mineral aviation medical dive cluster scroll organize painting helpful critical spend smug tackle cowboy identify pitch permit wavy unkind"
],
""
],
[
[
"seafood learn academic always adult harvest inform custody space ending frequent coding lamp amazing museum year argue mandate should minister jacket float duke client survive large entrance unknown rhythm angel genuine safari talent",
"seafood learn academic agency ancient clay execute browser railroad system brother anxiety focus elephant merit standard slap numerous insect hush hospital emphasis depend nail morning epidemic lawsuit aluminum buyer drift metric meaning burning",
"seafood learn academic acid armed survive flip soul analysis crucial species similar rescue regret mixture twin marvel pleasure trend soul afraid depict rumor champion skunk bumpy obtain impulse texture fantasy year maiden kidney"
],
"e05e0da0ecce1278f75ff58d9853f19dcaeed5de104aae37c7af367d74bc8756"
],
[
[
"seafood learn academic acid armed survive flip soul analysis crucial species similar rescue regret mixture twin marvel pleasure trend soul afraid depict rumor champion skunk bumpy obtain impulse texture fantasy year maiden kidney",
"seafood learn academic agency ancient clay execute browser railroad system brother anxiety focus elephant merit standard slap numerous insect hush hospital emphasis depend nail morning epidemic lawsuit aluminum buyer drift metric meaning burning"
],
"e05e0da0ecce1278f75ff58d9853f19dcaeed5de104aae37c7af367d74bc8756"
],
[
[
"seafood learn academic agency ancient clay execute browser railroad system brother anxiety focus elephant merit standard slap numerous insect hush hospital emphasis depend nail morning epidemic lawsuit aluminum buyer drift metric meaning burning"
],
""
],
[
[
"practice enlarge academic acid born verdict dominant cylinder earth bracelet dining resident public garden desktop course fact exact vanish branch",
"practice easy academic agency advance broken firm elder exact axle plunge dream glance depict username flexible airline station include priority"
],
""
],
[
[
"expand ecology academic acid drug coastal forbid sister blanket salary unusual fridge phrase amuse hesitate repair much findings practice slavery",
"expand easy academic agency column lungs dilemma chest depict trial member intimate gums rich mayor grin husband reward western debris"
],
""
],
[
[
"depart branch academic enlarge august moisture snapshot smear genuine sister order crisis gums afraid dress romantic imply corner justice center",
"depart branch beard echo dilemma likely building hand treat trial pumps writing photo fragment lunar ranked sympathy wrap alien debris",
"depart branch beard email darkness tracks national discuss fluff merit gesture order mule firefly evaluate both element adult walnut stick"
],
""
],
[
[
"race flea academic leaf crazy carpet item lyrics galaxy remind lungs distance thank lunch cowboy emission fridge center music costume",
"race flea academic agency cricket response sack geology maiden spray salon enforce fishing junction oven repair video phrase pumps duration"
],
""
],
[
[
"cleanup garden acrobat acid branch idle season upstairs mountain energy aspect intimate black exchange purchase arcade making tricycle primary thunder",
"cleanup garden acrobat agency clay that finger phantom solution forward evil rival item much mansion lecture device decrease advocate ounce",
"cleanup garden beard academic cowboy desktop submit liquid branch olympic smith render category grumpy group campus impulse drift society starting"
],
""
],
[
[
"paper branch academic always bolt secret senior starting society artwork ocean marathon parcel large revenue hush jerky sympathy indicate therapy",
"paper branch academic agency auction plan charity identify yelp glad webcam provide evaluate response hunting belong veteran unknown speak twin",
"paper branch academic always coastal bumpy burning nylon silent speak forward findings velvet terminal busy aircraft glasses firm extra mandate"
],
""
],
[
[
"payroll guest academic academic being holiday relate alive wrist shadow intimate inmate ocean method system repeat capital dress iris zero",
"payroll guest academic agency body type agree single amuse charity jump building medical omit fancy losing obtain again flash mandate"
],
""
],
[
[
"triumph necklace academic acid campus deadline estate corner result dress main debris research stick fawn freshman alarm obtain keyboard standard",
"triumph necklace academic agency decorate cradle charity ajar multiple station tidy cricket fiction slice midst mobile writing company chest jewelry"
],
""
],
[
[
"grumpy upgrade beard romp academic owner package lawsuit industry auction market install acrobat blimp pregnant trial agency dismiss species wildlife briefing painting mailman spine angel mental violence rainbow cowboy kidney industry friendly sled"
],
""
],
[
[
"grumpy upgrade decision shadow aspect transfer mailman intimate payroll raspy mother sack unfair soul famous ladybug leaf width evaluate acrobat envy mailman crystal rich spark forbid revenue findings permit formal sled twice unknown",
"grumpy upgrade decision spew advance column ladybug hanger ancestor cinema require material dwarf laden peanut teacher market parking typical space quantity mayor dynamic radar paid leaves swimming writing flip style fishing yield award"
],
""
],
[
[
"grumpy upgrade decision shadow aspect transfer mailman intimate payroll raspy mother sack unfair soul famous ladybug leaf width evaluate acrobat envy mailman crystal rich spark forbid revenue findings permit formal sled twice unknown",
"grumpy upgrade beard romp academic owner package lawsuit industry auction market install acrobat blimp pregnant trial agency dismiss species wildlife briefing painting mailman spine angel mental violence rainbow cowboy kidney industry friendly sled"
],
""
],
[
[
"grumpy upgrade decision scared aviation echo mama junk mama robin finance industry piece rapids leader slush swimming insect holiday document ancient tofu exotic square shaped artist easel diet thorn railroad fridge hobo dictate",
"grumpy upgrade ceramic round acrobat blue enemy admit wildlife exotic inform mustang symbolic pickup salary insect plunge miracle retailer clogs shelter focus privacy bolt carbon rebound cinema tidy lying born pregnant divorce payroll",
"grumpy upgrade decision shadow aspect transfer mailman intimate payroll raspy mother sack unfair soul famous ladybug leaf width evaluate acrobat envy mailman crystal rich spark forbid revenue findings permit formal sled twice unknown",
"grumpy upgrade ceramic skin august review medical dynamic year tackle juice percent profile magazine violence arena gums anxiety move election paper withdraw petition fangs tension segment junk pickup ranked alto animal snapshot spider",
"grumpy upgrade ceramic shaft acrobat fitness much slow vintage scout agree dismiss mustang remove violence reunion kidney campus moment quarter rocky hairy calcium machine editor living floral evidence trash tracks fridge believe transfer"
],
"cde0a3318493262591e78b8c14c6686167123b7d868483aa5b67e2fb45294834"
],
[
[
"grumpy upgrade beard romp academic owner package lawsuit industry auction market install acrobat blimp pregnant trial agency dismiss species wildlife briefing painting mailman spine angel mental violence rainbow cowboy kidney industry friendly sled",
"grumpy upgrade acrobat romp album herald epidemic rebuild alcohol rapids element receiver belong wrap liquid large script magazine pregnant election estate vegan density beard duckling counter grill island screw humidity engage wireless detect"
],
"cde0a3318493262591e78b8c14c6686167123b7d868483aa5b67e2fb45294834"
],
[
[
"grumpy upgrade decision spew advance column ladybug hanger ancestor cinema require material dwarf laden peanut teacher market parking typical space quantity mayor dynamic radar paid leaves swimming writing flip style fishing yield award",
"grumpy upgrade decision roster acquire necklace knife laundry glimpse warmth theory dynamic valuable flash slow fridge ivory usher loan volume scatter knife numerous artwork pajamas response editor deploy crucial problem material fatal various",
"grumpy upgrade beard romp academic owner package lawsuit industry auction market install acrobat blimp pregnant trial agency dismiss species wildlife briefing painting mailman spine angel mental violence rainbow cowboy kidney industry friendly sled",
"grumpy upgrade decision scared aviation echo mama junk mama robin finance industry piece rapids leader slush swimming insect holiday document ancient tofu exotic square shaped artist easel diet thorn railroad fridge hobo dictate",
"grumpy upgrade acrobat romp album herald epidemic rebuild alcohol rapids element receiver belong wrap liquid large script magazine pregnant election estate vegan density beard duckling counter grill island screw humidity engage wireless detect",
"grumpy upgrade ceramic snake ambition strike estate dance multiple clothes mixture cubic staff very husband valid burning sheriff emission raisin huge snake merchant omit firm security deadline home cleanup chubby airport forget taxi",
"grumpy upgrade ceramic skin august review medical dynamic year tackle juice percent profile magazine violence arena gums anxiety move election paper withdraw petition fangs tension segment junk pickup ranked alto animal snapshot spider",
"grumpy upgrade decision smug arena zero lunch hobo testify graduate artist seafood gums olympic unfair include counter climate alto carve ladle blind traffic huge vitamins declare patent window music romp lobe rumor silent",
"grumpy upgrade decision shadow aspect transfer mailman intimate payroll raspy mother sack unfair soul famous ladybug leaf width evaluate acrobat envy mailman crystal rich spark forbid revenue findings permit formal sled twice unknown",
"grumpy upgrade ceramic shaft acrobat fitness much slow vintage scout agree dismiss mustang remove violence reunion kidney campus moment quarter rocky hairy calcium machine editor living floral evidence trash tracks fridge believe transfer",
"grumpy upgrade decision sister actress decent keyboard imply evening valid chemical makeup resident deploy calcium undergo clay lawsuit pants single loan become wine gravity repair training says eyebrow hawk ecology camera spirit crowd",
"grumpy upgrade ceramic scatter august wits fragment very username finger ancient angel tidy mustang salary scandal rhythm liquid reunion spew standard skunk born skunk reward vintage activity amuse spray voice welcome warmth paces",
"grumpy upgrade ceramic round acrobat blue enemy admit wildlife exotic inform mustang symbolic pickup salary insect plunge miracle retailer clogs shelter focus privacy bolt carbon rebound cinema tidy lying born pregnant divorce payroll"
],
"cde0a3318493262591e78b8c14c6686167123b7d868483aa5b67e2fb45294834"
],
[
[
"home aluminum academic academic acid member verdict purchase blind camera duration email fortune forbid obtain birthday mountain distance agree"
],
""
],
[
[
"regular guest academic academic award saver tidy filter destroy infant luxury pants member boundary valid hesitate mama hairy avoid insect acrobat"
],
""
]
]

View File

@ -0,0 +1,78 @@
from common import *
from trezor.crypto import slip39
from slip39_vectors import vectors
class TestCryptoSlip39(unittest.TestCase):
MS = b"ABCDEFGHIJKLMNOP"
def test_basic_sharing_random(self):
mnemonics = slip39.generate_mnemonics_random(1, [(3, 5)])[0]
self.assertEqual(slip39.combine_mnemonics(mnemonics[1:4]), slip39.combine_mnemonics(mnemonics))
def test_basic_sharing_fixed(self):
mnemonics = slip39.generate_mnemonics(1, [(3, 5)], self.MS)[0]
identifier, exponent, ems = slip39.combine_mnemonics(mnemonics)
self.assertEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS)
self.assertEqual(slip39.combine_mnemonics(mnemonics[1:4])[2], ems)
with self.assertRaises(slip39.MnemonicError):
slip39.combine_mnemonics(mnemonics[1:3])
def test_passphrase(self):
mnemonics = slip39.generate_mnemonics(1, [(3, 5)], self.MS, b"TREZOR")[0]
identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[1:4])
self.assertEqual(slip39.decrypt(identifier, exponent, ems, b"TREZOR"), self.MS)
self.assertNotEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS)
def test_iteration_exponent(self):
mnemonics = slip39.generate_mnemonics(1, [(3, 5)], self.MS, b"TREZOR", 1)[0]
identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[1:4])
self.assertEqual(slip39.decrypt(identifier, exponent, ems, b"TREZOR"), self.MS)
self.assertNotEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS)
mnemonics = slip39.generate_mnemonics(1, [(3, 5)], self.MS, b"TREZOR", 2)[0]
identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[1:4])
self.assertEqual(slip39.decrypt(identifier, exponent, ems, b"TREZOR"), self.MS)
self.assertNotEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS)
def test_group_sharing(self):
mnemonics = slip39.generate_mnemonics(2, [(3, 5), (2, 3), (2, 5), (1, 1)], self.MS)
# All mnemonics.
identifier, exponent, ems = slip39.combine_mnemonics([mnemonic for group in mnemonics for mnemonic in group])
self.assertEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS)
# Minimal sets of mnemonics.
self.assertEqual(slip39.combine_mnemonics([mnemonics[2][0], mnemonics[2][2], mnemonics[3][0]])[2], ems)
self.assertEqual(slip39.combine_mnemonics([mnemonics[2][3], mnemonics[3][0], mnemonics[2][4]])[2], ems)
# Two complete groups and one incomplete group.
self.assertEqual(slip39.combine_mnemonics(mnemonics[0] + [mnemonics[1][1]] + mnemonics[2])[2], ems)
self.assertEqual(slip39.combine_mnemonics(mnemonics[0][1:4] + mnemonics[1][1:3] + mnemonics[2][2:4])[2], ems)
# One complete group and one incomplete group out of two groups required.
with self.assertRaises(slip39.MnemonicError):
slip39.combine_mnemonics(mnemonics[0][2:] + [mnemonics[1][0]])
# One group of two required.
with self.assertRaises(slip39.MnemonicError):
slip39.combine_mnemonics(mnemonics[0][1:4])
def test_vectors(self):
for mnemonics, secret in vectors:
if secret:
identifier, exponent, ems = slip39.combine_mnemonics(mnemonics)
self.assertEqual(slip39.decrypt(identifier, exponent, ems, b"TREZOR"), unhexlify(secret))
else:
with self.assertRaises(slip39.MnemonicError):
slip39.combine_mnemonics(mnemonics)
if __name__ == '__main__':
unittest.main()