async def _get_secret(ctx: wire.Context, cache_entry: int) -> bytes: secret = cache.get(cache_entry) if secret is None: await derive_and_store_roots(ctx) secret = cache.get(cache_entry) assert secret is not None return secret
def test_Initialize(self): def call_Initialize(**kwargs): msg = Initialize(**kwargs) return await_result(handle_Initialize(DUMMY_CONTEXT, msg)) # calling Initialize without an ID allocates a new one session_id = cache.start_session() features = call_Initialize() self.assertNotEqual(session_id, features.session_id) # calling Initialize with the current ID does not allocate a new one features = call_Initialize(session_id=session_id) self.assertEqual(session_id, features.session_id) # store "hello" cache.set(KEY, b"hello") # check that it is cleared features = call_Initialize() session_id = features.session_id self.assertIsNone(cache.get(KEY)) # store "hello" again cache.set(KEY, b"hello") self.assertEqual(cache.get(KEY), b"hello") # supplying a different session ID starts a new cache call_Initialize(session_id=b"A" * cache._SESSION_ID_LENGTH) self.assertIsNone(cache.get(KEY)) # but resuming a session loads the previous one call_Initialize(session_id=session_id) self.assertEqual(cache.get(KEY), b"hello")
def test_start_session(self): session_id_a = cache.start_session() self.assertIsNotNone(session_id_a) session_id_b = cache.start_session() self.assertNotEqual(session_id_a, session_id_b) cache.clear_all() with self.assertRaises(cache.InvalidSessionError): cache.set(KEY, "something") with self.assertRaises(cache.InvalidSessionError): cache.get(KEY)
def test_end_session(self): session_id = cache.start_session() self.assertTrue(cache.is_session_started()) cache.set(KEY, b"A") cache.end_current_session() self.assertFalse(cache.is_session_started()) self.assertRaises(cache.InvalidSessionError, cache.get, KEY) # ending an ended session should be a no-op cache.end_current_session() self.assertFalse(cache.is_session_started()) session_id_a = cache.start_session(session_id) # original session no longer exists self.assertNotEqual(session_id_a, session_id) # original session data no longer exists self.assertIsNone(cache.get(KEY)) # create a new session session_id_b = cache.start_session() # switch back to original session session_id = cache.start_session(session_id_a) self.assertEqual(session_id, session_id_a) # end original session cache.end_current_session() # switch back to B session_id = cache.start_session(session_id_b) self.assertEqual(session_id, session_id_b)
def test_EndSession(self): self.assertRaises(cache.InvalidSessionError, cache.get, KEY) session_id = cache.start_session() self.assertTrue(cache.is_session_started()) self.assertIsNone(cache.get(KEY)) await_result(handle_EndSession(DUMMY_CONTEXT, EndSession())) self.assertFalse(cache.is_session_started()) self.assertRaises(cache.InvalidSessionError, cache.get, KEY)
def test_session_queue(self): session_id = cache.start_session() self.assertEqual(cache.start_session(session_id), session_id) cache.set(KEY, b"A") for i in range(cache._MAX_SESSIONS_COUNT): cache.start_session() self.assertNotEqual(cache.start_session(session_id), session_id) self.assertIsNone(cache.get(KEY))
async def get_keychain(ctx: wire.Context, namespaces: list) -> Keychain: if not storage.is_initialized(): raise wire.NotInitialized("Device is not initialized") seed = cache.get(cache.APP_COMMON_SEED) if seed is None: passphrase = await get_passphrase(ctx) seed = mnemonic.get_seed(passphrase) cache.set(cache.APP_COMMON_SEED, seed) keychain = Keychain(seed, namespaces) return keychain
def derive_slip21_node_without_passphrase(path: list) -> Slip21Node: if not storage.is_initialized(): raise Exception("Device is not initialized") seed = cache.get(cache.APP_COMMON_SEED_WITHOUT_PASSPHRASE) if seed is None: seed = mnemonic.get_seed(progress_bar=False) cache.set(cache.APP_COMMON_SEED_WITHOUT_PASSPHRASE, seed) node = Slip21Node(seed) node.derive_path(path) return node
def derive_node_without_passphrase( path: list, curve_name: str = "secp256k1" ) -> bip32.HDNode: if not storage.is_initialized(): raise Exception("Device is not initialized") seed = cache.get(cache.APP_COMMON_SEED_WITHOUT_PASSPHRASE) if seed is None: seed = mnemonic.get_seed(progress_bar=False) cache.set(cache.APP_COMMON_SEED_WITHOUT_PASSPHRASE, seed) node = bip32.from_seed(seed, curve_name) node.derive_path(path) return node
def test_empty_value(self): cache.start_session() self.assertIsNone(cache.get(KEY)) cache.set(KEY, b"") self.assertEqual(cache.get(KEY), b"") cache.delete(KEY) run_count = 0 @cache.stored(KEY) def func(): nonlocal run_count run_count += 1 return b"" self.assertEqual(func(), b"") # function gets called once self.assertEqual(run_count, 1) self.assertEqual(func(), b"") # function is not called for a second time self.assertEqual(run_count, 1)
def test_decorators(self): run_count = 0 cache.start_session() @cache.stored(KEY) def func(): nonlocal run_count run_count += 1 return b"foo" # cache is empty self.assertIsNone(cache.get(KEY)) self.assertEqual(run_count, 0) self.assertEqual(func(), b"foo") # function was run self.assertEqual(run_count, 1) self.assertEqual(cache.get(KEY), b"foo") # function does not run again but returns cached value self.assertEqual(func(), b"foo") self.assertEqual(run_count, 1) @cache.stored_async(KEY) async def async_func(): nonlocal run_count run_count += 1 return b"bar" # cache is still full self.assertEqual(await_result(async_func()), b"foo") self.assertEqual(run_count, 1) cache.start_session() self.assertEqual(await_result(async_func()), b"bar") self.assertEqual(run_count, 2) # awaitable is also run only once self.assertEqual(await_result(async_func()), b"bar") self.assertEqual(run_count, 2)
def test_delete(self): session_id1 = cache.start_session() self.assertIsNone(cache.get(KEY)) cache.set(KEY, b"hello") self.assertEqual(cache.get(KEY), b"hello") cache.delete(KEY) self.assertIsNone(cache.get(KEY)) cache.set(KEY, b"hello") session_id2 = cache.start_session() self.assertIsNone(cache.get(KEY)) cache.set(KEY, b"hello") self.assertEqual(cache.get(KEY), b"hello") cache.delete(KEY) self.assertIsNone(cache.get(KEY)) cache.start_session(session_id1) self.assertEqual(cache.get(KEY), b"hello")
async def _get_keychain_bip39( ctx: wire.Context, derivation_type: CardanoDerivationType) -> Keychain: if not device.is_initialized(): raise wire.NotInitialized("Device is not initialized") if derivation_type == CardanoDerivationType.LEDGER: seed = await get_seed(ctx) return Keychain(cardano.from_seed_ledger(seed)) if not cache.get(cache.APP_COMMON_DERIVE_CARDANO): raise wire.ProcessError( "Cardano derivation is not enabled for this session") if derivation_type == CardanoDerivationType.ICARUS: cache_entry = cache.APP_CARDANO_ICARUS_SECRET else: cache_entry = cache.APP_CARDANO_ICARUS_TREZOR_SECRET secret = await _get_secret(ctx, cache_entry) root = cardano.from_secret(secret) return Keychain(root)
async def derive_and_store_roots(ctx: wire.Context) -> None: if not device.is_initialized(): raise wire.NotInitialized("Device is not initialized") need_seed = not cache.is_set(cache.APP_COMMON_SEED) need_cardano_secret = cache.get( cache.APP_COMMON_DERIVE_CARDANO ) and not cache.is_set(cache.APP_CARDANO_ICARUS_SECRET) if not need_seed and not need_cardano_secret: return passphrase = await get_passphrase(ctx) if need_seed: common_seed = mnemonic.get_seed(passphrase) cache.set(cache.APP_COMMON_SEED, common_seed) if need_cardano_secret: from apps.cardano.seed import derive_and_store_secrets derive_and_store_secrets(passphrase)
def test_get_set(self): session_id1 = cache.start_session() cache.set(KEY, b"hello") self.assertEqual(cache.get(KEY), b"hello") session_id2 = cache.start_session() cache.set(KEY, b"world") self.assertEqual(cache.get(KEY), b"world") cache.start_session(session_id2) self.assertEqual(cache.get(KEY), b"world") cache.start_session(session_id1) self.assertEqual(cache.get(KEY), b"hello") cache.clear_all() with self.assertRaises(cache.InvalidSessionError): cache.get(KEY)
async def get_keychain(ctx: wire.Context) -> Keychain: root = cache.get(cache.APP_CARDANO_ROOT) if not storage.is_initialized(): raise wire.NotInitialized("Device is not initialized") if root is None: passphrase = await get_passphrase(ctx) if mnemonic.is_bip39(): # derive the root node from mnemonic and passphrase root = bip32.from_mnemonic_cardano(mnemonic.get_secret().decode(), passphrase) else: seed = mnemonic.get_seed(passphrase) root = bip32.from_seed(seed, "ed25519 cardano seed") # derive the namespaced root node for i in SEED_NAMESPACE: root.derive_cardano(i) storage.cache.set(cache.APP_CARDANO_ROOT, root) keychain = Keychain(SEED_NAMESPACE, root) return keychain
def derive_and_store_secrets(passphrase: str) -> None: assert device.is_initialized() assert cache.get(cache.APP_COMMON_DERIVE_CARDANO) if not mnemonic.is_bip39(): # nothing to do for SLIP-39, where we can derive the root from the main seed return icarus_secret = mnemonic.derive_cardano_icarus(passphrase, trezor_derivation=False) words = mnemonic.get_secret() assert words is not None, "Mnemonic is not set" # count ASCII spaces, add 1 to get number of words words_count = sum(c == 0x20 for c in words) + 1 if words_count == 24: icarus_trezor_secret = mnemonic.derive_cardano_icarus( passphrase, trezor_derivation=True) else: icarus_trezor_secret = icarus_secret cache.set(cache.APP_CARDANO_ICARUS_SECRET, icarus_secret) cache.set(cache.APP_CARDANO_ICARUS_TREZOR_SECRET, icarus_trezor_secret)
def test_lru_cache(self): class Deletable: def __init__(self): self.deleted = False def __del__(self): self.deleted = True cache = LRUCache(10) obj_a = Deletable() self.assertIsNone(cache.get("a")) cache.insert("a", obj_a) self.assertIs(cache.get("a"), obj_a) # test eviction objects = [(i, Deletable()) for i in range(10)] for key, obj in objects: cache.insert(key, obj) # object A should have been evicted self.assertIsNone(cache.get("a")) self.assertTrue(obj_a.deleted) cache.insert("a", obj_a) for key, obj in objects[:-1]: # objects should have been evicted in insertion order self.assertIsNone(cache.get(key)) self.assertTrue(obj.deleted) cache.insert(key, obj) # use "a" object self.assertIs(cache.get("a"), obj_a) # insert last object key, obj = objects[-1] cache.insert(key, obj) # "a" is recently used so should not be evicted now self.assertIs(cache.get("a"), obj_a)
async def get_seed(ctx: wire.Context) -> bytes: await derive_and_store_roots(ctx) common_seed = cache.get(cache.APP_COMMON_SEED) assert common_seed is not None return common_seed
def train(self, docs_train, y_train, extra = {}, useCrossValidation = False, vect_options={}): options = dict(self.options.items() + extra.items()) cv = StratifiedKFold(y_train, n_folds=10) if useCrossValidation else None pipeline = Pipeline([ ('vect', TfidfVectorizer(charset_error='ignore', tokenizer=t.tokenize, **vect_options)), ('clf', self.clf), ]) useGrid = sys.flags.optimize if useGrid: self.grid = GridSearchCV( pipeline, options, cv=cv, refit=True, n_jobs=-1, verbose=1 ) else: self.grid = pipeline cache_key = str(self.grid) + str(docs_train) cached = cache.get(cache_key) if cached and sys.flags.debug == 0: logging.debug("# Fetched cached version of %s " % self.clf.__class__.__name__) self.best_estimator = cached['est'] self.best_score = cached['scr'] self.best_params = cached['parm'] else: logging.debug("# Training new instance of %s " % self.clf.__class__.__name__) self.grid.fit(docs_train, y_train) if useGrid: self.best_estimator = self.grid.best_estimator_ self.best_params = self.grid.best_params_ self.best_score = self.grid.best_score_ else: self.best_estimator = self.grid self.best_params = self.grid.get_params(False) self.best_score = 1 logging.debug("Saving to cache for %s " % self.clf.__class__.__name__) cache.save(cache_key, { "est": self.best_estimator, "scr": self.best_score, "parm": self.best_params }) self.steps = self.best_estimator.named_steps logging.debug("# Best params for %s :" % self.clf.__class__.__name__) logging.debug(self.best_params) logging.debug("# Best score for %s :" % self.clf.__class__.__name__) logging.debug(self.best_score) return self.grid
def train(self, docs_train, y_train, extra={}, useCrossValidation=False, vect_options={}): options = dict(self.options.items() + extra.items()) cv = StratifiedKFold(y_train, n_folds=10) if useCrossValidation else None pipeline = Pipeline([ ('vect', TfidfVectorizer(charset_error='ignore', tokenizer=t.tokenize, **vect_options)), ('clf', self.clf), ]) useGrid = sys.flags.optimize if useGrid: self.grid = GridSearchCV(pipeline, options, cv=cv, refit=True, n_jobs=-1, verbose=1) else: self.grid = pipeline cache_key = str(self.grid) + str(docs_train) cached = cache.get(cache_key) if cached and sys.flags.debug == 0: logging.debug("# Fetched cached version of %s " % self.clf.__class__.__name__) self.best_estimator = cached['est'] self.best_score = cached['scr'] self.best_params = cached['parm'] else: logging.debug("# Training new instance of %s " % self.clf.__class__.__name__) self.grid.fit(docs_train, y_train) if useGrid: self.best_estimator = self.grid.best_estimator_ self.best_params = self.grid.best_params_ self.best_score = self.grid.best_score_ else: self.best_estimator = self.grid self.best_params = self.grid.get_params(False) self.best_score = 1 logging.debug("Saving to cache for %s " % self.clf.__class__.__name__) cache.save( cache_key, { "est": self.best_estimator, "scr": self.best_score, "parm": self.best_params }) self.steps = self.best_estimator.named_steps logging.debug("# Best params for %s :" % self.clf.__class__.__name__) logging.debug(self.best_params) logging.debug("# Best score for %s :" % self.clf.__class__.__name__) logging.debug(self.best_score) return self.grid