示例#1
0
def process_slip39(words: str) -> Tuple[Optional[bytes], slip39.Share]:
    """
    Processes a single mnemonic share. Returns the encrypted master secret
    (or None if more shares are needed) and the share's group index and member index.
    """
    share = slip39.decode_mnemonic(words)

    remaining = storage.recovery.fetch_slip39_remaining_shares()

    # if this is the first share, parse and store metadata
    if not remaining:
        storage.recovery.set_slip39_group_count(share.group_count)
        storage.recovery.set_slip39_iteration_exponent(
            share.iteration_exponent)
        storage.recovery.set_slip39_identifier(share.identifier)
        storage.recovery.set_slip39_remaining_shares(share.threshold - 1,
                                                     share.group_index)
        storage.recovery_shares.set(share.index, share.group_index, words)

        # if share threshold and group threshold are 1
        # we can calculate the secret right away
        if share.threshold == 1 and share.group_threshold == 1:
            identifier, iteration_exponent, secret, _ = slip39.combine_mnemonics(
                [words])
            return secret, share
        else:
            # we need more shares
            return None, share

    # These should be checked by UI before so it's a Runtime exception otherwise
    if share.identifier != storage.recovery.get_slip39_identifier():
        raise RuntimeError("Slip39: Share identifiers do not match")
    if storage.recovery_shares.get(share.index, share.group_index):
        raise RuntimeError("Slip39: This mnemonic was already entered")

    remaining_for_share = (storage.recovery.get_slip39_remaining_shares(
        share.group_index) or share.threshold)
    storage.recovery.set_slip39_remaining_shares(remaining_for_share - 1,
                                                 share.group_index)
    remaining[share.group_index] = remaining_for_share - 1
    storage.recovery_shares.set(share.index, share.group_index, words)

    if remaining.count(0) < share.group_threshold:
        # we need more shares
        return None, share

    if share.group_count > 1:
        mnemonics = []
        for i, r in enumerate(remaining):
            # if we have multiple groups pass only the ones with threshold reached
            if r == 0:
                group = storage.recovery_shares.fetch_group(i)
                mnemonics.extend(group)
    else:
        # in case of slip39 basic we only need the first and only group
        mnemonics = storage.recovery_shares.fetch_group(0)

    identifier, iteration_exponent, secret, _ = slip39.combine_mnemonics(
        mnemonics)
    return secret, share
 def test_basic_sharing_fixed(self):
     mnemonics = slip39.generate_mnemonics(1, [(3, 5)], self.MS)[0]
     identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[:3])
     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])
示例#3
0
 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)
 def test_basic_sharing_random(self):
     ems = random.bytes(32)
     identifier = slip39.generate_random_identifier()
     mnemonics = slip39.generate_mnemonics_from_data(
         ems, identifier, 1, [(3, 5)], 1)
     mnemonics = mnemonics[0]
     self.assertEqual(slip39.combine_mnemonics(mnemonics[:3]),
                      slip39.combine_mnemonics(mnemonics[2:]))
    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)
示例#6
0
 def test_basic_sharing_fixed(self):
     generated_identifier = slip39.generate_random_identifier()
     mnemonics = slip39.generate_mnemonics_from_data(self.MS, generated_identifier, 1, [(3, 5)])
     mnemonics = mnemonics[0]
     identifier, exponent, ems = slip39.combine_mnemonics(mnemonics[:3])
     self.assertEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS)
     self.assertEqual(generated_identifier, identifier)
     self.assertEqual(slip39.combine_mnemonics(mnemonics[1:4])[2], ems)
     with self.assertRaises(slip39.MnemonicError):
         slip39.combine_mnemonics(mnemonics[1:3])
示例#7
0
    def test_iteration_exponent(self):
        identifier = slip39.generate_random_identifier()
        mnemonics = slip39.generate_mnemonics_from_data(self.MS, identifier, 1, [(3, 5)], b"TREZOR", 1)
        mnemonics = mnemonics[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)

        identifier = slip39.generate_random_identifier()
        mnemonics = slip39.generate_mnemonics_from_data(self.MS, identifier, 1, [(3, 5)], b"TREZOR", 2)
        mnemonics = mnemonics[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)
示例#8
0
def process_all(mnemonics: list) -> bytes:
    """
    Receives all mnemonics and processes it into pre-master secret which is usually then
    stored in the storage.
    """
    _, _, secret = slip39.combine_mnemonics(mnemonics)
    return secret
示例#9
0
async def load_device(ctx, msg):
    word_count = _validate(msg)
    is_slip39 = backup_types.is_slip39_word_count(word_count)

    if not is_slip39 and not msg.skip_checksum and not bip39.check(
            msg.mnemonics[0]):
        raise wire.ProcessError("Mnemonic is not valid")

    await _warn(ctx)

    if not is_slip39:  # BIP-39
        secret = msg.mnemonics[0].encode()
        backup_type = BackupType.Bip39
    else:
        identifier, iteration_exponent, secret, group_count = slip39.combine_mnemonics(
            msg.mnemonics)
        if group_count == 1:
            backup_type = BackupType.Slip39_Basic
        elif group_count > 1:
            backup_type = BackupType.Slip39_Advanced
        else:
            raise RuntimeError("Invalid group count")
        storage.device.set_slip39_identifier(identifier)
        storage.device.set_slip39_iteration_exponent(iteration_exponent)

    storage.device.store_mnemonic_secret(secret,
                                         backup_type,
                                         needs_backup=True,
                                         no_backup=False)
    storage.device.load_settings(use_passphrase=msg.passphrase_protection,
                                 label=msg.label)
    if msg.pin:
        config.change_pin(pin_to_int(""), pin_to_int(msg.pin), None, None)

    return Success(message="Device loaded")
示例#10
0
    def test_group_sharing(self):
        group_threshold = 2
        group_sizes = (5, 3, 5, 1)
        member_thresholds = (3, 2, 2, 1)
        identifier = slip39.generate_random_identifier()
        mnemonics = slip39.generate_mnemonics_from_data(
            self.MS, identifier, group_threshold, list(zip(member_thresholds, group_sizes))
        )

        # Test all valid combinations of mnemonics.
        for groups in combinations(zip(mnemonics, member_thresholds), group_threshold):
            for group1_subset in combinations(groups[0][0], groups[0][1]):
                for group2_subset in combinations(groups[1][0], groups[1][1]):
                    mnemonic_subset = list(group1_subset + group2_subset)
                    random.shuffle(mnemonic_subset)
                    identifier, exponent, ems = slip39.combine_mnemonics(mnemonic_subset)
                    self.assertEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS)


        # Minimal sets of mnemonics.
        identifier, exponent, ems = slip39.combine_mnemonics([mnemonics[2][0], mnemonics[2][2], mnemonics[3][0]])
        self.assertEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS)
        self.assertEqual(slip39.combine_mnemonics([mnemonics[2][3], mnemonics[3][0], mnemonics[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_iteration_exponent(self):
        identifier = slip39.generate_random_identifier()
        mnemonics = slip39.generate_mnemonics_from_data(
            self.EMS, identifier, 1, [(3, 5)], 1)
        mnemonics = mnemonics[0]
        identifier, exponent, ems, group_count = slip39.combine_mnemonics(
            mnemonics[1:4])
        self.assertEqual(ems, self.EMS)
        self.assertEqual(group_count, 1)

        identifier = slip39.generate_random_identifier()
        mnemonics = slip39.generate_mnemonics_from_data(
            self.EMS, identifier, 1, [(3, 5)], 2)
        mnemonics = mnemonics[0]
        identifier, exponent, ems, group_count = slip39.combine_mnemonics(
            mnemonics[1:4])
        self.assertEqual(group_count, 1)
        self.assertEqual(ems, self.EMS)
示例#12
0
 def test_passphrase(self):
     _, mnemonics = slip39.generate_mnemonics_from_data(
         self.MS, 1, [(3, 5)], b"TREZOR")
     mnemonics = mnemonics[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)
示例#13
0
def process_all(mnemonics: list) -> bytes:
    """
    Receives all mnemonics and processes it into pre-master secret which is usually then
    stored in the storage.
    """
    identifier, iteration_exponent, secret = slip39.combine_mnemonics(
        mnemonics)
    storage.set_slip39_iteration_exponent(iteration_exponent)
    storage.set_slip39_identifier(identifier)
    return secret
示例#14
0
def process_slip39(words: str) -> Optional[bytes, int, int]:
    """
    Receives single mnemonic and processes it. Returns what is then stored in storage or
    None if more shares are needed.
    """
    identifier, iteration_exponent, group_index, group_threshold, group_count, index, threshold, value = slip39.decode_mnemonic(
        words)  # TODO: use better data structure for this

    remaining = storage.recovery.fetch_slip39_remaining_shares()
    index_with_group_offset = index + group_index * _GROUP_STORAGE_OFFSET

    # if this is the first share, parse and store metadata
    if not remaining:
        storage.recovery.set_slip39_group_count(group_count)
        storage.recovery.set_slip39_group_threshold(group_threshold)
        storage.recovery.set_slip39_iteration_exponent(iteration_exponent)
        storage.recovery.set_slip39_identifier(identifier)
        storage.recovery.set_slip39_threshold(threshold)
        storage.recovery.set_slip39_remaining_shares(threshold - 1,
                                                     group_index)
        storage.recovery_shares.set(index_with_group_offset, words)

        return None, group_index, index  # we need more shares

    if remaining[group_index] == 0:
        raise GroupThresholdReachedError()
    # These should be checked by UI before so it's a Runtime exception otherwise
    if identifier != storage.recovery.get_slip39_identifier():
        raise RuntimeError("Slip39: Share identifiers do not match")
    if storage.recovery_shares.get(index_with_group_offset):
        raise RuntimeError("Slip39: This mnemonic was already entered")

    remaining_for_share = (
        storage.recovery.get_slip39_remaining_shares(group_index) or threshold)
    storage.recovery.set_slip39_remaining_shares(remaining_for_share - 1,
                                                 group_index)
    remaining[group_index] = remaining_for_share - 1
    storage.recovery_shares.set(index_with_group_offset, words)

    if remaining.count(0) < group_threshold:
        return None, group_index, index  # we need more shares

    if len(remaining) > 1:
        mnemonics = []
        for i, r in enumerate(remaining):
            # if we have multiple groups pass only the ones with threshold reached
            if r == 0:
                group = storage.recovery_shares.fetch_group(i)
                mnemonics.extend(group)
    else:
        mnemonics = storage.recovery_shares.fetch()

    identifier, iteration_exponent, secret = slip39.combine_mnemonics(
        mnemonics)
    return secret, group_index, index
示例#15
0
    def test_slip39_128(self):
        mnemonics = [
            "extra extend academic bishop cricket bundle tofu goat apart victim "
            "enlarge program behavior permit course armed jerky faint language modern",
            "extra extend academic acne away best indicate impact square oasis "
            "prospect painting voting guest either argue username racism enemy eclipse",
            "extra extend academic arcade born dive legal hush gross briefing "
            "talent drug much home firefly toxic analysis idea umbrella slice"
        ]
        passphrase = b"TREZOR"
        identifier, exponent, ems, _ = slip39.combine_mnemonics(mnemonics)
        master_secret = slip39.decrypt(identifier, exponent, ems, passphrase)

        node = bip32.from_seed(master_secret, "ed25519 cardano seed")

        node.derive_cardano(0x80000000 | 44)
        node.derive_cardano(0x80000000 | 1815)
        keychain = Keychain([0x80000000 | 44, 0x80000000 | 1815], node)

        # 44'/1815'/0'/0/i
        derivation_paths = [[
            0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, 0
        ], [0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0,
            1], [0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, 2]]

        public_keys = [
            b'bc043d84b8b891d49890edb6aced6f2d78395f255c5b6aea8878b913f83e8579',
            b'24c4fe188a39103db88818bc191fd8571eae7b284ebcbdf2462bde97b058a95c',
            b'831a63d381a8dab1e6e1ee991a4300fc70687aae5f97f4fcf92ed1b6c2bd99de'
        ]

        chain_codes = [
            b"dc3f0d2b5cccb822335ef6213fd133f4ca934151ec44a6000aee43b8a101078c",
            b"6f7a744035f4b3ddb8f861c18446169643cc3ae85e271b4b4f0eda05cf84c65b",
            b"672d6af4707aba201b7940231e83dd357f92f8851b3dfdc224ef311e1b64cdeb"
        ]

        xpub_keys = [
            "bc043d84b8b891d49890edb6aced6f2d78395f255c5b6aea8878b913f83e8579dc3f0d2b5cccb822335ef6213fd133f4ca934151ec44a6000aee43b8a101078c",
            "24c4fe188a39103db88818bc191fd8571eae7b284ebcbdf2462bde97b058a95c6f7a744035f4b3ddb8f861c18446169643cc3ae85e271b4b4f0eda05cf84c65b",
            "831a63d381a8dab1e6e1ee991a4300fc70687aae5f97f4fcf92ed1b6c2bd99de672d6af4707aba201b7940231e83dd357f92f8851b3dfdc224ef311e1b64cdeb"
        ]

        for index, derivation_path in enumerate(derivation_paths):
            key = _get_public_key(keychain, derivation_path)

            self.assertEqual(hexlify(key.node.public_key), public_keys[index])
            self.assertEqual(hexlify(key.node.chain_code), chain_codes[index])
            self.assertEqual(key.xpub, xpub_keys[index])
示例#16
0
    def test_slip39_256(self):
        mnemonics = [
            "hobo romp academic axis august founder knife legal recover alien expect "
            "emphasis loan kitchen involve teacher capture rebuild trial numb spider forward "
            "ladle lying voter typical security quantity hawk legs idle leaves gasoline",
            "hobo romp academic agency ancestor industry argue sister scene midst graduate "
            "profile numb paid headset airport daisy flame express scene usual welcome "
            "quick silent downtown oral critical step remove says rhythm venture aunt"
        ]
        passphrase = b"TREZOR"
        identifier, exponent, ems, _ = slip39.combine_mnemonics(mnemonics)
        master_secret = slip39.decrypt(identifier, exponent, ems, passphrase)

        node = bip32.from_seed(master_secret, "ed25519 cardano seed")

        node.derive_cardano(0x80000000 | 44)
        node.derive_cardano(0x80000000 | 1815)
        keychain = Keychain([0x80000000 | 44, 0x80000000 | 1815], node)

        # 44'/1815'/0'/0/i
        derivation_paths = [[
            0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, 0
        ], [0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0,
            1], [0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, 2]]

        public_keys = [
            b"967a9a041ad1379e31c2c7f2aa4bc2b3f7769341c0ea89ccfb12a904f2e10877",
            b"6f3805bbc1b7a75afa95dffec331671f3c4662800615e80d2ec1202a9d874c86",
            b"7f145b50ef07fb9accc40ee07a01fe93ceb6fa07d5a9f20fc3c8a48246dd4d02",
        ]

        chain_codes = [
            b"7b15d8d9006afe3cd7e04f375a1126a8c7c7c07c59a6f0c5b0310f4245f4edbb",
            b"44baf30fd549e6a1e05f99c2a2c8971aea8894ee8d9c5fc2c5ae6ee839a56b2d",
            b"e67d2864614ada5eec8fb8ee1225a94a6fb0a1b3c347c854ec3037351c6a0fc7",
        ]

        xpub_keys = [
            "967a9a041ad1379e31c2c7f2aa4bc2b3f7769341c0ea89ccfb12a904f2e108777b15d8d9006afe3cd7e04f375a1126a8c7c7c07c59a6f0c5b0310f4245f4edbb",
            "6f3805bbc1b7a75afa95dffec331671f3c4662800615e80d2ec1202a9d874c8644baf30fd549e6a1e05f99c2a2c8971aea8894ee8d9c5fc2c5ae6ee839a56b2d",
            "7f145b50ef07fb9accc40ee07a01fe93ceb6fa07d5a9f20fc3c8a48246dd4d02e67d2864614ada5eec8fb8ee1225a94a6fb0a1b3c347c854ec3037351c6a0fc7",
        ]

        for index, derivation_path in enumerate(derivation_paths):
            key = _get_public_key(keychain, derivation_path)

            self.assertEqual(hexlify(key.node.public_key), public_keys[index])
            self.assertEqual(hexlify(key.node.chain_code), chain_codes[index])
            self.assertEqual(key.xpub, xpub_keys[index])
    def test_group_sharing_threshold_1(self):
        group_threshold = 1
        group_sizes = (5, 3, 5, 1)
        member_thresholds = (3, 2, 2, 1)
        mnemonics = slip39.generate_mnemonics(
            group_threshold, list(zip(member_thresholds, group_sizes)), self.MS
        )

        # Test all valid combinations of mnemonics.
        for group, threshold in zip(mnemonics, member_thresholds):
            for group_subset in combinations(group, threshold):
                mnemonic_subset = list(group_subset)
                random.shuffle(mnemonic_subset)
                identifier, exponent, ems = slip39.combine_mnemonics(mnemonic_subset)
                self.assertEqual(slip39.decrypt(identifier, exponent, ems, b""), self.MS)
示例#18
0
async def load_device(ctx, msg):
    if storage.is_initialized():
        raise wire.UnexpectedMessage("Already initialized")

    if msg.node is not None:
        raise wire.ProcessError("LoadDevice.node is not supported")

    if not msg.mnemonics:
        raise wire.ProcessError("No mnemonic provided")

    word_count = len(msg.mnemonics[0].split(" "))
    for m in msg.mnemonics[1:]:
        if word_count != len(m.split(" ")):
            raise wire.ProcessError(
                "All shares are required to have the same number of words")

    mnemonic_type = mnemonic.type_from_word_count(word_count)

    if (mnemonic_type == mnemonic.TYPE_BIP39 and not msg.skip_checksum
            and not bip39.check(msg.mnemonics[0])):
        raise wire.ProcessError("Mnemonic is not valid")

    text = Text("Loading seed")
    text.bold("Loading private seed", "is not recommended.")
    text.normal("Continue only if you", "know what you are doing!")
    await require_confirm(ctx, text)

    if mnemonic_type == mnemonic.TYPE_BIP39:
        secret = msg.mnemonics[0].encode()
    elif mnemonic_type == mnemonic.TYPE_SLIP39:
        identifier, iteration_exponent, secret = slip39.combine_mnemonics(
            msg.mnemonics)
        storage.device.set_slip39_identifier(identifier)
        storage.device.set_slip39_iteration_exponent(iteration_exponent)
    else:
        raise RuntimeError("Unknown mnemonic type")

    storage.device.store_mnemonic_secret(secret,
                                         mnemonic_type,
                                         needs_backup=True,
                                         no_backup=False)
    storage.device.load_settings(use_passphrase=msg.passphrase_protection,
                                 label=msg.label)
    if msg.pin:
        config.change_pin(pin_to_int(""), pin_to_int(msg.pin))

    return Success(message="Device loaded")
    def test_group_sharing_threshold_1(self):
        group_threshold = 1
        group_sizes = (5, 3, 5, 1)
        member_thresholds = (3, 2, 2, 1)
        identifier = slip39.generate_random_identifier()
        mnemonics = slip39.generate_mnemonics_from_data(
            self.EMS, identifier, group_threshold,
            list(zip(member_thresholds, group_sizes)), 1)

        # Test all valid combinations of mnemonics.
        for group, threshold in zip(mnemonics, member_thresholds):
            for group_subset in combinations(group, threshold):
                mnemonic_subset = list(group_subset)
                random.shuffle(mnemonic_subset)
                identifier, exponent, ems, group_count = slip39.combine_mnemonics(
                    mnemonic_subset)
                self.assertEqual(group_count, len(group_sizes))
                self.assertEqual(ems, self.EMS)
示例#20
0
def process_single(mnemonic: str) -> bytes:
    """
    Receives single mnemonic and processes it. Returns what is then stored in storage or
    None if more shares are needed.
    """
    identifier, iteration_exponent, _, _, _, index, threshold, value = slip39.decode_mnemonic(
        mnemonic)  # TODO: use better data structure for this
    if threshold == 1:
        raise ValueError("Threshold equal to 1 is not allowed.")

    # if recovery is not in progress already, start it and wait for more mnemonics
    if not storage.is_slip39_in_progress():
        storage.set_slip39_in_progress(True)
        storage.set_slip39_iteration_exponent(iteration_exponent)
        storage.set_slip39_identifier(identifier)
        storage.set_slip39_threshold(threshold)
        storage.set_slip39_remaining(threshold - 1)
        storage.set_slip39_words_count(len(mnemonic.split()))
        storage.set_slip39_mnemonic(index, mnemonic)
        return None  # we need more shares

    # check identifier and member index of this share against stored values
    if identifier != storage.get_slip39_identifier():
        # TODO: improve UX (tell user)
        raise ValueError("Share identifiers do not match")
    if storage.get_slip39_mnemonic(index):
        # TODO: improve UX (tell user)
        raise ValueError("This mnemonic was already entered")

    # append to storage
    remaining = storage.get_slip39_remaining() - 1
    storage.set_slip39_remaining(remaining)
    storage.set_slip39_mnemonic(index, mnemonic)
    if remaining != 0:
        return None  # we need more shares

    # combine shares and return the master secret
    mnemonics = storage.get_slip39_mnemonics()
    if len(mnemonics) != threshold:
        raise ValueError("Some mnemonics are still missing.")
    _, _, secret = slip39.combine_mnemonics(mnemonics)
    return secret
示例#21
0
def _process_slip39(words: str) -> Optional[bytes]:
    """
    Receives single mnemonic and processes it. Returns what is then stored in storage or
    None if more shares are needed.
    """
    identifier, iteration_exponent, _, _, _, index, threshold, value = slip39.decode_mnemonic(
        words)  # TODO: use better data structure for this
    if threshold == 1:
        raise ValueError("Threshold equal to 1 is not allowed.")

    remaining = storage.recovery.get_remaining()

    # if this is the first share, parse and store metadata
    if not remaining:
        storage.recovery.set_slip39_iteration_exponent(iteration_exponent)
        storage.recovery.set_slip39_identifier(identifier)
        storage.recovery.set_slip39_threshold(threshold)
        storage.recovery.set_remaining(threshold - 1)
        storage.recovery_shares.set(index, words)
        return None  # we need more shares

    # These should be checked by UI before so it's a Runtime exception otherwise
    if identifier != storage.recovery.get_slip39_identifier():
        raise RuntimeError("Slip39: Share identifiers do not match")
    if storage.recovery_shares.get(index):
        raise RuntimeError("Slip39: This mnemonic was already entered")

    # add mnemonic to storage
    remaining -= 1
    storage.recovery.set_remaining(remaining)
    storage.recovery_shares.set(index, words)
    if remaining != 0:
        return None  # we need more shares

    # combine shares and return the master secret
    mnemonics = storage.recovery_shares.fetch()
    identifier, iteration_exponent, secret = slip39.combine_mnemonics(
        mnemonics)
    return secret
 def test_basic_sharing_random(self):
     mnemonics = slip39.generate_mnemonics_random(1, [(3, 5)])[0]
     self.assertEqual(slip39.combine_mnemonics(mnemonics[:3]),
                      slip39.combine_mnemonics(mnemonics[2:]))
示例#23
0
 def test_basic_sharing_random(self):
     ms = random.bytes(32)
     _, mnemonics = slip39.generate_mnemonics_from_data(ms, 1, [(3, 5)])
     mnemonics = mnemonics[0]
     self.assertEqual(slip39.combine_mnemonics(mnemonics[:3]),
                      slip39.combine_mnemonics(mnemonics[2:]))
    def test_slip39_128(self):
        mnemonics = [
            "extra extend academic bishop cricket bundle tofu goat apart victim "
                "enlarge program behavior permit course armed jerky faint language modern",
            "extra extend academic acne away best indicate impact square oasis "
                "prospect painting voting guest either argue username racism enemy eclipse",
            "extra extend academic arcade born dive legal hush gross briefing "
                "talent drug much home firefly toxic analysis idea umbrella slice"
        ]
        passphrase = b"TREZOR"
        identifier, exponent, ems = slip39.combine_mnemonics(mnemonics)
        master_secret = slip39.decrypt(identifier, exponent, ems, passphrase)

        node = bip32.from_seed(master_secret, "ed25519 cardano seed")

        # Check root node.
        root_priv = b"c0fe4a6973df4de06262693fc9186f71faf292960350882d49456bf108d13954"
        root_ext = b"4064253ffefc4127489bce1b825a47329010c5afb4d21154ef949ef786204405"
        root_pub = b"83e3ecaf57f90f022c45e10d1b8cb78499c30819515ad9a81ad82139fdb12a90"
        root_chain = b"22c12755afdd192742613b3062069390743ea232bc1b366c8f41e37292af9305"

        self.assertEqual(hexlify(node.private_key()), root_priv)
        self.assertEqual(hexlify(node.private_key_ext()), root_ext)
        self.assertEqual(hexlify(seed.remove_ed25519_prefix(node.public_key())), root_pub)
        self.assertEqual(hexlify(node.chain_code()), root_chain)

        # Check derived nodes and addresses.
        node.derive_cardano(0x80000000 | 44)
        node.derive_cardano(0x80000000 | 1815)
        keychain = Keychain([0x80000000 | 44, 0x80000000 | 1815], node)

        nodes = [
            (
                "Ae2tdPwUPEYxF9NAMNdd3v2LZoMeWp7gCZiDb6bZzFQeeVASzoP7HC4V9s6",
                b"e0acfe234aa6e1219ce7d3d8d91853e0808bab92ecb8a0ff0f345ff31ad13954",
                b"ff89dc71365c4b67bb7bb75d566e65b8a95f16e4d70cce51c25937db15614530",
                b"bc043d84b8b891d49890edb6aced6f2d78395f255c5b6aea8878b913f83e8579",
                b"dc3f0d2b5cccb822335ef6213fd133f4ca934151ec44a6000aee43b8a101078c",
            ),
            (
                "Ae2tdPwUPEZ1TjYcvfkWAbiHtGVxv4byEHHZoSyQXjPJ362DifCe1ykgqgy",
                b"d0ce3e7a6445bc91801319b9bbaf47fdfca9364257295fb13bc5046a20d13954",
                b"c800359abdc875944754ae7368bab7ef75184d48816c368f5a28af4bcf1d1ee8",
                b"24c4fe188a39103db88818bc191fd8571eae7b284ebcbdf2462bde97b058a95c",
                b"6f7a744035f4b3ddb8f861c18446169643cc3ae85e271b4b4f0eda05cf84c65b",
            ),
            (
                "Ae2tdPwUPEZGXmSbda1kBNfyhRQGRcQxJFdk7mhWZXAGnapyejv2b2U3aRb",
                b"e8320644cce22a6e9fc33865fc5a598b1cda061c47a548aead3af4ed1cd13954",
                b"9e2ece5d7fe8119cb76090009be926a84fc5d3b95855b5962ffe2f880836cf09",
                b"831a63d381a8dab1e6e1ee991a4300fc70687aae5f97f4fcf92ed1b6c2bd99de",
                b"672d6af4707aba201b7940231e83dd357f92f8851b3dfdc224ef311e1b64cdeb"
            )
        ]

        for i, (address, priv, ext, pub, chain) in enumerate(nodes):
            # 44'/1815'/0'/0/i
            a, n = derive_address_and_node(keychain, [0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, i])
            self.assertEqual(a, address)
            self.assertEqual(hexlify(n.private_key()), priv)
            self.assertEqual(hexlify(n.private_key_ext()), ext)
            self.assertEqual(hexlify(seed.remove_ed25519_prefix(n.public_key())), pub)
            self.assertEqual(hexlify(n.chain_code()), chain)
    def test_slip39_256(self):
        mnemonics = [
            "hobo romp academic axis august founder knife legal recover alien expect "
                "emphasis loan kitchen involve teacher capture rebuild trial numb spider forward "
                "ladle lying voter typical security quantity hawk legs idle leaves gasoline",
            "hobo romp academic agency ancestor industry argue sister scene midst graduate "
                "profile numb paid headset airport daisy flame express scene usual welcome "
                "quick silent downtown oral critical step remove says rhythm venture aunt"
        ]
        passphrase = b"TREZOR"
        identifier, exponent, ems = slip39.combine_mnemonics(mnemonics)
        master_secret = slip39.decrypt(identifier, exponent, ems, passphrase)

        node = bip32.from_seed(master_secret, "ed25519 cardano seed")

        # Check root node.
        root_priv = b"90633724b5daf770a8b420b8658e7d8bc21e066b60ec8cd4d5730681cc294e4f"
        root_ext = b"f9d99bf3cd9c7e12663e8646afa40cb3aecf15d91f2abc15d21056c6bccb3414"
        root_pub = b"eea170f0ef97b59d22907cb429888029721ed67d3e7a1b56b81731086ab7db64"
        root_chain = b"04f1de750b62725fcc1ae1b93ca4063acb53c486b959cadaa100ebd7828e5460"

        self.assertEqual(hexlify(node.private_key()), root_priv)
        self.assertEqual(hexlify(node.private_key_ext()), root_ext)
        self.assertEqual(hexlify(seed.remove_ed25519_prefix(node.public_key())), root_pub)
        self.assertEqual(hexlify(node.chain_code()), root_chain)

        # Check derived nodes and addresses.
        node.derive_cardano(0x80000000 | 44)
        node.derive_cardano(0x80000000 | 1815)
        keychain = Keychain([0x80000000 | 44, 0x80000000 | 1815], node)

        nodes = [
            (
                "Ae2tdPwUPEYyDD1C2FbVJFAE3FuAxLspfMYt29TJ1urnSKr57cVhEcioSCC",
                b"38e8a4b17ca07b6a309f1cee83f87593e34a1fc3a289785ea451ef65df294e4f",
                b"405d10ef71c2b0019250d11837de8db825d8556bf1e57f8866920af6d8c90002",
                b"967a9a041ad1379e31c2c7f2aa4bc2b3f7769341c0ea89ccfb12a904f2e10877",
                b"7b15d8d9006afe3cd7e04f375a1126a8c7c7c07c59a6f0c5b0310f4245f4edbb",
            ),
            (
                "Ae2tdPwUPEZHJGtyz47F6wD7qAegt1JNRJWuiE36QLvFzeqJPBZ2EBvhr8M",
                b"a09f90e3f76a7bdb7f8721cc0c142dbd6398fd704b83455e123fa886dc294e4f",
                b"917e4166bb404def9f12634e84ecbcb98afdea051ba7c38745e208178a9e9baf",
                b"6f3805bbc1b7a75afa95dffec331671f3c4662800615e80d2ec1202a9d874c86",
                b"44baf30fd549e6a1e05f99c2a2c8971aea8894ee8d9c5fc2c5ae6ee839a56b2d",
            ),
            (
                "Ae2tdPwUPEYxD9xNPBJTzYmtFVVWEPB6KW4TCDijQ4pDwU11wt5621PyCi4",
                b"78dd824aea33bed5c1502d1a17f11a4adbe923aac1cd1f7ae98c9506db294e4f",
                b"ddfe7f27e2894b983df773d8ac2a07973fc37ff36e93a2f2d71fb7327d4e18f4",
                b"7f145b50ef07fb9accc40ee07a01fe93ceb6fa07d5a9f20fc3c8a48246dd4d02",
                b"e67d2864614ada5eec8fb8ee1225a94a6fb0a1b3c347c854ec3037351c6a0fc7",
            )
        ]

        for i, (address, priv, ext, pub, chain) in enumerate(nodes):
            # 44'/1815'/0'/0/i
            a, n = derive_address_and_node(keychain, [0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, i])
            self.assertEqual(a, address)
            self.assertEqual(hexlify(n.private_key()), priv)
            self.assertEqual(hexlify(n.private_key_ext()), ext)
            self.assertEqual(hexlify(seed.remove_ed25519_prefix(n.public_key())), pub)
            self.assertEqual(hexlify(n.chain_code()), chain)