HW Accelerated AES CTR for passphrase saver, MiscroSD 2FA, and Tapsigner backup decryption

This commit is contained in:
scgbckbone 2024-01-12 10:33:23 +01:00 committed by doc-hex
parent a3f1f7f5de
commit 3c016d2669
5 changed files with 121 additions and 6 deletions

View File

@ -2,7 +2,7 @@
#
# pwsave.py - Save bip39 passphrases into encrypted file on MicroSD (if desired)
#
import stash, ujson, ngu, pyb, os, version
import stash, ujson, ngu, pyb, os, version, aes256ctr
from files import CardSlot, CardMissingError, needs_microsd
from ux import ux_dramatic_pause, ux_confirm, ux_show_story
from utils import xfp2str, problem_file_line, B2A
@ -49,7 +49,7 @@ class PassphraseSaver:
# Return a list of saved passphrases, or empty list if fail.
# Fail silently in all cases. Expect to see lots of noise here.
assert self.key
decrypt = ngu.aes.CTR(self.key)
decrypt = aes256ctr.new(self.key)
try:
fname = self.filename(card)
@ -63,7 +63,7 @@ class PassphraseSaver:
async def _save(self, card, data):
assert self.key
encrypt = ngu.aes.CTR(self.key)
encrypt = aes256ctr.new(self.key)
msg = encrypt.cipher(ujson.dumps(data))
# overwrites whatever already there
@ -331,7 +331,7 @@ class MicroSD2FA(PassphraseSaver):
data = dict(nonce=nonce)
encrypt = ngu.aes.CTR(self.key)
encrypt = aes256ctr.new(self.key)
msg = encrypt.cipher(ujson.dumps(data))
with open(self.filename(card), 'wb') as fd:

View File

@ -2,7 +2,7 @@
#
# tapsigner.py - TAPSIGNER backup file support
#
import ustruct, ngu, ure
import ustruct, ngu, ure, aes256ctr
from ubinascii import unhexlify as a2b_hex
from ubinascii import a2b_base64
from ux import ux_show_story
@ -14,7 +14,7 @@ from actions import file_picker, import_extended_key_as_secret
def decrypt_tapsigner_backup(backup_key, data):
try:
backup_key = a2b_hex(backup_key)
decrypt = ngu.aes.CTR(backup_key, bytes(16)) # IV 0
decrypt = aes256ctr.new(backup_key, bytes(16)) # IV 0
decrypted = decrypt.cipher(data).decode().strip()
# format of TAPSIGNER backup is known in advance
# extended private key is expected at the beginning of the first line

View File

@ -0,0 +1,27 @@
import utime, ngu, aes256ctr, math
# Cifra
start = utime.ticks_ms()
for i in range(100):
enc = ngu.aes.CTR(b"a" * 32, "b"*16)
dec = ngu.aes.CTR(b"a" * 32, "b"*16)
em = enc.cipher(b"msg" * i)
dm = dec.cipher(em)
assert dm == b"msg" * i
end = utime.ticks_ms()
cifra_res = utime.ticks_diff(end, start)
# aes256ctr
start = utime.ticks_ms()
for i in range(100):
enc = aes256ctr.new(b"a" * 32, "b"*16)
dec = aes256ctr.new(b"a" * 32, "b"*16)
em = enc.cipher(b"msg" * i)
dm = dec.cipher(em)
assert dm == b"msg" * i
end = utime.ticks_ms()
hwa_res = utime.ticks_diff(end, start)
r = math.ceil(cifra_res / hwa_res)
print("Hardware accelerated AES is approximatelly %dX faster than Cifra AES." % r)

View File

@ -0,0 +1,84 @@
import ngu, aes256ctr, ujson, ustruct
key = b"a" * 32
bsms_signer = b"""BSMS 1.0
a54044308ceac9b7
[eedff89a/48'/0'/0'/2']xpub6EhJvMneoLWAf8cuyLBLQiKiwh89RAmqXEqYeFuaCEHdHwxSRfzLrUxKXEBap7nZSHAYP7Jfq6gZmucotNzpMQ9Sb1nTqerqW8hrtmx6Y6o
Signer 2 key
H/IHW5dMGYsrRdYEz3ux+kKnkWBtxHzfYkREpnYbco38VnMvIxCbDuf7iu6960qDhBLR/RLjlb9UPtLmCMbczDE="""
bsms_coord = b"""BSMS 1.0
wsh(sortedmulti(2,[b7868815/48'/0'/0'/2']xpub6FA5rfxJc94K1kNtxRby1hoHwi7YDyTWwx1KUR3FwskaF6HzCbZMz3zQwGnCqdiFeMTPV3YneTGS2YQPiuNYsSvtggWWMQpEJD4jXU7ZzEh/**,[eedff89a/48'/0'/0'/2']xpub6EhJvMneoLWAf8cuyLBLQiKiwh89RAmqXEqYeFuaCEHdHwxSRfzLrUxKXEBap7nZSHAYP7Jfq6gZmucotNzpMQ9Sb1nTqerqW8hrtmx6Y6o/**))
/0/*,/1/*
bc1qhs4u273g4azq7kqqpe6vh5wfhasfmrq7nheyzsnq77humd7rwtkqagvakf"""
pws = [dict(xfp=0x4369050f, pw=pw) for pw in ["about abandon about", "@#$%^&*()", "ksjdfh78$%"]]
pws_ser = ujson.dumps(pws).encode()
# mimic real data that we use
TEST_CASES = [
b'Hello World!',
pws_ser,
bsms_coord,
bsms_signer
]
def secret_msg_exchange(alice, bob, msg):
e_msg = alice.cipher(msg)
assert bob.cipher(e_msg) == msg
return_msg = msg + b"\x00ACK"
e_msg = bob.cipher(return_msg)
assert alice.cipher(e_msg) == return_msg
for i, msg in enumerate(TEST_CASES):
# 16 bytes random IV
# encrypt with Cifra, decrypt with HW accelerated AES
iv = ngu.random.bytes(16)
encrypt = ngu.aes.CTR(key, iv)
decrypt = aes256ctr.new(key, iv)
secret_msg_exchange(encrypt, decrypt, msg)
print("Cifra AES --> HW AES\tIV=0b16\t\tOK")
# encrypt with HW accelerated AES, decrypt with Cifra
encrypt = aes256ctr.new(key, iv)
decrypt = ngu.aes.CTR(key, iv)
secret_msg_exchange(encrypt, decrypt, msg)
print("HW AES --> Cifra AES\tIV=0b16\t\tOK")
# empty IV
# encrypt with Cifra, decrypt with HW accelerated AES
encrypt = ngu.aes.CTR(key)
decrypt = aes256ctr.new(key)
secret_msg_exchange(encrypt, decrypt, msg)
print("Cifra AES --> HW AES\tIV=NONE\t\tOK")
# encrypt with HW accelerated AES, decrypt with Cifra
encrypt = aes256ctr.new(key)
decrypt = ngu.aes.CTR(key)
secret_msg_exchange(encrypt, decrypt, msg)
print("HW AES --> Cifra AES\tIV=NONE\t\tOK")
print("RANDOM TEST CASES")
for i in range(10):
key = ngu.random.bytes(32)
iv = ngu.random.bytes(16)
msg = (key + iv)
if i:
msg = msg * i
# encrypt with Cifra, decrypt with HW accelerated AES
encrypt = ngu.aes.CTR(key, iv)
decrypt = aes256ctr.new(key, iv)
secret_msg_exchange(encrypt, decrypt, msg)
print("Cifra AES --> HW AES\tIV=0b16\t\tOK")
# encrypt with HW accelerated AES, decrypt with Cifra
encrypt = aes256ctr.new(key, iv)
decrypt = ngu.aes.CTR(key, iv)
secret_msg_exchange(encrypt, decrypt, msg)
print("HW AES --> Cifra AES\tIV=0b16\t\tOK")

View File

@ -352,4 +352,8 @@ def test_af(sim_execfile):
assert res == ""
def test_aes_compatibility(sim_execfile):
res = sim_execfile('devtest/unit_aes_compat.py')
assert res == ""
# EOF