|
|
|
@ -67,10 +67,13 @@ def _xor(a: bytes, b: bytes) -> bytes:
|
|
|
|
|
_ID_LENGTH_BITS = const(15)
|
|
|
|
|
"""The length of the random identifier in bits."""
|
|
|
|
|
|
|
|
|
|
_ITERATION_EXP_LENGTH_BITS = const(5)
|
|
|
|
|
_EXTENDABLE_FLAG_LENGTH_BITS = const(4)
|
|
|
|
|
"""The length of the extendable backup flag in bits."""
|
|
|
|
|
|
|
|
|
|
_ITERATION_EXP_LENGTH_BITS = const(4)
|
|
|
|
|
"""The length of the iteration exponent in bits."""
|
|
|
|
|
|
|
|
|
|
_ID_EXP_LENGTH_WORDS = _bits_to_words(_ID_LENGTH_BITS + _ITERATION_EXP_LENGTH_BITS)
|
|
|
|
|
_ID_EXP_LENGTH_WORDS = _bits_to_words(_ID_LENGTH_BITS + _EXTENDABLE_FLAG_LENGTH_BITS + _ITERATION_EXP_LENGTH_BITS)
|
|
|
|
|
"""The length of the random identifier and iteration exponent in words."""
|
|
|
|
|
|
|
|
|
|
_CHECKSUM_LENGTH_WORDS = const(3)
|
|
|
|
@ -111,6 +114,7 @@ MAX_GROUP_COUNT = const(16)
|
|
|
|
|
"""The maximum number of groups that can be created."""
|
|
|
|
|
|
|
|
|
|
DEFAULT_ITERATION_EXPONENT = const(1)
|
|
|
|
|
DEFAULT_EXTENDABLE_FLAG = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Share:
|
|
|
|
@ -121,6 +125,7 @@ class Share:
|
|
|
|
|
def __init__(
|
|
|
|
|
self,
|
|
|
|
|
identifier: int,
|
|
|
|
|
extendable: bool,
|
|
|
|
|
iteration_exponent: int,
|
|
|
|
|
group_index: int,
|
|
|
|
|
group_threshold: int,
|
|
|
|
@ -130,6 +135,7 @@ class Share:
|
|
|
|
|
share_value: bytes,
|
|
|
|
|
):
|
|
|
|
|
self.identifier = identifier
|
|
|
|
|
self.extendable = extendable
|
|
|
|
|
self.iteration_exponent = iteration_exponent
|
|
|
|
|
self.group_index = group_index
|
|
|
|
|
self.group_threshold = group_threshold
|
|
|
|
@ -144,6 +150,7 @@ def decrypt(
|
|
|
|
|
passphrase: bytes,
|
|
|
|
|
iteration_exponent: int,
|
|
|
|
|
identifier: int,
|
|
|
|
|
extendable: bool,
|
|
|
|
|
progress_callback: Callable[[int, int], None] | None = None,
|
|
|
|
|
) -> bytes:
|
|
|
|
|
"""
|
|
|
|
@ -154,7 +161,7 @@ def decrypt(
|
|
|
|
|
"""
|
|
|
|
|
l = encrypted_master_secret[: len(encrypted_master_secret) // 2]
|
|
|
|
|
r = encrypted_master_secret[len(encrypted_master_secret) // 2 :]
|
|
|
|
|
salt = _get_salt(identifier)
|
|
|
|
|
salt = _get_salt(identifier, extendable)
|
|
|
|
|
for i in reversed(range(_ROUND_COUNT)):
|
|
|
|
|
(l, r) = (
|
|
|
|
|
r,
|
|
|
|
@ -176,6 +183,7 @@ def split_ems(
|
|
|
|
|
group_threshold: int, # The number of groups required to reconstruct the master secret.
|
|
|
|
|
groups: list[tuple[int, int]], # A list of (member_threshold, member_count).
|
|
|
|
|
identifier: int,
|
|
|
|
|
extendable: bool,
|
|
|
|
|
iteration_exponent: int,
|
|
|
|
|
encrypted_master_secret: bytes, # The encrypted master secret to split.
|
|
|
|
|
) -> list[list[str]]:
|
|
|
|
@ -216,6 +224,7 @@ def split_ems(
|
|
|
|
|
group_mnemonics.append(
|
|
|
|
|
_encode_mnemonic(
|
|
|
|
|
identifier,
|
|
|
|
|
extendable,
|
|
|
|
|
iteration_exponent,
|
|
|
|
|
group_index,
|
|
|
|
|
group_threshold,
|
|
|
|
@ -229,11 +238,11 @@ def split_ems(
|
|
|
|
|
return mnemonics
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def recover_ems(mnemonics: list[str]) -> tuple[int, int, bytes]:
|
|
|
|
|
def recover_ems(mnemonics: list[str]) -> tuple[int, bool, int, bytes]:
|
|
|
|
|
"""
|
|
|
|
|
Combines mnemonic shares to obtain the encrypted master secret which was previously
|
|
|
|
|
split using Shamir's secret sharing scheme.
|
|
|
|
|
Returns identifier, iteration exponent and the encrypted master secret.
|
|
|
|
|
Returns identifier, extendable backup flag, iteration exponent and the encrypted master secret.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
if not mnemonics:
|
|
|
|
@ -241,6 +250,7 @@ def recover_ems(mnemonics: list[str]) -> tuple[int, int, bytes]:
|
|
|
|
|
|
|
|
|
|
(
|
|
|
|
|
identifier,
|
|
|
|
|
extendable,
|
|
|
|
|
iteration_exponent,
|
|
|
|
|
group_threshold,
|
|
|
|
|
_group_count,
|
|
|
|
@ -264,7 +274,7 @@ def recover_ems(mnemonics: list[str]) -> tuple[int, int, bytes]:
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
encrypted_master_secret = _recover_secret(group_threshold, group_shares)
|
|
|
|
|
return identifier, iteration_exponent, encrypted_master_secret
|
|
|
|
|
return identifier, extendable, iteration_exponent, encrypted_master_secret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def decode_mnemonic(mnemonic: str) -> Share:
|
|
|
|
@ -285,7 +295,8 @@ def decode_mnemonic(mnemonic: str) -> Share:
|
|
|
|
|
raise MnemonicError("Invalid mnemonic checksum.")
|
|
|
|
|
|
|
|
|
|
id_exp_int = _int_from_indices(mnemonic_data[:_ID_EXP_LENGTH_WORDS])
|
|
|
|
|
identifier = id_exp_int >> _ITERATION_EXP_LENGTH_BITS
|
|
|
|
|
identifier = id_exp_int >> (_EXTENDABLE_FLAG_LENGTH_BITS + _ITERATION_EXP_LENGTH_BITS)
|
|
|
|
|
extendable = bool((id_exp_int >> _ITERATION_EXP_LENGTH_BITS) & 1)
|
|
|
|
|
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]
|
|
|
|
@ -312,6 +323,7 @@ def decode_mnemonic(mnemonic: str) -> Share:
|
|
|
|
|
|
|
|
|
|
return Share(
|
|
|
|
|
identifier,
|
|
|
|
|
extendable,
|
|
|
|
|
iteration_exponent,
|
|
|
|
|
group_index,
|
|
|
|
|
group_threshold + 1,
|
|
|
|
@ -407,11 +419,11 @@ def _round_function(i: int, passphrase: bytes, e: int, salt: bytes, r: bytes) ->
|
|
|
|
|
).key()[: len(r)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _get_salt(identifier: int) -> bytes:
|
|
|
|
|
return _CUSTOMIZATION_STRING + identifier.to_bytes(
|
|
|
|
|
_bits_to_bytes(_ID_LENGTH_BITS), "big"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def _get_salt(identifier: int, extendable: bool) -> bytes:
|
|
|
|
|
salt = _CUSTOMIZATION_STRING
|
|
|
|
|
if not extendable:
|
|
|
|
|
salt += identifier.to_bytes(_bits_to_bytes(_ID_LENGTH_BITS), "big")
|
|
|
|
|
return salt
|
|
|
|
|
|
|
|
|
|
def _create_digest(random_data: bytes, shared_secret: bytes) -> bytes:
|
|
|
|
|
from trezor.crypto import hmac
|
|
|
|
@ -479,12 +491,13 @@ def _recover_secret(threshold: int, shares: list[tuple[int, bytes]]) -> bytes:
|
|
|
|
|
|
|
|
|
|
def _group_prefix(
|
|
|
|
|
identifier: int,
|
|
|
|
|
extendable: bool,
|
|
|
|
|
iteration_exponent: int,
|
|
|
|
|
group_index: int,
|
|
|
|
|
group_threshold: int,
|
|
|
|
|
group_count: int,
|
|
|
|
|
) -> Indices:
|
|
|
|
|
id_exp_int = (identifier << _ITERATION_EXP_LENGTH_BITS) + iteration_exponent
|
|
|
|
|
id_exp_int = (identifier << (_EXTENDABLE_FLAG_LENGTH_BITS + _ITERATION_EXP_LENGTH_BITS)) + (int(extendable) << _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),
|
|
|
|
|
)
|
|
|
|
@ -492,6 +505,7 @@ def _group_prefix(
|
|
|
|
|
|
|
|
|
|
def _encode_mnemonic(
|
|
|
|
|
identifier: int,
|
|
|
|
|
extendable: bool,
|
|
|
|
|
iteration_exponent: int,
|
|
|
|
|
group_index: int, # The x coordinate of the group share.
|
|
|
|
|
group_threshold: int, # The number of group shares needed to reconstruct the encrypted master secret.
|
|
|
|
@ -511,7 +525,7 @@ def _encode_mnemonic(
|
|
|
|
|
|
|
|
|
|
share_data = (
|
|
|
|
|
_group_prefix(
|
|
|
|
|
identifier, iteration_exponent, group_index, group_threshold, group_count
|
|
|
|
|
identifier, extendable, iteration_exponent, group_index, group_threshold, group_count
|
|
|
|
|
)
|
|
|
|
|
+ (
|
|
|
|
|
(((group_count - 1) & 3) << 8)
|
|
|
|
@ -527,8 +541,9 @@ def _encode_mnemonic(
|
|
|
|
|
|
|
|
|
|
def _decode_mnemonics(
|
|
|
|
|
mnemonics: list[str],
|
|
|
|
|
) -> tuple[int, int, int, int, MnemonicGroups]:
|
|
|
|
|
) -> tuple[int, bool, int, int, int, MnemonicGroups]:
|
|
|
|
|
identifiers = set()
|
|
|
|
|
extendable_flags = set()
|
|
|
|
|
iteration_exponents = set()
|
|
|
|
|
group_thresholds = set()
|
|
|
|
|
group_counts = set()
|
|
|
|
@ -538,6 +553,7 @@ def _decode_mnemonics(
|
|
|
|
|
for mnemonic in mnemonics:
|
|
|
|
|
share = decode_mnemonic(mnemonic)
|
|
|
|
|
identifiers.add(share.identifier)
|
|
|
|
|
extendable_flags.add(share.extendable)
|
|
|
|
|
iteration_exponents.add(share.iteration_exponent)
|
|
|
|
|
group_thresholds.add(share.group_threshold)
|
|
|
|
|
group_counts.add(share.group_count)
|
|
|
|
@ -548,7 +564,7 @@ def _decode_mnemonics(
|
|
|
|
|
)
|
|
|
|
|
group[1].add((share.index, share.share_value))
|
|
|
|
|
|
|
|
|
|
if len(identifiers) != 1 or len(iteration_exponents) != 1:
|
|
|
|
|
if len(identifiers) != 1 or len(extendable_flags) != 1 or len(iteration_exponents) != 1:
|
|
|
|
|
raise MnemonicError(
|
|
|
|
|
f"Invalid set of mnemonics. All mnemonics must begin with the same {_ID_EXP_LENGTH_WORDS} words."
|
|
|
|
|
)
|
|
|
|
@ -571,6 +587,7 @@ def _decode_mnemonics(
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
identifiers.pop(),
|
|
|
|
|
extendable_flags.pop(),
|
|
|
|
|
iteration_exponents.pop(),
|
|
|
|
|
group_thresholds.pop(),
|
|
|
|
|
group_counts.pop(),
|
|
|
|
|