From 6f4e9af1facfc93b40db23975ef47b5660ae2b4e Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Fri, 15 Mar 2019 10:53:25 +0100 Subject: [PATCH 01/78] Add support for tezos voting (proposal and ballot operations) --- protob/messages-tezos.proto | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/protob/messages-tezos.proto b/protob/messages-tezos.proto index e429ce8b86..1bc37b8a00 100644 --- a/protob/messages-tezos.proto +++ b/protob/messages-tezos.proto @@ -55,6 +55,8 @@ message TezosSignTx { optional TezosTransactionOp transaction = 4; // Tezos transaction operation optional TezosOriginationOp origination = 5; // Tezos origination operation optional TezosDelegationOp delegation = 6; // Tezos delegation operation + optional TezosProposalOp proposal = 7; // Tezos proposal operation + optional TezosBallotOp ballot = 8; // Tezos ballot operation /* * Tezos contract ID */ @@ -120,6 +122,24 @@ message TezosSignTx { optional uint64 storage_limit = 5; optional bytes delegate = 6; } + /** + * Structure representing information for proposal + */ + message TezosProposalOp { + optional bytes source = 1; + optional uint64 period = 2; + optional uint64 bytes_in_next_field = 3; + optional bytes proposals = 4; + } + /** + * Structure representing information for ballot + */ + message TezosBallotOp { + optional bytes source = 1; + optional uint64 period = 2; + optional bytes proposal = 3; + optional bytes ballot = 4; + } } /** From a0d89c0785f04ba7dd8473b803703e0074824e33 Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Fri, 15 Mar 2019 19:39:16 +0100 Subject: [PATCH 02/78] Add support for tezos voting operations (proposal and ballot) --- src/apps/tezos/helpers.py | 2 + src/apps/tezos/layout.py | 51 ++++++++++++++++- src/apps/tezos/sign_tx.py | 78 +++++++++++++++++++++++++- src/apps/tezos/writers.py | 24 ++++++++ src/trezor/messages/TezosBallotOp.py | 27 +++++++++ src/trezor/messages/TezosProposalOp.py | 27 +++++++++ src/trezor/messages/TezosSignTx.py | 8 +++ 7 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 src/apps/tezos/writers.py create mode 100644 src/trezor/messages/TezosBallotOp.py create mode 100644 src/trezor/messages/TezosProposalOp.py diff --git a/src/apps/tezos/helpers.py b/src/apps/tezos/helpers.py index 0050b93fd5..5669cd1c59 100644 --- a/src/apps/tezos/helpers.py +++ b/src/apps/tezos/helpers.py @@ -22,6 +22,8 @@ TEZOS_PREFIX_BYTES = { "edsig": [9, 245, 205, 134, 18], # operation hash "o": [5, 116], + # protocola hash + "P": [2, 170], } diff --git a/src/apps/tezos/layout.py b/src/apps/tezos/layout.py index 4b1528efe2..14d474e318 100644 --- a/src/apps/tezos/layout.py +++ b/src/apps/tezos/layout.py @@ -1,5 +1,9 @@ -from trezor import ui +from micropython import const + +from trezor import ui, wire from trezor.messages import ButtonRequestType +from trezor.ui.confirm import CANCELLED, CONFIRMED, ConfirmDialog +from trezor.ui.scroll import Scrollpage, animate_swipe, paginate from trezor.ui.text import Text from trezor.utils import chunks, format_amount @@ -66,6 +70,51 @@ def split_address(address): return chunks(address, 18) +def split_proposal(proposal): + return chunks(proposal, 17) + + def format_tezos_amount(value): formatted_value = format_amount(value, TEZOS_AMOUNT_DIVISIBILITY) return formatted_value + " XTZ" + + +async def require_confirm_proposal(ctx, proposals): + text = Text("Submit proposal", ui.ICON_SEND, icon_color=ui.PURPLE) + text.bold("Proposal:") + text.mono(*split_proposal(proposals[0])) + await require_confirm(ctx, text, ButtonRequestType.SignTx) + + +async def require_confirm_ballot(ctx, proposal, ballot): + text = Text("Submit ballot", ui.ICON_SEND, icon_color=ui.PURPLE) + text.bold("Ballot: {}".format(ballot)) + text.bold("Proposal:") + text.mono(*split_proposal(proposal[0])) + await require_confirm(ctx, text, ButtonRequestType.SignTx) + + +# use, when there are more then one proposals in one operation +async def show_proposals(ctx, proposals): + first_page = const(0) + pages = proposals + + paginator = paginate(show_proposal_page, len(pages), first_page, pages) + return await ctx.wait(paginator) + + +@ui.layout +async def show_proposal_page(page: int, page_count: int, pages: list): + + text = Text("Submit proposals", ui.ICON_SEND, icon_color=ui.PURPLE) + text.bold("Proposal {}: ".format(page + 1)) + text.mono(*split_proposal(pages[page])) + content = Scrollpage(text, page, page_count) + + if page + 1 >= page_count: + confirm = await ConfirmDialog(content) + if confirm == CANCELLED: + raise wire.ActionCancelled("Cancelled") + else: + content.render() + await animate_swipe() diff --git a/src/apps/tezos/sign_tx.py b/src/apps/tezos/sign_tx.py index 5cf0e11d3b..ffd665ed99 100644 --- a/src/apps/tezos/sign_tx.py +++ b/src/apps/tezos/sign_tx.py @@ -1,3 +1,5 @@ +from micropython import const + from trezor import wire from trezor.crypto import hashlib from trezor.crypto.curve import ed25519 @@ -5,8 +7,19 @@ from trezor.messages import TezosContractType from trezor.messages.TezosSignedTx import TezosSignedTx from apps.common import paths -from apps.common.writers import write_bytes, write_uint8 + +# from apps.common.writers import write_bytes, write_uint8 from apps.tezos import helpers, layout +from apps.tezos.writers import ( + write_bool, + write_bytes, + write_uint8, + write_uint16, + write_uint32, + write_uint64, +) + +PROPOSAL_LENGTH = const(32) async def sign_tx(ctx, msg, keychain): @@ -50,6 +63,20 @@ async def sign_tx(ctx, msg, keychain): ctx, source, msg.delegation.fee ) + elif msg.proposal is not None: + proposed_protocols = _get_protocol_hash_from_msg(msg.proposal.proposals) + + # byte count larger than PROPOSAL_LENGTH indicates more than 1 proposal, use paginated screen + if msg.proposal.bytes_in_next_field > PROPOSAL_LENGTH: + await layout.show_proposals(ctx, proposed_protocols) + else: + await layout.require_confirm_proposal(ctx, proposed_protocols) + + elif msg.ballot is not None: + proposed_protocol = _get_protocol_hash_from_msg(msg.ballot.proposal) + submitted_ballot = _get_ballot(msg.ballot.ballot) + await layout.require_confirm_ballot(ctx, proposed_protocol, submitted_ballot) + else: raise wire.DataError("Invalid operation") @@ -99,11 +126,34 @@ def _get_address_from_contract(address): raise wire.DataError("Invalid contract type") +def _get_protocol_hash_from_msg(proposals): + # split the proposals + proposal_list = list( + [ + proposals[i : i + PROPOSAL_LENGTH] + for i in range(0, len(proposals), PROPOSAL_LENGTH) + ] + ) + return [ + helpers.base58_encode_check(proposal, prefix="P") for proposal in proposal_list + ] + + +def _get_ballot(encoded_ballot): + encoded_ballot = int(encoded_ballot[0]) + if encoded_ballot == 0: + return "yay" + elif encoded_ballot == 1: + return "nay" + elif encoded_ballot == 2: + return "pass" + + def _get_operation_bytes(w: bytearray, msg): write_bytes(w, msg.branch) # when the account sends first operation in lifetime, - # we need to reveal its publickey + # we need to reveal its public key if msg.reveal is not None: _encode_common(w, msg.reveal, "reveal") write_bytes(w, msg.reveal.public_key) @@ -127,6 +177,10 @@ def _get_operation_bytes(w: bytearray, msg): elif msg.delegation is not None: _encode_common(w, msg.delegation, "delegation") _encode_data_with_bool_prefix(w, msg.delegation.delegate) + elif msg.proposal is not None: + _encode_proposal(w, msg.proposal) + elif msg.ballot is not None: + _encode_ballot(w, msg.ballot) def _encode_common(w: bytearray, operation, str_operation): @@ -169,3 +223,23 @@ def _encode_zarith(w: bytearray, num): break write_uint8(w, 128 | byte) + + +def _encode_proposal(w: bytearray, proposal): + proposal_tag = 5 + + write_uint8(w, proposal_tag) + write_bytes(w, proposal.source) + write_uint32(w, proposal.period) + write_uint32(w, proposal.bytes_in_next_field) + write_bytes(w, proposal.proposals) + + +def _encode_ballot(w: bytearray, ballot): + ballot_tag = 6 + + write_uint8(w, ballot_tag) + write_bytes(w, ballot.source) + write_uint32(w, ballot.period) + write_bytes(w, ballot.proposal) + write_bytes(w, ballot.ballot) diff --git a/src/apps/tezos/writers.py b/src/apps/tezos/writers.py new file mode 100644 index 0000000000..0c10054c82 --- /dev/null +++ b/src/apps/tezos/writers.py @@ -0,0 +1,24 @@ +from apps.common.writers import ( + write_bytes, + write_uint8, + write_uint32_be, + write_uint64_be, +) + +write_uint8 = write_uint8 +write_uint32 = write_uint32_be +write_uint64 = write_uint64_be +write_bytes = write_bytes + + +def write_bool(w: bytearray, boolean: bool): + if boolean: + write_uint8(w, 255) + else: + write_uint8(w, 0) + + +# write uint16 in be +def write_uint16(w: bytearray, n: int): + w.append((n >> 8) & 0xFF) + w.append(n & 0xFF) diff --git a/src/trezor/messages/TezosBallotOp.py b/src/trezor/messages/TezosBallotOp.py new file mode 100644 index 0000000000..260bf2a4b5 --- /dev/null +++ b/src/trezor/messages/TezosBallotOp.py @@ -0,0 +1,27 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + + +class TezosBallotOp(p.MessageType): + + def __init__( + self, + source: bytes = None, + period: int = None, + proposal: bytes = None, + ballot: bytes = None, + ) -> None: + self.source = source + self.period = period + self.proposal = proposal + self.ballot = ballot + + @classmethod + def get_fields(cls): + return { + 1: ('source', p.BytesType, 0), + 2: ('period', p.UVarintType, 0), + 3: ('proposal', p.BytesType, 0), + 4: ('ballot', p.BytesType, 0), + } diff --git a/src/trezor/messages/TezosProposalOp.py b/src/trezor/messages/TezosProposalOp.py new file mode 100644 index 0000000000..94f0dd532e --- /dev/null +++ b/src/trezor/messages/TezosProposalOp.py @@ -0,0 +1,27 @@ +# Automatically generated by pb2py +# fmt: off +import protobuf as p + + +class TezosProposalOp(p.MessageType): + + def __init__( + self, + source: bytes = None, + period: int = None, + bytes_in_next_field: int = None, + proposals: bytes = None, + ) -> None: + self.source = source + self.period = period + self.bytes_in_next_field = bytes_in_next_field + self.proposals = proposals + + @classmethod + def get_fields(cls): + return { + 1: ('source', p.BytesType, 0), + 2: ('period', p.UVarintType, 0), + 3: ('bytes_in_next_field', p.UVarintType, 0), + 4: ('proposals', p.BytesType, 0), + } diff --git a/src/trezor/messages/TezosSignTx.py b/src/trezor/messages/TezosSignTx.py index 8127603780..cbba27e04f 100644 --- a/src/trezor/messages/TezosSignTx.py +++ b/src/trezor/messages/TezosSignTx.py @@ -2,8 +2,10 @@ # fmt: off import protobuf as p +from .TezosBallotOp import TezosBallotOp from .TezosDelegationOp import TezosDelegationOp from .TezosOriginationOp import TezosOriginationOp +from .TezosProposalOp import TezosProposalOp from .TezosRevealOp import TezosRevealOp from .TezosTransactionOp import TezosTransactionOp @@ -25,6 +27,8 @@ class TezosSignTx(p.MessageType): transaction: TezosTransactionOp = None, origination: TezosOriginationOp = None, delegation: TezosDelegationOp = None, + proposal: TezosProposalOp = None, + ballot: TezosBallotOp = None, ) -> None: self.address_n = address_n if address_n is not None else [] self.branch = branch @@ -32,6 +36,8 @@ class TezosSignTx(p.MessageType): self.transaction = transaction self.origination = origination self.delegation = delegation + self.proposal = proposal + self.ballot = ballot @classmethod def get_fields(cls): @@ -42,4 +48,6 @@ class TezosSignTx(p.MessageType): 4: ('transaction', TezosTransactionOp, 0), 5: ('origination', TezosOriginationOp, 0), 6: ('delegation', TezosDelegationOp, 0), + 7: ('proposal', TezosProposalOp, 0), + 8: ('ballot', TezosBallotOp, 0), } From fd95ff05312da4e5196481d8f5dc9437a6285e72 Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Tue, 16 Apr 2019 19:20:40 +0200 Subject: [PATCH 03/78] made requested changes --- common/protob/messages-tezos.proto | 15 +- core/src/apps/common/writers.py | 7 + core/src/apps/tezos/helpers.py | 15 +- core/src/apps/tezos/layout.py | 17 +-- core/src/apps/tezos/sign_tx.py | 68 ++++----- core/src/trezor/messages/TezosBallotOp.py | 4 +- core/src/trezor/messages/TezosBallotType.py | 5 + core/src/trezor/messages/TezosProposalOp.py | 15 +- .../device_tests/test_msg_tezos_sign_tx.py | 138 ++++++++++++++++++ 9 files changed, 216 insertions(+), 68 deletions(-) create mode 100644 core/src/trezor/messages/TezosBallotType.py diff --git a/common/protob/messages-tezos.proto b/common/protob/messages-tezos.proto index 1bc37b8a00..d6fbfea65a 100644 --- a/common/protob/messages-tezos.proto +++ b/common/protob/messages-tezos.proto @@ -126,19 +126,24 @@ message TezosSignTx { * Structure representing information for proposal */ message TezosProposalOp { - optional bytes source = 1; + optional bytes source = 1; //Contains only public_key_hash, not to be confused with TezosContractID optional uint64 period = 2; - optional uint64 bytes_in_next_field = 3; - optional bytes proposals = 4; + repeated bytes proposals = 4; } /** * Structure representing information for ballot */ message TezosBallotOp { - optional bytes source = 1; + optional bytes source = 1; //Contains only public_key_hash, not to be confused with TezosContractID optional uint64 period = 2; optional bytes proposal = 3; - optional bytes ballot = 4; + optional TezosBallotType ballot = 4; + + enum TezosBallotType { + Yay = 0; + Nay = 1; + Pass = 2; + } } } diff --git a/core/src/apps/common/writers.py b/core/src/apps/common/writers.py index a4ea21a580..feeb32590e 100644 --- a/core/src/apps/common/writers.py +++ b/core/src/apps/common/writers.py @@ -24,6 +24,13 @@ def write_uint16_le(w: bytearray, n: int) -> int: return 2 +def write_uint16_be(w: bytearray, n: int): + ensure(0 <= n <= 0xFFFF) + w.append((n >> 8) & 0xFF) + w.append(n & 0xFF) + return 2 + + def write_uint32_le(w: bytearray, n: int) -> int: ensure(0 <= n <= 0xFFFFFFFF) w.append(n & 0xFF) diff --git a/core/src/apps/tezos/helpers.py b/core/src/apps/tezos/helpers.py index 102b0b6e5a..e93b493881 100644 --- a/core/src/apps/tezos/helpers.py +++ b/core/src/apps/tezos/helpers.py @@ -3,6 +3,7 @@ from micropython import const from trezor.crypto import base58 from apps.common import HARDENED +from apps.common.writers import write_uint8 TEZOS_AMOUNT_DIVISIBILITY = const(6) TEZOS_ED25519_ADDRESS_PREFIX = "tz1" @@ -21,7 +22,7 @@ TEZOS_PREFIX_BYTES = { "edsig": [9, 245, 205, 134, 18], # operation hash "o": [5, 116], - # protocola hash + # protocol hash "P": [2, 170], } @@ -45,7 +46,8 @@ def validate_full_path(path: list) -> bool: Validates derivation path to equal 44'/1729'/a', where `a` is an account index from 0 to 1 000 000. """ - if len(path) != 3: + length = len(path) + if length < 3 or length > 4: return False if path[0] != 44 | HARDENED: return False @@ -53,4 +55,13 @@ def validate_full_path(path: list) -> bool: return False if path[2] < HARDENED or path[2] > 1000000 | HARDENED: return False + if length > 3 and (path[3] < HARDENED or path[3] > 1000000 | HARDENED): + return False return True + + +def write_bool(w: bytearray, boolean: bool): + if boolean: + write_uint8(w, 255) + else: + write_uint8(w, 0) diff --git a/core/src/apps/tezos/layout.py b/core/src/apps/tezos/layout.py index 14d474e318..315875fea7 100644 --- a/core/src/apps/tezos/layout.py +++ b/core/src/apps/tezos/layout.py @@ -79,18 +79,11 @@ def format_tezos_amount(value): return formatted_value + " XTZ" -async def require_confirm_proposal(ctx, proposals): - text = Text("Submit proposal", ui.ICON_SEND, icon_color=ui.PURPLE) - text.bold("Proposal:") - text.mono(*split_proposal(proposals[0])) - await require_confirm(ctx, text, ButtonRequestType.SignTx) - - async def require_confirm_ballot(ctx, proposal, ballot): text = Text("Submit ballot", ui.ICON_SEND, icon_color=ui.PURPLE) text.bold("Ballot: {}".format(ballot)) text.bold("Proposal:") - text.mono(*split_proposal(proposal[0])) + text.mono(*split_proposal(proposal)) await require_confirm(ctx, text, ButtonRequestType.SignTx) @@ -98,15 +91,15 @@ async def require_confirm_ballot(ctx, proposal, ballot): async def show_proposals(ctx, proposals): first_page = const(0) pages = proposals + title = "Submit proposals" if len(proposals) > 1 else "Submit proposal" - paginator = paginate(show_proposal_page, len(pages), first_page, pages) + paginator = paginate(show_proposal_page, len(pages), first_page, pages, title) return await ctx.wait(paginator) @ui.layout -async def show_proposal_page(page: int, page_count: int, pages: list): - - text = Text("Submit proposals", ui.ICON_SEND, icon_color=ui.PURPLE) +async def show_proposal_page(page: int, page_count: int, pages: list, title: str): + text = Text(title, ui.ICON_SEND, icon_color=ui.PURPLE) text.bold("Proposal {}: ".format(page + 1)) text.mono(*split_proposal(pages[page])) content = Scrollpage(text, page, page_count) diff --git a/core/src/apps/tezos/sign_tx.py b/core/src/apps/tezos/sign_tx.py index 987ef553fb..98c4ec4ded 100644 --- a/core/src/apps/tezos/sign_tx.py +++ b/core/src/apps/tezos/sign_tx.py @@ -3,18 +3,16 @@ from micropython import const from trezor import wire from trezor.crypto import hashlib from trezor.crypto.curve import ed25519 -from trezor.messages import TezosContractType +from trezor.messages import TezosContractType, TezosBallotType from trezor.messages.TezosSignedTx import TezosSignedTx from apps.common import paths from apps.tezos import CURVE, helpers, layout -from apps.tezos.writers import ( - write_bool, +from apps.common.writers import ( write_bytes, write_uint8, - write_uint16, - write_uint32, - write_uint64, + write_uint16_be, + write_uint32_be, ) PROPOSAL_LENGTH = const(32) @@ -65,12 +63,7 @@ async def sign_tx(ctx, msg, keychain): elif msg.proposal is not None: proposed_protocols = _get_protocol_hash_from_msg(msg.proposal.proposals) - - # byte count larger than PROPOSAL_LENGTH indicates more than 1 proposal, use paginated screen - if msg.proposal.bytes_in_next_field > PROPOSAL_LENGTH: - await layout.show_proposals(ctx, proposed_protocols) - else: - await layout.require_confirm_proposal(ctx, proposed_protocols) + await layout.show_proposals(ctx, proposed_protocols) elif msg.ballot is not None: proposed_protocol = _get_protocol_hash_from_msg(msg.ballot.proposal) @@ -127,25 +120,20 @@ def _get_address_from_contract(address): def _get_protocol_hash_from_msg(proposals): - # split the proposals - proposal_list = list( - [ - proposals[i : i + PROPOSAL_LENGTH] - for i in range(0, len(proposals), PROPOSAL_LENGTH) - ] - ) + print(proposals) + if type(proposals) is not list: + return helpers.base58_encode_check(proposals, prefix="P") return [ - helpers.base58_encode_check(proposal, prefix="P") for proposal in proposal_list + helpers.base58_encode_check(proposal, prefix="P") for proposal in proposals ] -def _get_ballot(encoded_ballot): - encoded_ballot = int(encoded_ballot[0]) - if encoded_ballot == 0: +def _get_ballot(ballot): + if ballot == TezosBallotType.Yay: return "yay" - elif encoded_ballot == 1: + elif ballot == TezosBallotType.Nay: return "nay" - elif encoded_ballot == 2: + elif ballot == TezosBallotType.Pass: return "pass" @@ -169,8 +157,8 @@ def _get_operation_bytes(w: bytearray, msg): _encode_common(w, msg.origination, "origination") write_bytes(w, msg.origination.manager_pubkey) _encode_zarith(w, msg.origination.balance) - _encode_bool(w, msg.origination.spendable) - _encode_bool(w, msg.origination.delegatable) + helpers.write_bool(w, msg.origination.spendable) + helpers.write_bool(w, msg.origination.delegatable) _encode_data_with_bool_prefix(w, msg.origination.delegate) _encode_data_with_bool_prefix(w, msg.origination.script) # delegation operation @@ -198,19 +186,12 @@ def _encode_contract_id(w: bytearray, contract_id): write_bytes(w, contract_id.hash) -def _encode_bool(w: bytearray, boolean): - if boolean: - write_uint8(w, 255) - else: - write_uint8(w, 0) - - def _encode_data_with_bool_prefix(w: bytearray, data): if data: - _encode_bool(w, True) + helpers.write_bool(w, True) write_bytes(w, data) else: - _encode_bool(w, False) + helpers.write_bool(w, False) def _encode_zarith(w: bytearray, num): @@ -230,9 +211,10 @@ def _encode_proposal(w: bytearray, proposal): write_uint8(w, proposal_tag) write_bytes(w, proposal.source) - write_uint32(w, proposal.period) - write_uint32(w, proposal.bytes_in_next_field) - write_bytes(w, proposal.proposals) + write_uint32_be(w, proposal.period) + write_uint32_be(w, _get_proposals_bytecount(proposal.proposals)) + for proposal_hash in proposal.proposals: + write_bytes(w, proposal_hash) def _encode_ballot(w: bytearray, ballot): @@ -240,6 +222,10 @@ def _encode_ballot(w: bytearray, ballot): write_uint8(w, ballot_tag) write_bytes(w, ballot.source) - write_uint32(w, ballot.period) + write_uint32_be(w, ballot.period) write_bytes(w, ballot.proposal) - write_bytes(w, ballot.ballot) + write_uint8(w, ballot.ballot) + + +def _get_proposals_bytecount(proposals): + return len(proposals) * PROPOSAL_LENGTH diff --git a/core/src/trezor/messages/TezosBallotOp.py b/core/src/trezor/messages/TezosBallotOp.py index 260bf2a4b5..cf6b969cd9 100644 --- a/core/src/trezor/messages/TezosBallotOp.py +++ b/core/src/trezor/messages/TezosBallotOp.py @@ -10,7 +10,7 @@ class TezosBallotOp(p.MessageType): source: bytes = None, period: int = None, proposal: bytes = None, - ballot: bytes = None, + ballot: int = None, ) -> None: self.source = source self.period = period @@ -23,5 +23,5 @@ class TezosBallotOp(p.MessageType): 1: ('source', p.BytesType, 0), 2: ('period', p.UVarintType, 0), 3: ('proposal', p.BytesType, 0), - 4: ('ballot', p.BytesType, 0), + 4: ('ballot', p.UVarintType, 0), } diff --git a/core/src/trezor/messages/TezosBallotType.py b/core/src/trezor/messages/TezosBallotType.py new file mode 100644 index 0000000000..1d955862f5 --- /dev/null +++ b/core/src/trezor/messages/TezosBallotType.py @@ -0,0 +1,5 @@ +# Automatically generated by pb2py +# fmt: off +Yay = 0 +Nay = 1 +Pass = 2 diff --git a/core/src/trezor/messages/TezosProposalOp.py b/core/src/trezor/messages/TezosProposalOp.py index 94f0dd532e..d533ce6c94 100644 --- a/core/src/trezor/messages/TezosProposalOp.py +++ b/core/src/trezor/messages/TezosProposalOp.py @@ -2,6 +2,12 @@ # fmt: off import protobuf as p +if __debug__: + try: + from typing import List + except ImportError: + List = None # type: ignore + class TezosProposalOp(p.MessageType): @@ -9,19 +15,16 @@ class TezosProposalOp(p.MessageType): self, source: bytes = None, period: int = None, - bytes_in_next_field: int = None, - proposals: bytes = None, + proposals: List[bytes] = None, ) -> None: self.source = source self.period = period - self.bytes_in_next_field = bytes_in_next_field - self.proposals = proposals + self.proposals = proposals if proposals is not None else [] @classmethod def get_fields(cls): return { 1: ('source', p.BytesType, 0), 2: ('period', p.UVarintType, 0), - 3: ('bytes_in_next_field', p.UVarintType, 0), - 4: ('proposals', p.BytesType, 0), + 4: ('proposals', p.BytesType, p.FLAG_REPEATED), } diff --git a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py index d0478b5763..db5643c388 100644 --- a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py +++ b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py @@ -23,6 +23,7 @@ from trezorlib.tools import parse_path from .common import TrezorTest TEZOS_PATH = parse_path("m/44'/1729'/0'") +TEZOS_PATH_10 = parse_path("m/44'/1729'/10'") @pytest.mark.tezos @@ -194,3 +195,140 @@ class TestMsgTezosSignTx(TrezorTest): assert ( resp.operation_hash == "oocgc3hyKsGHPsw6WFWJpWT8jBwQLtebQAXF27KNisThkzoj635" ) + + def test_tezos_sign_tx_proposal(self): + self.setup_mnemonic_allallall() + + resp = tezos.sign_tx( + self.client, + TEZOS_PATH_10, + dict_to_proto( + messages.TezosSignTx, + { + "branch": "dee04042c0832d68a43699b2001c0a38065436eb05e578071a763e1972d0bc81", + "proposal": { + "source": "005f450441f41ee11eee78a31d1e1e55627c783bd6", + "period": 17, + "proposals": ["dfa974df171c2dad9a9b8f25d99af41fd9702ce5d04521d2f9943c84d88aa572"], + }, + }, + ), + ) + assert ( + resp.signature + == "edsigtfY16R32k2WVMYfFr7ymnro4ib5zMckk28vsuViYNN77DJAvCJLRNArd9L531pUCxT4YdcvCvBym5dhcZ1rknEVm6yZ8bB" + ) + assert ( + resp.sig_op_contents.hex() + == "dee04042c0832d68a43699b2001c0a38065436eb05e578071a763e1972d0bc8105005f450441f41ee11eee78a31d1e1e55627c783bd60000001100000020dfa974df171c2dad9a9b8f25d99af41fd9702ce5d04521d2f9943c84d88aa5723b12621296a679b3a74ea790df5347995a76e20a09e76590baaacf4e09341965a04123f5cbbba8427f045b5f7d59157a3098e44839babe7c247d19b58bbb2405" + ) + assert ( + resp.operation_hash == "opLqntFUu984M7LnGsFvfGW6kWe9QjAz4AfPDqQvwJ1wPM4Si4c" + ) + + def test_tezos_sign_tx_multiple_proposals(self): + self.setup_mnemonic_allallall() + + resp = tezos.sign_tx( + self.client, + TEZOS_PATH_10, + dict_to_proto( + messages.TezosSignTx, + { + "branch": "7e0be36a90c663c73c60da3889ffefff1383fb65cc29f0639f173d8f95a52df7", + "proposal": { + "source": "005f450441f41ee11eee78a31d1e1e55627c783bd6", + "period": 17, + "proposals": ["2a6ff28ab4d0ccb18f7129aaaf9a4b8027d794f2562849665fdb6999db2a4e57", + "47cd60c09ab8437cc9fe19add494dce1b9844100f660f02ce77510a0c66d2762"], + }, + }, + ), + ) + assert ( + resp.signature + == "edsigu6GAjhiWAQ64ctWTGEDYAZ16tYzLgzWzqc4CUyixK4FGRE8YUBVzFaVJ2fUCexZjZLMLdiNZGcUdzeL1bQhZ2h5oLrh7pA" + ) + assert ( + resp.sig_op_contents.hex() + == "7e0be36a90c663c73c60da3889ffefff1383fb65cc29f0639f173d8f95a52df705005f450441f41ee11eee78a31d1e1e55627c783bd600000011000000402a6ff28ab4d0ccb18f7129aaaf9a4b8027d794f2562849665fdb6999db2a4e5747cd60c09ab8437cc9fe19add494dce1b9844100f660f02ce77510a0c66d2762f813361ac00ada7e3256f23973ae25b112229476a3cb3e506fe929ea1e9358299fed22178d1be689cddeedd1f303abfef859b664f159a528576a1c807079f005" + ) + assert ( + resp.operation_hash == "onobSyNgiitGXxSVFJN6949MhUomkkxvH4ZJ2owgWwNeDdntF9Y" + ) + + + def test_tezos_sing_tx_ballot_yay(self): + self.setup_mnemonic_allallall() + + resp = tezos.sign_tx( + self.client, + TEZOS_PATH_10, + dict_to_proto( + messages.TezosSignTx, + { + "branch": "3a8f60c4cd394cee5b50136c7fc8cb157e8aaa476a9e5c68709be6fc1cdb5395", + "ballot": { + "source": "0002298c03ed7d454a101eb7022bc95f7e5f41ac78", + "period": 2, + "proposal": "def7ed9c84af23ab37ebb60dd83cd103d1272ad6c63d4c05931567e65ed027e3", + "ballot": 0 + }, + } + ) + ) + + assert ( + resp.signature + == "edsigtkxNm6YXwtV24DqeuimeZFTeFCn2jDYheSsXT4rHMcEjNvzsiSo55nVyVsQxtEe8M7U4PWJWT4rGYYGckQCgtkNJkd2roX" + ) + + def test_tezos_sing_tx_ballot_nay(self): + self.setup_mnemonic_allallall() + + resp = tezos.sign_tx( + self.client, + TEZOS_PATH_10, + dict_to_proto( + messages.TezosSignTx, + { + "branch": "3a8f60c4cd394cee5b50136c7fc8cb157e8aaa476a9e5c68709be6fc1cdb5395", + "ballot": { + "source": "0002298c03ed7d454a101eb7022bc95f7e5f41ac78", + "period": 2, + "proposal": "def7ed9c84af23ab37ebb60dd83cd103d1272ad6c63d4c05931567e65ed027e3", + "ballot": 1 + }, + } + ) + ) + assert ( + resp.signature + == "edsigtqLaizfF6Cfc2JQL7TrsyniGhpZEojZAKMFW6AeudaUoU8KGXEHJH69Q4Lf27qFyUSTfbeHNnnCt69SGEPWkmpkgkgqMbL" + ) + + def test_tezos_sing_tx_ballot_pass(self): + self.setup_mnemonic_allallall() + + resp = tezos.sign_tx( + self.client, + TEZOS_PATH_10, + dict_to_proto( + messages.TezosSignTx, + { + "branch": "3a8f60c4cd394cee5b50136c7fc8cb157e8aaa476a9e5c68709be6fc1cdb5395", + "ballot": { + "source": "0002298c03ed7d454a101eb7022bc95f7e5f41ac78", + "period": 2, + "proposal": "def7ed9c84af23ab37ebb60dd83cd103d1272ad6c63d4c05931567e65ed027e3", + "ballot": 2 + }, + } + ) + ) + + assert ( + resp.signature + == "edsigu6YX7EegPwrpcEbdNQsNhrRiEagBNGJBmFamP4mixZZw1UynhahGQ8RNiZLSUVLERUZwygrsSVenBqXGt9VnknTxtzjKzv" + ) + From c588d0b4f3244ad73cad2355e25cb5f66516987f Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Thu, 18 Apr 2019 08:41:38 +0200 Subject: [PATCH 04/78] requested changes #2 --- core/src/apps/tezos/sign_tx.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/core/src/apps/tezos/sign_tx.py b/core/src/apps/tezos/sign_tx.py index 98c4ec4ded..f6aed917ef 100644 --- a/core/src/apps/tezos/sign_tx.py +++ b/core/src/apps/tezos/sign_tx.py @@ -62,11 +62,11 @@ async def sign_tx(ctx, msg, keychain): ) elif msg.proposal is not None: - proposed_protocols = _get_protocol_hash_from_msg(msg.proposal.proposals) + proposed_protocols = [_get_protocol_hash(p) for p in msg.proposal.proposals] await layout.show_proposals(ctx, proposed_protocols) elif msg.ballot is not None: - proposed_protocol = _get_protocol_hash_from_msg(msg.ballot.proposal) + proposed_protocol = _get_protocol_hash(msg.ballot.proposal) submitted_ballot = _get_ballot(msg.ballot.ballot) await layout.require_confirm_ballot(ctx, proposed_protocol, submitted_ballot) @@ -119,13 +119,8 @@ def _get_address_from_contract(address): raise wire.DataError("Invalid contract type") -def _get_protocol_hash_from_msg(proposals): - print(proposals) - if type(proposals) is not list: - return helpers.base58_encode_check(proposals, prefix="P") - return [ - helpers.base58_encode_check(proposal, prefix="P") for proposal in proposals - ] +def _get_protocol_hash(proposal): + return helpers.base58_encode_check(proposal, prefix="P") def _get_ballot(ballot): @@ -212,7 +207,7 @@ def _encode_proposal(w: bytearray, proposal): write_uint8(w, proposal_tag) write_bytes(w, proposal.source) write_uint32_be(w, proposal.period) - write_uint32_be(w, _get_proposals_bytecount(proposal.proposals)) + write_uint32_be(w, len(proposal.proposals) * PROPOSAL_LENGTH) for proposal_hash in proposal.proposals: write_bytes(w, proposal_hash) @@ -225,7 +220,3 @@ def _encode_ballot(w: bytearray, ballot): write_uint32_be(w, ballot.period) write_bytes(w, ballot.proposal) write_uint8(w, ballot.ballot) - - -def _get_proposals_bytecount(proposals): - return len(proposals) * PROPOSAL_LENGTH From da7bb8abf8d9bcba8a095c624200d6c0f2e6d88a Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Thu, 18 Apr 2019 12:32:43 +0200 Subject: [PATCH 05/78] fix path validation and update unit tests --- core/src/apps/tezos/helpers.py | 5 +++-- core/tests/test_apps.tezos.address.py | 6 +++++- core/tests/test_apps.tezos.encode.py | 7 +++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/core/src/apps/tezos/helpers.py b/core/src/apps/tezos/helpers.py index e93b493881..4640d7eb30 100644 --- a/core/src/apps/tezos/helpers.py +++ b/core/src/apps/tezos/helpers.py @@ -47,15 +47,16 @@ def validate_full_path(path: list) -> bool: where `a` is an account index from 0 to 1 000 000. """ length = len(path) + print(path) if length < 3 or length > 4: return False if path[0] != 44 | HARDENED: return False if path[1] != 1729 | HARDENED: return False - if path[2] < HARDENED or path[2] > 1000000 | HARDENED: + if length == 3 and (path[2] < HARDENED or path[2] > 1000000 | HARDENED): return False - if length > 3 and (path[3] < HARDENED or path[3] > 1000000 | HARDENED): + if length == 4 and (path[2] != 0 | HARDENED or path[3] < HARDENED or path[3] > 1000000 | HARDENED): return False return True diff --git a/core/tests/test_apps.tezos.address.py b/core/tests/test_apps.tezos.address.py index 9b093f9e0b..c3870fd313 100644 --- a/core/tests/test_apps.tezos.address.py +++ b/core/tests/test_apps.tezos.address.py @@ -46,10 +46,11 @@ class TestTezosAddress(unittest.TestCase): [44 | HARDENED], [44 | HARDENED, 1729 | HARDENED], [44 | HARDENED, 1729 | HARDENED, 0], - [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 0 | HARDENED], + [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 0], [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED], [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 1, 0], [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 0, 0], + [44 | HARDENED, 1729 | HARDENED, 1 | HARDENED, 1 | HARDENED], [44 | HARDENED, 1729 | HARDENED, 9999000 | HARDENED], [44 | HARDENED, 60 | HARDENED, 0 | HARDENED, 0, 0], [1 | HARDENED, 1 | HARDENED, 1 | HARDENED], @@ -58,6 +59,9 @@ class TestTezosAddress(unittest.TestCase): [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED], [44 | HARDENED, 1729 | HARDENED, 3 | HARDENED], [44 | HARDENED, 1729 | HARDENED, 9 | HARDENED], + [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 0 | HARDENED], + [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 3 | HARDENED], + [44 | HARDENED, 1729 | HARDENED, 0 | HARDENED, 9 | HARDENED], ] for path in incorrect_paths: diff --git a/core/tests/test_apps.tezos.encode.py b/core/tests/test_apps.tezos.encode.py index ee875d3757..16f1142f4d 100644 --- a/core/tests/test_apps.tezos.encode.py +++ b/core/tests/test_apps.tezos.encode.py @@ -4,9 +4,8 @@ from common import * from trezor.messages import TezosContractType from trezor.messages.TezosContractID import TezosContractID -from apps.tezos.helpers import base58_decode_check, base58_encode_check +from apps.tezos.helpers import base58_decode_check, base58_encode_check, write_bool from apps.tezos.sign_tx import ( - _encode_bool, _encode_contract_id, _encode_data_with_bool_prefix, _encode_zarith, @@ -35,11 +34,11 @@ class TestTezosEncoding(unittest.TestCase): def test_tezos_encode_bool(self): w = bytearray() - _encode_bool(w, True) + write_bool(w, True) self.assertEqual(bytes(w), bytes([255])) w = bytearray() - _encode_bool(w, False) + write_bool(w, False) self.assertEqual(bytes(w), bytes([0])) def test_tezos_encode_contract_id(self): From 2dd1ae64914bb259bd9b92493099ff81c90c421b Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Thu, 18 Apr 2019 14:25:42 +0200 Subject: [PATCH 06/78] refactor path validation --- core/src/apps/tezos/helpers.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/core/src/apps/tezos/helpers.py b/core/src/apps/tezos/helpers.py index 4640d7eb30..6cf5e331ba 100644 --- a/core/src/apps/tezos/helpers.py +++ b/core/src/apps/tezos/helpers.py @@ -54,10 +54,14 @@ def validate_full_path(path: list) -> bool: return False if path[1] != 1729 | HARDENED: return False - if length == 3 and (path[2] < HARDENED or path[2] > 1000000 | HARDENED): - return False - if length == 4 and (path[2] != 0 | HARDENED or path[3] < HARDENED or path[3] > 1000000 | HARDENED): - return False + if length == 3: + if path[2] < HARDENED or path[2] > 1000000 | HARDENED: + return False + if length == 4: + if path[2] != 0 | HARDENED: + return False + if path[3] < HARDENED or path[3] > 1000000 | HARDENED: + return False return True From dce60da6ec00046b84ff6dfdbb5f0246daf68e18 Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Thu, 18 Apr 2019 14:37:11 +0200 Subject: [PATCH 07/78] Added comment to path validation --- core/src/apps/tezos/helpers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/apps/tezos/helpers.py b/core/src/apps/tezos/helpers.py index 6cf5e331ba..631947a012 100644 --- a/core/src/apps/tezos/helpers.py +++ b/core/src/apps/tezos/helpers.py @@ -45,6 +45,8 @@ def validate_full_path(path: list) -> bool: """ Validates derivation path to equal 44'/1729'/a', where `a` is an account index from 0 to 1 000 000. + Additional component added to allow ledger migration + 44'/1729'/0'/b' where `b` is an account index from 0 to 1 000 000 """ length = len(path) print(path) From 03790e992fbb8608c439ba91eed43458a0a8edeb Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Thu, 18 Apr 2019 14:40:04 +0200 Subject: [PATCH 08/78] reformated with black --- .../device_tests/test_msg_tezos_sign_tx.py | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py index db5643c388..7bcee80876 100644 --- a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py +++ b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py @@ -209,21 +209,23 @@ class TestMsgTezosSignTx(TrezorTest): "proposal": { "source": "005f450441f41ee11eee78a31d1e1e55627c783bd6", "period": 17, - "proposals": ["dfa974df171c2dad9a9b8f25d99af41fd9702ce5d04521d2f9943c84d88aa572"], + "proposals": [ + "dfa974df171c2dad9a9b8f25d99af41fd9702ce5d04521d2f9943c84d88aa572" + ], }, }, ), ) assert ( - resp.signature - == "edsigtfY16R32k2WVMYfFr7ymnro4ib5zMckk28vsuViYNN77DJAvCJLRNArd9L531pUCxT4YdcvCvBym5dhcZ1rknEVm6yZ8bB" + resp.signature + == "edsigtfY16R32k2WVMYfFr7ymnro4ib5zMckk28vsuViYNN77DJAvCJLRNArd9L531pUCxT4YdcvCvBym5dhcZ1rknEVm6yZ8bB" ) assert ( - resp.sig_op_contents.hex() - == "dee04042c0832d68a43699b2001c0a38065436eb05e578071a763e1972d0bc8105005f450441f41ee11eee78a31d1e1e55627c783bd60000001100000020dfa974df171c2dad9a9b8f25d99af41fd9702ce5d04521d2f9943c84d88aa5723b12621296a679b3a74ea790df5347995a76e20a09e76590baaacf4e09341965a04123f5cbbba8427f045b5f7d59157a3098e44839babe7c247d19b58bbb2405" + resp.sig_op_contents.hex() + == "dee04042c0832d68a43699b2001c0a38065436eb05e578071a763e1972d0bc8105005f450441f41ee11eee78a31d1e1e55627c783bd60000001100000020dfa974df171c2dad9a9b8f25d99af41fd9702ce5d04521d2f9943c84d88aa5723b12621296a679b3a74ea790df5347995a76e20a09e76590baaacf4e09341965a04123f5cbbba8427f045b5f7d59157a3098e44839babe7c247d19b58bbb2405" ) assert ( - resp.operation_hash == "opLqntFUu984M7LnGsFvfGW6kWe9QjAz4AfPDqQvwJ1wPM4Si4c" + resp.operation_hash == "opLqntFUu984M7LnGsFvfGW6kWe9QjAz4AfPDqQvwJ1wPM4Si4c" ) def test_tezos_sign_tx_multiple_proposals(self): @@ -239,25 +241,26 @@ class TestMsgTezosSignTx(TrezorTest): "proposal": { "source": "005f450441f41ee11eee78a31d1e1e55627c783bd6", "period": 17, - "proposals": ["2a6ff28ab4d0ccb18f7129aaaf9a4b8027d794f2562849665fdb6999db2a4e57", - "47cd60c09ab8437cc9fe19add494dce1b9844100f660f02ce77510a0c66d2762"], + "proposals": [ + "2a6ff28ab4d0ccb18f7129aaaf9a4b8027d794f2562849665fdb6999db2a4e57", + "47cd60c09ab8437cc9fe19add494dce1b9844100f660f02ce77510a0c66d2762", + ], }, }, ), ) assert ( - resp.signature - == "edsigu6GAjhiWAQ64ctWTGEDYAZ16tYzLgzWzqc4CUyixK4FGRE8YUBVzFaVJ2fUCexZjZLMLdiNZGcUdzeL1bQhZ2h5oLrh7pA" + resp.signature + == "edsigu6GAjhiWAQ64ctWTGEDYAZ16tYzLgzWzqc4CUyixK4FGRE8YUBVzFaVJ2fUCexZjZLMLdiNZGcUdzeL1bQhZ2h5oLrh7pA" ) assert ( - resp.sig_op_contents.hex() - == "7e0be36a90c663c73c60da3889ffefff1383fb65cc29f0639f173d8f95a52df705005f450441f41ee11eee78a31d1e1e55627c783bd600000011000000402a6ff28ab4d0ccb18f7129aaaf9a4b8027d794f2562849665fdb6999db2a4e5747cd60c09ab8437cc9fe19add494dce1b9844100f660f02ce77510a0c66d2762f813361ac00ada7e3256f23973ae25b112229476a3cb3e506fe929ea1e9358299fed22178d1be689cddeedd1f303abfef859b664f159a528576a1c807079f005" + resp.sig_op_contents.hex() + == "7e0be36a90c663c73c60da3889ffefff1383fb65cc29f0639f173d8f95a52df705005f450441f41ee11eee78a31d1e1e55627c783bd600000011000000402a6ff28ab4d0ccb18f7129aaaf9a4b8027d794f2562849665fdb6999db2a4e5747cd60c09ab8437cc9fe19add494dce1b9844100f660f02ce77510a0c66d2762f813361ac00ada7e3256f23973ae25b112229476a3cb3e506fe929ea1e9358299fed22178d1be689cddeedd1f303abfef859b664f159a528576a1c807079f005" ) assert ( - resp.operation_hash == "onobSyNgiitGXxSVFJN6949MhUomkkxvH4ZJ2owgWwNeDdntF9Y" + resp.operation_hash == "onobSyNgiitGXxSVFJN6949MhUomkkxvH4ZJ2owgWwNeDdntF9Y" ) - def test_tezos_sing_tx_ballot_yay(self): self.setup_mnemonic_allallall() @@ -272,15 +275,15 @@ class TestMsgTezosSignTx(TrezorTest): "source": "0002298c03ed7d454a101eb7022bc95f7e5f41ac78", "period": 2, "proposal": "def7ed9c84af23ab37ebb60dd83cd103d1272ad6c63d4c05931567e65ed027e3", - "ballot": 0 + "ballot": 0, }, - } - ) + }, + ), ) assert ( - resp.signature - == "edsigtkxNm6YXwtV24DqeuimeZFTeFCn2jDYheSsXT4rHMcEjNvzsiSo55nVyVsQxtEe8M7U4PWJWT4rGYYGckQCgtkNJkd2roX" + resp.signature + == "edsigtkxNm6YXwtV24DqeuimeZFTeFCn2jDYheSsXT4rHMcEjNvzsiSo55nVyVsQxtEe8M7U4PWJWT4rGYYGckQCgtkNJkd2roX" ) def test_tezos_sing_tx_ballot_nay(self): @@ -297,14 +300,14 @@ class TestMsgTezosSignTx(TrezorTest): "source": "0002298c03ed7d454a101eb7022bc95f7e5f41ac78", "period": 2, "proposal": "def7ed9c84af23ab37ebb60dd83cd103d1272ad6c63d4c05931567e65ed027e3", - "ballot": 1 + "ballot": 1, }, - } - ) + }, + ), ) assert ( - resp.signature - == "edsigtqLaizfF6Cfc2JQL7TrsyniGhpZEojZAKMFW6AeudaUoU8KGXEHJH69Q4Lf27qFyUSTfbeHNnnCt69SGEPWkmpkgkgqMbL" + resp.signature + == "edsigtqLaizfF6Cfc2JQL7TrsyniGhpZEojZAKMFW6AeudaUoU8KGXEHJH69Q4Lf27qFyUSTfbeHNnnCt69SGEPWkmpkgkgqMbL" ) def test_tezos_sing_tx_ballot_pass(self): @@ -321,14 +324,13 @@ class TestMsgTezosSignTx(TrezorTest): "source": "0002298c03ed7d454a101eb7022bc95f7e5f41ac78", "period": 2, "proposal": "def7ed9c84af23ab37ebb60dd83cd103d1272ad6c63d4c05931567e65ed027e3", - "ballot": 2 + "ballot": 2, }, - } - ) + }, + ), ) assert ( - resp.signature - == "edsigu6YX7EegPwrpcEbdNQsNhrRiEagBNGJBmFamP4mixZZw1UynhahGQ8RNiZLSUVLERUZwygrsSVenBqXGt9VnknTxtzjKzv" + resp.signature + == "edsigu6YX7EegPwrpcEbdNQsNhrRiEagBNGJBmFamP4mixZZw1UynhahGQ8RNiZLSUVLERUZwygrsSVenBqXGt9VnknTxtzjKzv" ) - From 8ee605484e13b1a51fe1b31e56e3710e23572eaf Mon Sep 17 00:00:00 2001 From: Adrian Nagy Date: Thu, 18 Apr 2019 14:45:36 +0200 Subject: [PATCH 09/78] remove stray print --- core/src/apps/tezos/helpers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/apps/tezos/helpers.py b/core/src/apps/tezos/helpers.py index 631947a012..73bd02d1f8 100644 --- a/core/src/apps/tezos/helpers.py +++ b/core/src/apps/tezos/helpers.py @@ -49,7 +49,6 @@ def validate_full_path(path: list) -> bool: 44'/1729'/0'/b' where `b` is an account index from 0 to 1 000 000 """ length = len(path) - print(path) if length < 3 or length > 4: return False if path[0] != 44 | HARDENED: From 9f8ebcf1833e2f56c86df854c915385b0e9f43af Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 18 Apr 2019 14:52:24 +0200 Subject: [PATCH 10/78] build: make build scripts work from monorepo root incidentally fixes #9 and the dreaded non-bug #8 --- .gitignore | 1 + core/build-docker.sh | 23 +++++++++---- legacy/build.sh | 17 ++++++---- legacy/script/bootstrap | 2 +- legacy/script/fullbuild | 73 ++++++++++++++++++++++++----------------- 5 files changed, 71 insertions(+), 45 deletions(-) diff --git a/.gitignore b/.gitignore index 8090026217..492144ebd5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ .vscode/ __pycache__/ *.pyc +/build diff --git a/core/build-docker.sh b/core/build-docker.sh index deec6c7bd3..2543b645a5 100755 --- a/core/build-docker.sh +++ b/core/build-docker.sh @@ -1,6 +1,8 @@ #!/bin/sh set -e +cd "$(dirname $0)/.." + if [ "$1" = "--gcc_source" ]; then TOOLCHAIN_FLAVOR=src shift @@ -9,23 +11,30 @@ else fi IMAGE=trezor-core-build.$TOOLCHAIN_FLAVOR -TAG=${1:-master} +if [ -z "$1" ]; then + TAG=master +else + TAG=core/${1} +fi REPOSITORY=${2:-trezor} PRODUCTION=${PRODUCTION:-0} if [ "$REPOSITORY" = "local" ]; then REPOSITORY=file:///local/ else - REPOSITORY=https://github.com/$REPOSITORY/trezor-core.git + REPOSITORY=https://github.com/$REPOSITORY/trezor-firmware.git fi -docker build -t $IMAGE --build-arg TOOLCHAIN_FLAVOR=$TOOLCHAIN_FLAVOR . +docker build -t $IMAGE --build-arg TOOLCHAIN_FLAVOR=$TOOLCHAIN_FLAVOR core -mkdir -p $(pwd)/build-docker -docker run -t -v $(pwd):/local -v $(pwd)/build-docker:/build:z --user="$(stat -c "%u:%g" .)" $IMAGE /bin/sh -c "\ +USER=$(ls -lnd . | awk '{ print $3 }') +GROUP=$(ls -lnd . | awk '{ print $4 }') + +mkdir -p $(pwd)/build/core +docker run -t -v $(pwd):/local -v $(pwd)/build/core:/build:z --user="$USER:$GROUP" $IMAGE /bin/sh -c "\ cd /tmp && \ - git clone $REPOSITORY trezor-core && \ - cd trezor-core && \ + git clone $REPOSITORY trezor-firmware && \ + cd trezor-firmware/core && \ ln -s /build build && git checkout $TAG && \ git submodule update --init --recursive && \ diff --git a/legacy/build.sh b/legacy/build.sh index 6afb96c5da..02a3e6f46a 100755 --- a/legacy/build.sh +++ b/legacy/build.sh @@ -1,19 +1,24 @@ #!/bin/bash set -e +cd "$(dirname $0)/.." + BOOTLOADER_COMMIT=${1:-HEAD} FIRMWARE_COMMIT=${2:-HEAD} if [ "$BOOTLOADER_COMMIT" = "EMU" ]; then - export EMULATOR=1 + export EMULATOR=1 fi if [ "$EMULATOR" = 1 ]; then - IMAGE=trezor-mcu-emulator + IMAGE=trezor-mcu-emulator else - IMAGE=trezor-mcu-build + IMAGE=trezor-mcu-build fi -docker build -t "$IMAGE" --build-arg EMULATOR=$EMULATOR . -docker run -it -v $(pwd):/src:z --user="$(stat -c "%u:%g" .)" "$IMAGE" \ - /src/script/fullbuild "$BOOTLOADER_COMMIT" "$FIRMWARE_COMMIT" +USER=$(ls -lnd . | awk '{ print $3 }') +GROUP=$(ls -lnd . | awk '{ print $4 }') + +docker build -t "$IMAGE" --build-arg EMULATOR=$EMULATOR legacy +docker run -it -v $(pwd):/src:z --user="$USER:$GROUP" "$IMAGE" \ + /src/legacy/script/fullbuild "$BOOTLOADER_COMMIT" "$FIRMWARE_COMMIT" diff --git a/legacy/script/bootstrap b/legacy/script/bootstrap index 500e9a0612..617aa69118 100755 --- a/legacy/script/bootstrap +++ b/legacy/script/bootstrap @@ -5,6 +5,6 @@ set -e -cd "$(dirname "$0")/.." +cd "$(dirname "$0")/../.." git submodule update --init --recursive diff --git a/legacy/script/fullbuild b/legacy/script/fullbuild index f8ddb87424..193d52d548 100755 --- a/legacy/script/fullbuild +++ b/legacy/script/fullbuild @@ -9,22 +9,24 @@ export LANG=C.UTF-8 set -eu -cd "$(dirname "$0")/.." +cd "$(dirname "$0")/../.." + +readonly MCU_ROOT=legacy readonly ARTIFACT_EXTENSIONS=(bin elf) readonly BUILD_DIR="$(readlink -f build)" -readonly BOOTLOADER_DIR="$BUILD_DIR/bootloader" -readonly BOOTLOADER_FILENAME="bootloader/bootloader.bin" +readonly BOOTLOADER_DIR="$BUILD_DIR/legacy/bootloader" +readonly BOOTLOADER_FILENAME="$MCU_ROOT/bootloader/bootloader.bin" readonly BOOTLOADER_PATH="$BOOTLOADER_DIR/$BOOTLOADER_FILENAME" -readonly FIRMWARE_DIR="$BUILD_DIR/firmware" -readonly FIRMWARE_FILENAME="firmware/trezor.bin" +readonly FIRMWARE_DIR="$BUILD_DIR/legacy/firmware" +readonly FIRMWARE_FILENAME="$MCU_ROOT/firmware/trezor.bin" readonly FIRMWARE_PATH="$FIRMWARE_DIR/$FIRMWARE_FILENAME" readonly EMULATOR_DIR="$FIRMWARE_DIR" -readonly EMULATOR_FILENAME="firmware/trezor-emulator.elf" -readonly EMULATOR_PATH="$EMULATOR_DIR/firmware/trezor.elf" +readonly EMULATOR_FILENAME="$MCU_ROOT/firmware/trezor-emulator.elf" +readonly EMULATOR_PATH="$EMULATOR_DIR/$MCU_ROOT/firmware/trezor.elf" worktree_setup() { local path="$1" @@ -36,27 +38,25 @@ worktree_setup() { # Use `git rev-parse` so that we can use any reference from the working repository. git -C "$path" checkout "$(git rev-parse "$commit")" - ( cd "$path" && script/setup ) + ( cd "$path/$MCU_ROOT" && script/setup ) } worktree_build() { local path="$1" - if [ -e "$path/Pipfile" ]; then - pushd $path - if ! pipenv install; then - # older tags can fail because they don't have protobuf in Pipfile - pipenv run pip install "protobuf==3.4.0" - pipenv install - fi - pipenv run script/cibuild - popd - else - # even older tags don't have Pipfile! - # use current one - pipenv install - ( cd "$path" && pipenv run script/cibuild ) + if [ ! -e "$path/$MCU_ROOT/Pipfile" ]; then + echo "Can't handle pre-monorepo tags properly. You will have to check out manually" + exit 1 fi + + pushd $path/$MCU_ROOT + if ! pipenv install; then + # older tags can fail because they don't have protobuf in Pipfile + pipenv run pip install "protobuf==3.4.0" + pipenv install + fi + pipenv run script/cibuild + popd } worktree_copy() { @@ -64,18 +64,23 @@ worktree_copy() { local filename="$2" local pattern="$3" - local describe="$(git -C "$path" describe --tags --match "$pattern")" + local describe="$(git -C "$path" describe --tags --match "legacy/$pattern")" + describe="${describe##legacy/}" local src="$path/$filename" local basename="$(basename "$filename")" local dest="$BUILD_DIR/${basename%.*}-$describe.${basename##*.}" - for extension in "${ARTIFACT_EXTENSIONS[@]}"; do - install -Dm0644 \ - "${src%.*}.$extension" \ - "${dest%.*}.$extension" - done + if [ "$EMULATOR" = 1 ]; then + install -Dm0644 "${src%.*}.elf" "${dest%.*}.elf" + else + for extension in "${ARTIFACT_EXTENSIONS[@]}"; do + install -Dm0644 \ + "${src%.*}.$extension" \ + "${dest%.*}.$extension" + done + fi printf "%s" "$dest" } @@ -84,7 +89,13 @@ main() { local bootloader_commit="$1" local firmware_commit="$2" - script/bootstrap + if [ "$bootloader_commit" != "HEAD" ]; then + bootloader_commit="legacy/$bootloader_commit" + fi + if [ "$firmware_commit" != "HEAD" ]; then + firmware_commit="legacy/$firmware_commit" + fi + worktree_setup "$FIRMWARE_DIR" "$firmware_commit" if [ "$EMULATOR" != 1 ]; then @@ -116,13 +127,13 @@ main() { "$BOOTLOADER_FILENAME" \ "bl*")" - printf "\n\n"; $PYTHON script/fingerprint \ + printf "\n\n"; $PYTHON $MCU_ROOT/script/fingerprint \ "$bootloader_path" \ --max-size 32768 \ --double fi - printf "\n\n"; $PYTHON script/fingerprint \ + printf "\n\n"; $PYTHON $MCU_ROOT/script/fingerprint \ "$firmware_path" \ --offset 256 \ --max-size 983296 # 256 + 64*1024 + 3*128*1024 + 4*128*1024 From 1e7357db8026346c8d4651c8b5b969eed9df04c0 Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 18 Apr 2019 15:03:24 +0200 Subject: [PATCH 11/78] core/ui: fix crash when trying to scroll with only one page --- core/src/trezor/ui/scroll.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/trezor/ui/scroll.py b/core/src/trezor/ui/scroll.py index 89d2fad39b..d144f76d7c 100644 --- a/core/src/trezor/ui/scroll.py +++ b/core/src/trezor/ui/scroll.py @@ -21,9 +21,9 @@ async def change_page(page, page_count): else: s = await swipe if s == SWIPE_UP: - return page + 1 # scroll down + return min(page + 1, page_count - 1) # scroll down elif s == SWIPE_DOWN: - return page - 1 # scroll up + return max(page - 1, 0) # scroll up async def paginate(render_page, page_count, page=0, *args): From 1e1e6513a03ba2504292547c91efff8a9aae844e Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 18 Apr 2019 15:43:33 +0200 Subject: [PATCH 12/78] core/tezos: add ButtonRequest in front of proposal paginator --- core/src/apps/tezos/layout.py | 8 +++++--- core/src/apps/tezos/sign_tx.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/apps/tezos/layout.py b/core/src/apps/tezos/layout.py index 315875fea7..325d73b26d 100644 --- a/core/src/apps/tezos/layout.py +++ b/core/src/apps/tezos/layout.py @@ -1,8 +1,9 @@ from micropython import const from trezor import ui, wire -from trezor.messages import ButtonRequestType -from trezor.ui.confirm import CANCELLED, CONFIRMED, ConfirmDialog +from trezor.messages import ButtonRequestType, MessageType +from trezor.messages.ButtonRequest import ButtonRequest +from trezor.ui.confirm import CANCELLED, ConfirmDialog from trezor.ui.scroll import Scrollpage, animate_swipe, paginate from trezor.ui.text import Text from trezor.utils import chunks, format_amount @@ -88,7 +89,8 @@ async def require_confirm_ballot(ctx, proposal, ballot): # use, when there are more then one proposals in one operation -async def show_proposals(ctx, proposals): +async def require_confirm_proposals(ctx, proposals): + await ctx.call(ButtonRequest(code=ButtonRequestType.SignTx), MessageType.ButtonAck) first_page = const(0) pages = proposals title = "Submit proposals" if len(proposals) > 1 else "Submit proposal" diff --git a/core/src/apps/tezos/sign_tx.py b/core/src/apps/tezos/sign_tx.py index f6aed917ef..ff575e3187 100644 --- a/core/src/apps/tezos/sign_tx.py +++ b/core/src/apps/tezos/sign_tx.py @@ -63,7 +63,7 @@ async def sign_tx(ctx, msg, keychain): elif msg.proposal is not None: proposed_protocols = [_get_protocol_hash(p) for p in msg.proposal.proposals] - await layout.show_proposals(ctx, proposed_protocols) + await layout.require_confirm_proposals(ctx, proposed_protocols) elif msg.ballot is not None: proposed_protocol = _get_protocol_hash(msg.ballot.proposal) From 3e74ed927bbfbafb668687d8dca0dcdfd48cf885 Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 18 Apr 2019 15:44:09 +0200 Subject: [PATCH 13/78] python/tezos: add input flow for paginators --- .../tests/device_tests/test_msg_tezos_sign_tx.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py index 7bcee80876..bee4700ce0 100644 --- a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py +++ b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py @@ -14,6 +14,8 @@ # You should have received a copy of the License along with this library. # If not, see . +import time + import pytest from trezorlib import messages, tezos @@ -196,9 +198,19 @@ class TestMsgTezosSignTx(TrezorTest): resp.operation_hash == "oocgc3hyKsGHPsw6WFWJpWT8jBwQLtebQAXF27KNisThkzoj635" ) + def input_flow(self, num_pages): + yield + time.sleep(1) + for _ in range(num_pages - 1): + self.client.debug.swipe_down() + time.sleep(1) + self.client.debug.press_yes() + + def test_tezos_sign_tx_proposal(self): self.setup_mnemonic_allallall() + self.client.set_input_flow(self.input_flow(num_pages=1)) resp = tezos.sign_tx( self.client, TEZOS_PATH_10, @@ -231,6 +243,7 @@ class TestMsgTezosSignTx(TrezorTest): def test_tezos_sign_tx_multiple_proposals(self): self.setup_mnemonic_allallall() + self.client.set_input_flow(self.input_flow(num_pages=2)) resp = tezos.sign_tx( self.client, TEZOS_PATH_10, From 834a483e1a30284b9b2a87692affb769612b8a47 Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 18 Apr 2019 15:53:57 +0200 Subject: [PATCH 14/78] tezos: make style --- core/src/apps/tezos/sign_tx.py | 4 ++-- python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/apps/tezos/sign_tx.py b/core/src/apps/tezos/sign_tx.py index ff575e3187..04ef8d5470 100644 --- a/core/src/apps/tezos/sign_tx.py +++ b/core/src/apps/tezos/sign_tx.py @@ -3,17 +3,17 @@ from micropython import const from trezor import wire from trezor.crypto import hashlib from trezor.crypto.curve import ed25519 -from trezor.messages import TezosContractType, TezosBallotType +from trezor.messages import TezosBallotType, TezosContractType from trezor.messages.TezosSignedTx import TezosSignedTx from apps.common import paths -from apps.tezos import CURVE, helpers, layout from apps.common.writers import ( write_bytes, write_uint8, write_uint16_be, write_uint32_be, ) +from apps.tezos import CURVE, helpers, layout PROPOSAL_LENGTH = const(32) diff --git a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py index bee4700ce0..ecd6101a76 100644 --- a/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py +++ b/python/trezorlib/tests/device_tests/test_msg_tezos_sign_tx.py @@ -206,7 +206,6 @@ class TestMsgTezosSignTx(TrezorTest): time.sleep(1) self.client.debug.press_yes() - def test_tezos_sign_tx_proposal(self): self.setup_mnemonic_allallall() From 8b065984744d9a9301a726d5ceef07ef1f67ebc8 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 18 Apr 2019 16:27:27 +0200 Subject: [PATCH 15/78] core+legacy: rework code styling checks --- core/.clang-format => .clang-format | 0 .gitmodules | 1 + Makefile | 24 + common/defs/webauthn/gen.py | 20 +- common/keys/public_trezor1.h | 1 + common/keys/sample.h | 1 + common/protob/check.py | 6 +- common/tools/coin_info.py | 8 +- common/tools/coindef.py | 66 +- common/tools/coins_details.py | 11 +- common/tools/cointool.py | 9 +- common/tools/diffize_coins_details.py | 7 +- common/tools/support.py | 6 +- core/Makefile | 23 +- .../extmod/modtrezorui/display-stm32_1.h | 4 +- .../extmod/modtrezorui/display-stm32_t.h | 4 +- core/embed/unix/mpconfigport.h | 2 + core/src/trezor/messages/__init__.py | 2 +- crypto/.clang-format | 2 - crypto/tests/test_check_cardano.h | 136 +- crypto/tests/test_curves.py | 29 +- crypto/tests/test_wycheproof.py | 26 +- legacy/.clang-format | 2 - legacy/bootloader/combine/prepare.py | 14 +- legacy/bootloader/firmware_align.py | 13 +- legacy/bootloader/firmware_sign.py | 8 +- legacy/bootloader/firmware_sign_split.py | 30 +- legacy/firmware/bl_data.py | 18 +- legacy/firmware/fsm_msg_common.h | 4 +- legacy/firmware/protob/messages_map.py | 54 +- legacy/firmware/u2f/u2f.h | 1 + legacy/firmware/u2f/u2f_hid.h | 1 + legacy/gen/bitmaps.c | 1 + legacy/gen/bitmaps.h | 4 +- legacy/gen/bitmaps/generate.py | 76 +- legacy/gen/fonts.c | 20 +- legacy/gen/fonts.h | 6 +- legacy/gen/fonts/generate.py | 33 +- legacy/gen/handlers/handlers.py | 191 +- legacy/gen/strwidth.c | 46 +- setup.cfg | 35 + storage/norcow.c | 766 +++--- storage/norcow.h | 12 +- storage/storage.c | 2064 +++++++++-------- storage/storage.h | 11 +- {core/tools => tools}/clang-format-check | 0 {core => tools}/help.awk | 0 tools/style.c.exclude | 15 + tools/style.c.include | 5 + tools/style.py.exclude | 2 + tools/style.py.include | 4 + 51 files changed, 1998 insertions(+), 1826 deletions(-) rename core/.clang-format => .clang-format (100%) create mode 100644 Makefile delete mode 100644 crypto/.clang-format delete mode 100644 legacy/.clang-format create mode 100644 setup.cfg rename {core/tools => tools}/clang-format-check (100%) rename {core => tools}/help.awk (100%) create mode 100644 tools/style.c.exclude create mode 100644 tools/style.c.include create mode 100644 tools/style.py.exclude create mode 100644 tools/style.py.include diff --git a/core/.clang-format b/.clang-format similarity index 100% rename from core/.clang-format rename to .clang-format diff --git a/.gitmodules b/.gitmodules index fe32821490..d4e39156fe 100644 --- a/.gitmodules +++ b/.gitmodules @@ -23,6 +23,7 @@ [submodule "legacy/vendor/nanopb"] path = legacy/vendor/nanopb url = https://github.com/nanopb/nanopb.git + ignore = untracked [submodule "legacy/vendor/QR-Code-generator"] path = legacy/vendor/QR-Code-generator url = https://github.com/nayuki/QR-Code-generator.git diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..d4aa18166c --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +## help commands: + +help: ## show this help + @awk -f ./tools/help.awk $(MAKEFILE_LIST) + +## style commands: + +style_check: ## run code style check on application sources and tests + flake8 --version + isort --version | awk '/VERSION/{print $$2}' + black --version + flake8 $(shell find -type f -name '*.py' | grep -f ./tools/style.py.include | grep -v -f ./tools/style.py.exclude ) + isort --check-only $(shell find -type f -name '*.py' | grep -f ./tools/style.py.include | grep -v -f ./tools/style.py.exclude ) + black --check $(shell find -type f -name '*.py' | grep -f ./tools/style.py.include | grep -v -f ./tools/style.py.exclude ) + +style: ## apply code style on application sources and tests + isort $(shell find -type f -name '*.py' | grep -f ./tools/style.py.include | grep -v -f ./tools/style.py.exclude ) + black $(shell find -type f -name '*.py' | grep -f ./tools/style.py.include | grep -v -f ./tools/style.py.exclude ) + +cstyle_check: ## run code style check on low-level C code + ./tools/clang-format-check $(shell find -type f -name '*.c' -o -name '*.h' | grep -f ./tools/style.c.include | grep -v -f ./tools/style.c.exclude ) + +cstyle: ## apply code style on low-level C code + clang-format -i $(shell find -type f -name '*.c' -o -name '*.h' | grep -f ./tools/style.c.include | grep -v -f ./tools/style.c.exclude ) diff --git a/common/defs/webauthn/gen.py b/common/defs/webauthn/gen.py index e7b094439d..420cd6602d 100755 --- a/common/defs/webauthn/gen.py +++ b/common/defs/webauthn/gen.py @@ -1,13 +1,12 @@ #!/usr/bin/env python3 +import json import sys from glob import glob -import json from hashlib import sha256 - try: opt = sys.argv[1] -except: +except IndexError: print("Usage: gen.py [core|mcu|check])") sys.exit(1) @@ -22,12 +21,12 @@ def gen_core(data): for d in data: if "u2f" in d: url, label = d["u2f"], d["label"] - print(" \"%s\": \"%s\"," % (url, label)) + print(' "%s": "%s",' % (url, label)) print(" # WebAuthn") for d in data: if "webauthn" in d: origin, label = d["webauthn"], d["label"] - print(" \"%s\": \"%s\"," % (origin, label)) + print(' "%s": "%s",' % (origin, label)) print("}") @@ -36,12 +35,17 @@ def gen_mcu(data): if "u2f" in d: url, label = d["u2f"], d["label"] h = sha256(url.encode()).digest() - print("\t{\n\t\t// U2F: %s\n\t\t%s,\n\t\t\"%s\"\n\t}," % (url, c_bytes(h), label)) + print( + '\t{\n\t\t// U2F: %s\n\t\t%s,\n\t\t"%s"\n\t},' + % (url, c_bytes(h), label) + ) if "webauthn" in d: origin, label = d["webauthn"], d["label"] h = sha256(origin.encode()).digest() - print("\t{\n\t\t// WebAuthn: %s\n\t\t%s,\n\t\t\"%s\"\n\t}," % (origin, c_bytes(h), label)) - + print( + '\t{\n\t\t// WebAuthn: %s\n\t\t%s,\n\t\t"%s"\n\t},' + % (origin, c_bytes(h), label) + ) data = [] diff --git a/common/keys/public_trezor1.h b/common/keys/public_trezor1.h index ebd7d5b3e1..f4e1d83739 100644 --- a/common/keys/public_trezor1.h +++ b/common/keys/public_trezor1.h @@ -1,3 +1,4 @@ +// clang-format off // TREZORv1 production public keys "\x04\xd5\x71\xb7\xf1\x48\xc5\xe4\x23\x2c\x38\x14\xf7\x77\xd8\xfa\xea\xf1\xa8\x42\x16\xc7\x8d\x56\x9b\x71\x04\x1f\xfc\x76\x8a\x5b\x2d\x81\x0f\xc3\xbb\x13\x4d\xd0\x26\xb5\x7e\x65\x00\x52\x75\xae\xde\xf4\x3e\x15\x5f\x48\xfc\x11\xa3\x2e\xc7\x90\xa9\x33\x12\xbd\x58", "\x04\x63\x27\x9c\x0c\x08\x66\xe5\x0c\x05\xc7\x99\xd3\x2b\xd6\xba\xb0\x18\x8b\x6d\xe0\x65\x36\xd1\x10\x9d\x2e\xd9\xce\x76\xcb\x33\x5c\x49\x0e\x55\xae\xe1\x0c\xc9\x01\x21\x51\x32\xe8\x53\x09\x7d\x54\x32\xed\xa0\x6b\x79\x20\x73\xbd\x77\x40\xc9\x4c\xe4\x51\x6c\xb1", diff --git a/common/keys/sample.h b/common/keys/sample.h index 1518d4f3b1..ce9f2d417f 100644 --- a/common/keys/sample.h +++ b/common/keys/sample.h @@ -1,2 +1,3 @@ +// clang-format off // sample public key "correct horse battery staple" "\x04\x78\xd4\x30\x27\x4f\x8c\x5e\xc1\x32\x13\x38\x15\x1e\x9f\x27\xf4\xc6\x76\xa0\x08\xbd\xf8\x63\x8d\x07\xc0\xb6\xbe\x9a\xb3\x5c\x71\xa1\x51\x80\x63\x24\x3a\xcd\x4d\xfe\x96\xb6\x6e\x3f\x2e\xc8\x01\x3c\x8e\x07\x2c\xd0\x9b\x38\x34\xa1\x9f\x81\xf6\x59\xcc\x34\x55" diff --git a/common/protob/check.py b/common/protob/check.py index 0b612d4e8d..9c64682166 100755 --- a/common/protob/check.py +++ b/common/protob/check.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 -from glob import glob import os import re import sys +from glob import glob error = False @@ -21,7 +21,9 @@ for fn in sorted(glob(os.path.join(MYDIR, "messages-*.proto"))): line = line.strip().split(" ") if line[0] not in ["enum", "message"]: continue - if not line[1].startswith(prefix) and not line[1].startswith("Debug" + prefix): + if not line[1].startswith(prefix) and not line[1].startswith( + "Debug" + prefix + ): print("ERROR:", fn, line[1]) error = True diff --git a/common/tools/coin_info.py b/common/tools/coin_info.py index 9ca8026af1..df0880ebf9 100755 --- a/common/tools/coin_info.py +++ b/common/tools/coin_info.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 -from collections import defaultdict, OrderedDict -import re -import os -import json import glob +import json import logging +import os +import re +from collections import OrderedDict, defaultdict try: import requests diff --git a/common/tools/coindef.py b/common/tools/coindef.py index e96246054e..50eb50c79a 100644 --- a/common/tools/coindef.py +++ b/common/tools/coindef.py @@ -3,38 +3,38 @@ from trezorlib import protobuf as p class CoinDef(p.MessageType): FIELDS = { - 1: ('coin_name', p.UnicodeType, 0), - 2: ('coin_shortcut', p.UnicodeType, 0), - 3: ('coin_label', p.UnicodeType, 0), - 4: ('curve_name', p.UnicodeType, 0), - 5: ('address_type', p.UVarintType, 0), - 6: ('address_type_p2sh', p.UVarintType, 0), - 7: ('maxfee_kb', p.UVarintType, 0), - 8: ('minfee_kb', p.UVarintType, 0), - 9: ('signed_message_header', p.BytesType, 0), - 10: ('hash_genesis_block', p.BytesType, 0), - 11: ('xprv_magic', p.UVarintType, 0), - 12: ('xpub_magic', p.UVarintType, 0), - 13: ('xpub_magic_segwit_p2sh', p.UVarintType, 0), - 14: ('xpub_magic_segwit_native', p.UVarintType, 0), - 15: ('bech32_prefix', p.UnicodeType, 0), - 16: ('cashaddr_prefix', p.UnicodeType, 0), - 17: ('slip44', p.UVarintType, 0), - 18: ('segwit', p.BoolType, 0), - 19: ('decred', p.BoolType, 0), - 20: ('fork_id', p.UVarintType, 0), - 21: ('force_bip143', p.BoolType, 0), - 22: ('dust_limit', p.UVarintType, 0), - 23: ('uri_prefix', p.UnicodeType, 0), - 24: ('min_address_length', p.UVarintType, 0), - 25: ('max_address_length', p.UVarintType, 0), - 26: ('icon', p.BytesType, 0), - 28: ('website', p.UnicodeType, 0), - 29: ('github', p.UnicodeType, 0), - 30: ('maintainer', p.UnicodeType, 0), - 31: ('blocktime_seconds', p.UVarintType, 0), - 32: ('bip115', p.BoolType, 0), - 33: ('cooldown', p.UVarintType, 0), + 1: ("coin_name", p.UnicodeType, 0), + 2: ("coin_shortcut", p.UnicodeType, 0), + 3: ("coin_label", p.UnicodeType, 0), + 4: ("curve_name", p.UnicodeType, 0), + 5: ("address_type", p.UVarintType, 0), + 6: ("address_type_p2sh", p.UVarintType, 0), + 7: ("maxfee_kb", p.UVarintType, 0), + 8: ("minfee_kb", p.UVarintType, 0), + 9: ("signed_message_header", p.BytesType, 0), + 10: ("hash_genesis_block", p.BytesType, 0), + 11: ("xprv_magic", p.UVarintType, 0), + 12: ("xpub_magic", p.UVarintType, 0), + 13: ("xpub_magic_segwit_p2sh", p.UVarintType, 0), + 14: ("xpub_magic_segwit_native", p.UVarintType, 0), + 15: ("bech32_prefix", p.UnicodeType, 0), + 16: ("cashaddr_prefix", p.UnicodeType, 0), + 17: ("slip44", p.UVarintType, 0), + 18: ("segwit", p.BoolType, 0), + 19: ("decred", p.BoolType, 0), + 20: ("fork_id", p.UVarintType, 0), + 21: ("force_bip143", p.BoolType, 0), + 22: ("dust_limit", p.UVarintType, 0), + 23: ("uri_prefix", p.UnicodeType, 0), + 24: ("min_address_length", p.UVarintType, 0), + 25: ("max_address_length", p.UVarintType, 0), + 26: ("icon", p.BytesType, 0), + 28: ("website", p.UnicodeType, 0), + 29: ("github", p.UnicodeType, 0), + 30: ("maintainer", p.UnicodeType, 0), + 31: ("blocktime_seconds", p.UVarintType, 0), + 32: ("bip115", p.BoolType, 0), + 33: ("cooldown", p.UVarintType, 0), } def __init__( @@ -73,7 +73,7 @@ class CoinDef(p.MessageType): default_fee_b: dict = None, bitcore: dict = None, blockbook: dict = None, - cooldown: int = None + cooldown: int = None, ): self.coin_name = coin_name self.coin_shortcut = coin_shortcut diff --git a/common/tools/coins_details.py b/common/tools/coins_details.py index a637fc2d5f..e9bcd986bb 100755 --- a/common/tools/coins_details.py +++ b/common/tools/coins_details.py @@ -1,14 +1,15 @@ #!/usr/bin/env python3 """Fetch information about coins and tokens supported by Trezor and update it in coins_details.json.""" -import os -import time import json import logging -import requests +import os import sys -import coin_info +import time import click +import requests + +import coin_info LOG = logging.getLogger(__name__) @@ -149,7 +150,7 @@ def summary(coins, api_key): try: ret = coinmarketcap_call("global-metrics/quotes/latest", api_key) total_marketcap = int(ret["data"]["quote"]["USD"]["total_market_cap"]) - except: + except Exception: pass return dict( diff --git a/common/tools/cointool.py b/common/tools/cointool.py index d9ad5526ff..98ba220767 100755 --- a/common/tools/cointool.py +++ b/common/tools/cointool.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 import fnmatch +import glob import io import json import logging -import re -import sys import os -import glob +import re import struct +import sys import zlib from collections import defaultdict from hashlib import sha256 @@ -524,8 +524,7 @@ def cli(colors): # fmt: off @click.option("--backend/--no-backend", "-b", default=False, help="Check blockbook/bitcore responses") @click.option("--icons/--no-icons", default=True, help="Check icon files") -@click.option("-d", "--show-duplicates", type=click.Choice(("all", "nontoken", "errors")), - default="errors", help="How much information about duplicate shortcuts should be shown.") +@click.option("-d", "--show-duplicates", type=click.Choice(("all", "nontoken", "errors")), default="errors", help="How much information about duplicate shortcuts should be shown.") # fmt: on def check(backend, icons, show_duplicates): """Validate coin definitions. diff --git a/common/tools/diffize_coins_details.py b/common/tools/diffize_coins_details.py index 1e1a697960..2ad579a8f3 100755 --- a/common/tools/diffize_coins_details.py +++ b/common/tools/diffize_coins_details.py @@ -1,13 +1,12 @@ #!/usr/bin/env python3 -import click import json import os -import requests -import tempfile import subprocess -import sys +import tempfile +import click +import requests LIVE_URL = "https://trezor.io/static/json/coins_details.json" COINS_DETAILS = os.path.join( diff --git a/common/tools/support.py b/common/tools/support.py index 593387eb57..57bf24d2b6 100755 --- a/common/tools/support.py +++ b/common/tools/support.py @@ -1,11 +1,13 @@ #!/usr/bin/env python3 -import re +import json import os +import re import subprocess import sys + import click + import coin_info -import json SUPPORT_INFO = coin_info.get_support_data() diff --git a/core/Makefile b/core/Makefile index fb945738fb..c684a6eb3a 100644 --- a/core/Makefile +++ b/core/Makefile @@ -39,7 +39,7 @@ CFLAGS += -DGITREV=$(GITREV) ## help commands: help: ## show this help - @awk -f help.awk $(MAKEFILE_LIST) + @awk -f ../tools/help.awk $(MAKEFILE_LIST) ## dependencies commands: @@ -71,27 +71,6 @@ test_emu_monero: ## run selected monero device tests from monero-agent pylint: ## run pylint on application sources and tests pylint -E $(shell find src tests -name *.py) -## style commands: - -style_check: ## run code style check on application sources and tests - flake8 --version - isort --version | grep "VERSION" - black --version - flake8 $(shell find src -name *.py) - isort --check-only $(shell find src -name *.py ! -path 'src/trezor/messages/*') - black --check $(shell find src -name *.py ! -path 'src/trezor/messages/*') - -style: ## apply code style on application sources and tests - isort $(shell find src -name *.py ! -path 'src/trezor/messages/*') - black $(shell find src -name *.py ! -path 'src/trezor/messages/*') - -cstyle_check: ## run code style check on low-level C code - ./tools/clang-format-check embed/*/*.{c,h} embed/extmod/modtrezor*/*.{c,h} - - -cstyle: ## apply code style on low-level C code - clang-format -i embed/*/*.{c,h} embed/extmod/modtrezor*/*.{c,h} - ## code generation: templates: ## render Mako templates (for lists of coins, tokens, etc.) diff --git a/core/embed/extmod/modtrezorui/display-stm32_1.h b/core/embed/extmod/modtrezorui/display-stm32_1.h index cf3c82f2e7..7b05938687 100644 --- a/core/embed/extmod/modtrezorui/display-stm32_1.h +++ b/core/embed/extmod/modtrezorui/display-stm32_1.h @@ -241,6 +241,4 @@ void display_refresh(void) { HAL_GPIO_WritePin(OLED_DC_PORT, OLED_DC_PIN, GPIO_PIN_RESET); // set to CMD } -const char *display_save(const char *prefix) { - return NULL; -} +const char *display_save(const char *prefix) { return NULL; } diff --git a/core/embed/extmod/modtrezorui/display-stm32_t.h b/core/embed/extmod/modtrezorui/display-stm32_t.h index 653c83da98..bedd24aa9c 100644 --- a/core/embed/extmod/modtrezorui/display-stm32_t.h +++ b/core/embed/extmod/modtrezorui/display-stm32_t.h @@ -498,6 +498,4 @@ void display_refresh(void) { } } -const char *display_save(const char *prefix) { - return NULL; -} +const char *display_save(const char *prefix) { return NULL; } diff --git a/core/embed/unix/mpconfigport.h b/core/embed/unix/mpconfigport.h index 8619bf1a57..6f618d2b76 100644 --- a/core/embed/unix/mpconfigport.h +++ b/core/embed/unix/mpconfigport.h @@ -1,3 +1,5 @@ +// clang-format off + /* * This file is part of the MicroPython project, http://micropython.org/ * diff --git a/core/src/trezor/messages/__init__.py b/core/src/trezor/messages/__init__.py index 9e4ecb13ab..95c9b6fa6e 100644 --- a/core/src/trezor/messages/__init__.py +++ b/core/src/trezor/messages/__init__.py @@ -31,6 +31,6 @@ def get_type(wire_type): for msg_name in dir(MessageType): # Modules contain internal variables that may cause exception here. # No Message begins with underscore so it's safe to skip those. - if (msg_name[0] == '_'): + if msg_name[0] == "_": continue type_to_name[getattr(MessageType, msg_name)] = msg_name diff --git a/crypto/.clang-format b/crypto/.clang-format deleted file mode 100644 index 58d4b3b682..0000000000 --- a/crypto/.clang-format +++ /dev/null @@ -1,2 +0,0 @@ ---- -BasedOnStyle: Google diff --git a/crypto/tests/test_check_cardano.h b/crypto/tests/test_check_cardano.h index 434ea96b9a..5fd830f24a 100644 --- a/crypto/tests/test_check_cardano.h +++ b/crypto/tests/test_check_cardano.h @@ -5,82 +5,80 @@ START_TEST(test_ed25519_cardano_sign_vectors) { ed25519_secret_key secret_key_extension; ed25519_signature signature; - static const char - *vectors[] = - { - "6065a956b1b34145c4416fdc3ba3276801850e91a77a31a7be782463288aea5" - "3", // private key - "60ba6e25b1a02157fb69c5d1d7b96c4619736e545447069a6a6f0ba90844bc8" - "e", // private key extension - "64b20fa082b3143d6b5eed42c6ef63f99599d0888afe060620abc1b319935fe" - "1", // public key - "45b1a75fe3119e13c6f60ab9ba674b42f946fdc558e07c83dfa0751c2eba69c7" - "9331bd8a4a975662b23628a438a0eba76367e44c12ca91b39ec59063f860f10" - "d", // signature + static const char *vectors[] = { + "6065a956b1b34145c4416fdc3ba3276801850e91a77a31a7be782463288aea5" + "3", // private key + "60ba6e25b1a02157fb69c5d1d7b96c4619736e545447069a6a6f0ba90844bc8" + "e", // private key extension + "64b20fa082b3143d6b5eed42c6ef63f99599d0888afe060620abc1b319935fe" + "1", // public key + "45b1a75fe3119e13c6f60ab9ba674b42f946fdc558e07c83dfa0751c2eba69c7" + "9331bd8a4a975662b23628a438a0eba76367e44c12ca91b39ec59063f860f10" + "d", // signature - "e7d27516538403a53a8b041656a3f570909df641a0ab811fe7d87c9ba02a830" - "c", // private key - "794a2c54ad8b525b781773c87d38cbf4197636bc427a9d551368286fe4c294a" - "4", // private key extension - "95bb82ffd5707716bc65170ab4e8dafeed90fbe0ce9258713b7751e962d931d" - "f", // public key - "f2c9171782e7df7665126ac545ae53b05964b0160536efdb545e2460dbbec2b1" - "9ec6b338b8f1bf4dfee94360ed024b115e37b1d7e6f3f9ae4beb79539428560" - "f", // signature + "e7d27516538403a53a8b041656a3f570909df641a0ab811fe7d87c9ba02a830" + "c", // private key + "794a2c54ad8b525b781773c87d38cbf4197636bc427a9d551368286fe4c294a" + "4", // private key extension + "95bb82ffd5707716bc65170ab4e8dafeed90fbe0ce9258713b7751e962d931d" + "f", // public key + "f2c9171782e7df7665126ac545ae53b05964b0160536efdb545e2460dbbec2b1" + "9ec6b338b8f1bf4dfee94360ed024b115e37b1d7e6f3f9ae4beb79539428560" + "f", // signature - "9b5a3d9a4c60bcd49bb64b72c082b164314d0f61d842f2575fd1d4fb30a28a0" - "c", // private key - "b093e376f41eb7bf80abcd0073a52455d25b5d21815bc758e5f6f81536aedeb" - "b", // private key extension - "79fc8154554b97e4c56ef2f9dbb4c1421ff19509688931a1e964bda5dec0f19" - "f", // public key - "2ba1439ae648a7e8da7c9ab1ee6da94fd4ebe37abd0978306e8fba2afa8f111a" - "88a993dbf008bedae9167f4f68409e4c9ddaf02cba12418447b1848907ad800" - "f", // signature + "9b5a3d9a4c60bcd49bb64b72c082b164314d0f61d842f2575fd1d4fb30a28a0" + "c", // private key + "b093e376f41eb7bf80abcd0073a52455d25b5d21815bc758e5f6f81536aedeb" + "b", // private key extension + "79fc8154554b97e4c56ef2f9dbb4c1421ff19509688931a1e964bda5dec0f19" + "f", // public key + "2ba1439ae648a7e8da7c9ab1ee6da94fd4ebe37abd0978306e8fba2afa8f111a" + "88a993dbf008bedae9167f4f68409e4c9ddaf02cba12418447b1848907ad800" + "f", // signature - "52e0c98aa600cfdcd1ff28fcda5227ed87063f4a98547a78b771052cf102b40" - "c", // private key - "6c18d9f8075b1a6a1833540607479bd58b7beb8a83d2bb01ca7ae02452a2580" - "3", // private key extension - "dc907c7c06e6314eedd9e18c9f6c6f9cc4e205fb1c70da608234c319f1f7b0d" - "6", // public key - "0cd34f84e0d2fcb1800bdb0e869b9041349955ced66aedbe6bda187ebe8d36a6" - "2a05b39647e92fcc42aa7a7368174240afba08b8c81f981a22f942d6bd78160" - "2", // signature + "52e0c98aa600cfdcd1ff28fcda5227ed87063f4a98547a78b771052cf102b40" + "c", // private key + "6c18d9f8075b1a6a1833540607479bd58b7beb8a83d2bb01ca7ae02452a2580" + "3", // private key extension + "dc907c7c06e6314eedd9e18c9f6c6f9cc4e205fb1c70da608234c319f1f7b0d" + "6", // public key + "0cd34f84e0d2fcb1800bdb0e869b9041349955ced66aedbe6bda187ebe8d36a6" + "2a05b39647e92fcc42aa7a7368174240afba08b8c81f981a22f942d6bd78160" + "2", // signature - "11fd6462a3a92b35c22703f6f1c124ddcf36b7c2b09cc2784f320e1cfa12ec0" - "4", // private key - "c2785803c61c46aeca192a1bb1b7b20a8c4cc7fa01db57fc5d1d8a547340235" - "2", // private key extension - "839775a41876e328986aa26168958bba1176e67819b357eea84afceab8b1db7" - "8", // public key - "e41f73db2f8d2896a687802b2be76b7cabb73dfbb4891494883a0cbd9bbb9e5f" - "9d3e14d2d0b06c6674333508496db660936737c0efd9511514147dac79fa490" - "5", // signature + "11fd6462a3a92b35c22703f6f1c124ddcf36b7c2b09cc2784f320e1cfa12ec0" + "4", // private key + "c2785803c61c46aeca192a1bb1b7b20a8c4cc7fa01db57fc5d1d8a547340235" + "2", // private key extension + "839775a41876e328986aa26168958bba1176e67819b357eea84afceab8b1db7" + "8", // public key + "e41f73db2f8d2896a687802b2be76b7cabb73dfbb4891494883a0cbd9bbb9e5f" + "9d3e14d2d0b06c6674333508496db660936737c0efd9511514147dac79fa490" + "5", // signature - "5b1e5cad02274ba461f4708d8598d3497faf8fe3e894a379573aa6ac3a03e50" - "5", // private key - "ba179d2e3c67aabb486c48d16002b51ad32eab434c738a1550962313b07098c" - "d", // private key extension - "75eb8d197ec8627c85af88e66aa1e49065dd8ac98ed8991db52ece01635dfb7" - "6", // public key - "631015357cee3051116b4c2ff4d1c5beb13b6e5023635aa1eeb0563cadf0d4fb" - "c10bd5e31b4a4220c67875558c41b5cc0328104ae39cc7ff20ff0c2bda59890" - "6", // signature + "5b1e5cad02274ba461f4708d8598d3497faf8fe3e894a379573aa6ac3a03e50" + "5", // private key + "ba179d2e3c67aabb486c48d16002b51ad32eab434c738a1550962313b07098c" + "d", // private key extension + "75eb8d197ec8627c85af88e66aa1e49065dd8ac98ed8991db52ece01635dfb7" + "6", // public key + "631015357cee3051116b4c2ff4d1c5beb13b6e5023635aa1eeb0563cadf0d4fb" + "c10bd5e31b4a4220c67875558c41b5cc0328104ae39cc7ff20ff0c2bda59890" + "6", // signature - "624b47150f58dfa44284fbc63c9f99b9b79f808c4955a461f0e2be44eb0be50" - "d", // private key - "097aa006d694b165ef37cf23562e5967c96e49255d2f20faae478dee83aa5b0" - "2", // private key extension - "0588589cd9b51dfc028cf225674069cbe52e0e70deb02dc45b79b26ee3548b0" - "0", // public key - "1de1d275428ba9491a433cd473cd076c027f61e7a8b5391df9dea5cb4bc88d8a" - "57b095906a30b13e68259851a8dd3f57b6f0ffa37a5d3ffc171240f2d404f90" - "1", // signature + "624b47150f58dfa44284fbc63c9f99b9b79f808c4955a461f0e2be44eb0be50" + "d", // private key + "097aa006d694b165ef37cf23562e5967c96e49255d2f20faae478dee83aa5b0" + "2", // private key extension + "0588589cd9b51dfc028cf225674069cbe52e0e70deb02dc45b79b26ee3548b0" + "0", // public key + "1de1d275428ba9491a433cd473cd076c027f61e7a8b5391df9dea5cb4bc88d8a" + "57b095906a30b13e68259851a8dd3f57b6f0ffa37a5d3ffc171240f2d404f90" + "1", // signature - 0, - 0, - }; + 0, + 0, + }; const char **test_data; test_data = vectors; diff --git a/crypto/tests/test_curves.py b/crypto/tests/test_curves.py index 7056cb6363..093f95ab08 100755 --- a/crypto/tests/test_curves.py +++ b/crypto/tests/test_curves.py @@ -5,10 +5,11 @@ import hashlib import os import random -import curve25519 import ecdsa import pytest +import curve25519 + def bytes2num(s): res = 0 @@ -30,48 +31,48 @@ class Point: points = [ Point( "secp256k1", - 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, - 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8, + 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, + 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, ), Point( "secp256k1", 0x1, - 0x4218f20ae6c646b363db68605822fb14264ca8d2587fdd6fbc750d587e76a7ee, + 0x4218F20AE6C646B363DB68605822FB14264CA8D2587FDD6FBC750D587E76A7EE, ), Point( "secp256k1", 0x2, - 0x66fbe727b2ba09e09f5a98d70a5efce8424c5fa425bbda1c511f860657b8535e, + 0x66FBE727B2BA09E09F5A98D70A5EFCE8424C5FA425BBDA1C511F860657B8535E, ), Point( "secp256k1", - 0x1b, - 0x1adcea1cf831b0ad1653e769d1a229091d0cc68d4b0328691b9caacc76e37c90, + 0x1B, + 0x1ADCEA1CF831B0AD1653E769D1A229091D0CC68D4B0328691B9CAACC76E37C90, ), Point( "nist256p1", - 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, - 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5, + 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296, + 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5, ), Point( "nist256p1", 0x0, - 0x66485c780e2f83d72433bd5d84a06bb6541c2af31dae871728bf856a174f93f4, + 0x66485C780E2F83D72433BD5D84A06BB6541C2AF31DAE871728BF856A174F93F4, ), Point( "nist256p1", 0x0, - 0x99b7a386f1d07c29dbcc42a27b5f9449abe3d50de25178e8d7407a95e8b06c0b, + 0x99B7A386F1D07C29DBCC42A27B5F9449ABE3D50DE25178E8D7407A95E8B06C0B, ), Point( "nist256p1", - 0xaf8bbdfe8cdd5577acbf345b543d28cf402f4e94d3865b97ea0787f2d3aa5d22, - 0x35802b8b376b995265918b078bc109c21a535176585c40f519aca52d6afc147c, + 0xAF8BBDFE8CDD5577ACBF345B543D28CF402F4E94D3865B97EA0787F2D3AA5D22, + 0x35802B8B376B995265918B078BC109C21A535176585C40F519ACA52D6AFC147C, ), Point( "nist256p1", 0x80000, - 0x580610071f440f0dcc14a22e2d5d5afc1224c0cd11a3b4b51b8ecd2224ee1ce2, + 0x580610071F440F0DCC14A22E2D5D5AFC1224C0CD11A3B4B51B8ECD2224EE1CE2, ), ] diff --git a/crypto/tests/test_wycheproof.py b/crypto/tests/test_wycheproof.py index 3bdedbfac4..c449b29c8a 100755 --- a/crypto/tests/test_wycheproof.py +++ b/crypto/tests/test_wycheproof.py @@ -102,7 +102,7 @@ def is_valid_der(data): try: structure, _ = der_decode(data) return data == der_encode(structure) - except: + except Exception: return False @@ -140,7 +140,7 @@ def parse_ec_pubkey(public_key): try: public_key = bytes(public_key["public_key"].asOctets()) - except: + except Exception: raise ParseError("Not a BER encoded named elliptic curve public key") return curve_name, public_key @@ -152,13 +152,13 @@ def parse_ecdsa256_signature(signature): raise ParseError("Not a valid DER") try: signature, _ = der_decode(signature, asn1Spec=EcSignature()) - except: + except Exception: raise ParseError("Not a valid DER encoded ECDSA signature") try: r = int(signature["r"]).to_bytes(32, byteorder="big") s = int(signature["s"]).to_bytes(32, byteorder="big") signature = r + s - except: + except Exception: raise ParseError("Not a valid DER encoded 256 bit ECDSA signature") return signature @@ -281,7 +281,7 @@ def aes_cbc_decrypt(key, iv, ciphertext): def load_json_testvectors(filename): try: result = json.loads(open(os.path.join(testvectors_directory, filename)).read()) - except: + except Exception: raise DataError() return result @@ -310,7 +310,7 @@ def generate_aes(filename): plaintext = unhexlify(test["msg"]) ciphertext = unhexlify(test["ct"]) result = parse_result(test["result"]) - except: + except Exception: raise DataError() if len(key) not in [128 / 8, 192 / 8, 256 / 8]: @@ -359,7 +359,7 @@ def generate_chacha_poly(filename): ciphertext = unhexlify(test["ct"]) tag = unhexlify(test["tag"]) result = parse_result(test["result"]) - except: + except Exception: raise DataError() if result is None: @@ -406,7 +406,7 @@ def generate_curve25519_dh(filename): private_key = unhexlify(test["private"]) shared = unhexlify(test["shared"]) result = parse_result(test["result"]) - except: + except Exception: raise DataError() if curve_name != "curve25519": @@ -448,7 +448,7 @@ def generate_ecdh(filename): private_key = parse_signed_hex(test["private"]) shared = unhexlify(test["shared"]) result = parse_result(test["result"]) - except: + except Exception: raise DataError() try: @@ -498,7 +498,7 @@ def generate_ecdsa(filename): try: public_key = unhexlify(test_group["keyDer"]) - except: + except Exception: raise DataError() try: @@ -521,7 +521,7 @@ def generate_ecdsa(filename): signature = unhexlify(test["sig"]) message = unhexlify(test["msg"]) result = parse_result(test["result"]) - except: + except Exception: raise DataError() if result is None: @@ -563,7 +563,7 @@ def generate_eddsa(filename): try: public_key = unhexlify(test_group["keyDer"]) - except: + except Exception: raise DataError() try: @@ -579,7 +579,7 @@ def generate_eddsa(filename): signature = unhexlify(test["sig"]) message = unhexlify(test["msg"]) result = parse_result(test["result"]) - except: + except Exception: raise DataError() if result is None: diff --git a/legacy/.clang-format b/legacy/.clang-format deleted file mode 100644 index 58d4b3b682..0000000000 --- a/legacy/.clang-format +++ /dev/null @@ -1,2 +0,0 @@ ---- -BasedOnStyle: Google diff --git a/legacy/bootloader/combine/prepare.py b/legacy/bootloader/combine/prepare.py index eb073e9820..7918e063e1 100755 --- a/legacy/bootloader/combine/prepare.py +++ b/legacy/bootloader/combine/prepare.py @@ -1,12 +1,12 @@ #!/usr/bin/env python from __future__ import print_function -bl = open('bl.bin').read() -fw = open('fw.bin').read() -combined = bl + fw[:256] + (32768-256)*'\x00' + fw[256:] +bl = open("bl.bin").read() +fw = open("fw.bin").read() +combined = bl + fw[:256] + (32768 - 256) * "\x00" + fw[256:] -open('combined.bin', 'w').write(combined) +open("combined.bin", "w").write(combined) -print('bootloader : %d bytes' % len(bl)) -print('firmware : %d bytes' % len(fw)) -print('combined : %d bytes' % len(combined)) +print("bootloader : %d bytes" % len(bl)) +print("firmware : %d bytes" % len(fw)) +print("combined : %d bytes" % len(combined)) diff --git a/legacy/bootloader/firmware_align.py b/legacy/bootloader/firmware_align.py index 3c1434987e..0866593f56 100755 --- a/legacy/bootloader/firmware_align.py +++ b/legacy/bootloader/firmware_align.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -import sys import os +import sys TOTALSIZE = 32768 MAXSIZE = TOTALSIZE - 32 @@ -8,7 +8,10 @@ MAXSIZE = TOTALSIZE - 32 fn = sys.argv[1] fs = os.stat(fn).st_size if fs > MAXSIZE: - raise Exception('bootloader has to be smaller than %d bytes (current size is %d)' % (MAXSIZE, fs)) -with open(fn, 'ab') as f: - f.write(b'\x00' * (TOTALSIZE - fs)) - f.close() + raise Exception( + "bootloader has to be smaller than %d bytes (current size is %d)" + % (MAXSIZE, fs) + ) +with open(fn, "ab") as f: + f.write(b"\x00" * (TOTALSIZE - fs)) + f.close() diff --git a/legacy/bootloader/firmware_sign.py b/legacy/bootloader/firmware_sign.py index a7b8e13cf6..948a08f993 100755 --- a/legacy/bootloader/firmware_sign.py +++ b/legacy/bootloader/firmware_sign.py @@ -7,7 +7,6 @@ import struct import ecdsa - SLOTS = 3 pubkeys = { @@ -94,7 +93,7 @@ def update_hashes_in_header(data): data = bytearray(data) o = 0 for h in prepare_hashes(data[FWHEADER_SIZE:]): - data[0x20 + o:0x20 + o + 32] = h + data[0x20 + o : 0x20 + o + 32] = h o += 32 return bytes(data) @@ -112,6 +111,7 @@ def check_size(data): size = struct.unpack(" 32768: - raise Exception('bootloader has to be smaller than 32768 bytes') + raise Exception("bootloader has to be smaller than 32768 bytes") -data += b'\x00' * (32768 - len(data)) +data += b"\x00" * (32768 - len(data)) h = sha256(sha256(data).digest()).digest() -bl_hash = ', '.join('0x%02x' % x for x in bytearray(h)) -bl_data = ', '.join('0x%02x' % x for x in bytearray(data)) +bl_hash = ", ".join("0x%02x" % x for x in bytearray(h)) +bl_data = ", ".join("0x%02x" % x for x in bytearray(data)) -with open('bl_data.h', 'wt') as f: - f.write('static const uint8_t bl_hash[32] = {%s};\n' % bl_hash) - f.write('static const uint8_t bl_data[32768] = {%s};\n' % bl_data) +with open("bl_data.h", "wt") as f: + f.write("static const uint8_t bl_hash[32] = {%s};\n" % bl_hash) + f.write("static const uint8_t bl_data[32768] = {%s};\n" % bl_data) diff --git a/legacy/firmware/fsm_msg_common.h b/legacy/firmware/fsm_msg_common.h index 265b629d40..e8c4c447d3 100644 --- a/legacy/firmware/fsm_msg_common.h +++ b/legacy/firmware/fsm_msg_common.h @@ -256,12 +256,12 @@ void fsm_msgEntropyAck(const EntropyAck *msg) { } void fsm_msgBackupDevice(const BackupDevice *msg) { + (void)msg; + CHECK_INITIALIZED CHECK_PIN_UNCACHED - (void) - msg; char mnemonic[MAX_MNEMONIC_LEN + 1]; if (config_getMnemonic(mnemonic, sizeof(mnemonic))) { reset_backup(true, mnemonic); diff --git a/legacy/firmware/protob/messages_map.py b/legacy/firmware/protob/messages_map.py index c8c763047f..3bb9d4a5e8 100755 --- a/legacy/firmware/protob/messages_map.py +++ b/legacy/firmware/protob/messages_map.py @@ -1,11 +1,16 @@ #!/usr/bin/env python import sys - from collections import defaultdict -from messages_pb2 import MessageType -from messages_pb2 import wire_in, wire_out -from messages_pb2 import wire_debug_in, wire_debug_out -from messages_pb2 import wire_bootloader, wire_no_fsm + +from messages_pb2 import ( + MessageType, + wire_bootloader, + wire_debug_in, + wire_debug_out, + wire_in, + wire_no_fsm, + wire_out, +) fh = open("messages_map.h", "wt") fl = open("messages_map_limits.h", "wt") @@ -24,7 +29,7 @@ LABELS = { def handle_message(fh, fl, skipped, message, extension): name = message.name short_name = name.split("MessageType_", 1).pop() - assert(short_name != name) + assert short_name != name for s in skipped: if short_name.startswith(s): @@ -37,14 +42,14 @@ def handle_message(fh, fl, skipped, message, extension): bootloader = options.Extensions[wire_bootloader] no_fsm = options.Extensions[wire_no_fsm] - if getattr(options, 'deprecated', None): - fh.write('\t// Message %s is deprecated\n' % short_name) + if getattr(options, "deprecated", None): + fh.write("\t// Message %s is deprecated\n" % short_name) return if bootloader: - fh.write('\t// Message %s is used in bootloader mode only\n' % short_name) + fh.write("\t// Message %s is used in bootloader mode only\n" % short_name) return if no_fsm: - fh.write('\t// Message %s is not used in FSM\n' % short_name) + fh.write("\t// Message %s is not used in FSM\n" % short_name) return if direction == "i": @@ -52,13 +57,15 @@ def handle_message(fh, fl, skipped, message, extension): else: process_func = "0" - fh.write(TEMPLATE.format( - type="'%c'," % interface, - dir="'%c'," % direction, - msg_id="MessageType_%s," % name, - fields="%s_fields," % short_name, - process_func=process_func, - )) + fh.write( + TEMPLATE.format( + type="'%c'," % interface, + dir="'%c'," % direction, + msg_id="MessageType_%s," % name, + fields="%s_fields," % short_name, + process_func=process_func, + ) + ) bufsize = None t = interface + direction @@ -69,13 +76,20 @@ def handle_message(fh, fl, skipped, message, extension): elif t == "do": bufsize = "MSG_DEBUG_OUT_SIZE" if bufsize: - fl.write("_Static_assert(%s >= sizeof(%s), \"msg buffer too small\");\n" % (bufsize, short_name)) + fl.write( + '_Static_assert(%s >= sizeof(%s), "msg buffer too small");\n' + % (bufsize, short_name) + ) skipped = sys.argv[1:] -fh.write("\t// This file is automatically generated by messages_map.py -- DO NOT EDIT!\n") -fl.write("// This file is automatically generated by messages_map.py -- DO NOT EDIT!\n\n") +fh.write( + "\t// This file is automatically generated by messages_map.py -- DO NOT EDIT!\n" +) +fl.write( + "// This file is automatically generated by messages_map.py -- DO NOT EDIT!\n\n" +) messages = defaultdict(list) diff --git a/legacy/firmware/u2f/u2f.h b/legacy/firmware/u2f/u2f.h index b067713963..865778d33c 100644 --- a/legacy/firmware/u2f/u2f.h +++ b/legacy/firmware/u2f/u2f.h @@ -1,3 +1,4 @@ +// clang-format off /** * Copyright FIDO Alliance, 2017 * diff --git a/legacy/firmware/u2f/u2f_hid.h b/legacy/firmware/u2f/u2f_hid.h index f29059da21..39586cdd67 100644 --- a/legacy/firmware/u2f/u2f_hid.h +++ b/legacy/firmware/u2f/u2f_hid.h @@ -1,3 +1,4 @@ +// clang-format off /** * Copyright FIDO Alliance, 2017 * diff --git a/legacy/gen/bitmaps.c b/legacy/gen/bitmaps.c index 5d60730d1a..c47c1a0bdd 100644 --- a/legacy/gen/bitmaps.c +++ b/legacy/gen/bitmaps.c @@ -1,3 +1,4 @@ +// clang-format off #include "bitmaps.h" const uint8_t bmp_digit0_data[] = { 0xff, 0xff, 0xf8, 0x1f, 0xf0, 0x0f, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xf0, 0x0f, 0xf8, 0x1f, 0xff, 0xff, }; diff --git a/legacy/gen/bitmaps.h b/legacy/gen/bitmaps.h index c06351db07..41bc1507dc 100644 --- a/legacy/gen/bitmaps.h +++ b/legacy/gen/bitmaps.h @@ -4,8 +4,8 @@ #include typedef struct { - uint8_t width, height; - const uint8_t *data; + uint8_t width, height; + const uint8_t *data; } BITMAP; extern const BITMAP bmp_digit0; diff --git a/legacy/gen/bitmaps/generate.py b/legacy/gen/bitmaps/generate.py index d0005427ce..0e23ae435a 100755 --- a/legacy/gen/bitmaps/generate.py +++ b/legacy/gen/bitmaps/generate.py @@ -1,58 +1,64 @@ #!/usr/bin/env python3 import glob import os + from PIL import Image hdrs = [] data = [] imgs = [] + def encode_pixels(img): - r = '' - img = [ (x[0] + x[1] + x[2] > 384 and '1' or '0') for x in img] - for i in range(len(img) // 8): - c = ''.join(img[i * 8 : i * 8 + 8]) - r += '0x%02x, ' % int(c, 2) - return r + r = "" + img = [(x[0] + x[1] + x[2] > 384 and "1" or "0") for x in img] + for i in range(len(img) // 8): + c = "".join(img[i * 8 : i * 8 + 8]) + r += "0x%02x, " % int(c, 2) + return r + cnt = 0 -for fn in sorted(glob.glob('*.png')): - print('Processing:', fn) - im = Image.open(fn) - name = os.path.splitext(fn)[0] - w, h = im.size - if w % 8 != 0: - raise Exception('Width must be divisable by 8! (%s is %dx%d)' % (fn, w, h)) - img = list(im.getdata()) - hdrs.append('extern const BITMAP bmp_%s;\n' % name) - imgs.append('const BITMAP bmp_%s = {%d, %d, bmp_%s_data};\n' % (name, w, h, name)) - data.append('const uint8_t bmp_%s_data[] = { %s};\n' % (name, encode_pixels(img))) - cnt += 1 +for fn in sorted(glob.glob("*.png")): + print("Processing:", fn) + im = Image.open(fn) + name = os.path.splitext(fn)[0] + w, h = im.size + if w % 8 != 0: + raise Exception("Width must be divisable by 8! (%s is %dx%d)" % (fn, w, h)) + img = list(im.getdata()) + hdrs.append("extern const BITMAP bmp_%s;\n" % name) + imgs.append("const BITMAP bmp_%s = {%d, %d, bmp_%s_data};\n" % (name, w, h, name)) + data.append("const uint8_t bmp_%s_data[] = { %s};\n" % (name, encode_pixels(img))) + cnt += 1 -with open('../bitmaps.c', 'wt') as f: - f.write('#include "bitmaps.h"\n\n') - for i in range(cnt): - f.write(data[i]) - f.write('\n') - for i in range(cnt): - f.write(imgs[i]) - f.close() +with open("../bitmaps.c", "wt") as f: + f.write("// clang-format off\n") + f.write('#include "bitmaps.h"\n\n') + for i in range(cnt): + f.write(data[i]) + f.write("\n") + for i in range(cnt): + f.write(imgs[i]) + f.close() -with open('../bitmaps.h', 'wt') as f: - f.write('''#ifndef __BITMAPS_H__ +with open("../bitmaps.h", "wt") as f: + f.write( + """#ifndef __BITMAPS_H__ #define __BITMAPS_H__ #include typedef struct { - uint8_t width, height; - const uint8_t *data; + uint8_t width, height; + const uint8_t *data; } BITMAP; -''') +""" + ) - for i in range(cnt): - f.write(hdrs[i]) + for i in range(cnt): + f.write(hdrs[i]) - f.write('\n#endif\n') - f.close() + f.write("\n#endif\n") + f.close() diff --git a/legacy/gen/fonts.c b/legacy/gen/fonts.c index 68deb549bc..5bcf305a4c 100644 --- a/legacy/gen/fonts.c +++ b/legacy/gen/fonts.c @@ -1,18 +1,16 @@ #include "fonts.h" -const uint8_t * const font_data[2][128] = { - { -#include"font.inc" - }, - { -#include"fontfixed.inc" - }, +const uint8_t *const font_data[2][128] = { + { +#include "font.inc" + }, + { +#include "fontfixed.inc" + }, }; -int fontCharWidth(int font, char c) { - return font_data[font][c & 0x7f][0]; -} +int fontCharWidth(int font, char c) { return font_data[font][c & 0x7f][0]; } const uint8_t *fontCharData(int font, char c) { - return font_data[font][c & 0x7f] + 1; + return font_data[font][c & 0x7f] + 1; } diff --git a/legacy/gen/fonts.h b/legacy/gen/fonts.h index dbbbef1303..2c1a9b2a52 100644 --- a/legacy/gen/fonts.h +++ b/legacy/gen/fonts.h @@ -5,10 +5,10 @@ #define FONT_HEIGHT 8 #define FONT_STANDARD 0 -#define FONT_FIXED 1 -#define FONT_DOUBLE 0x80 +#define FONT_FIXED 1 +#define FONT_DOUBLE 0x80 -extern const uint8_t * const font_data[2][128]; +extern const uint8_t *const font_data[2][128]; int fontCharWidth(int font, char c); const uint8_t *fontCharData(int font, char c); diff --git a/legacy/gen/fonts/generate.py b/legacy/gen/fonts/generate.py index 3299138c71..463d73e63a 100755 --- a/legacy/gen/fonts/generate.py +++ b/legacy/gen/fonts/generate.py @@ -1,39 +1,40 @@ #!/usr/bin/env python3 from PIL import Image -class Img(object): +class Img(object): def __init__(self, fn): im = Image.open(fn) self.w, self.h = im.size self.data = list(im.getdata()) def pixel(self, r, c): - p = self.data[ r + c * self.w ] + p = self.data[r + c * self.w] if p == (255, 255, 255): - return '0' + return "0" if p == (0, 0, 0): - return '1' + return "1" if p == (255, 0, 255): return None - raise Exception('Unknown color', p) + raise Exception("Unknown color", p) def convert(imgfile, outfile): img = Img(imgfile) - cur = '' - with open(outfile, 'w') as f: + cur = "" + with open(outfile, "w") as f: for i in range(128): x = (i % 16) * 10 y = (i // 16) * 10 - cur = '' - while img.pixel(x, y) != None: - val = ''.join(img.pixel(x, y + j) for j in range(8)) + cur = "" + while img.pixel(x, y) is not None: + val = "".join(img.pixel(x, y + j) for j in range(8)) x += 1 - cur += '\\x%02x' % int(val, 2) - cur = '\\x%02x' % (len(cur) // 4) + cur - ch = chr(i) if i >= 32 and i <= 126 else '_' - f.write('\t/* 0x%02x %c */ (uint8_t *)"%s",\n' % (i, ch , cur)) + cur += "\\x%02x" % int(val, 2) + cur = "\\x%02x" % (len(cur) // 4) + cur + ch = chr(i) if i >= 32 and i <= 126 else "_" + f.write('\t/* 0x%02x %c */ (uint8_t *)"%s",\n' % (i, ch, cur)) -convert('fonts/fontfixed.png', 'fontfixed.inc') -convert('fonts/font.png', 'font.inc') + +convert("fonts/fontfixed.png", "fontfixed.inc") +convert("fonts/font.png", "font.inc") diff --git a/legacy/gen/handlers/handlers.py b/legacy/gen/handlers/handlers.py index 5e79610675..7d5802af06 100755 --- a/legacy/gen/handlers/handlers.py +++ b/legacy/gen/handlers/handlers.py @@ -2,99 +2,102 @@ from __future__ import print_function handlers = [ - 'hard_fault_handler', - 'mem_manage_handler', - 'bus_fault_handler', - 'usage_fault_handler', - 'nvic_wwdg_isr', - 'pvd_isr', - 'tamp_stamp_isr', - 'rtc_wkup_isr', - 'flash_isr', - 'rcc_isr', - 'exti0_isr', - 'exti1_isr', - 'exti2_isr', - 'exti3_isr', - 'exti4_isr', - 'dma1_stream0_isr', - 'dma1_stream1_isr', - 'dma1_stream2_isr', - 'dma1_stream3_isr', - 'dma1_stream4_isr', - 'dma1_stream5_isr', - 'dma1_stream6_isr', - 'adc_isr', - 'can1_tx_isr', - 'can1_rx0_isr', - 'can1_rx1_isr', - 'can1_sce_isr', - 'exti9_5_isr', - 'tim1_brk_tim9_isr', - 'tim1_up_tim10_isr', - 'tim1_trg_com_tim11_isr', - 'tim1_cc_isr', - 'tim2_isr', - 'tim3_isr', - 'tim4_isr', - 'i2c1_ev_isr', - 'i2c1_er_isr', - 'i2c2_ev_isr', - 'i2c2_er_isr', - 'spi1_isr', - 'spi2_isr', - 'usart1_isr', - 'usart2_isr', - 'usart3_isr', - 'exti15_10_isr', - 'rtc_alarm_isr', - 'usb_fs_wkup_isr', - 'tim8_brk_tim12_isr', - 'tim8_up_tim13_isr', - 'tim8_trg_com_tim14_isr', - 'tim8_cc_isr', - 'dma1_stream7_isr', - 'fsmc_isr', - 'sdio_isr', - 'tim5_isr', - 'spi3_isr', - 'uart4_isr', - 'uart5_isr', - 'tim6_dac_isr', - 'tim7_isr', - 'dma2_stream0_isr', - 'dma2_stream1_isr', - 'dma2_stream2_isr', - 'dma2_stream3_isr', - 'dma2_stream4_isr', - 'eth_isr', - 'eth_wkup_isr', - 'can2_tx_isr', - 'can2_rx0_isr', - 'can2_rx1_isr', - 'can2_sce_isr', - 'otg_fs_isr', - 'dma2_stream5_isr', - 'dma2_stream6_isr', - 'dma2_stream7_isr', - 'usart6_isr', - 'i2c3_ev_isr', - 'i2c3_er_isr', - 'otg_hs_ep1_out_isr', - 'otg_hs_ep1_in_isr', - 'otg_hs_wkup_isr', - 'otg_hs_isr', - 'dcmi_isr', - 'cryp_isr', - 'hash_rng_isr', + "hard_fault_handler", + "mem_manage_handler", + "bus_fault_handler", + "usage_fault_handler", + "nvic_wwdg_isr", + "pvd_isr", + "tamp_stamp_isr", + "rtc_wkup_isr", + "flash_isr", + "rcc_isr", + "exti0_isr", + "exti1_isr", + "exti2_isr", + "exti3_isr", + "exti4_isr", + "dma1_stream0_isr", + "dma1_stream1_isr", + "dma1_stream2_isr", + "dma1_stream3_isr", + "dma1_stream4_isr", + "dma1_stream5_isr", + "dma1_stream6_isr", + "adc_isr", + "can1_tx_isr", + "can1_rx0_isr", + "can1_rx1_isr", + "can1_sce_isr", + "exti9_5_isr", + "tim1_brk_tim9_isr", + "tim1_up_tim10_isr", + "tim1_trg_com_tim11_isr", + "tim1_cc_isr", + "tim2_isr", + "tim3_isr", + "tim4_isr", + "i2c1_ev_isr", + "i2c1_er_isr", + "i2c2_ev_isr", + "i2c2_er_isr", + "spi1_isr", + "spi2_isr", + "usart1_isr", + "usart2_isr", + "usart3_isr", + "exti15_10_isr", + "rtc_alarm_isr", + "usb_fs_wkup_isr", + "tim8_brk_tim12_isr", + "tim8_up_tim13_isr", + "tim8_trg_com_tim14_isr", + "tim8_cc_isr", + "dma1_stream7_isr", + "fsmc_isr", + "sdio_isr", + "tim5_isr", + "spi3_isr", + "uart4_isr", + "uart5_isr", + "tim6_dac_isr", + "tim7_isr", + "dma2_stream0_isr", + "dma2_stream1_isr", + "dma2_stream2_isr", + "dma2_stream3_isr", + "dma2_stream4_isr", + "eth_isr", + "eth_wkup_isr", + "can2_tx_isr", + "can2_rx0_isr", + "can2_rx1_isr", + "can2_sce_isr", + "otg_fs_isr", + "dma2_stream5_isr", + "dma2_stream6_isr", + "dma2_stream7_isr", + "usart6_isr", + "i2c3_ev_isr", + "i2c3_er_isr", + "otg_hs_ep1_out_isr", + "otg_hs_ep1_in_isr", + "otg_hs_wkup_isr", + "otg_hs_isr", + "dcmi_isr", + "cryp_isr", + "hash_rng_isr", ] -with open('handlers.c', 'wt') as f: - f.write('#include "layout.h"\n') - f.write('#include "oled.h"\n\n') - for i in handlers: - f.write('void __attribute__((noreturn)) %s(void)\n' % i) - f.write('{\n') - f.write('\tlayoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Encountered", NULL, "%s", NULL, "Please restart", "the device.");\n' % i.upper()) - f.write('\tfor (;;) {} // loop forever\n') - f.write('}\n\n') +with open("handlers.c", "wt") as f: + f.write('#include "layout.h"\n') + f.write('#include "oled.h"\n\n') + for i in handlers: + f.write("void __attribute__((noreturn)) %s(void)\n" % i) + f.write("{\n") + f.write( + '\tlayoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Encountered", NULL, "%s", NULL, "Please restart", "the device.");\n' + % i.upper() + ) + f.write("\tfor (;;) {} // loop forever\n") + f.write("}\n\n") diff --git a/legacy/gen/strwidth.c b/legacy/gen/strwidth.c index 8ba3f1c367..c7c0125941 100644 --- a/legacy/gen/strwidth.c +++ b/legacy/gen/strwidth.c @@ -1,35 +1,35 @@ +#include +#include #include #include -#include -#include #include "fonts.h" static inline char convert(char c) { - if (c < 0x80) { - return c; - } else if (c >= 0xC0) { - return '_'; - } else { - return '\0'; - } + if (c < 0x80) { + return c; + } else if (c >= 0xC0) { + return '_'; + } else { + return '\0'; + } } int main(int argc, char **argv) { - char *line; - int font = FONT_STANDARD; - while ((line = readline(NULL)) != NULL) { - size_t length = strlen(line); - if (length) { - add_history(line); - } + char *line; + int font = FONT_STANDARD; + while ((line = readline(NULL)) != NULL) { + size_t length = strlen(line); + if (length) { + add_history(line); + } - size_t width = 0; - for (size_t i = 0; i < length; i++) { - width += fontCharWidth(font, convert(line[i])) + 1; - } + size_t width = 0; + for (size_t i = 0; i < length; i++) { + width += fontCharWidth(font, convert(line[i])) + 1; + } - printf("%zu\n", width); - free(line); - } + printf("%zu\n", width); + free(line); + } } diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000000..60d212f457 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,35 @@ +[flake8] +ignore = + # E203 whitespace before ':' + E203, + # E221: multiple spaces before operator + E221, + # E241: multiple spaces after ':' + E241, + # E402: module level import not at top of file + E402, + # E501: line too long + E501, + # E741 ambiguous variable name + E741, + # F403: star import used, unable to detect undefined names + F403, + # F405: name may be undefined, or defined from star imports + F405, + # W503: line break before binary operator + W503 + +[isort] +multi_line_output = 3 +include_trailing_comma = True +force_grid_wrap = 0 +combine_as_imports = True +line_length = 88 +not_skip=__init__.py +forced_separate = apps +known_standard_library = micropython,ubinascii,ustruct,uctypes,utime,utimeq,trezorio,trezorui,trezorutils,trezorconfig + +[tool:pytest] +addopts = --pyargs trezorlib.tests.device_tests +xfail_strict = true +run_xfail = diff --git a/storage/norcow.c b/storage/norcow.c index 3ef5d6f9cc..d0a845c522 100644 --- a/storage/norcow.c +++ b/storage/norcow.c @@ -19,33 +19,35 @@ #include -#include "norcow.h" -#include "flash.h" #include "common.h" +#include "flash.h" +#include "norcow.h" // NRC2 = 4e524332 -#define NORCOW_MAGIC ((uint32_t)0x3243524e) +#define NORCOW_MAGIC ((uint32_t)0x3243524e) // NRCW = 4e524357 -#define NORCOW_MAGIC_V0 ((uint32_t)0x5743524e) +#define NORCOW_MAGIC_V0 ((uint32_t)0x5743524e) -#define NORCOW_WORD_SIZE (sizeof(uint32_t)) +#define NORCOW_WORD_SIZE (sizeof(uint32_t)) #define NORCOW_PREFIX_LEN NORCOW_WORD_SIZE -#define NORCOW_MAGIC_LEN NORCOW_WORD_SIZE +#define NORCOW_MAGIC_LEN NORCOW_WORD_SIZE #define NORCOW_VERSION_LEN NORCOW_WORD_SIZE // The key value which is used to indicate that the entry is not set. -#define NORCOW_KEY_FREE (0xFFFF) +#define NORCOW_KEY_FREE (0xFFFF) // The key value which is used to indicate that the entry has been deleted. #define NORCOW_KEY_DELETED (0x0000) // The offset from the beginning of the sector where stored items start. -#define NORCOW_STORAGE_START (NORCOW_HEADER_LEN + NORCOW_MAGIC_LEN + NORCOW_VERSION_LEN) +#define NORCOW_STORAGE_START \ + (NORCOW_HEADER_LEN + NORCOW_MAGIC_LEN + NORCOW_VERSION_LEN) // Map from sector index to sector number. static const uint8_t norcow_sectors[NORCOW_SECTOR_COUNT] = NORCOW_SECTORS; -// The index of the active reading sector and writing sector. These should be equal except when storage version upgrade or compaction is in progress. +// The index of the active reading sector and writing sector. These should be +// equal except when storage version upgrade or compaction is in progress. static uint8_t norcow_active_sector = 0; static uint8_t norcow_write_sector = 0; @@ -59,76 +61,79 @@ static uint32_t norcow_free_offset = 0; * Returns pointer to sector, starting with offset * Fails when there is not enough space for data of given size */ -static const void *norcow_ptr(uint8_t sector, uint32_t offset, uint32_t size) -{ - ensure(sectrue * (sector <= NORCOW_SECTOR_COUNT), "invalid sector"); - return flash_get_address(norcow_sectors[sector], offset, size); +static const void *norcow_ptr(uint8_t sector, uint32_t offset, uint32_t size) { + ensure(sectrue * (sector <= NORCOW_SECTOR_COUNT), "invalid sector"); + return flash_get_address(norcow_sectors[sector], offset, size); } /* * Writes data to given sector, starting from offset */ -static secbool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix, const uint8_t *data, uint16_t len) -{ - if (sector >= NORCOW_SECTOR_COUNT) { - return secfalse; +static secbool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix, + const uint8_t *data, uint16_t len) { + if (sector >= NORCOW_SECTOR_COUNT) { + return secfalse; + } + + if (offset + NORCOW_PREFIX_LEN + len > NORCOW_SECTOR_SIZE) { + return secfalse; + } + + ensure(flash_unlock_write(), NULL); + + // write prefix + ensure(flash_write_word(norcow_sectors[sector], offset, prefix), NULL); + offset += NORCOW_PREFIX_LEN; + + if (data != NULL) { + // write data + for (uint16_t i = 0; i < len; i++, offset++) { + ensure(flash_write_byte(norcow_sectors[sector], offset, data[i]), NULL); } + } else { + offset += len; + } - if (offset + NORCOW_PREFIX_LEN + len > NORCOW_SECTOR_SIZE) { - return secfalse; - } + // pad with zeroes + for (; offset % NORCOW_WORD_SIZE; offset++) { + ensure(flash_write_byte(norcow_sectors[sector], offset, 0x00), NULL); + } - ensure(flash_unlock_write(), NULL); - - // write prefix - ensure(flash_write_word(norcow_sectors[sector], offset, prefix), NULL); - offset += NORCOW_PREFIX_LEN; - - if (data != NULL) { - // write data - for (uint16_t i = 0; i < len; i++, offset++) { - ensure(flash_write_byte(norcow_sectors[sector], offset, data[i]), NULL); - } - } else { - offset += len; - } - - // pad with zeroes - for (; offset % NORCOW_WORD_SIZE; offset++) { - ensure(flash_write_byte(norcow_sectors[sector], offset, 0x00), NULL); - } - - ensure(flash_lock_write(), NULL); - return sectrue; + ensure(flash_lock_write(), NULL); + return sectrue; } /* * Erases sector (and sets a magic) */ -static void erase_sector(uint8_t sector, secbool set_magic) -{ +static void erase_sector(uint8_t sector, secbool set_magic) { #if NORCOW_HEADER_LEN > 0 - // Backup the sector header. - uint32_t header_backup[NORCOW_HEADER_LEN/sizeof(uint32_t)]; - const void *sector_start = norcow_ptr(sector, 0, NORCOW_HEADER_LEN); - memcpy(header_backup, sector_start, sizeof(header_backup)); + // Backup the sector header. + uint32_t header_backup[NORCOW_HEADER_LEN / sizeof(uint32_t)]; + const void *sector_start = norcow_ptr(sector, 0, NORCOW_HEADER_LEN); + memcpy(header_backup, sector_start, sizeof(header_backup)); #endif - ensure(flash_erase(norcow_sectors[sector]), "erase failed"); + ensure(flash_erase(norcow_sectors[sector]), "erase failed"); #if NORCOW_HEADER_LEN > 0 - // Copy the sector header back. - ensure(flash_unlock_write(), NULL); - for (uint32_t i = 0; i < NORCOW_HEADER_LEN/sizeof(uint32_t); ++i) { - ensure(flash_write_word(norcow_sectors[sector], i*sizeof(uint32_t), header_backup[i]), NULL); - } - ensure(flash_lock_write(), NULL); + // Copy the sector header back. + ensure(flash_unlock_write(), NULL); + for (uint32_t i = 0; i < NORCOW_HEADER_LEN / sizeof(uint32_t); ++i) { + ensure(flash_write_word(norcow_sectors[sector], i * sizeof(uint32_t), + header_backup[i]), + NULL); + } + ensure(flash_lock_write(), NULL); #endif - if (sectrue == set_magic) { - ensure(norcow_write(sector, NORCOW_HEADER_LEN, NORCOW_MAGIC, NULL, 0), "set magic failed"); - ensure(norcow_write(sector, NORCOW_HEADER_LEN + NORCOW_MAGIC_LEN, ~NORCOW_VERSION, NULL, 0), "set version failed"); - } + if (sectrue == set_magic) { + ensure(norcow_write(sector, NORCOW_HEADER_LEN, NORCOW_MAGIC, NULL, 0), + "set magic failed"); + ensure(norcow_write(sector, NORCOW_HEADER_LEN + NORCOW_MAGIC_LEN, + ~NORCOW_VERSION, NULL, 0), + "set version failed"); + } } #define ALIGN4(X) (X) = ((X) + 3) & ~3 @@ -136,263 +141,262 @@ static void erase_sector(uint8_t sector, secbool set_magic) /* * Reads one item starting from offset */ -static secbool read_item(uint8_t sector, uint32_t offset, uint16_t *key, const void **val, uint16_t *len, uint32_t *pos) -{ - *pos = offset; +static secbool read_item(uint8_t sector, uint32_t offset, uint16_t *key, + const void **val, uint16_t *len, uint32_t *pos) { + *pos = offset; - const void *k = norcow_ptr(sector, *pos, 2); - if (k == NULL) return secfalse; - *pos += 2; - memcpy(key, k, sizeof(uint16_t)); - if (*key == NORCOW_KEY_FREE) { - return secfalse; - } + const void *k = norcow_ptr(sector, *pos, 2); + if (k == NULL) return secfalse; + *pos += 2; + memcpy(key, k, sizeof(uint16_t)); + if (*key == NORCOW_KEY_FREE) { + return secfalse; + } - const void *l = norcow_ptr(sector, *pos, 2); - if (l == NULL) return secfalse; - *pos += 2; - memcpy(len, l, sizeof(uint16_t)); + const void *l = norcow_ptr(sector, *pos, 2); + if (l == NULL) return secfalse; + *pos += 2; + memcpy(len, l, sizeof(uint16_t)); - *val = norcow_ptr(sector, *pos, *len); - if (*val == NULL) return secfalse; - *pos += *len; - ALIGN4(*pos); - return sectrue; + *val = norcow_ptr(sector, *pos, *len); + if (*val == NULL) return secfalse; + *pos += *len; + ALIGN4(*pos); + return sectrue; } /* * Writes one item starting from offset */ -static secbool write_item(uint8_t sector, uint32_t offset, uint16_t key, const void *val, uint16_t len, uint32_t *pos) -{ - uint32_t prefix = ((uint32_t)len << 16) | key; - *pos = offset + NORCOW_PREFIX_LEN + len; - ALIGN4(*pos); - return norcow_write(sector, offset, prefix, val, len); +static secbool write_item(uint8_t sector, uint32_t offset, uint16_t key, + const void *val, uint16_t len, uint32_t *pos) { + uint32_t prefix = ((uint32_t)len << 16) | key; + *pos = offset + NORCOW_PREFIX_LEN + len; + ALIGN4(*pos); + return norcow_write(sector, offset, prefix, val, len); } /* * Finds the offset from the beginning of the sector where stored items start. */ -static secbool find_start_offset(uint8_t sector, uint32_t *offset, uint32_t *version) -{ - const uint32_t *magic = norcow_ptr(sector, NORCOW_HEADER_LEN, NORCOW_MAGIC_LEN + NORCOW_VERSION_LEN); - if (magic == NULL) { - return secfalse; - } +static secbool find_start_offset(uint8_t sector, uint32_t *offset, + uint32_t *version) { + const uint32_t *magic = norcow_ptr(sector, NORCOW_HEADER_LEN, + NORCOW_MAGIC_LEN + NORCOW_VERSION_LEN); + if (magic == NULL) { + return secfalse; + } - if (*magic == NORCOW_MAGIC) { - *offset = NORCOW_STORAGE_START; - *version = ~(magic[1]); - } else if (*magic == NORCOW_MAGIC_V0) { - *offset = NORCOW_HEADER_LEN + NORCOW_MAGIC_LEN; - *version = 0; - } else { - return secfalse; - } + if (*magic == NORCOW_MAGIC) { + *offset = NORCOW_STORAGE_START; + *version = ~(magic[1]); + } else if (*magic == NORCOW_MAGIC_V0) { + *offset = NORCOW_HEADER_LEN + NORCOW_MAGIC_LEN; + *version = 0; + } else { + return secfalse; + } - return sectrue; + return sectrue; } /* * Finds item in given sector */ -static secbool find_item(uint8_t sector, uint16_t key, const void **val, uint16_t *len) -{ - *val = NULL; - *len = 0; +static secbool find_item(uint8_t sector, uint16_t key, const void **val, + uint16_t *len) { + *val = NULL; + *len = 0; - uint32_t offset; - uint32_t version; - if (sectrue != find_start_offset(sector, &offset, &version)) { - return secfalse; - } + uint32_t offset; + uint32_t version; + if (sectrue != find_start_offset(sector, &offset, &version)) { + return secfalse; + } - for (;;) { - uint16_t k, l; - const void *v; - uint32_t pos; - if (sectrue != read_item(sector, offset, &k, &v, &l, &pos)) { - break; - } - if (key == k) { - *val = v; - *len = l; - } - offset = pos; + for (;;) { + uint16_t k, l; + const void *v; + uint32_t pos; + if (sectrue != read_item(sector, offset, &k, &v, &l, &pos)) { + break; } - return sectrue * (*val != NULL); + if (key == k) { + *val = v; + *len = l; + } + offset = pos; + } + return sectrue * (*val != NULL); } /* * Finds first unused offset in given sector */ -static uint32_t find_free_offset(uint8_t sector) -{ - uint32_t offset; - uint32_t version; - if (sectrue != find_start_offset(sector, &offset, &version)) { - return secfalse; - } +static uint32_t find_free_offset(uint8_t sector) { + uint32_t offset; + uint32_t version; + if (sectrue != find_start_offset(sector, &offset, &version)) { + return secfalse; + } - for (;;) { - uint16_t key, len; - const void *val; - uint32_t pos; - if (sectrue != read_item(sector, offset, &key, &val, &len, &pos)) { - break; - } - offset = pos; + for (;;) { + uint16_t key, len; + const void *val; + uint32_t pos; + if (sectrue != read_item(sector, offset, &key, &val, &len, &pos)) { + break; } - return offset; + offset = pos; + } + return offset; } /* * Compacts active sector and sets new active sector */ -static void compact(void) -{ - uint32_t offsetr; - uint32_t version; - if (sectrue != find_start_offset(norcow_active_sector, &offsetr, &version)) { - return; +static void compact(void) { + uint32_t offsetr; + uint32_t version; + if (sectrue != find_start_offset(norcow_active_sector, &offsetr, &version)) { + return; + } + + norcow_write_sector = (norcow_active_sector + 1) % NORCOW_SECTOR_COUNT; + erase_sector(norcow_write_sector, sectrue); + uint32_t offsetw = NORCOW_STORAGE_START; + + for (;;) { + // read item + uint16_t k, l; + const void *v; + uint32_t posr; + secbool r = read_item(norcow_active_sector, offsetr, &k, &v, &l, &posr); + if (sectrue != r) { + break; + } + offsetr = posr; + + // skip deleted items + if (k == NORCOW_KEY_DELETED) { + continue; } - norcow_write_sector = (norcow_active_sector + 1) % NORCOW_SECTOR_COUNT; - erase_sector(norcow_write_sector, sectrue); - uint32_t offsetw = NORCOW_STORAGE_START; + // copy the item + uint32_t posw; + ensure(write_item(norcow_write_sector, offsetw, k, v, l, &posw), + "compaction write failed"); + offsetw = posw; + } - for (;;) { - // read item - uint16_t k, l; - const void *v; - uint32_t posr; - secbool r = read_item(norcow_active_sector, offsetr, &k, &v, &l, &posr); - if (sectrue != r) { - break; - } - offsetr = posr; - - // skip deleted items - if (k == NORCOW_KEY_DELETED) { - continue; - } - - // copy the item - uint32_t posw; - ensure(write_item(norcow_write_sector, offsetw, k, v, l, &posw), "compaction write failed"); - offsetw = posw; - } - - erase_sector(norcow_active_sector, secfalse); - norcow_active_sector = norcow_write_sector; - norcow_active_version = NORCOW_VERSION; - norcow_free_offset = find_free_offset(norcow_write_sector); + erase_sector(norcow_active_sector, secfalse); + norcow_active_sector = norcow_write_sector; + norcow_active_version = NORCOW_VERSION; + norcow_free_offset = find_free_offset(norcow_write_sector); } /* * Initializes storage */ -void norcow_init(uint32_t *norcow_version) -{ - flash_init(); - secbool found = secfalse; - *norcow_version = 0; - // detect active sector - starts with magic and has highest version - for (uint8_t i = 0; i < NORCOW_SECTOR_COUNT; i++) { - uint32_t offset; - if (sectrue == find_start_offset(i, &offset, &norcow_active_version) && norcow_active_version >= *norcow_version) { - found = sectrue; - norcow_active_sector = i; - *norcow_version = norcow_active_version; - } +void norcow_init(uint32_t *norcow_version) { + flash_init(); + secbool found = secfalse; + *norcow_version = 0; + // detect active sector - starts with magic and has highest version + for (uint8_t i = 0; i < NORCOW_SECTOR_COUNT; i++) { + uint32_t offset; + if (sectrue == find_start_offset(i, &offset, &norcow_active_version) && + norcow_active_version >= *norcow_version) { + found = sectrue; + norcow_active_sector = i; + *norcow_version = norcow_active_version; } + } - // If no active sectors found or version downgrade, then erase. - if (sectrue != found || *norcow_version > NORCOW_VERSION) { - norcow_wipe(); - *norcow_version = NORCOW_VERSION; - } else if (*norcow_version < NORCOW_VERSION) { - // Prepare write sector for storage upgrade. - norcow_write_sector = (norcow_active_sector + 1) % NORCOW_SECTOR_COUNT; - erase_sector(norcow_write_sector, sectrue); - norcow_free_offset = find_free_offset(norcow_write_sector); - } else { - norcow_write_sector = norcow_active_sector; - norcow_free_offset = find_free_offset(norcow_write_sector); - } + // If no active sectors found or version downgrade, then erase. + if (sectrue != found || *norcow_version > NORCOW_VERSION) { + norcow_wipe(); + *norcow_version = NORCOW_VERSION; + } else if (*norcow_version < NORCOW_VERSION) { + // Prepare write sector for storage upgrade. + norcow_write_sector = (norcow_active_sector + 1) % NORCOW_SECTOR_COUNT; + erase_sector(norcow_write_sector, sectrue); + norcow_free_offset = find_free_offset(norcow_write_sector); + } else { + norcow_write_sector = norcow_active_sector; + norcow_free_offset = find_free_offset(norcow_write_sector); + } } /* * Wipe the storage */ -void norcow_wipe(void) -{ - erase_sector(0, sectrue); - for (uint8_t i = 1; i < NORCOW_SECTOR_COUNT; i++) { - erase_sector(i, secfalse); - } - norcow_active_sector = 0; - norcow_active_version = NORCOW_VERSION; - norcow_write_sector = 0; - norcow_free_offset = NORCOW_STORAGE_START; +void norcow_wipe(void) { + erase_sector(0, sectrue); + for (uint8_t i = 1; i < NORCOW_SECTOR_COUNT; i++) { + erase_sector(i, secfalse); + } + norcow_active_sector = 0; + norcow_active_version = NORCOW_VERSION; + norcow_write_sector = 0; + norcow_free_offset = NORCOW_STORAGE_START; } /* * Looks for the given key, returns status of the operation */ -secbool norcow_get(uint16_t key, const void **val, uint16_t *len) -{ - return find_item(norcow_active_sector, key, val, len); +secbool norcow_get(uint16_t key, const void **val, uint16_t *len) { + return find_item(norcow_active_sector, key, val, len); } /* - * Reads the next entry in the storage starting at offset. Returns secfalse if there is none. + * Reads the next entry in the storage starting at offset. Returns secfalse if + * there is none. */ -secbool norcow_get_next(uint32_t *offset, uint16_t *key, const void **val, uint16_t *len) -{ - if (*offset == 0) { - uint32_t version; - if (sectrue != find_start_offset(norcow_active_sector, offset, &version)) { - return secfalse; - } +secbool norcow_get_next(uint32_t *offset, uint16_t *key, const void **val, + uint16_t *len) { + if (*offset == 0) { + uint32_t version; + if (sectrue != find_start_offset(norcow_active_sector, offset, &version)) { + return secfalse; + } + } + + for (;;) { + uint32_t pos = 0; + secbool ret = read_item(norcow_active_sector, *offset, key, val, len, &pos); + if (sectrue != ret) { + break; + } + *offset = pos; + + // Skip deleted items. + if (*key == NORCOW_KEY_DELETED) { + continue; } - for (;;) { - uint32_t pos = 0; - secbool ret = read_item(norcow_active_sector, *offset, key, val, len, &pos); + if (norcow_active_version == 0) { + // Check whether the item is the latest instance. + uint32_t offsetr = *offset; + for (;;) { + uint16_t k; + uint16_t l; + const void *v; + ret = read_item(norcow_active_sector, offsetr, &k, &v, &l, &offsetr); if (sectrue != ret) { - break; + // There is no newer instance of the item. + return sectrue; } - *offset = pos; - - // Skip deleted items. - if (*key == NORCOW_KEY_DELETED) { - continue; - } - - if (norcow_active_version == 0) { - // Check whether the item is the latest instance. - uint32_t offsetr = *offset; - for (;;) { - uint16_t k; - uint16_t l; - const void *v; - ret = read_item(norcow_active_sector, offsetr, &k, &v, &l, &offsetr); - if (sectrue != ret) { - // There is no newer instance of the item. - return sectrue; - } - if (*key == k) { - // There exists a newer instance of the item. - break; - } - } - } else { - return sectrue; + if (*key == k) { + // There exists a newer instance of the item. + break; } + } + } else { + return sectrue; } - return secfalse; + } + return secfalse; } /* @@ -400,163 +404,175 @@ secbool norcow_get_next(uint32_t *offset, uint16_t *key, const void **val, uint1 * as val, then norcow_set allocates a new key of size len. The value should * then be written using norcow_update_bytes(). */ -secbool norcow_set(uint16_t key, const void *val, uint16_t len) -{ - secbool found; - return norcow_set_ex(key, val, len, &found); +secbool norcow_set(uint16_t key, const void *val, uint16_t len) { + secbool found; + return norcow_set_ex(key, val, len, &found); } -secbool norcow_set_ex(uint16_t key, const void *val, uint16_t len, secbool *found) -{ - // Key 0xffff is used as a marker to indicate that the entry is not set. - if (key == NORCOW_KEY_FREE) { - return secfalse; +secbool norcow_set_ex(uint16_t key, const void *val, uint16_t len, + secbool *found) { + // Key 0xffff is used as a marker to indicate that the entry is not set. + if (key == NORCOW_KEY_FREE) { + return secfalse; + } + + const uint8_t sector_num = norcow_sectors[norcow_write_sector]; + secbool ret = secfalse; + const void *ptr = NULL; + uint16_t len_old = 0; + *found = find_item(norcow_write_sector, key, &ptr, &len_old); + + // Try to update the entry if it already exists. + uint32_t offset = 0; + if (sectrue == *found) { + offset = + (const uint8_t *)ptr - + (const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE); + if (val != NULL && len_old == len) { + ret = sectrue; + ensure(flash_unlock_write(), NULL); + for (uint16_t i = 0; i < len; i++) { + if (sectrue != flash_write_byte(sector_num, offset + i, + ((const uint8_t *)val)[i])) { + ret = secfalse; + break; + } + } + ensure(flash_lock_write(), NULL); } + } - const uint8_t sector_num = norcow_sectors[norcow_write_sector]; - secbool ret = secfalse; - const void *ptr = NULL; - uint16_t len_old = 0; - *found = find_item(norcow_write_sector, key, &ptr, &len_old); - - // Try to update the entry if it already exists. - uint32_t offset = 0; + // If the update was not possible then write the entry as a new item. + if (secfalse == ret) { + // Delete the old item. if (sectrue == *found) { - offset = (const uint8_t*) ptr - (const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE); - if (val != NULL && len_old == len) { - ret = sectrue; - ensure(flash_unlock_write(), NULL); - for (uint16_t i = 0; i < len; i++) { - if (sectrue != flash_write_byte(sector_num, offset + i, ((const uint8_t*)val)[i])) { - ret = secfalse; - break; - } - } - ensure(flash_lock_write(), NULL); - } + ensure(flash_unlock_write(), NULL); + + // Update the prefix to indicate that the old item has been deleted. + uint32_t prefix = (uint32_t)len_old << 16; + ensure(flash_write_word(sector_num, offset - NORCOW_PREFIX_LEN, prefix), + NULL); + + // Delete the old item data. + uint32_t end = offset + len_old; + while (offset < end) { + ensure(flash_write_word(sector_num, offset, 0x00000000), NULL); + offset += NORCOW_WORD_SIZE; + } + + ensure(flash_lock_write(), NULL); } - - // If the update was not possible then write the entry as a new item. - if (secfalse == ret) { - // Delete the old item. - if (sectrue == *found) { - ensure(flash_unlock_write(), NULL); - - // Update the prefix to indicate that the old item has been deleted. - uint32_t prefix = (uint32_t)len_old << 16; - ensure(flash_write_word(sector_num, offset - NORCOW_PREFIX_LEN, prefix), NULL); - - // Delete the old item data. - uint32_t end = offset + len_old; - while (offset < end) { - ensure(flash_write_word(sector_num, offset, 0x00000000), NULL); - offset += NORCOW_WORD_SIZE; - } - - ensure(flash_lock_write(), NULL); - } - // Check whether there is enough free space and compact if full. - if (norcow_free_offset + NORCOW_PREFIX_LEN + len > NORCOW_SECTOR_SIZE) { - compact(); - } - // Write new item. - uint32_t pos; - ret = write_item(norcow_write_sector, norcow_free_offset, key, val, len, &pos); - if (sectrue == ret) { - norcow_free_offset = pos; - } + // Check whether there is enough free space and compact if full. + if (norcow_free_offset + NORCOW_PREFIX_LEN + len > NORCOW_SECTOR_SIZE) { + compact(); } - return ret; + // Write new item. + uint32_t pos; + ret = write_item(norcow_write_sector, norcow_free_offset, key, val, len, + &pos); + if (sectrue == ret) { + norcow_free_offset = pos; + } + } + return ret; } /* * Deletes the given key, returns status of the operation. */ -secbool norcow_delete(uint16_t key) -{ - // Key 0xffff is used as a marker to indicate that the entry is not set. - if (key == NORCOW_KEY_FREE) { - return secfalse; - } +secbool norcow_delete(uint16_t key) { + // Key 0xffff is used as a marker to indicate that the entry is not set. + if (key == NORCOW_KEY_FREE) { + return secfalse; + } - const uint8_t sector_num = norcow_sectors[norcow_write_sector]; - const void *ptr = NULL; - uint16_t len = 0; - if (sectrue != find_item(norcow_write_sector, key, &ptr, &len)) { - return secfalse; - } + const uint8_t sector_num = norcow_sectors[norcow_write_sector]; + const void *ptr = NULL; + uint16_t len = 0; + if (sectrue != find_item(norcow_write_sector, key, &ptr, &len)) { + return secfalse; + } - uint32_t offset = (const uint8_t*) ptr - (const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE); + uint32_t offset = + (const uint8_t *)ptr - + (const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE); - ensure(flash_unlock_write(), NULL); + ensure(flash_unlock_write(), NULL); - // Update the prefix to indicate that the item has been deleted. - uint32_t prefix = (uint32_t)len << 16; - ensure(flash_write_word(sector_num, offset - NORCOW_PREFIX_LEN, prefix), NULL); + // Update the prefix to indicate that the item has been deleted. + uint32_t prefix = (uint32_t)len << 16; + ensure(flash_write_word(sector_num, offset - NORCOW_PREFIX_LEN, prefix), + NULL); - // Delete the item data. - uint32_t end = offset + len; - while (offset < end) { - ensure(flash_write_word(sector_num, offset, 0x00000000), NULL); - offset += NORCOW_WORD_SIZE; - } + // Delete the item data. + uint32_t end = offset + len; + while (offset < end) { + ensure(flash_write_word(sector_num, offset, 0x00000000), NULL); + offset += NORCOW_WORD_SIZE; + } - ensure(flash_lock_write(), NULL); + ensure(flash_lock_write(), NULL); - return sectrue; + return sectrue; } /* * Update a word in flash at the given pointer. The pointer must point * into the NORCOW area. */ -secbool norcow_update_word(uint16_t key, uint16_t offset, uint32_t value) -{ - const void *ptr; - uint16_t len; - if (sectrue != find_item(norcow_write_sector, key, &ptr, &len)) { - return secfalse; - } - if ((offset & 3) != 0 || offset >= len) { - return secfalse; - } - uint32_t sector_offset = (const uint8_t*) ptr - (const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE) + offset; - ensure(flash_unlock_write(), NULL); - ensure(flash_write_word(norcow_sectors[norcow_write_sector], sector_offset, value), NULL); - ensure(flash_lock_write(), NULL); - return sectrue; +secbool norcow_update_word(uint16_t key, uint16_t offset, uint32_t value) { + const void *ptr; + uint16_t len; + if (sectrue != find_item(norcow_write_sector, key, &ptr, &len)) { + return secfalse; + } + if ((offset & 3) != 0 || offset >= len) { + return secfalse; + } + uint32_t sector_offset = + (const uint8_t *)ptr - + (const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE) + + offset; + ensure(flash_unlock_write(), NULL); + ensure(flash_write_word(norcow_sectors[norcow_write_sector], sector_offset, + value), + NULL); + ensure(flash_lock_write(), NULL); + return sectrue; } /* * Update the value of the given key starting at the given offset. */ -secbool norcow_update_bytes(const uint16_t key, const uint16_t offset, const uint8_t *data, const uint16_t len) -{ - const void *ptr; - uint16_t allocated_len; - if (sectrue != find_item(norcow_write_sector, key, &ptr, &allocated_len)) { - return secfalse; - } - if (offset + len > allocated_len) { - return secfalse; - } - uint32_t sector_offset = (const uint8_t*) ptr - (const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE) + offset; - uint8_t sector = norcow_sectors[norcow_write_sector]; - ensure(flash_unlock_write(), NULL); - for (uint16_t i = 0; i < len; i++, sector_offset++) { - ensure(flash_write_byte(sector, sector_offset, data[i]), NULL); - } - ensure(flash_lock_write(), NULL); - return sectrue; +secbool norcow_update_bytes(const uint16_t key, const uint16_t offset, + const uint8_t *data, const uint16_t len) { + const void *ptr; + uint16_t allocated_len; + if (sectrue != find_item(norcow_write_sector, key, &ptr, &allocated_len)) { + return secfalse; + } + if (offset + len > allocated_len) { + return secfalse; + } + uint32_t sector_offset = + (const uint8_t *)ptr - + (const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE) + + offset; + uint8_t sector = norcow_sectors[norcow_write_sector]; + ensure(flash_unlock_write(), NULL); + for (uint16_t i = 0; i < len; i++, sector_offset++) { + ensure(flash_write_byte(sector, sector_offset, data[i]), NULL); + } + ensure(flash_lock_write(), NULL); + return sectrue; } /* * Complete storage version upgrade */ -secbool norcow_upgrade_finish(void) -{ - erase_sector(norcow_active_sector, secfalse); - norcow_active_sector = norcow_write_sector; - norcow_active_version = NORCOW_VERSION; - return sectrue; +secbool norcow_upgrade_finish(void) { + erase_sector(norcow_active_sector, secfalse); + norcow_active_sector = norcow_write_sector; + norcow_active_version = NORCOW_VERSION; + return sectrue; } diff --git a/storage/norcow.h b/storage/norcow.h index 953f6f1d01..c078c992a9 100644 --- a/storage/norcow.h +++ b/storage/norcow.h @@ -45,9 +45,11 @@ void norcow_wipe(void); secbool norcow_get(uint16_t key, const void **val, uint16_t *len); /* - * Reads the next entry in the storage starting at offset. Returns secfalse if there is none. + * Reads the next entry in the storage starting at offset. Returns secfalse if + * there is none. */ -secbool norcow_get_next(uint32_t *offset, uint16_t *key, const void **val, uint16_t *len); +secbool norcow_get_next(uint32_t *offset, uint16_t *key, const void **val, + uint16_t *len); /* * Sets the given key, returns status of the operation. If NULL is passed @@ -55,7 +57,8 @@ secbool norcow_get_next(uint32_t *offset, uint16_t *key, const void **val, uint1 * then be written using norcow_update_bytes(). */ secbool norcow_set(uint16_t key, const void *val, uint16_t len); -secbool norcow_set_ex(uint16_t key, const void *val, uint16_t len, secbool *found); +secbool norcow_set_ex(uint16_t key, const void *val, uint16_t len, + secbool *found); /* * Deletes the given key, returns status of the operation. @@ -72,7 +75,8 @@ secbool norcow_update_word(uint16_t key, uint16_t offset, uint32_t value); * Update the value of the given key starting at the given offset. * Note that you can only change bits from 1 to 0. */ -secbool norcow_update_bytes(const uint16_t key, const uint16_t offset, const uint8_t *data, const uint16_t len); +secbool norcow_update_bytes(const uint16_t key, const uint16_t offset, + const uint8_t *data, const uint16_t len); /* * Complete storage version upgrade diff --git a/storage/storage.c b/storage/storage.c index 655e85c31f..fa6e90ca38 100644 --- a/storage/storage.c +++ b/storage/storage.c @@ -19,105 +19,108 @@ #include -#include "common.h" -#include "norcow.h" -#include "storage.h" -#include "pbkdf2.h" -#include "sha2.h" -#include "hmac.h" -#include "rand.h" -#include "memzero.h" #include "chacha20poly1305/rfc7539.h" +#include "common.h" +#include "hmac.h" +#include "memzero.h" +#include "norcow.h" +#include "pbkdf2.h" +#include "rand.h" +#include "sha2.h" +#include "storage.h" -#define LOW_MASK 0x55555555 +#define LOW_MASK 0x55555555 // The APP namespace which is reserved for storage related values. -#define APP_STORAGE 0x00 +#define APP_STORAGE 0x00 // Norcow storage key of the PIN entry log and PIN success log. -#define PIN_LOGS_KEY ((APP_STORAGE << 8) | 0x01) +#define PIN_LOGS_KEY ((APP_STORAGE << 8) | 0x01) -// Norcow storage key of the combined salt, EDEK, ESAK and PIN verification code entry. -#define EDEK_PVC_KEY ((APP_STORAGE << 8) | 0x02) +// Norcow storage key of the combined salt, EDEK, ESAK and PIN verification code +// entry. +#define EDEK_PVC_KEY ((APP_STORAGE << 8) | 0x02) // Norcow storage key of the PIN set flag. -#define PIN_NOT_SET_KEY ((APP_STORAGE << 8) | 0x03) +#define PIN_NOT_SET_KEY ((APP_STORAGE << 8) | 0x03) // Norcow storage key of the storage version. -#define VERSION_KEY ((APP_STORAGE << 8) | 0x04) +#define VERSION_KEY ((APP_STORAGE << 8) | 0x04) // Norcow storage key of the storage authentication tag. -#define STORAGE_TAG_KEY ((APP_STORAGE << 8) | 0x05) +#define STORAGE_TAG_KEY ((APP_STORAGE << 8) | 0x05) // The PIN value corresponding to an empty PIN. -#define PIN_EMPTY 1 +#define PIN_EMPTY 1 // Maximum number of failed unlock attempts. -// NOTE: The PIN counter logic relies on this constant being less than or equal to 16. -#define PIN_MAX_TRIES 16 +// NOTE: The PIN counter logic relies on this constant being less than or equal +// to 16. +#define PIN_MAX_TRIES 16 // The total number of iterations to use in PBKDF2. -#define PIN_ITER_COUNT 20000 +#define PIN_ITER_COUNT 20000 // The number of seconds required to derive the KEK and KEIV. -#define DERIVE_SECS 1 +#define DERIVE_SECS 1 // If the top bit of APP is set, then the value is not encrypted. -#define FLAG_PUBLIC 0x80 +#define FLAG_PUBLIC 0x80 // If the top two bits of APP are set, then the value is not encrypted and it // can be written even when the storage is locked. -#define FLAGS_WRITE 0xC0 +#define FLAGS_WRITE 0xC0 // The length of the guard key in words. -#define GUARD_KEY_WORDS 1 +#define GUARD_KEY_WORDS 1 // The length of the PIN entry log or the PIN success log in words. -#define PIN_LOG_WORDS 16 +#define PIN_LOG_WORDS 16 // The length of a word in bytes. -#define WORD_SIZE (sizeof(uint32_t)) +#define WORD_SIZE (sizeof(uint32_t)) // The length of the hashed hardware salt in bytes. -#define HARDWARE_SALT_SIZE SHA256_DIGEST_LENGTH +#define HARDWARE_SALT_SIZE SHA256_DIGEST_LENGTH // The length of the random salt in bytes. -#define RANDOM_SALT_SIZE 4 +#define RANDOM_SALT_SIZE 4 // The length of the data encryption key in bytes. -#define DEK_SIZE 32 +#define DEK_SIZE 32 // The length of the storage authentication key in bytes. -#define SAK_SIZE 16 +#define SAK_SIZE 16 -// The combined length of the data encryption key and the storage authentication key in bytes. -#define KEYS_SIZE (DEK_SIZE + SAK_SIZE) +// The combined length of the data encryption key and the storage authentication +// key in bytes. +#define KEYS_SIZE (DEK_SIZE + SAK_SIZE) // The length of the PIN verification code in bytes. -#define PVC_SIZE 8 +#define PVC_SIZE 8 // The length of the storage authentication tag in bytes. -#define STORAGE_TAG_SIZE 16 +#define STORAGE_TAG_SIZE 16 // The length of the Poly1305 authentication tag in bytes. -#define POLY1305_TAG_SIZE 16 +#define POLY1305_TAG_SIZE 16 // The length of the ChaCha20 IV (aka nonce) in bytes as per RFC 7539. -#define CHACHA20_IV_SIZE 12 +#define CHACHA20_IV_SIZE 12 // The length of the ChaCha20 block in bytes. #define CHACHA20_BLOCK_SIZE 64 // The length of the counter tail in words. -#define COUNTER_TAIL_WORDS 2 +#define COUNTER_TAIL_WORDS 2 // Values used in the guard key integrity check. -#define GUARD_KEY_MODULUS 6311 +#define GUARD_KEY_MODULUS 6311 #define GUARD_KEY_REMAINDER 15 -const char* const VERIFYING_PIN_MSG = "Verifying PIN"; -const char* const PROCESSING_MSG = "Processing"; -const char* const STARTING_MSG = "Starting up"; +const char *const VERIFYING_PIN_MSG = "Verifying PIN"; +const char *const PROCESSING_MSG = "Processing"; +const char *const STARTING_MSG = "Starting up"; static secbool initialized = secfalse; static secbool unlocked = secfalse; @@ -134,742 +137,781 @@ static uint32_t norcow_active_version = 0; static const uint8_t TRUE_BYTE = 0x01; static const uint8_t FALSE_BYTE = 0x00; -static void __handle_fault(const char *msg, const char *file, int line, const char *func); +static void __handle_fault(const char *msg, const char *file, int line, + const char *func); #define handle_fault(msg) (__handle_fault(msg, __FILE__, __LINE__, __func__)) static secbool storage_upgrade(void); -static secbool storage_set_encrypted(const uint16_t key, const void *val, const uint16_t len); -static secbool storage_get_encrypted(const uint16_t key, void *val_dest, const uint16_t max_len, uint16_t *len); +static secbool storage_set_encrypted(const uint16_t key, const void *val, + const uint16_t len); +static secbool storage_get_encrypted(const uint16_t key, void *val_dest, + const uint16_t max_len, uint16_t *len); -static secbool secequal(const void* ptr1, const void* ptr2, size_t n) { - const uint8_t* p1 = ptr1; - const uint8_t* p2 = ptr2; - uint8_t diff = 0; - size_t i; - for (i = 0; i < n; ++i) { - diff |= *p1 ^ *p2; - ++p1; - ++p2; - } +static secbool secequal(const void *ptr1, const void *ptr2, size_t n) { + const uint8_t *p1 = ptr1; + const uint8_t *p2 = ptr2; + uint8_t diff = 0; + size_t i; + for (i = 0; i < n; ++i) { + diff |= *p1 ^ *p2; + ++p1; + ++p2; + } - // Check loop completion in case of a fault injection attack. - if (i != n) { - handle_fault("loop completion check"); - } + // Check loop completion in case of a fault injection attack. + if (i != n) { + handle_fault("loop completion check"); + } - return diff ? secfalse : sectrue; + return diff ? secfalse : sectrue; } -static secbool secequal32(const uint32_t* ptr1, const uint32_t* ptr2, size_t n) { - uint32_t diff = 0; - size_t i; - for (i = 0; i < n; ++i) { - uint32_t mask = random32(); - diff |= (*ptr1 + mask - *ptr2) ^ mask; - ++ptr1; - ++ptr2; - } +static secbool secequal32(const uint32_t *ptr1, const uint32_t *ptr2, + size_t n) { + uint32_t diff = 0; + size_t i; + for (i = 0; i < n; ++i) { + uint32_t mask = random32(); + diff |= (*ptr1 + mask - *ptr2) ^ mask; + ++ptr1; + ++ptr2; + } - // Check loop completion in case of a fault injection attack. - if (i != n) { - handle_fault("loop completion check"); - } + // Check loop completion in case of a fault injection attack. + if (i != n) { + handle_fault("loop completion check"); + } - return diff ? secfalse : sectrue; + return diff ? secfalse : sectrue; } static secbool is_protected(uint16_t key) { - const uint8_t app = key >> 8; - return ((app & FLAG_PUBLIC) == 0 && app != APP_STORAGE) ? sectrue : secfalse; + const uint8_t app = key >> 8; + return ((app & FLAG_PUBLIC) == 0 && app != APP_STORAGE) ? sectrue : secfalse; } /* * Initialize the storage authentication tag for freshly wiped storage. */ static secbool auth_init(void) { - uint8_t tag[SHA256_DIGEST_LENGTH]; - memzero(authentication_sum, sizeof(authentication_sum)); - hmac_sha256(cached_sak, SAK_SIZE, authentication_sum, sizeof(authentication_sum), tag); - return norcow_set(STORAGE_TAG_KEY, tag, STORAGE_TAG_SIZE); + uint8_t tag[SHA256_DIGEST_LENGTH]; + memzero(authentication_sum, sizeof(authentication_sum)); + hmac_sha256(cached_sak, SAK_SIZE, authentication_sum, + sizeof(authentication_sum), tag); + return norcow_set(STORAGE_TAG_KEY, tag, STORAGE_TAG_SIZE); } /* * Update the storage authentication tag with the given key. */ static secbool auth_update(uint16_t key) { - if (sectrue != is_protected(key)) { - return sectrue; - } + if (sectrue != is_protected(key)) { + return sectrue; + } - uint8_t tag[SHA256_DIGEST_LENGTH]; - hmac_sha256(cached_sak, SAK_SIZE, (uint8_t*)&key, sizeof(key), tag); - for (uint32_t i = 0; i < SHA256_DIGEST_LENGTH; i++) { - authentication_sum[i] ^= tag[i]; - } - hmac_sha256(cached_sak, SAK_SIZE, authentication_sum, sizeof(authentication_sum), tag); - return norcow_set(STORAGE_TAG_KEY, tag, STORAGE_TAG_SIZE); + uint8_t tag[SHA256_DIGEST_LENGTH]; + hmac_sha256(cached_sak, SAK_SIZE, (uint8_t *)&key, sizeof(key), tag); + for (uint32_t i = 0; i < SHA256_DIGEST_LENGTH; i++) { + authentication_sum[i] ^= tag[i]; + } + hmac_sha256(cached_sak, SAK_SIZE, authentication_sum, + sizeof(authentication_sum), tag); + return norcow_set(STORAGE_TAG_KEY, tag, STORAGE_TAG_SIZE); } /* - * A secure version of norcow_set(), which updates the storage authentication tag. + * A secure version of norcow_set(), which updates the storage authentication + * tag. */ static secbool auth_set(uint16_t key, const void *val, uint16_t len) { - secbool found; - secbool ret = norcow_set_ex(key, val, len, &found); - if (sectrue == ret && secfalse == found) { - ret = auth_update(key); - if (sectrue != ret) { - norcow_delete(key); - } + secbool found; + secbool ret = norcow_set_ex(key, val, len, &found); + if (sectrue == ret && secfalse == found) { + ret = auth_update(key); + if (sectrue != ret) { + norcow_delete(key); } - return ret; + } + return ret; } /* - * A secure version of norcow_get(), which checks the storage authentication tag. + * A secure version of norcow_get(), which checks the storage authentication + * tag. */ -static secbool auth_get(uint16_t key, const void **val, uint16_t *len) -{ - *val = NULL; - *len = 0; - uint32_t sum[SHA256_DIGEST_LENGTH/sizeof(uint32_t)] = {0}; +static secbool auth_get(uint16_t key, const void **val, uint16_t *len) { + *val = NULL; + *len = 0; + uint32_t sum[SHA256_DIGEST_LENGTH / sizeof(uint32_t)] = {0}; - // Prepare inner and outer digest. - uint32_t odig[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; - uint32_t idig[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; - hmac_sha256_prepare(cached_sak, SAK_SIZE, odig, idig); + // Prepare inner and outer digest. + uint32_t odig[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; + uint32_t idig[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; + hmac_sha256_prepare(cached_sak, SAK_SIZE, odig, idig); - // Prepare SHA-256 message padding. - uint32_t g[SHA256_BLOCK_LENGTH / sizeof(uint32_t)] = {0}; - uint32_t h[SHA256_BLOCK_LENGTH / sizeof(uint32_t)] = {0}; - g[15] = (SHA256_BLOCK_LENGTH + 2) * 8; - h[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH) * 8; - h[8] = 0x80000000; + // Prepare SHA-256 message padding. + uint32_t g[SHA256_BLOCK_LENGTH / sizeof(uint32_t)] = {0}; + uint32_t h[SHA256_BLOCK_LENGTH / sizeof(uint32_t)] = {0}; + g[15] = (SHA256_BLOCK_LENGTH + 2) * 8; + h[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH) * 8; + h[8] = 0x80000000; - uint32_t offset = 0; - uint16_t k = 0; - uint16_t l = 0; - uint16_t tag_len = 0; - uint16_t entry_count = 0; // Mitigation against fault injection. - uint16_t other_count = 0; // Mitigation against fault injection. - const void *v = NULL; - const void *tag_val = NULL; - while (sectrue == norcow_get_next(&offset, &k, &v, &l)) { - ++entry_count; - if (k == key) { - *val = v; - *len = l; - } else { - ++other_count; - } - if (sectrue != is_protected(k)) { - if (k == STORAGE_TAG_KEY) { - tag_val = v; - tag_len = l; - } - continue; - } - g[0] = (((uint32_t)k & 0xff) << 24) | (((uint32_t)k & 0xff00) << 8) | 0x8000; // Add SHA message padding. - sha256_Transform(idig, g, h); - sha256_Transform(odig, h, h); - for (uint32_t i = 0; i < SHA256_DIGEST_LENGTH/sizeof(uint32_t); i++) { - sum[i] ^= h[i]; - } + uint32_t offset = 0; + uint16_t k = 0; + uint16_t l = 0; + uint16_t tag_len = 0; + uint16_t entry_count = 0; // Mitigation against fault injection. + uint16_t other_count = 0; // Mitigation against fault injection. + const void *v = NULL; + const void *tag_val = NULL; + while (sectrue == norcow_get_next(&offset, &k, &v, &l)) { + ++entry_count; + if (k == key) { + *val = v; + *len = l; + } else { + ++other_count; } - memcpy(h, sum, sizeof(sum)); - - sha256_Transform(idig, h, h); + if (sectrue != is_protected(k)) { + if (k == STORAGE_TAG_KEY) { + tag_val = v; + tag_len = l; + } + continue; + } + g[0] = (((uint32_t)k & 0xff) << 24) | (((uint32_t)k & 0xff00) << 8) | + 0x8000; // Add SHA message padding. + sha256_Transform(idig, g, h); sha256_Transform(odig, h, h); + for (uint32_t i = 0; i < SHA256_DIGEST_LENGTH / sizeof(uint32_t); i++) { + sum[i] ^= h[i]; + } + } + memcpy(h, sum, sizeof(sum)); - memzero(odig, sizeof(odig)); - memzero(idig, sizeof(idig)); + sha256_Transform(idig, h, h); + sha256_Transform(odig, h, h); - // Cache the authentication sum. - for (size_t i = 0; i < SHA256_DIGEST_LENGTH/sizeof(uint32_t); i++) { + memzero(odig, sizeof(odig)); + memzero(idig, sizeof(idig)); + + // Cache the authentication sum. + for (size_t i = 0; i < SHA256_DIGEST_LENGTH / sizeof(uint32_t); i++) { #if BYTE_ORDER == LITTLE_ENDIAN - REVERSE32(sum[i], ((uint32_t*)authentication_sum)[i]); + REVERSE32(sum[i], ((uint32_t *)authentication_sum)[i]); #else - ((uint32_t*)authentication_sum)[i] = sum[i]; + ((uint32_t *)authentication_sum)[i] = sum[i]; #endif - } + } - // Check loop completion in case of a fault injection attack. - if (secfalse != norcow_get_next(&offset, &k, &v, &l)) { - handle_fault("loop completion check"); - } + // Check loop completion in case of a fault injection attack. + if (secfalse != norcow_get_next(&offset, &k, &v, &l)) { + handle_fault("loop completion check"); + } - // Check storage authentication tag. + // Check storage authentication tag. #if BYTE_ORDER == LITTLE_ENDIAN - for (size_t i = 0; i < SHA256_DIGEST_LENGTH/sizeof(uint32_t); i++) { - REVERSE32(h[i], h[i]); - } + for (size_t i = 0; i < SHA256_DIGEST_LENGTH / sizeof(uint32_t); i++) { + REVERSE32(h[i], h[i]); + } #endif - if (tag_val == NULL || tag_len != STORAGE_TAG_SIZE || sectrue != secequal(h, tag_val, STORAGE_TAG_SIZE)) { - handle_fault("storage tag check"); + if (tag_val == NULL || tag_len != STORAGE_TAG_SIZE || + sectrue != secequal(h, tag_val, STORAGE_TAG_SIZE)) { + handle_fault("storage tag check"); + } + + if (*val == NULL) { + // Check for fault injection. + if (other_count != entry_count) { + handle_fault("sanity check"); } - - if (*val == NULL) { - // Check for fault injection. - if (other_count != entry_count) { - handle_fault("sanity check"); - } - return secfalse; - } - return sectrue; -} - -/* - * Generates a delay of random length. Use this to protect sensitive code against fault injection. - */ -static void wait_random(void) -{ -#ifndef TREZOR_STORAGE_TEST - int wait = random32() & 0xff; - volatile int i = 0; - volatile int j = wait; - while (i < wait) { - if (i + j != wait) { - handle_fault("sanity check"); - } - ++i; - --j; - } - - // Double-check loop completion. - if (i != wait) { - handle_fault("loop completion check"); - } -#endif -} - -static void derive_kek(uint32_t pin, const uint8_t *random_salt, uint8_t kek[SHA256_DIGEST_LENGTH], uint8_t keiv[SHA256_DIGEST_LENGTH]) -{ -#if BYTE_ORDER == BIG_ENDIAN - REVERSE32(pin, pin); -#endif - - uint8_t salt[HARDWARE_SALT_SIZE + RANDOM_SALT_SIZE]; - memcpy(salt, hardware_salt, HARDWARE_SALT_SIZE); - memcpy(salt + HARDWARE_SALT_SIZE, random_salt, RANDOM_SALT_SIZE); - - uint32_t progress = (ui_total - ui_rem) * 1000 / ui_total; - if (ui_callback && ui_message) { - ui_callback(ui_rem, progress, ui_message); - } - - PBKDF2_HMAC_SHA256_CTX ctx; - pbkdf2_hmac_sha256_Init(&ctx, (const uint8_t*) &pin, sizeof(pin), salt, sizeof(salt), 1); - for (int i = 1; i <= 5; i++) { - pbkdf2_hmac_sha256_Update(&ctx, PIN_ITER_COUNT / 10); - if (ui_callback && ui_message) { - progress = ((ui_total - ui_rem) * 1000 + i * DERIVE_SECS * 100) / ui_total; - ui_callback(ui_rem - i * DERIVE_SECS / 10, progress, ui_message); - } - } - pbkdf2_hmac_sha256_Final(&ctx, kek); - - pbkdf2_hmac_sha256_Init(&ctx, (const uint8_t*) &pin, sizeof(pin), salt, sizeof(salt), 2); - for (int i = 6; i <= 10; i++) { - pbkdf2_hmac_sha256_Update(&ctx, PIN_ITER_COUNT / 10); - if (ui_callback && ui_message) { - progress = ((ui_total - ui_rem) * 1000 + i * DERIVE_SECS * 100) / ui_total; - ui_callback(ui_rem - i * DERIVE_SECS / 10, progress, ui_message); - } - } - pbkdf2_hmac_sha256_Final(&ctx, keiv); - - ui_rem -= DERIVE_SECS; - memzero(&ctx, sizeof(PBKDF2_HMAC_SHA256_CTX)); - memzero(&pin, sizeof(pin)); - memzero(&salt, sizeof(salt)); -} - -static secbool set_pin(uint32_t pin) -{ - uint8_t buffer[RANDOM_SALT_SIZE + KEYS_SIZE + POLY1305_TAG_SIZE]; - uint8_t *salt = buffer; - uint8_t *ekeys = buffer + RANDOM_SALT_SIZE; - uint8_t *pvc = buffer + RANDOM_SALT_SIZE + KEYS_SIZE; - - uint8_t kek[SHA256_DIGEST_LENGTH]; - uint8_t keiv[SHA256_DIGEST_LENGTH]; - chacha20poly1305_ctx ctx; - random_buffer(salt, RANDOM_SALT_SIZE); - derive_kek(pin, salt, kek, keiv); - rfc7539_init(&ctx, kek, keiv); - memzero(kek, sizeof(kek)); - memzero(keiv, sizeof(keiv)); - chacha20poly1305_encrypt(&ctx, cached_keys, ekeys, KEYS_SIZE); - rfc7539_finish(&ctx, 0, KEYS_SIZE, pvc); - memzero(&ctx, sizeof(ctx)); - secbool ret = norcow_set(EDEK_PVC_KEY, buffer, RANDOM_SALT_SIZE + KEYS_SIZE + PVC_SIZE); - memzero(buffer, sizeof(buffer)); - - if (ret == sectrue) - { - if (pin == PIN_EMPTY) { - ret = norcow_set(PIN_NOT_SET_KEY, &TRUE_BYTE, sizeof(TRUE_BYTE)); - } else { - ret = norcow_set(PIN_NOT_SET_KEY, &FALSE_BYTE, sizeof(FALSE_BYTE)); - } - } - - memzero(&pin, sizeof(pin)); - return ret; -} - -static secbool check_guard_key(const uint32_t guard_key) -{ - if (guard_key % GUARD_KEY_MODULUS != GUARD_KEY_REMAINDER) { - return secfalse; - } - - // Check that each byte of (guard_key & 0xAAAAAAAA) has exactly two bits set. - uint32_t count = (guard_key & 0x22222222) + ((guard_key >> 2) & 0x22222222); - count = count + (count >> 4); - if ((count & 0x0e0e0e0e) != 0x04040404) { - return secfalse; - } - - // Check that the guard_key does not contain a run of 5 (or more) zeros or ones. - uint32_t zero_runs = ~guard_key; - zero_runs = zero_runs & (zero_runs >> 2); - zero_runs = zero_runs & (zero_runs >> 1); - zero_runs = zero_runs & (zero_runs >> 1); - - uint32_t one_runs = guard_key; - one_runs = one_runs & (one_runs >> 2); - one_runs = one_runs & (one_runs >> 1); - one_runs = one_runs & (one_runs >> 1); - - if ((one_runs != 0) || (zero_runs != 0)) { - return secfalse; - } - - return sectrue; -} - -static uint32_t generate_guard_key(void) -{ - uint32_t guard_key = 0; - do { - guard_key = random_uniform((UINT32_MAX/GUARD_KEY_MODULUS) + 1) * GUARD_KEY_MODULUS + GUARD_KEY_REMAINDER; - } while (sectrue != check_guard_key(guard_key)); - return guard_key; -} - -static secbool expand_guard_key(const uint32_t guard_key, uint32_t *guard_mask, uint32_t *guard) -{ - if (sectrue != check_guard_key(guard_key)) { - handle_fault("guard key check"); - return secfalse; - } - *guard_mask = ((guard_key & LOW_MASK) << 1) | ((~guard_key) & LOW_MASK); - *guard = (((guard_key & LOW_MASK) << 1) & guard_key) | (((~guard_key) & LOW_MASK) & (guard_key >> 1)); - return sectrue; -} - -static secbool pin_logs_init(uint32_t fails) -{ - if (fails >= PIN_MAX_TRIES) { - return secfalse; - } - - // The format of the PIN_LOGS_KEY entry is: - // guard_key (1 word), pin_success_log (PIN_LOG_WORDS), pin_entry_log (PIN_LOG_WORDS) - uint32_t logs[GUARD_KEY_WORDS + 2*PIN_LOG_WORDS]; - - logs[0] = generate_guard_key(); - - uint32_t guard_mask; - uint32_t guard; - wait_random(); - if (sectrue != expand_guard_key(logs[0], &guard_mask, &guard)) { - return secfalse; - } - - uint32_t unused = guard | ~guard_mask; - for (size_t i = 0; i < 2*PIN_LOG_WORDS; ++i) { - logs[GUARD_KEY_WORDS + i] = unused; - } - - // Set the first word of the PIN entry log to indicate the requested number of fails. - logs[GUARD_KEY_WORDS + PIN_LOG_WORDS] = ((((uint32_t)0xFFFFFFFF) >> (2*fails)) & ~guard_mask) | guard; - - return norcow_set(PIN_LOGS_KEY, logs, sizeof(logs)); -} - -/* - * Initializes the values of VERSION_KEY, EDEK_PVC_KEY, PIN_NOT_SET_KEY and PIN_LOGS_KEY using an empty PIN. - * This function should be called to initialize freshly wiped storage. - */ -static void init_wiped_storage(void) -{ - if (sectrue != initialized) { - // We cannot initialize the storage contents if the hardware_salt is not set. - return; - } - random_buffer(cached_keys, sizeof(cached_keys)); - uint32_t version = NORCOW_VERSION; - ensure(auth_init(), "set_storage_auth_tag failed"); - ensure(storage_set_encrypted(VERSION_KEY, &version, sizeof(version)), "set_storage_version failed"); - ensure(pin_logs_init(0), "init_pin_logs failed"); - ui_total = DERIVE_SECS; - ui_rem = ui_total; - ui_message = PROCESSING_MSG; - ensure(set_pin(PIN_EMPTY), "init_pin failed"); - if (unlocked != sectrue) { - memzero(cached_keys, sizeof(cached_keys)); - } -} - -void storage_init(PIN_UI_WAIT_CALLBACK callback, const uint8_t *salt, const uint16_t salt_len) -{ - initialized = secfalse; - unlocked = secfalse; - norcow_init(&norcow_active_version); - initialized = sectrue; - ui_callback = callback; - - sha256_Raw(salt, salt_len, hardware_salt); - - if (norcow_active_version < NORCOW_VERSION) { - if (sectrue != storage_upgrade()) { - storage_wipe(); - ensure(secfalse, "storage_upgrade failed"); - } - } - - // If there is no EDEK, then generate a random DEK and SAK and store them. - const void *val; - uint16_t len; - if (secfalse == norcow_get(EDEK_PVC_KEY, &val, &len)) { - init_wiped_storage(); - } - memzero(cached_keys, sizeof(cached_keys)); -} - -static secbool pin_fails_reset(void) -{ - const void *logs = NULL; - uint16_t len = 0; - - if (sectrue != norcow_get(PIN_LOGS_KEY, &logs, &len) || len != WORD_SIZE*(GUARD_KEY_WORDS + 2*PIN_LOG_WORDS)) { - return secfalse; - } - - uint32_t guard_mask; - uint32_t guard; - wait_random(); - if (sectrue != expand_guard_key(*(const uint32_t*)logs, &guard_mask, &guard)) { - return secfalse; - } - - uint32_t unused = guard | ~guard_mask; - const uint32_t *success_log = ((const uint32_t*)logs) + GUARD_KEY_WORDS; - const uint32_t *entry_log = success_log + PIN_LOG_WORDS; - for (size_t i = 0; i < PIN_LOG_WORDS; ++i) { - if (entry_log[i] == unused) { - return sectrue; - } - if (success_log[i] != guard) { - if (sectrue != norcow_update_word(PIN_LOGS_KEY, sizeof(uint32_t)*(i + GUARD_KEY_WORDS), entry_log[i])) { - return secfalse; - } - } - } - return pin_logs_init(0); -} - -secbool storage_pin_fails_increase(void) -{ - if (sectrue != initialized) { - return secfalse; - } - - const void *logs = NULL; - uint16_t len = 0; - - wait_random(); - if (sectrue != norcow_get(PIN_LOGS_KEY, &logs, &len) || len != WORD_SIZE*(GUARD_KEY_WORDS + 2*PIN_LOG_WORDS)) { - handle_fault("no PIN logs"); - return secfalse; - } - - uint32_t guard_mask; - uint32_t guard; - wait_random(); - if (sectrue != expand_guard_key(*(const uint32_t*)logs, &guard_mask, &guard)) { - handle_fault("guard key expansion"); - return secfalse; - } - - const uint32_t *entry_log = ((const uint32_t*)logs) + GUARD_KEY_WORDS + PIN_LOG_WORDS; - for (size_t i = 0; i < PIN_LOG_WORDS; ++i) { - wait_random(); - if ((entry_log[i] & guard_mask) != guard) { - handle_fault("guard bits check"); - return secfalse; - } - if (entry_log[i] != guard) { - wait_random(); - uint32_t word = entry_log[i] & ~guard_mask; - word = ((word >> 1) | word) & LOW_MASK; - word = (word >> 2) | (word >> 1); - - wait_random(); - if (sectrue != norcow_update_word(PIN_LOGS_KEY, sizeof(uint32_t)*(i + GUARD_KEY_WORDS + PIN_LOG_WORDS), (word & ~guard_mask) | guard)) { - handle_fault("PIN logs update"); - return secfalse; - } - return sectrue; - } - - } - handle_fault("PIN log exhausted"); return secfalse; + } + return sectrue; } -static uint32_t hamming_weight(uint32_t value) -{ - value = value - ((value >> 1) & 0x55555555); - value = (value & 0x33333333) + ((value >> 2) & 0x33333333); - value = (value + (value >> 4)) & 0x0F0F0F0F; - value = value + (value >> 8); - value = value + (value >> 16); - return value & 0x3F; +/* + * Generates a delay of random length. Use this to protect sensitive code + * against fault injection. + */ +static void wait_random(void) { +#ifndef TREZOR_STORAGE_TEST + int wait = random32() & 0xff; + volatile int i = 0; + volatile int j = wait; + while (i < wait) { + if (i + j != wait) { + handle_fault("sanity check"); + } + ++i; + --j; + } + + // Double-check loop completion. + if (i != wait) { + handle_fault("loop completion check"); + } +#endif } -static secbool pin_get_fails(uint32_t *ctr) -{ - *ctr = PIN_MAX_TRIES; +static void derive_kek(uint32_t pin, const uint8_t *random_salt, + uint8_t kek[SHA256_DIGEST_LENGTH], + uint8_t keiv[SHA256_DIGEST_LENGTH]) { +#if BYTE_ORDER == BIG_ENDIAN + REVERSE32(pin, pin); +#endif - const void *logs = NULL; - uint16_t len = 0; - wait_random(); - if (sectrue != norcow_get(PIN_LOGS_KEY, &logs, &len) || len != WORD_SIZE*(GUARD_KEY_WORDS + 2*PIN_LOG_WORDS)) { - handle_fault("no PIN logs"); + uint8_t salt[HARDWARE_SALT_SIZE + RANDOM_SALT_SIZE]; + memcpy(salt, hardware_salt, HARDWARE_SALT_SIZE); + memcpy(salt + HARDWARE_SALT_SIZE, random_salt, RANDOM_SALT_SIZE); + + uint32_t progress = (ui_total - ui_rem) * 1000 / ui_total; + if (ui_callback && ui_message) { + ui_callback(ui_rem, progress, ui_message); + } + + PBKDF2_HMAC_SHA256_CTX ctx; + pbkdf2_hmac_sha256_Init(&ctx, (const uint8_t *)&pin, sizeof(pin), salt, + sizeof(salt), 1); + for (int i = 1; i <= 5; i++) { + pbkdf2_hmac_sha256_Update(&ctx, PIN_ITER_COUNT / 10); + if (ui_callback && ui_message) { + progress = + ((ui_total - ui_rem) * 1000 + i * DERIVE_SECS * 100) / ui_total; + ui_callback(ui_rem - i * DERIVE_SECS / 10, progress, ui_message); + } + } + pbkdf2_hmac_sha256_Final(&ctx, kek); + + pbkdf2_hmac_sha256_Init(&ctx, (const uint8_t *)&pin, sizeof(pin), salt, + sizeof(salt), 2); + for (int i = 6; i <= 10; i++) { + pbkdf2_hmac_sha256_Update(&ctx, PIN_ITER_COUNT / 10); + if (ui_callback && ui_message) { + progress = + ((ui_total - ui_rem) * 1000 + i * DERIVE_SECS * 100) / ui_total; + ui_callback(ui_rem - i * DERIVE_SECS / 10, progress, ui_message); + } + } + pbkdf2_hmac_sha256_Final(&ctx, keiv); + + ui_rem -= DERIVE_SECS; + memzero(&ctx, sizeof(PBKDF2_HMAC_SHA256_CTX)); + memzero(&pin, sizeof(pin)); + memzero(&salt, sizeof(salt)); +} + +static secbool set_pin(uint32_t pin) { + uint8_t buffer[RANDOM_SALT_SIZE + KEYS_SIZE + POLY1305_TAG_SIZE]; + uint8_t *salt = buffer; + uint8_t *ekeys = buffer + RANDOM_SALT_SIZE; + uint8_t *pvc = buffer + RANDOM_SALT_SIZE + KEYS_SIZE; + + uint8_t kek[SHA256_DIGEST_LENGTH]; + uint8_t keiv[SHA256_DIGEST_LENGTH]; + chacha20poly1305_ctx ctx; + random_buffer(salt, RANDOM_SALT_SIZE); + derive_kek(pin, salt, kek, keiv); + rfc7539_init(&ctx, kek, keiv); + memzero(kek, sizeof(kek)); + memzero(keiv, sizeof(keiv)); + chacha20poly1305_encrypt(&ctx, cached_keys, ekeys, KEYS_SIZE); + rfc7539_finish(&ctx, 0, KEYS_SIZE, pvc); + memzero(&ctx, sizeof(ctx)); + secbool ret = + norcow_set(EDEK_PVC_KEY, buffer, RANDOM_SALT_SIZE + KEYS_SIZE + PVC_SIZE); + memzero(buffer, sizeof(buffer)); + + if (ret == sectrue) { + if (pin == PIN_EMPTY) { + ret = norcow_set(PIN_NOT_SET_KEY, &TRUE_BYTE, sizeof(TRUE_BYTE)); + } else { + ret = norcow_set(PIN_NOT_SET_KEY, &FALSE_BYTE, sizeof(FALSE_BYTE)); + } + } + + memzero(&pin, sizeof(pin)); + return ret; +} + +static secbool check_guard_key(const uint32_t guard_key) { + if (guard_key % GUARD_KEY_MODULUS != GUARD_KEY_REMAINDER) { + return secfalse; + } + + // Check that each byte of (guard_key & 0xAAAAAAAA) has exactly two bits set. + uint32_t count = (guard_key & 0x22222222) + ((guard_key >> 2) & 0x22222222); + count = count + (count >> 4); + if ((count & 0x0e0e0e0e) != 0x04040404) { + return secfalse; + } + + // Check that the guard_key does not contain a run of 5 (or more) zeros or + // ones. + uint32_t zero_runs = ~guard_key; + zero_runs = zero_runs & (zero_runs >> 2); + zero_runs = zero_runs & (zero_runs >> 1); + zero_runs = zero_runs & (zero_runs >> 1); + + uint32_t one_runs = guard_key; + one_runs = one_runs & (one_runs >> 2); + one_runs = one_runs & (one_runs >> 1); + one_runs = one_runs & (one_runs >> 1); + + if ((one_runs != 0) || (zero_runs != 0)) { + return secfalse; + } + + return sectrue; +} + +static uint32_t generate_guard_key(void) { + uint32_t guard_key = 0; + do { + guard_key = random_uniform((UINT32_MAX / GUARD_KEY_MODULUS) + 1) * + GUARD_KEY_MODULUS + + GUARD_KEY_REMAINDER; + } while (sectrue != check_guard_key(guard_key)); + return guard_key; +} + +static secbool expand_guard_key(const uint32_t guard_key, uint32_t *guard_mask, + uint32_t *guard) { + if (sectrue != check_guard_key(guard_key)) { + handle_fault("guard key check"); + return secfalse; + } + *guard_mask = ((guard_key & LOW_MASK) << 1) | ((~guard_key) & LOW_MASK); + *guard = (((guard_key & LOW_MASK) << 1) & guard_key) | + (((~guard_key) & LOW_MASK) & (guard_key >> 1)); + return sectrue; +} + +static secbool pin_logs_init(uint32_t fails) { + if (fails >= PIN_MAX_TRIES) { + return secfalse; + } + + // The format of the PIN_LOGS_KEY entry is: + // guard_key (1 word), pin_success_log (PIN_LOG_WORDS), pin_entry_log + // (PIN_LOG_WORDS) + uint32_t logs[GUARD_KEY_WORDS + 2 * PIN_LOG_WORDS]; + + logs[0] = generate_guard_key(); + + uint32_t guard_mask; + uint32_t guard; + wait_random(); + if (sectrue != expand_guard_key(logs[0], &guard_mask, &guard)) { + return secfalse; + } + + uint32_t unused = guard | ~guard_mask; + for (size_t i = 0; i < 2 * PIN_LOG_WORDS; ++i) { + logs[GUARD_KEY_WORDS + i] = unused; + } + + // Set the first word of the PIN entry log to indicate the requested number of + // fails. + logs[GUARD_KEY_WORDS + PIN_LOG_WORDS] = + ((((uint32_t)0xFFFFFFFF) >> (2 * fails)) & ~guard_mask) | guard; + + return norcow_set(PIN_LOGS_KEY, logs, sizeof(logs)); +} + +/* + * Initializes the values of VERSION_KEY, EDEK_PVC_KEY, PIN_NOT_SET_KEY and + * PIN_LOGS_KEY using an empty PIN. This function should be called to initialize + * freshly wiped storage. + */ +static void init_wiped_storage(void) { + if (sectrue != initialized) { + // We cannot initialize the storage contents if the hardware_salt is not + // set. + return; + } + random_buffer(cached_keys, sizeof(cached_keys)); + uint32_t version = NORCOW_VERSION; + ensure(auth_init(), "set_storage_auth_tag failed"); + ensure(storage_set_encrypted(VERSION_KEY, &version, sizeof(version)), + "set_storage_version failed"); + ensure(pin_logs_init(0), "init_pin_logs failed"); + ui_total = DERIVE_SECS; + ui_rem = ui_total; + ui_message = PROCESSING_MSG; + ensure(set_pin(PIN_EMPTY), "init_pin failed"); + if (unlocked != sectrue) { + memzero(cached_keys, sizeof(cached_keys)); + } +} + +void storage_init(PIN_UI_WAIT_CALLBACK callback, const uint8_t *salt, + const uint16_t salt_len) { + initialized = secfalse; + unlocked = secfalse; + norcow_init(&norcow_active_version); + initialized = sectrue; + ui_callback = callback; + + sha256_Raw(salt, salt_len, hardware_salt); + + if (norcow_active_version < NORCOW_VERSION) { + if (sectrue != storage_upgrade()) { + storage_wipe(); + ensure(secfalse, "storage_upgrade failed"); + } + } + + // If there is no EDEK, then generate a random DEK and SAK and store them. + const void *val; + uint16_t len; + if (secfalse == norcow_get(EDEK_PVC_KEY, &val, &len)) { + init_wiped_storage(); + } + memzero(cached_keys, sizeof(cached_keys)); +} + +static secbool pin_fails_reset(void) { + const void *logs = NULL; + uint16_t len = 0; + + if (sectrue != norcow_get(PIN_LOGS_KEY, &logs, &len) || + len != WORD_SIZE * (GUARD_KEY_WORDS + 2 * PIN_LOG_WORDS)) { + return secfalse; + } + + uint32_t guard_mask; + uint32_t guard; + wait_random(); + if (sectrue != + expand_guard_key(*(const uint32_t *)logs, &guard_mask, &guard)) { + return secfalse; + } + + uint32_t unused = guard | ~guard_mask; + const uint32_t *success_log = ((const uint32_t *)logs) + GUARD_KEY_WORDS; + const uint32_t *entry_log = success_log + PIN_LOG_WORDS; + for (size_t i = 0; i < PIN_LOG_WORDS; ++i) { + if (entry_log[i] == unused) { + return sectrue; + } + if (success_log[i] != guard) { + if (sectrue != norcow_update_word( + PIN_LOGS_KEY, sizeof(uint32_t) * (i + GUARD_KEY_WORDS), + entry_log[i])) { return secfalse; + } } + } + return pin_logs_init(0); +} - uint32_t guard_mask; - uint32_t guard; +secbool storage_pin_fails_increase(void) { + if (sectrue != initialized) { + return secfalse; + } + + const void *logs = NULL; + uint16_t len = 0; + + wait_random(); + if (sectrue != norcow_get(PIN_LOGS_KEY, &logs, &len) || + len != WORD_SIZE * (GUARD_KEY_WORDS + 2 * PIN_LOG_WORDS)) { + handle_fault("no PIN logs"); + return secfalse; + } + + uint32_t guard_mask; + uint32_t guard; + wait_random(); + if (sectrue != + expand_guard_key(*(const uint32_t *)logs, &guard_mask, &guard)) { + handle_fault("guard key expansion"); + return secfalse; + } + + const uint32_t *entry_log = + ((const uint32_t *)logs) + GUARD_KEY_WORDS + PIN_LOG_WORDS; + for (size_t i = 0; i < PIN_LOG_WORDS; ++i) { wait_random(); - if (sectrue != expand_guard_key(*(const uint32_t*)logs, &guard_mask, &guard)) { - handle_fault("guard key expansion"); + if ((entry_log[i] & guard_mask) != guard) { + handle_fault("guard bits check"); + return secfalse; + } + if (entry_log[i] != guard) { + wait_random(); + uint32_t word = entry_log[i] & ~guard_mask; + word = ((word >> 1) | word) & LOW_MASK; + word = (word >> 2) | (word >> 1); + + wait_random(); + if (sectrue != + norcow_update_word( + PIN_LOGS_KEY, + sizeof(uint32_t) * (i + GUARD_KEY_WORDS + PIN_LOG_WORDS), + (word & ~guard_mask) | guard)) { + handle_fault("PIN logs update"); return secfalse; + } + return sectrue; } - const uint32_t unused = guard | ~guard_mask; + } + handle_fault("PIN log exhausted"); + return secfalse; +} - const uint32_t *success_log = ((const uint32_t*)logs) + GUARD_KEY_WORDS; - const uint32_t *entry_log = success_log + PIN_LOG_WORDS; - volatile int current = -1; - volatile size_t i; - for (i = 0; i < PIN_LOG_WORDS; ++i) { - if ((entry_log[i] & guard_mask) != guard || (success_log[i] & guard_mask) != guard || (entry_log[i] & success_log[i]) != entry_log[i]) { - handle_fault("PIN logs format check"); - return secfalse; - } +static uint32_t hamming_weight(uint32_t value) { + value = value - ((value >> 1) & 0x55555555); + value = (value & 0x33333333) + ((value >> 2) & 0x33333333); + value = (value + (value >> 4)) & 0x0F0F0F0F; + value = value + (value >> 8); + value = value + (value >> 16); + return value & 0x3F; +} - if (current == -1) { - if (entry_log[i] != guard) { - current = i; - } - } else { - if (entry_log[i] != unused) { - handle_fault("PIN entry log format check"); - return secfalse; - } - } +static secbool pin_get_fails(uint32_t *ctr) { + *ctr = PIN_MAX_TRIES; + + const void *logs = NULL; + uint16_t len = 0; + wait_random(); + if (sectrue != norcow_get(PIN_LOGS_KEY, &logs, &len) || + len != WORD_SIZE * (GUARD_KEY_WORDS + 2 * PIN_LOG_WORDS)) { + handle_fault("no PIN logs"); + return secfalse; + } + + uint32_t guard_mask; + uint32_t guard; + wait_random(); + if (sectrue != + expand_guard_key(*(const uint32_t *)logs, &guard_mask, &guard)) { + handle_fault("guard key expansion"); + return secfalse; + } + const uint32_t unused = guard | ~guard_mask; + + const uint32_t *success_log = ((const uint32_t *)logs) + GUARD_KEY_WORDS; + const uint32_t *entry_log = success_log + PIN_LOG_WORDS; + volatile int current = -1; + volatile size_t i; + for (i = 0; i < PIN_LOG_WORDS; ++i) { + if ((entry_log[i] & guard_mask) != guard || + (success_log[i] & guard_mask) != guard || + (entry_log[i] & success_log[i]) != entry_log[i]) { + handle_fault("PIN logs format check"); + return secfalse; } - if (current < 0 || current >= PIN_LOG_WORDS || i != PIN_LOG_WORDS) { - handle_fault("PIN log exhausted"); - return secfalse; - } - - // Strip the guard bits from the current entry word and duplicate each data bit. - wait_random(); - uint32_t word = entry_log[current] & ~guard_mask; - word = ((word >> 1) | word ) & LOW_MASK; - word = word | (word << 1); - // Verify that the entry word has form 0*1*. - if ((word & (word + 1)) != 0) { + if (current == -1) { + if (entry_log[i] != guard) { + current = i; + } + } else { + if (entry_log[i] != unused) { handle_fault("PIN entry log format check"); return secfalse; + } } + } - if (current == 0) { - ++current; - } + if (current < 0 || current >= PIN_LOG_WORDS || i != PIN_LOG_WORDS) { + handle_fault("PIN log exhausted"); + return secfalse; + } - // Count the number of set bits in the two current words of the success log. - wait_random(); - *ctr = hamming_weight(success_log[current-1] ^ entry_log[current-1]) + hamming_weight(success_log[current] ^ entry_log[current]); - return sectrue; + // Strip the guard bits from the current entry word and duplicate each data + // bit. + wait_random(); + uint32_t word = entry_log[current] & ~guard_mask; + word = ((word >> 1) | word) & LOW_MASK; + word = word | (word << 1); + // Verify that the entry word has form 0*1*. + if ((word & (word + 1)) != 0) { + handle_fault("PIN entry log format check"); + return secfalse; + } + + if (current == 0) { + ++current; + } + + // Count the number of set bits in the two current words of the success log. + wait_random(); + *ctr = hamming_weight(success_log[current - 1] ^ entry_log[current - 1]) + + hamming_weight(success_log[current] ^ entry_log[current]); + return sectrue; } -secbool storage_is_unlocked(void) -{ - if (sectrue != initialized) { - return secfalse; - } +secbool storage_is_unlocked(void) { + if (sectrue != initialized) { + return secfalse; + } - return unlocked; + return unlocked; } -void storage_lock(void) -{ - unlocked = secfalse; - memzero(cached_keys, sizeof(cached_keys)); - memzero(authentication_sum, sizeof(authentication_sum)); +void storage_lock(void) { + unlocked = secfalse; + memzero(cached_keys, sizeof(cached_keys)); + memzero(authentication_sum, sizeof(authentication_sum)); } -static secbool decrypt_dek(const uint8_t *kek, const uint8_t *keiv) -{ - const void *buffer = NULL; - uint16_t len = 0; - if (sectrue != initialized || sectrue != norcow_get(EDEK_PVC_KEY, &buffer, &len) || len != RANDOM_SALT_SIZE + KEYS_SIZE + PVC_SIZE) { - handle_fault("no EDEK"); - return secfalse; - } +static secbool decrypt_dek(const uint8_t *kek, const uint8_t *keiv) { + const void *buffer = NULL; + uint16_t len = 0; + if (sectrue != initialized || + sectrue != norcow_get(EDEK_PVC_KEY, &buffer, &len) || + len != RANDOM_SALT_SIZE + KEYS_SIZE + PVC_SIZE) { + handle_fault("no EDEK"); + return secfalse; + } - const uint8_t *ekeys = (const uint8_t*) buffer + RANDOM_SALT_SIZE; - const uint32_t *pvc = (const uint32_t*) buffer + (RANDOM_SALT_SIZE + KEYS_SIZE)/sizeof(uint32_t); - _Static_assert(((RANDOM_SALT_SIZE + KEYS_SIZE) & 3) == 0, "PVC unaligned"); - _Static_assert((PVC_SIZE & 3) == 0, "PVC size unaligned"); + const uint8_t *ekeys = (const uint8_t *)buffer + RANDOM_SALT_SIZE; + const uint32_t *pvc = (const uint32_t *)buffer + + (RANDOM_SALT_SIZE + KEYS_SIZE) / sizeof(uint32_t); + _Static_assert(((RANDOM_SALT_SIZE + KEYS_SIZE) & 3) == 0, "PVC unaligned"); + _Static_assert((PVC_SIZE & 3) == 0, "PVC size unaligned"); - uint8_t keys[KEYS_SIZE]; - uint8_t tag[POLY1305_TAG_SIZE] __attribute__((aligned(sizeof(uint32_t)))); - chacha20poly1305_ctx ctx; + uint8_t keys[KEYS_SIZE]; + uint8_t tag[POLY1305_TAG_SIZE] __attribute__((aligned(sizeof(uint32_t)))); + chacha20poly1305_ctx ctx; - // Decrypt the data encryption key and the storage authentication key and check the PIN verification code. - rfc7539_init(&ctx, kek, keiv); - chacha20poly1305_decrypt(&ctx, ekeys, keys, KEYS_SIZE); - rfc7539_finish(&ctx, 0, KEYS_SIZE, tag); - memzero(&ctx, sizeof(ctx)); - wait_random(); - if (secequal32((const uint32_t*) tag, pvc, PVC_SIZE/sizeof(uint32_t)) != sectrue) { - memzero(keys, sizeof(keys)); - memzero(tag, sizeof(tag)); - return secfalse; - } - memcpy(cached_keys, keys, sizeof(keys)); + // Decrypt the data encryption key and the storage authentication key and + // check the PIN verification code. + rfc7539_init(&ctx, kek, keiv); + chacha20poly1305_decrypt(&ctx, ekeys, keys, KEYS_SIZE); + rfc7539_finish(&ctx, 0, KEYS_SIZE, tag); + memzero(&ctx, sizeof(ctx)); + wait_random(); + if (secequal32((const uint32_t *)tag, pvc, PVC_SIZE / sizeof(uint32_t)) != + sectrue) { memzero(keys, sizeof(keys)); memzero(tag, sizeof(tag)); + return secfalse; + } + memcpy(cached_keys, keys, sizeof(keys)); + memzero(keys, sizeof(keys)); + memzero(tag, sizeof(tag)); - // Check that the authenticated version number matches the norcow version. - // NOTE: storage_get_encrypted() calls auth_get(), which initializes the authentication_sum. - uint32_t version; - if (sectrue != storage_get_encrypted(VERSION_KEY, &version, sizeof(version), &len) || len != sizeof(version) || version != norcow_active_version) { - handle_fault("storage version check"); - return secfalse; - } + // Check that the authenticated version number matches the norcow version. + // NOTE: storage_get_encrypted() calls auth_get(), which initializes the + // authentication_sum. + uint32_t version; + if (sectrue != + storage_get_encrypted(VERSION_KEY, &version, sizeof(version), &len) || + len != sizeof(version) || version != norcow_active_version) { + handle_fault("storage version check"); + return secfalse; + } - return sectrue; + return sectrue; } -static secbool unlock(uint32_t pin) -{ - if (sectrue != initialized) { - return secfalse; - } +static secbool unlock(uint32_t pin) { + if (sectrue != initialized) { + return secfalse; + } - // Get the pin failure counter - uint32_t ctr; - if (sectrue != pin_get_fails(&ctr)) { - memzero(&pin, sizeof(pin)); - return secfalse; - } + // Get the pin failure counter + uint32_t ctr; + if (sectrue != pin_get_fails(&ctr)) { + memzero(&pin, sizeof(pin)); + return secfalse; + } + // Wipe storage if too many failures + wait_random(); + if (ctr >= PIN_MAX_TRIES) { + storage_wipe(); + error_shutdown("Too many wrong PIN", "attempts. Storage has", "been wiped.", + NULL); + return secfalse; + } + + // Sleep for 2^ctr - 1 seconds before checking the PIN. + uint32_t wait = (1 << ctr) - 1; + ui_total += wait; + uint32_t progress = 0; + for (ui_rem = ui_total; ui_rem > ui_total - wait; ui_rem--) { + for (int i = 0; i < 10; i++) { + if (ui_callback && ui_message) { + if (ui_total > 1000000) { // precise enough + progress = (ui_total - ui_rem) / (ui_total / 1000); + } else { + progress = ((ui_total - ui_rem) * 10 + i) * 100 / ui_total; + } + if (sectrue == ui_callback(ui_rem, progress, ui_message)) { + return secfalse; + } + } + hal_delay(100); + } + } + + // Read the random salt from EDEK_PVC_KEY and use it to derive the KEK and + // KEIV from the PIN. + const void *salt = NULL; + uint16_t len = 0; + if (sectrue != initialized || + sectrue != norcow_get(EDEK_PVC_KEY, &salt, &len) || + len != RANDOM_SALT_SIZE + KEYS_SIZE + PVC_SIZE) { + memzero(&pin, sizeof(pin)); + handle_fault("no EDEK"); + return secfalse; + } + uint8_t kek[SHA256_DIGEST_LENGTH]; + uint8_t keiv[SHA256_DIGEST_LENGTH]; + derive_kek(pin, (const uint8_t *)salt, kek, keiv); + memzero(&pin, sizeof(pin)); + + // First, we increase PIN fail counter in storage, even before checking the + // PIN. If the PIN is correct, we reset the counter afterwards. If not, we + // check if this is the last allowed attempt. + if (sectrue != storage_pin_fails_increase()) { + return secfalse; + } + + // Check that the PIN fail counter was incremented. + uint32_t ctr_ck; + if (sectrue != pin_get_fails(&ctr_ck) || ctr + 1 != ctr_ck) { + handle_fault("PIN counter increment"); + return secfalse; + } + + // Check that the PIN was correct. + if (sectrue != decrypt_dek(kek, keiv)) { // Wipe storage if too many failures wait_random(); - if (ctr >= PIN_MAX_TRIES) { - storage_wipe(); - error_shutdown("Too many wrong PIN", "attempts. Storage has", "been wiped.", NULL); - return secfalse; + if (ctr + 1 >= PIN_MAX_TRIES) { + storage_wipe(); + error_shutdown("Too many wrong PIN", "attempts. Storage has", + "been wiped.", NULL); } + return secfalse; + } + memzero(kek, sizeof(kek)); + memzero(keiv, sizeof(keiv)); - // Sleep for 2^ctr - 1 seconds before checking the PIN. - uint32_t wait = (1 << ctr) - 1; - ui_total += wait; - uint32_t progress = 0; - for (ui_rem = ui_total; ui_rem > ui_total - wait; ui_rem--) { - for (int i = 0; i < 10; i++) { - if (ui_callback && ui_message) { - if (ui_total > 1000000) { // precise enough - progress = (ui_total - ui_rem) / (ui_total / 1000); - } else { - progress = ((ui_total - ui_rem) * 10 + i) * 100 / ui_total; - } - if (sectrue == ui_callback(ui_rem, progress, ui_message)) { - return secfalse; - } - } - hal_delay(100); - } - } + unlocked = sectrue; - // Read the random salt from EDEK_PVC_KEY and use it to derive the KEK and KEIV from the PIN. - const void *salt = NULL; - uint16_t len = 0; - if (sectrue != initialized || sectrue != norcow_get(EDEK_PVC_KEY, &salt, &len) || len != RANDOM_SALT_SIZE + KEYS_SIZE + PVC_SIZE) { - memzero(&pin, sizeof(pin)); - handle_fault("no EDEK"); - return secfalse; - } - uint8_t kek[SHA256_DIGEST_LENGTH]; - uint8_t keiv[SHA256_DIGEST_LENGTH]; - derive_kek(pin, (const uint8_t*) salt, kek, keiv); - memzero(&pin, sizeof(pin)); - - // First, we increase PIN fail counter in storage, even before checking the - // PIN. If the PIN is correct, we reset the counter afterwards. If not, we - // check if this is the last allowed attempt. - if (sectrue != storage_pin_fails_increase()) { - return secfalse; - } - - // Check that the PIN fail counter was incremented. - uint32_t ctr_ck; - if (sectrue != pin_get_fails(&ctr_ck) || ctr + 1 != ctr_ck) { - handle_fault("PIN counter increment"); - return secfalse; - } - - // Check that the PIN was correct. - if (sectrue != decrypt_dek(kek, keiv)) { - // Wipe storage if too many failures - wait_random(); - if (ctr + 1 >= PIN_MAX_TRIES) { - storage_wipe(); - error_shutdown("Too many wrong PIN", "attempts. Storage has", "been wiped.", NULL); - } - return secfalse; - } - memzero(kek, sizeof(kek)); - memzero(keiv, sizeof(keiv)); - - unlocked = sectrue; - - // Finally set the counter to 0 to indicate success. - return pin_fails_reset(); + // Finally set the counter to 0 to indicate success. + return pin_fails_reset(); } -secbool storage_unlock(uint32_t pin) -{ - ui_total = DERIVE_SECS; - ui_rem = ui_total; - if (pin == PIN_EMPTY) { - if (ui_message == NULL) { - ui_message = STARTING_MSG; - } else { - ui_message = PROCESSING_MSG; - } +secbool storage_unlock(uint32_t pin) { + ui_total = DERIVE_SECS; + ui_rem = ui_total; + if (pin == PIN_EMPTY) { + if (ui_message == NULL) { + ui_message = STARTING_MSG; } else { - ui_message = VERIFYING_PIN_MSG; + ui_message = PROCESSING_MSG; } - return unlock(pin); + } else { + ui_message = VERIFYING_PIN_MSG; + } + return unlock(pin); } /* @@ -877,410 +919,414 @@ secbool storage_unlock(uint32_t pin) * If val_dest is not NULL and max_len >= len, then the data is decrypted * to val_dest using cached_dek as the decryption key. */ -static secbool storage_get_encrypted(const uint16_t key, void *val_dest, const uint16_t max_len, uint16_t *len) -{ - const void *val_stored = NULL; +static secbool storage_get_encrypted(const uint16_t key, void *val_dest, + const uint16_t max_len, uint16_t *len) { + const void *val_stored = NULL; - if (sectrue != auth_get(key, &val_stored, len)) { - return secfalse; - } + if (sectrue != auth_get(key, &val_stored, len)) { + return secfalse; + } - if (*len < CHACHA20_IV_SIZE + POLY1305_TAG_SIZE) { - handle_fault("ciphertext length check"); - return secfalse; - } - *len -= CHACHA20_IV_SIZE + POLY1305_TAG_SIZE; + if (*len < CHACHA20_IV_SIZE + POLY1305_TAG_SIZE) { + handle_fault("ciphertext length check"); + return secfalse; + } + *len -= CHACHA20_IV_SIZE + POLY1305_TAG_SIZE; - if (val_dest == NULL) { - return sectrue; - } - - if (*len > max_len) { - return secfalse; - } - - const uint8_t *iv = (const uint8_t*) val_stored; - const uint8_t *tag_stored = (const uint8_t*) val_stored + CHACHA20_IV_SIZE; - const uint8_t *ciphertext = (const uint8_t*) val_stored + CHACHA20_IV_SIZE + POLY1305_TAG_SIZE; - uint8_t tag_computed[POLY1305_TAG_SIZE]; - chacha20poly1305_ctx ctx; - rfc7539_init(&ctx, cached_dek, iv); - rfc7539_auth(&ctx, (const uint8_t*)&key, sizeof(key)); - chacha20poly1305_decrypt(&ctx, ciphertext, (uint8_t*) val_dest, *len); - rfc7539_finish(&ctx, sizeof(key), *len, tag_computed); - memzero(&ctx, sizeof(ctx)); - - // Verify authentication tag. - if (secequal(tag_computed, tag_stored, POLY1305_TAG_SIZE) != sectrue) { - memzero(val_dest, max_len); - memzero(tag_computed, sizeof(tag_computed)); - handle_fault("authentication tag check"); - return secfalse; - } - - memzero(tag_computed, sizeof(tag_computed)); + if (val_dest == NULL) { return sectrue; + } + + if (*len > max_len) { + return secfalse; + } + + const uint8_t *iv = (const uint8_t *)val_stored; + const uint8_t *tag_stored = (const uint8_t *)val_stored + CHACHA20_IV_SIZE; + const uint8_t *ciphertext = + (const uint8_t *)val_stored + CHACHA20_IV_SIZE + POLY1305_TAG_SIZE; + uint8_t tag_computed[POLY1305_TAG_SIZE]; + chacha20poly1305_ctx ctx; + rfc7539_init(&ctx, cached_dek, iv); + rfc7539_auth(&ctx, (const uint8_t *)&key, sizeof(key)); + chacha20poly1305_decrypt(&ctx, ciphertext, (uint8_t *)val_dest, *len); + rfc7539_finish(&ctx, sizeof(key), *len, tag_computed); + memzero(&ctx, sizeof(ctx)); + + // Verify authentication tag. + if (secequal(tag_computed, tag_stored, POLY1305_TAG_SIZE) != sectrue) { + memzero(val_dest, max_len); + memzero(tag_computed, sizeof(tag_computed)); + handle_fault("authentication tag check"); + return secfalse; + } + + memzero(tag_computed, sizeof(tag_computed)); + return sectrue; } /* * Finds the data stored under key and writes its length to len. If val_dest is * not NULL and max_len >= len, then the data is copied to val_dest. */ -secbool storage_get(const uint16_t key, void *val_dest, const uint16_t max_len, uint16_t *len) -{ - const uint8_t app = key >> 8; - // APP == 0 is reserved for PIN related values - if (sectrue != initialized || app == APP_STORAGE) { - return secfalse; - } +secbool storage_get(const uint16_t key, void *val_dest, const uint16_t max_len, + uint16_t *len) { + const uint8_t app = key >> 8; + // APP == 0 is reserved for PIN related values + if (sectrue != initialized || app == APP_STORAGE) { + return secfalse; + } - // If the top bit of APP is set, then the value is not encrypted and can be read from a locked device. - secbool ret = secfalse; - if ((app & FLAG_PUBLIC) != 0) { - const void *val_stored = NULL; - if (sectrue != norcow_get(key, &val_stored, len)) { - return secfalse; - } - if (val_dest == NULL) { - return sectrue; - } - if (*len > max_len) { - return secfalse; - } - memcpy(val_dest, val_stored, *len); - ret = sectrue; - } else { - if (sectrue != unlocked) { - return secfalse; - } - ret = storage_get_encrypted(key, val_dest, max_len, len); + // If the top bit of APP is set, then the value is not encrypted and can be + // read from a locked device. + secbool ret = secfalse; + if ((app & FLAG_PUBLIC) != 0) { + const void *val_stored = NULL; + if (sectrue != norcow_get(key, &val_stored, len)) { + return secfalse; } + if (val_dest == NULL) { + return sectrue; + } + if (*len > max_len) { + return secfalse; + } + memcpy(val_dest, val_stored, *len); + ret = sectrue; + } else { + if (sectrue != unlocked) { + return secfalse; + } + ret = storage_get_encrypted(key, val_dest, max_len, len); + } - return ret; + return ret; } /* - * Encrypts the data at val using cached_dek as the encryption key and stores the ciphertext under key. + * Encrypts the data at val using cached_dek as the encryption key and stores + * the ciphertext under key. */ -static secbool storage_set_encrypted(const uint16_t key, const void *val, const uint16_t len) -{ - if (len > UINT16_MAX - CHACHA20_IV_SIZE - POLY1305_TAG_SIZE) { - return secfalse; - } +static secbool storage_set_encrypted(const uint16_t key, const void *val, + const uint16_t len) { + if (len > UINT16_MAX - CHACHA20_IV_SIZE - POLY1305_TAG_SIZE) { + return secfalse; + } - // Preallocate space on the flash storage. - if (sectrue != auth_set(key, NULL, CHACHA20_IV_SIZE + POLY1305_TAG_SIZE + len)) { - return secfalse; - } + // Preallocate space on the flash storage. + if (sectrue != + auth_set(key, NULL, CHACHA20_IV_SIZE + POLY1305_TAG_SIZE + len)) { + return secfalse; + } - // Write the IV to the flash. - uint8_t buffer[CHACHA20_BLOCK_SIZE]; - random_buffer(buffer, CHACHA20_IV_SIZE); - uint16_t offset = 0; - if (sectrue != norcow_update_bytes(key, offset, buffer, CHACHA20_IV_SIZE)) { - return secfalse; - } - offset += CHACHA20_IV_SIZE + POLY1305_TAG_SIZE; + // Write the IV to the flash. + uint8_t buffer[CHACHA20_BLOCK_SIZE]; + random_buffer(buffer, CHACHA20_IV_SIZE); + uint16_t offset = 0; + if (sectrue != norcow_update_bytes(key, offset, buffer, CHACHA20_IV_SIZE)) { + return secfalse; + } + offset += CHACHA20_IV_SIZE + POLY1305_TAG_SIZE; - // Encrypt all blocks except for the last one. - chacha20poly1305_ctx ctx; - rfc7539_init(&ctx, cached_dek, buffer); - rfc7539_auth(&ctx, (const uint8_t*)&key, sizeof(key)); - size_t i; - for (i = 0; i + CHACHA20_BLOCK_SIZE < len; i += CHACHA20_BLOCK_SIZE, offset += CHACHA20_BLOCK_SIZE) { - chacha20poly1305_encrypt(&ctx, ((const uint8_t*) val) + i, buffer, CHACHA20_BLOCK_SIZE); - if (sectrue != norcow_update_bytes(key, offset, buffer, CHACHA20_BLOCK_SIZE)) { - memzero(&ctx, sizeof(ctx)); - memzero(buffer, sizeof(buffer)); - return secfalse; - } + // Encrypt all blocks except for the last one. + chacha20poly1305_ctx ctx; + rfc7539_init(&ctx, cached_dek, buffer); + rfc7539_auth(&ctx, (const uint8_t *)&key, sizeof(key)); + size_t i; + for (i = 0; i + CHACHA20_BLOCK_SIZE < len; + i += CHACHA20_BLOCK_SIZE, offset += CHACHA20_BLOCK_SIZE) { + chacha20poly1305_encrypt(&ctx, ((const uint8_t *)val) + i, buffer, + CHACHA20_BLOCK_SIZE); + if (sectrue != + norcow_update_bytes(key, offset, buffer, CHACHA20_BLOCK_SIZE)) { + memzero(&ctx, sizeof(ctx)); + memzero(buffer, sizeof(buffer)); + return secfalse; } + } - // Encrypt final block and compute message authentication tag. - chacha20poly1305_encrypt(&ctx, ((const uint8_t*) val) + i, buffer, len - i); - secbool ret = norcow_update_bytes(key, offset, buffer, len - i); - if (sectrue == ret) { - rfc7539_finish(&ctx, sizeof(key), len, buffer); - ret = norcow_update_bytes(key, CHACHA20_IV_SIZE, buffer, POLY1305_TAG_SIZE); - } - memzero(&ctx, sizeof(ctx)); - memzero(buffer, sizeof(buffer)); - return ret; + // Encrypt final block and compute message authentication tag. + chacha20poly1305_encrypt(&ctx, ((const uint8_t *)val) + i, buffer, len - i); + secbool ret = norcow_update_bytes(key, offset, buffer, len - i); + if (sectrue == ret) { + rfc7539_finish(&ctx, sizeof(key), len, buffer); + ret = norcow_update_bytes(key, CHACHA20_IV_SIZE, buffer, POLY1305_TAG_SIZE); + } + memzero(&ctx, sizeof(ctx)); + memzero(buffer, sizeof(buffer)); + return ret; } -secbool storage_set(const uint16_t key, const void *val, const uint16_t len) -{ - const uint8_t app = key >> 8; +secbool storage_set(const uint16_t key, const void *val, const uint16_t len) { + const uint8_t app = key >> 8; - // APP == 0 is reserved for PIN related values - if (sectrue != initialized || app == APP_STORAGE) { - return secfalse; - } + // APP == 0 is reserved for PIN related values + if (sectrue != initialized || app == APP_STORAGE) { + return secfalse; + } - if (sectrue != unlocked && (app & FLAGS_WRITE) != FLAGS_WRITE) { - return secfalse; - } + if (sectrue != unlocked && (app & FLAGS_WRITE) != FLAGS_WRITE) { + return secfalse; + } - secbool ret = secfalse; - if ((app & FLAG_PUBLIC) != 0) { - ret = norcow_set(key, val, len); - } else { - ret = storage_set_encrypted(key, val, len); - } - return ret; + secbool ret = secfalse; + if ((app & FLAG_PUBLIC) != 0) { + ret = norcow_set(key, val, len); + } else { + ret = storage_set_encrypted(key, val, len); + } + return ret; } -secbool storage_delete(const uint16_t key) -{ - const uint8_t app = key >> 8; +secbool storage_delete(const uint16_t key) { + const uint8_t app = key >> 8; - // APP == 0 is reserved for storage related values - if (sectrue != initialized || app == APP_STORAGE) { - return secfalse; - } + // APP == 0 is reserved for storage related values + if (sectrue != initialized || app == APP_STORAGE) { + return secfalse; + } - if (sectrue != unlocked && (app & FLAGS_WRITE) != FLAGS_WRITE) { - return secfalse; - } + if (sectrue != unlocked && (app & FLAGS_WRITE) != FLAGS_WRITE) { + return secfalse; + } - secbool ret = norcow_delete(key); - if (sectrue == ret) { - ret = auth_update(key); - } - return ret; + secbool ret = norcow_delete(key); + if (sectrue == ret) { + ret = auth_update(key); + } + return ret; } -secbool storage_set_counter(const uint16_t key, const uint32_t count) -{ - const uint8_t app = key >> 8; - if ((app & FLAG_PUBLIC) == 0) { - return secfalse; - } +secbool storage_set_counter(const uint16_t key, const uint32_t count) { + const uint8_t app = key >> 8; + if ((app & FLAG_PUBLIC) == 0) { + return secfalse; + } - // The count is stored as a 32-bit integer followed by a tail of "1" bits, - // which is used as a tally. - uint32_t value[1 + COUNTER_TAIL_WORDS]; - memset(value, 0xff, sizeof(value)); - value[0] = count; - return storage_set(key, value, sizeof(value)); + // The count is stored as a 32-bit integer followed by a tail of "1" bits, + // which is used as a tally. + uint32_t value[1 + COUNTER_TAIL_WORDS]; + memset(value, 0xff, sizeof(value)); + value[0] = count; + return storage_set(key, value, sizeof(value)); } -secbool storage_next_counter(const uint16_t key, uint32_t *count) -{ - const uint8_t app = key >> 8; - // APP == 0 is reserved for PIN related values - if (sectrue != initialized || app == APP_STORAGE || (app & FLAG_PUBLIC) == 0) { - return secfalse; - } +secbool storage_next_counter(const uint16_t key, uint32_t *count) { + const uint8_t app = key >> 8; + // APP == 0 is reserved for PIN related values + if (sectrue != initialized || app == APP_STORAGE || + (app & FLAG_PUBLIC) == 0) { + return secfalse; + } - if (sectrue != unlocked && (app & FLAGS_WRITE) != FLAGS_WRITE) { - return secfalse; - } + if (sectrue != unlocked && (app & FLAGS_WRITE) != FLAGS_WRITE) { + return secfalse; + } - uint16_t len = 0; - const uint32_t *val_stored = NULL; - if (sectrue != norcow_get(key, (const void**)&val_stored, &len)) { - *count = 0; - return storage_set_counter(key, 0); - } + uint16_t len = 0; + const uint32_t *val_stored = NULL; + if (sectrue != norcow_get(key, (const void **)&val_stored, &len)) { + *count = 0; + return storage_set_counter(key, 0); + } - if (len < sizeof(uint32_t) || len % sizeof(uint32_t) != 0) { - return secfalse; - } - uint16_t len_words = len / sizeof(uint32_t); + if (len < sizeof(uint32_t) || len % sizeof(uint32_t) != 0) { + return secfalse; + } + uint16_t len_words = len / sizeof(uint32_t); - uint16_t i = 1; - while (i < len_words && val_stored[i] == 0) { - ++i; - } + uint16_t i = 1; + while (i < len_words && val_stored[i] == 0) { + ++i; + } - *count = val_stored[0] + 1 + 32 * (i - 1); + *count = val_stored[0] + 1 + 32 * (i - 1); - if (i < len_words) { - *count += hamming_weight(~val_stored[i]); - return norcow_update_word(key, sizeof(uint32_t) * i, val_stored[i] >> 1); - } else { - return storage_set_counter(key, *count); - } + if (i < len_words) { + *count += hamming_weight(~val_stored[i]); + return norcow_update_word(key, sizeof(uint32_t) * i, val_stored[i] >> 1); + } else { + return storage_set_counter(key, *count); + } } -secbool storage_has_pin(void) -{ - if (sectrue != initialized) { - return secfalse; - } +secbool storage_has_pin(void) { + if (sectrue != initialized) { + return secfalse; + } - const void *val = NULL; - uint16_t len; - if (sectrue != norcow_get(PIN_NOT_SET_KEY, &val, &len) || (len > 0 && *(uint8_t*)val != FALSE_BYTE)) { - return secfalse; - } - return sectrue; + const void *val = NULL; + uint16_t len; + if (sectrue != norcow_get(PIN_NOT_SET_KEY, &val, &len) || + (len > 0 && *(uint8_t *)val != FALSE_BYTE)) { + return secfalse; + } + return sectrue; } -uint32_t storage_get_pin_rem(void) -{ - if (sectrue != initialized) { - return 0; - } +uint32_t storage_get_pin_rem(void) { + if (sectrue != initialized) { + return 0; + } - uint32_t ctr = 0; - if (sectrue != pin_get_fails(&ctr)) { - return 0; - } - return PIN_MAX_TRIES - ctr; + uint32_t ctr = 0; + if (sectrue != pin_get_fails(&ctr)) { + return 0; + } + return PIN_MAX_TRIES - ctr; } -secbool storage_change_pin(uint32_t oldpin, uint32_t newpin) -{ - if (sectrue != initialized) { - return secfalse; - } +secbool storage_change_pin(uint32_t oldpin, uint32_t newpin) { + if (sectrue != initialized) { + return secfalse; + } - ui_total = 2 * DERIVE_SECS; - ui_rem = ui_total; - ui_message = (oldpin != PIN_EMPTY && newpin == PIN_EMPTY) ? VERIFYING_PIN_MSG : PROCESSING_MSG; + ui_total = 2 * DERIVE_SECS; + ui_rem = ui_total; + ui_message = (oldpin != PIN_EMPTY && newpin == PIN_EMPTY) ? VERIFYING_PIN_MSG + : PROCESSING_MSG; - if (sectrue != unlock(oldpin)) { - return secfalse; - } - secbool ret = set_pin(newpin); - memzero(&oldpin, sizeof(oldpin)); - memzero(&newpin, sizeof(newpin)); - return ret; + if (sectrue != unlock(oldpin)) { + return secfalse; + } + secbool ret = set_pin(newpin); + memzero(&oldpin, sizeof(oldpin)); + memzero(&newpin, sizeof(newpin)); + return ret; } -void storage_wipe(void) -{ - norcow_wipe(); - norcow_active_version = NORCOW_VERSION; - memzero(authentication_sum, sizeof(authentication_sum)); - memzero(cached_keys, sizeof(cached_keys)); - init_wiped_storage(); +void storage_wipe(void) { + norcow_wipe(); + norcow_active_version = NORCOW_VERSION; + memzero(authentication_sum, sizeof(authentication_sum)); + memzero(cached_keys, sizeof(cached_keys)); + init_wiped_storage(); } -static void __handle_fault(const char *msg, const char *file, int line, const char *func) -{ - static secbool in_progress = secfalse; +static void __handle_fault(const char *msg, const char *file, int line, + const char *func) { + static secbool in_progress = secfalse; - // If fault handling is already in progress, then we are probably facing a fault injection attack, so wipe. - if (secfalse != in_progress) { - storage_wipe(); - __fatal_error("Fault detected", msg, file, line, func); - } - - // We use the PIN fail counter as a fault counter. Increment the counter, check that it was incremented and halt. - in_progress = sectrue; - uint32_t ctr; - if (sectrue != pin_get_fails(&ctr)) { - storage_wipe(); - __fatal_error("Fault detected", msg, file, line, func); - } - - if (sectrue != storage_pin_fails_increase()) { - storage_wipe(); - __fatal_error("Fault detected", msg, file, line, func); - } - - uint32_t ctr_new; - if (sectrue != pin_get_fails(&ctr_new) || ctr + 1 != ctr_new) { - storage_wipe(); - } + // If fault handling is already in progress, then we are probably facing a + // fault injection attack, so wipe. + if (secfalse != in_progress) { + storage_wipe(); __fatal_error("Fault detected", msg, file, line, func); + } + + // We use the PIN fail counter as a fault counter. Increment the counter, + // check that it was incremented and halt. + in_progress = sectrue; + uint32_t ctr; + if (sectrue != pin_get_fails(&ctr)) { + storage_wipe(); + __fatal_error("Fault detected", msg, file, line, func); + } + + if (sectrue != storage_pin_fails_increase()) { + storage_wipe(); + __fatal_error("Fault detected", msg, file, line, func); + } + + uint32_t ctr_new; + if (sectrue != pin_get_fails(&ctr_new) || ctr + 1 != ctr_new) { + storage_wipe(); + } + __fatal_error("Fault detected", msg, file, line, func); } /* - * Reads the PIN fail counter in version 0 format. Returns the current number of failed PIN entries. + * Reads the PIN fail counter in version 0 format. Returns the current number of + * failed PIN entries. */ -static secbool v0_pin_get_fails(uint32_t *ctr) -{ - const uint16_t V0_PIN_FAIL_KEY = 0x0001; - // The PIN_FAIL_KEY points to an area of words, initialized to - // 0xffffffff (meaning no PIN failures). The first non-zero word - // in this area is the current PIN failure counter. If PIN_FAIL_KEY - // has no configuration or is empty, the PIN failure counter is 0. - // We rely on the fact that flash allows to clear bits and we clear one - // bit to indicate PIN failure. On success, the word is set to 0, - // indicating that the next word is the PIN failure counter. +static secbool v0_pin_get_fails(uint32_t *ctr) { + const uint16_t V0_PIN_FAIL_KEY = 0x0001; + // The PIN_FAIL_KEY points to an area of words, initialized to + // 0xffffffff (meaning no PIN failures). The first non-zero word + // in this area is the current PIN failure counter. If PIN_FAIL_KEY + // has no configuration or is empty, the PIN failure counter is 0. + // We rely on the fact that flash allows to clear bits and we clear one + // bit to indicate PIN failure. On success, the word is set to 0, + // indicating that the next word is the PIN failure counter. - // Find the current pin failure counter - const void *val = NULL; - uint16_t len = 0; - if (secfalse != norcow_get(V0_PIN_FAIL_KEY, &val, &len)) { - for (unsigned int i = 0; i < len / sizeof(uint32_t); i++) { - uint32_t word = ((const uint32_t*)val)[i]; - if (word != 0) { - *ctr = hamming_weight(~word); - return sectrue; - } - } + // Find the current pin failure counter + const void *val = NULL; + uint16_t len = 0; + if (secfalse != norcow_get(V0_PIN_FAIL_KEY, &val, &len)) { + for (unsigned int i = 0; i < len / sizeof(uint32_t); i++) { + uint32_t word = ((const uint32_t *)val)[i]; + if (word != 0) { + *ctr = hamming_weight(~word); + return sectrue; + } } + } - // No PIN failures - *ctr = 0; - return sectrue; + // No PIN failures + *ctr = 0; + return sectrue; } -static secbool storage_upgrade(void) -{ - const uint16_t V0_PIN_KEY = 0x0000; - const uint16_t V0_PIN_FAIL_KEY = 0x0001; - uint16_t key = 0; - uint16_t len = 0; - const void *val = NULL; +static secbool storage_upgrade(void) { + const uint16_t V0_PIN_KEY = 0x0000; + const uint16_t V0_PIN_FAIL_KEY = 0x0001; + uint16_t key = 0; + uint16_t len = 0; + const void *val = NULL; - if (norcow_active_version == 0) { - random_buffer(cached_keys, sizeof(cached_keys)); + if (norcow_active_version == 0) { + random_buffer(cached_keys, sizeof(cached_keys)); - // Initialize the storage authentication tag. - auth_init(); + // Initialize the storage authentication tag. + auth_init(); - // Set the new storage version number. - uint32_t version = NORCOW_VERSION; - if (sectrue != storage_set_encrypted(VERSION_KEY, &version, sizeof(version))) { - return secfalse; - } + // Set the new storage version number. + uint32_t version = NORCOW_VERSION; + if (sectrue != + storage_set_encrypted(VERSION_KEY, &version, sizeof(version))) { + return secfalse; + } - // Set EDEK_PVC_KEY and PIN_NOT_SET_KEY. - ui_total = DERIVE_SECS; - ui_rem = ui_total; - ui_message = PROCESSING_MSG; - if (sectrue == norcow_get(V0_PIN_KEY, &val, &len)) { - set_pin(*(const uint32_t*)val); - } else { - set_pin(PIN_EMPTY); - } - - // Convert PIN failure counter. - uint32_t fails = 0; - v0_pin_get_fails(&fails); - pin_logs_init(fails); - - // Copy the remaining entries (encrypting the protected ones). - uint32_t offset = 0; - while (sectrue == norcow_get_next(&offset, &key, &val, &len)) { - if (key == V0_PIN_KEY || key == V0_PIN_FAIL_KEY) { - continue; - } - - secbool ret; - if (((key >> 8) & FLAG_PUBLIC) != 0) { - ret = norcow_set(key, val, len); - } else { - ret = storage_set_encrypted(key, val, len); - } - - if (sectrue != ret) { - return secfalse; - } - } - - unlocked = secfalse; - memzero(cached_keys, sizeof(cached_keys)); + // Set EDEK_PVC_KEY and PIN_NOT_SET_KEY. + ui_total = DERIVE_SECS; + ui_rem = ui_total; + ui_message = PROCESSING_MSG; + if (sectrue == norcow_get(V0_PIN_KEY, &val, &len)) { + set_pin(*(const uint32_t *)val); } else { - return secfalse; + set_pin(PIN_EMPTY); } - norcow_active_version = NORCOW_VERSION; - return norcow_upgrade_finish(); + // Convert PIN failure counter. + uint32_t fails = 0; + v0_pin_get_fails(&fails); + pin_logs_init(fails); + + // Copy the remaining entries (encrypting the protected ones). + uint32_t offset = 0; + while (sectrue == norcow_get_next(&offset, &key, &val, &len)) { + if (key == V0_PIN_KEY || key == V0_PIN_FAIL_KEY) { + continue; + } + + secbool ret; + if (((key >> 8) & FLAG_PUBLIC) != 0) { + ret = norcow_set(key, val, len); + } else { + ret = storage_set_encrypted(key, val, len); + } + + if (sectrue != ret) { + return secfalse; + } + } + + unlocked = secfalse; + memzero(cached_keys, sizeof(cached_keys)); + } else { + return secfalse; + } + + norcow_active_version = NORCOW_VERSION; + return norcow_upgrade_finish(); } diff --git a/storage/storage.h b/storage/storage.h index e3a163247e..becfb29907 100644 --- a/storage/storage.h +++ b/storage/storage.h @@ -20,13 +20,15 @@ #ifndef __STORAGE_H__ #define __STORAGE_H__ -#include #include +#include #include "secbool.h" -typedef secbool (*PIN_UI_WAIT_CALLBACK)(uint32_t wait, uint32_t progress, const char* message); +typedef secbool (*PIN_UI_WAIT_CALLBACK)(uint32_t wait, uint32_t progress, + const char *message); -void storage_init(PIN_UI_WAIT_CALLBACK callback, const uint8_t *salt, const uint16_t salt_len); +void storage_init(PIN_UI_WAIT_CALLBACK callback, const uint8_t *salt, + const uint16_t salt_len); void storage_wipe(void); secbool storage_is_unlocked(void); void storage_lock(void); @@ -35,7 +37,8 @@ secbool storage_has_pin(void); secbool storage_pin_fails_increase(void); uint32_t storage_get_pin_rem(void); secbool storage_change_pin(const uint32_t oldpin, const uint32_t newpin); -secbool storage_get(const uint16_t key, void *val, const uint16_t max_len, uint16_t *len); +secbool storage_get(const uint16_t key, void *val, const uint16_t max_len, + uint16_t *len); secbool storage_set(const uint16_t key, const void *val, uint16_t len); secbool storage_delete(const uint16_t key); secbool storage_set_counter(const uint16_t key, const uint32_t count); diff --git a/core/tools/clang-format-check b/tools/clang-format-check similarity index 100% rename from core/tools/clang-format-check rename to tools/clang-format-check diff --git a/core/help.awk b/tools/help.awk similarity index 100% rename from core/help.awk rename to tools/help.awk diff --git a/tools/style.c.exclude b/tools/style.c.exclude new file mode 100644 index 0000000000..e67f656a24 --- /dev/null +++ b/tools/style.c.exclude @@ -0,0 +1,15 @@ +^\./core/embed/bootloader/protob/ +^\./crypto/aes/ +^\./crypto/chacha20poly1305/ +^\./crypto/ed25519-donna/ +^\./crypto/gui/ +^\./crypto/monero/base58 +^\./crypto/monero/int-util +^\./crypto/blake2 +^\./crypto/check_mem +^\./crypto/groestl +^\./crypto/ripemd160 +^\./crypto/segwit_addr +^\./crypto/sha2 +^\./crypto/sha3 +^\./legacy/vendor diff --git a/tools/style.c.include b/tools/style.c.include new file mode 100644 index 0000000000..276419c797 --- /dev/null +++ b/tools/style.c.include @@ -0,0 +1,5 @@ +^\./common/ +^\./core/embed/ +^\./crypto/ +^\./legacy/ +^\./storage/ diff --git a/tools/style.py.exclude b/tools/style.py.exclude new file mode 100644 index 0000000000..fa02c76299 --- /dev/null +++ b/tools/style.py.exclude @@ -0,0 +1,2 @@ +^\./legacy/firmware/protob/messages_pb2\.py +^\./legacy/vendor diff --git a/tools/style.py.include b/tools/style.py.include new file mode 100644 index 0000000000..0e2706842e --- /dev/null +++ b/tools/style.py.include @@ -0,0 +1,4 @@ +^\./common/ +^\./core/src/ +^\./crypto/ +^\./legacy/ From 19ddcdc17d17627ebb9da55ff0c6c227399d183d Mon Sep 17 00:00:00 2001 From: motty Date: Fri, 19 Apr 2019 15:35:10 +0900 Subject: [PATCH 16/78] mod fujicoin tx fees --- common/defs/bitcoin/fujicoin.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/common/defs/bitcoin/fujicoin.json b/common/defs/bitcoin/fujicoin.json index 35fe18dc41..cfe8c572a6 100644 --- a/common/defs/bitcoin/fujicoin.json +++ b/common/defs/bitcoin/fujicoin.json @@ -8,8 +8,8 @@ "curve_name": "secp256k1", "address_type": 36, "address_type_p2sh": 16, - "maxfee_kb": 10000000, - "minfee_kb": 100000, + "maxfee_kb": 1000000000, + "minfee_kb": 10000000, "signed_message_header": "FujiCoin Signed Message:\n", "hash_genesis_block": "adb6d9cfd74075e7f91608add4bd2a2ea636f70856183086842667a1597714a0", "xpub_magic": 76067358, @@ -25,10 +25,10 @@ "force_bip143": false, "bip115": false, "default_fee_b": { - "Low": 100, - "Economy": 200, - "Normal": 500, - "High": 1000 + "Low": 10000, + "Economy": 20000, + "Normal": 50000, + "High": 100000 }, "dust_limit": 546, "blocktime_seconds": 60, From 021a8a0cb86fa44c673797661876749e67c9ebb3 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 19 Apr 2019 16:07:23 +0200 Subject: [PATCH 17/78] xmr: live refresh progress starts from 0 Migrated from https://github.com/trezor/trezor-core/pull/552 --- core/src/apps/monero/live_refresh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/apps/monero/live_refresh.py b/core/src/apps/monero/live_refresh.py index 8315be970a..adcbf29fd4 100644 --- a/core/src/apps/monero/live_refresh.py +++ b/core/src/apps/monero/live_refresh.py @@ -37,7 +37,7 @@ async def live_refresh(ctx, msg: MoneroLiveRefreshStartRequest, keychain): class LiveRefreshState: def __init__(self): - self.current_output = -1 + self.current_output = 0 self.creds = None From 1995169255c64409f777744e3de88efaa540631f Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 19 Apr 2019 16:36:39 +0200 Subject: [PATCH 18/78] crypto: style --- crypto/tests/test_wycheproof.py | 1 + 1 file changed, 1 insertion(+) diff --git a/crypto/tests/test_wycheproof.py b/crypto/tests/test_wycheproof.py index c449b29c8a..96edf3558c 100755 --- a/crypto/tests/test_wycheproof.py +++ b/crypto/tests/test_wycheproof.py @@ -5,6 +5,7 @@ import os from binascii import hexlify, unhexlify import pytest + from pyasn1.codec.ber.decoder import decode as ber_decode from pyasn1.codec.der.decoder import decode as der_decode from pyasn1.codec.der.encoder import encode as der_encode From de6be423a1dc85a125affe85915e465f7238e35c Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 19 Apr 2019 16:37:56 +0200 Subject: [PATCH 19/78] all: add Pipfile to root Although we have to a) discuss if we want this b) consolidate with other Pipfiles if so. --- core/Pipfile => Pipfile | 0 core/Pipfile.lock => Pipfile.lock | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) rename core/Pipfile => Pipfile (100%) rename core/Pipfile.lock => Pipfile.lock (99%) diff --git a/core/Pipfile b/Pipfile similarity index 100% rename from core/Pipfile rename to Pipfile diff --git a/core/Pipfile.lock b/Pipfile.lock similarity index 99% rename from core/Pipfile.lock rename to Pipfile.lock index 529769e90c..fff4d0e178 100644 --- a/core/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "f038c016ebf0f0551ad1a2d066dd5861661925767b56bcf3c82b6764156b3b60" + "sha256": "f3c2800ccd4e8ec7e07444b9bc81ce5c5f426ce15dcd6d55729ff81e7214525d" }, "pipfile-spec": 6, "requires": {}, @@ -453,6 +453,7 @@ "sha256:c53417ee0bbe77db852d5fd1036749f03696ebc2265de359fe17418d800196c4", "sha256:fbc9fcde75713930bc2a91b149e97be2401f7c9c56d735b46a109210f58d7358" ], + "index": "pypi", "version": "==1.1.2" }, "pycodestyle": { From 4c8ef80cc75958c894accda41920ffcd0af2213a Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 19 Apr 2019 17:21:00 +0200 Subject: [PATCH 20/78] core: build templates --- core/src/apps/common/coininfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/apps/common/coininfo.py b/core/src/apps/common/coininfo.py index 484a9e2e45..aafca7a645 100644 --- a/core/src/apps/common/coininfo.py +++ b/core/src/apps/common/coininfo.py @@ -549,7 +549,7 @@ COINS = [ coin_shortcut="FJC", address_type=36, address_type_p2sh=16, - maxfee_kb=10000000, + maxfee_kb=1000000000, signed_message_header="FujiCoin Signed Message:\n", xpub_magic=0x0488b21e, xpub_magic_segwit_p2sh=0x049d7cb2, From b439b536a178f9a89721ced3b20253c5526f6f64 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Sat, 20 Apr 2019 12:49:21 +0200 Subject: [PATCH 21/78] legacy: bump pipenv dependencies --- legacy/Pipfile.lock | 150 ++++++++++++++++++++++++++++---------------- 1 file changed, 95 insertions(+), 55 deletions(-) diff --git a/legacy/Pipfile.lock b/legacy/Pipfile.lock index cd379ed76a..6b77f6b424 100644 --- a/legacy/Pipfile.lock +++ b/legacy/Pipfile.lock @@ -16,24 +16,24 @@ "default": { "atomicwrites": { "hashes": [ - "sha256:240831ea22da9ab882b551b31d4225591e5e447a68c5e188db5b89ca1d487585", - "sha256:a24da68318b08ac9c9c45029f4a10371ab5b20e4226738e150e6e7c571630ae6" + "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", + "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" ], - "version": "==1.1.5" + "version": "==1.3.0" }, "attrs": { "hashes": [ - "sha256:4b90b09eeeb9b88c35bc642cbac057e45a5fd85367b985bd2809c62b7b939265", - "sha256:e0d0eb91441a3b53dab4d9b743eafc1ac44476296a2053b6ca3af0b139faf87b" + "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", + "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" ], - "version": "==18.1.0" + "version": "==19.1.0" }, "certifi": { "hashes": [ - "sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7", - "sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0" + "sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5", + "sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae" ], - "version": "==2018.4.16" + "version": "==2019.3.9" }, "chardet": { "hashes": [ @@ -44,43 +44,76 @@ }, "click": { "hashes": [ - "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", - "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" + "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", + "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" ], - "version": "==6.7" + "version": "==7.0" + }, + "construct": { + "hashes": [ + "sha256:2271a0efd0798679dea825ff47e22a4c550456a5db0ba8baa82f7eae0af0118c" + ], + "version": "==2.9.45" }, "ecdsa": { "hashes": [ - "sha256:40d002cf360d0e035cf2cb985e1308d41aaa087cbfc135b2dc2d844296ea546c", - "sha256:64cf1ee26d1cde3c73c6d7d107f835fed7c6a2904aef9eac223d57ad800c43fa" + "sha256:20c17e527e75acad8f402290e158a6ac178b91b881f941fc6ea305bfdfb9657c", + "sha256:5c034ffa23413ac923541ceb3ac14ec15a0d2530690413bff58c12b80e56d884" ], - "version": "==0.13" + "version": "==0.13.2" }, "idna": { "hashes": [ - "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", - "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" + "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", + "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" ], - "version": "==2.7" + "version": "==2.8" }, "libusb1": { "hashes": [ - "sha256:4707f81e933a97fed1c5bf7d4957f07bae1139cb8084bdee1f50201a40e3fd7c" + "sha256:9d4f66d2ed699986b06bc3082cd262101cb26af7a76a34bd15b7eb56cba37e0f" ], - "version": "==1.6.5" + "version": "==1.7" }, "mako": { "hashes": [ - "sha256:4e02fde57bd4abb5ec400181e4c314f56ac3e49ba4fb8b0d50bba18cb27d25ae" + "sha256:0728c404877cd4ca72c409c0ea372dc5f3b53fa1ad2bb434e1d216c0444ff1fd" ], "index": "pypi", - "version": "==1.0.7" + "version": "==1.0.9" }, "markupsafe": { "hashes": [ - "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665" + "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", + "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", + "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", + "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", + "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", + "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", + "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", + "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", + "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", + "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", + "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", + "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", + "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", + "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", + "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7" ], - "version": "==1.0" + "version": "==1.1.1" }, "mnemonic": { "hashes": [ @@ -98,11 +131,11 @@ }, "more-itertools": { "hashes": [ - "sha256:2b6b9893337bfd9166bee6a62c2b0c9fe7735dcf85948b387ec8cba30e85d8e8", - "sha256:6703844a52d3588f951883005efcf555e49566a48afd4db4e965d69b883980d3", - "sha256:a18d870ef2ffca2b8463c0070ad17b5978056f403fb64e3f15fe62a52db21cc0" + "sha256:2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7", + "sha256:c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a" ], - "version": "==4.2.0" + "markers": "python_version > '2.7'", + "version": "==7.0.0" }, "munch": { "hashes": [ @@ -119,18 +152,17 @@ }, "pbr": { "hashes": [ - "sha256:1b8be50d938c9bb75d0eaf7eda111eec1bf6dc88a62a6412e33bf077457e0f45", - "sha256:b486975c0cafb6beeb50ca0e17ba047647f229087bd74e37f4a7e2cac17d2caa" + "sha256:8257baf496c8522437e8a6cfe0f15e00aedc6c0e0e7c9d55eeeeab31e0853843", + "sha256:8c361cc353d988e4f5b998555c88098b9d5964c2e11acf7b0d21925a66bb5824" ], - "version": "==4.2.0" + "version": "==5.1.3" }, "pluggy": { "hashes": [ - "sha256:6e3836e39f4d36ae72840833db137f7b7d35105079aee6ec4a62d9f80d594dd1", - "sha256:95eb8364a4708392bae89035f45341871286a333f749c3141c20573d2b3876e1" + "sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f", + "sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746" ], - "markers": "python_version != '3.2.*' and python_version != '3.1.*' and python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.3.*'", - "version": "==0.7.1" + "version": "==0.9.0" }, "protobuf": { "hashes": [ @@ -147,10 +179,10 @@ }, "py": { "hashes": [ - "sha256:3fd59af7435864e1a243790d322d763925431213b6b8529c6ca71081ace3bbf7", - "sha256:e31fb2767eb657cbde86c454f02e99cb846d3cd9d61b318525140214fdc0e98e" + "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", + "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" ], - "version": "==1.5.4" + "version": "==1.8.0" }, "pyblake2": { "hashes": [ @@ -168,46 +200,54 @@ }, "pytest": { "hashes": [ - "sha256:341ec10361b64a24accaec3c7ba5f7d5ee1ca4cebea30f76fad3dd12db9f0541", - "sha256:952c0389db115437f966c4c2079ae9d54714b9455190e56acebe14e8c38a7efa" + "sha256:3773f4c235918987d51daf1db66d51c99fac654c81d6f2f709a046ab446d5e5d", + "sha256:b7802283b70ca24d7119b32915efa7c409982f59913c1a6c0640aacf118b95f5" ], "index": "pypi", - "version": "==3.6.4" + "version": "==4.4.1" }, "requests": { "hashes": [ - "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1", - "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a" + "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", + "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" ], - "version": "==2.19.1" + "version": "==2.21.0" }, "six": { "hashes": [ - "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", - "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" ], - "version": "==1.11.0" + "version": "==1.12.0" }, "trezor": { "editable": true, "git": "https://github.com/trezor/python-trezor", - "ref": "master" + "ref": "2813522b05cef4e0e545a101f8b3559a3183b45b" }, "typing": { "hashes": [ - "sha256:3a887b021a77b292e151afb75323dea88a7bc1b3dfa92176cff8e44c8b68bddf", - "sha256:b2c689d54e1144bbcfd191b0832980a21c2dbcf7b5ff7a66248a60c90e951eb8", - "sha256:d400a9344254803a2368533e4533a4200d21eb7b6b729c173bc38201a74db3f2" + "sha256:4027c5f6127a6267a435201981ba156de91ad0d1d98e9ddc2aa173453453492d", + "sha256:57dcf675a99b74d64dacf6fba08fb17cf7e3d5fdff53d4a30ea2a5e7e52543d4", + "sha256:a4c8473ce11a65999c8f59cb093e70686b6c84c98df58c1dae9b3b196089858a" ], "index": "pypi", - "version": "==3.6.4" + "version": "==3.6.6" + }, + "typing-extensions": { + "hashes": [ + "sha256:07b2c978670896022a43c4b915df8958bec4a6b84add7f2c87b2b728bda3ba64", + "sha256:f3f0e67e1d42de47b5c67c32c9b26641642e9170fe7e292991793705cd5fef7c", + "sha256:fb2cd053238d33a8ec939190f30cfd736c00653a85a2919415cecf7dc3d9da71" + ], + "version": "==3.7.2" }, "urllib3": { "hashes": [ - "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", - "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" + "sha256:4c291ca23bbb55c76518905869ef34bdd5f0e46af7afe6861e8375643ffee1a0", + "sha256:9a247273df709c4fedb38c711e44292304f73f39ab01beda9f6b9fc375669ac3" ], - "version": "==1.23" + "version": "==1.24.2" } }, "develop": {} From 8114e0f9fc7af5d41ac73d69209b4065d3638327 Mon Sep 17 00:00:00 2001 From: armatusmiles Date: Sat, 20 Apr 2019 17:01:04 +0300 Subject: [PATCH 22/78] crypto: add support for schnorr signatures --- crypto/Makefile | 1 + crypto/schnorr.c | 104 ++++++++++++++++++++ crypto/schnorr.h | 42 ++++++++ crypto/tests/test_check.c | 195 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 342 insertions(+) create mode 100644 crypto/schnorr.c create mode 100644 crypto/schnorr.h diff --git a/crypto/Makefile b/crypto/Makefile index 6d14fec616..8dd944be43 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -63,6 +63,7 @@ SRCS += rc4.c SRCS += nem.c SRCS += segwit_addr.c cash_addr.c SRCS += memzero.c +SRCS += schnorr.c OBJS = $(SRCS:.c=.o) diff --git a/crypto/schnorr.c b/crypto/schnorr.c new file mode 100644 index 0000000000..9be43eef2c --- /dev/null +++ b/crypto/schnorr.c @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2019 Anatolii Kurotych + * + * 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. + */ + +#include "schnorr.h" +#include "memzero.h" + +// r = H(Q, kpub, m) +static void calc_r(const curve_point *Q, const uint8_t pub_key[33], + const uint8_t *msg, const uint32_t msg_len, bignum256 *r) { + uint8_t Q_compress[33]; + compress_coords(Q, Q_compress); + + SHA256_CTX ctx; + uint8_t digest[SHA256_DIGEST_LENGTH]; + sha256_Init(&ctx); + sha256_Update(&ctx, Q_compress, 33); + sha256_Update(&ctx, pub_key, 33); + sha256_Update(&ctx, msg, msg_len); + sha256_Final(&ctx, digest); + bn_read_be(digest, r); +} + +// returns 0 if signing succeeded +int schnorr_sign(const ecdsa_curve *curve, const uint8_t *priv_key, + const bignum256 *k, const uint8_t *msg, const uint32_t msg_len, + schnorr_sign_pair *result) { + bignum256 private_key_scalar; + bn_read_be(priv_key, &private_key_scalar); + uint8_t pub_key[33]; + ecdsa_get_public_key33(curve, priv_key, pub_key); + + /* Q = kG */ + curve_point Q; + scalar_multiply(curve, k, &Q); + + /* r = H(Q, kpub, m) */ + calc_r(&Q, pub_key, msg, msg_len, &result->r); + + /* s = k - r*kpriv mod(order) */ + bignum256 s_temp; + bn_copy(&result->r, &s_temp); + bn_multiply(&private_key_scalar, &s_temp, &curve->order); + bn_subtractmod(k, &s_temp, &result->s, &curve->order); + memzero(&private_key_scalar, sizeof(private_key_scalar)); + + while (bn_is_less(&curve->order, &result->s)) { + bn_mod(&result->s, &curve->order); + } + + if (bn_is_zero(&result->s) || bn_is_zero(&result->r)) { + return 1; + } + + return 0; +} + +// returns 0 if verification succeeded +int schnorr_verify(const ecdsa_curve *curve, const uint8_t *pub_key, + const uint8_t *msg, const uint32_t msg_len, + const schnorr_sign_pair *sign) { + if (msg_len == 0) return 1; + if (bn_is_zero(&sign->r)) return 2; + if (bn_is_zero(&sign->s)) return 3; + if (bn_is_less(&curve->order, &sign->r)) return 4; + if (bn_is_less(&curve->order, &sign->s)) return 5; + + curve_point pub_key_point; + if (!ecdsa_read_pubkey(curve, pub_key, &pub_key_point)) { + return 6; + } + + // Compute Q = sG + r*kpub + curve_point sG, Q; + scalar_multiply(curve, &sign->s, &sG); + point_multiply(curve, &sign->r, &pub_key_point, &Q); + point_add(curve, &sG, &Q); + + /* r = H(Q, kpub, m) */ + bignum256 r; + calc_r(&Q, pub_key, msg, msg_len, &r); + + if (bn_is_equal(&r, &sign->r)) return 0; // success + + return 10; +} diff --git a/crypto/schnorr.h b/crypto/schnorr.h new file mode 100644 index 0000000000..57bfc46d76 --- /dev/null +++ b/crypto/schnorr.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2019 Anatolii Kurotych + * + * 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. + */ + +#ifndef __SCHNORR_H__ +#define __SCHNORR_H__ + +#include "ecdsa.h" + +// result of sign operation +typedef struct { + bignum256 r, s; +} schnorr_sign_pair; + +// sign/verify returns 0 if operation succeeded + +// k is a random from [1, ..., order-1] +int schnorr_sign(const ecdsa_curve *curve, const uint8_t *priv_key, + const bignum256 *k, const uint8_t *msg, const uint32_t msg_len, + schnorr_sign_pair *result); +int schnorr_verify(const ecdsa_curve *curve, const uint8_t *pub_key, + const uint8_t *msg, const uint32_t msg_len, + const schnorr_sign_pair *sign); +#endif diff --git a/crypto/tests/test_check.c b/crypto/tests/test_check.c index fc2de3033a..310ce808dc 100644 --- a/crypto/tests/test_check.c +++ b/crypto/tests/test_check.c @@ -61,6 +61,7 @@ #include "rand.h" #include "rc4.h" #include "rfc6979.h" +#include "schnorr.h" #include "script.h" #include "secp256k1.h" #include "sha2.h" @@ -8347,6 +8348,195 @@ START_TEST(test_compress_coords) { } END_TEST +// vectors generated using Schnorr::Sign function defined in +// https://github.com/Zilliqa/Zilliqa/blob/master/src/libCrypto/Schnorr.cpp#L88 +START_TEST(test_schnorr_sign_verify) { + static struct { + const char *message; + const char *priv_key; + const char *k_hex; + const char *s_hex; + const char *r_hex; + } test_cases[] = { + { + "123", + "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", + "669301F724C555D7BB1185C04909E9CACA3EC7A292B3A1C92DDCCD5A5A7DDDD3", + "FFD72C290B98C93A4BCEDC0EDCDF040C35579BE962FE83E6821D4F3CB4B795D2", + "74AAE9C3E069E2806E1B0D890970BE387AEBED8040F37991AACAD70B27895E39", + }, + { + "1234", + "51a2758eed776c40b367364909c8a9c98cc969104f69ff316f7a287495c37c9b", + "A0A1A9B3570AAE963535B8D4376C58A61646C18182C9FDDA5FB13703F88D4D1E", + "99A0CB942C81571B77C682F79CD3CB663CE9E1C55BB425BA24B9F11A0DE84FE2", + "C3C10363E38158BBA20556A36DE9358DFD81A31C180ABC9E7617C1CC1CAF03B3", + }, + { + "12345", + "2685adffdbb4b2c515054cffc25cfcbfe2e462df65bbe82fb50f71e1e68dd285", + "38DE7B3315F201433D271E91FBE62966576CA05CBFEC1770B77D7EC9D6A01D6D", + "28982FA6C2B620CBC550F7EF9EAB605F409C584FBE5A765678877B79AB517086", + "9A0788E5B0947DEDEDE386DF57A006CF3FE43919A74D9CA630F8A1A9D97B4650", + }, + { + "fun", + "7457dc574d927e5dae84b05264a5b637b5a68e34a85b3965084ed6fed5b7f12d", + "E005ABD242C7C602AB5EED080C5083C7C5F8DAEC6D046A54F384A8B8CDECF740", + "51070ABCA039DAC294F6BA3BFC8C36CFC66020EDF66D1ACF1A9B545B0BF09F52", + "330A924525EF722FA20E8E25CB6E8BD7DF4394886FA4414E4A0B6812AA25BBC0", + }, + { + "funny", + "52c395a6d304de1a959e73e4604e32c5ad3f2bf01c8f730af426b38d7d5dd908", + "0CF28B5C40A8830F3195BB99A9F0E2808F576105F41D16ABCF596AC5A8CFE88A", + "3D60FB4664C994AD956378B9402BC68F7B4799D74F4783A6199C0D74865EA2B6", + "5ED5EDEE0314DFFBEE39EE4E9C76DE8BC3EB8CB891AEC32B83957514284B205B", + }, + { + "What is great in man is that he is a bridge and not a goal", + "52c395a6d304de1a959e73e4604e32c5ad3f2bf01c8f730af426b38d7d5dd908", + "000000000000000000000000000000000000000000000000000000000000007B", + "546F70AA1FEE3718C95508240CDC073B9FEFED05959C5319DD8E2BF07A1DD028", + "B8667BE5E10B113608BFE5327C44E9F0462BE26F789177E10DCE53019AA33DAA", + }, + { + "123456789147258369qwertyuiopasdfghjklzxcvbnm,", + "2685adffdbb4b2c515054cffc25cfcbfe2e462df65bbe82fb50f71e1e68dd285", + "1D0CB70310C4D793A4561FE592B7C156771E3E26283B28AB588E968243B52DD0", + "54D7A435E5E3F2811AA542F8895C20CCB760F2713DBDDB7291DAB6DA4E4F927E", + "20A3BDABFFF2C1BF8E2AF709F6CDCAFE70DA9A1DBC22305B6332E36844092984", + }, + { + "11111111111111111111111111111111111111111111111111111111111111111" + "11111111111111111111111111111111111111111111111111111111111111111" + "111111111111111111", + "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", + "A669F372B3C2EEA351210082CAEC3B96767A7B222D19FF2EE3D814860F0D703A", + "4890F9AC3A8D102EE3A2A473930C01CAD29DCE3860ACB7A5DADAEF16FE808991", + "979F088E58F1814D5E462CB9F935D2924ABD8D32211D8F02DD7E0991726DF573", + }, + { + "qwertyuiop[]asdfghjkl;'zxcvbnm,./1234567890-=", + "7457dc574d927e5dae84b05264a5b637b5a68e34a85b3965084ed6fed5b7f12d", + "000000000000000000000000000000000000000000000000000000000000007C", + "0AA595A649E517133D3448CA657424DD07BBED289030F0C0AA6738D26AB9A910", + "83812632F1443A70B198D112D075D886BE7BBC6EC6275AE52661E52B7358BB8B", + }, + }; + + const ecdsa_curve *curve = &secp256k1; + bignum256 k; + uint8_t priv_key[32], buf_raw[32]; + schnorr_sign_pair result; + for (size_t i = 0; i < sizeof(test_cases) / sizeof(*test_cases); i++) { + memcpy(priv_key, fromhex(test_cases[i].priv_key), 32); + memcpy(&buf_raw, fromhex(test_cases[i].k_hex), 32); + bn_read_be(buf_raw, &k); + schnorr_sign(curve, priv_key, &k, (const uint8_t *)test_cases[i].message, + strlen(test_cases[i].message), &result); + + schnorr_sign_pair expected; + + memcpy(&buf_raw, fromhex(test_cases[i].s_hex), 32); + bn_read_be(buf_raw, &expected.s); + memcpy(&buf_raw, fromhex(test_cases[i].r_hex), 32); + bn_read_be(buf_raw, &expected.r); + + ck_assert_mem_eq(&expected.r, &result.r, 32); + ck_assert_mem_eq(&expected.s, &result.s, 32); + + uint8_t pub_key[33]; + ecdsa_get_public_key33(curve, priv_key, pub_key); + int res = + schnorr_verify(curve, pub_key, (const uint8_t *)test_cases[i].message, + strlen(test_cases[i].message), &result); + ck_assert_int_eq(res, 0); + } +} +END_TEST + +// vectors generated using Schnorr::Sign function defined in +// https://github.com/Zilliqa/Zilliqa/blob/master/src/libCrypto/Schnorr.cpp#L88 +START_TEST(test_schnorr_fail_verify) { + static struct { + const char *message; + const char *priv_key; + const char *k_hex; + const char *s_hex; + const char *r_hex; + } test_case = { + "123", + "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", + "669301F724C555D7BB1185C04909E9CACA3EC7A292B3A1C92DDCCD5A5A7DDDD3", + "FFD72C290B98C93A4BCEDC0EDCDF040C35579BE962FE83E6821D4F3CB4B795D2", + "74AAE9C3E069E2806E1B0D890970BE387AEBED8040F37991AACAD70B27895E39", + }; + + const ecdsa_curve *curve = &secp256k1; + bignum256 k; + uint8_t priv_key[32], buf_raw[32]; + memcpy(priv_key, fromhex(test_case.priv_key), 32); + memcpy(&buf_raw, fromhex(test_case.k_hex), 32); + bn_read_be(buf_raw, &k); + + schnorr_sign_pair result; + schnorr_sign(curve, priv_key, &k, (const uint8_t *)test_case.message, + strlen(test_case.message), &result); + uint8_t pub_key[33]; + ecdsa_get_public_key33(curve, priv_key, pub_key); + + // OK + int res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, + strlen(test_case.message), &result); + ck_assert_int_eq(res, 0); + + res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, 0, + &result); + ck_assert_int_eq(res, 1); + + schnorr_sign_pair bad_result; + + bn_copy(&result.s, &bad_result.s); + bn_zero(&bad_result.r); + // r == 0 + res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, + strlen(test_case.message), &bad_result); + ck_assert_int_eq(res, 2); + + bn_copy(&result.r, &bad_result.r); + bn_zero(&bad_result.s); + // s == 0 + res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, + strlen(test_case.message), &bad_result); + ck_assert_int_eq(res, 3); + + bn_copy(&result.s, &bad_result.s); + bn_copy(&curve->order, &bad_result.r); + bn_addi(&bad_result.r, 1); + // r == curve->order + 1 + res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, + strlen(test_case.message), &bad_result); + ck_assert_int_eq(res, 4); + + bn_copy(&result.r, &bad_result.r); + bn_copy(&curve->order, &bad_result.s); + bn_addi(&bad_result.s, 1); + // s == curve->order + 1 + res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, + strlen(test_case.message), &bad_result); + ck_assert_int_eq(res, 5); + + bn_copy(&result.r, &bad_result.r); + bn_copy(&result.s, &bad_result.s); + // change message + test_case.message = "12"; + res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, + strlen(test_case.message), &bad_result); + ck_assert_int_eq(res, 10); +} +END_TEST + static int my_strncasecmp(const char *s1, const char *s2, size_t n) { size_t i = 0; while (i < n) { @@ -8624,6 +8814,11 @@ Suite *test_suite(void) { tcase_add_test(tc, test_compress_coords); suite_add_tcase(s, tc); + tc = tcase_create("schnorr"); + tcase_add_test(tc, test_schnorr_sign_verify); + tcase_add_test(tc, test_schnorr_fail_verify); + suite_add_tcase(s, tc); + #if USE_CARDANO tc = tcase_create("bip32-cardano"); From af89ae88dc8937c9e94c899dc67ee483bd7ae01d Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Mon, 22 Apr 2019 18:57:14 +0200 Subject: [PATCH 23/78] core+legacy: update changelogs --- core/ChangeLog | 32 +++++++-------- legacy/bootloader/ChangeLog | 30 ++++++++------ legacy/firmware/ChangeLog | 82 ++++++++++++++----------------------- 3 files changed, 64 insertions(+), 80 deletions(-) diff --git a/core/ChangeLog b/core/ChangeLog index 1f0eb7c99a..dde615edc2 100644 --- a/core/ChangeLog +++ b/core/ChangeLog @@ -1,5 +1,11 @@ -Version 2.1.0 -* stable release, optional update +Version 2.1.1 [unreleased] +* More strict path validations +* Display non-zero locktime values +* Don't rotate the screen via swipe gesture +* Set screen rotation via user setting +* Monero UI fixes + +Version 2.1.0 [Mar 2019] * Security improvements * Upgraded to new storage format * Ripple, Stellar, Cardano and NEM fixes @@ -7,20 +13,17 @@ Version 2.1.0 PIVX, REOSC, XPM, XSN, ZCL * New ETH tokens -Version 2.0.10 -* stable release, optional update +Version 2.0.10 [Dec 2018] * Fix Monero payment ID computation * Fix issue with touch screen and flickering * Add support for OMNI layer: OMNI/MAID/USDT * Add support for new coins: BTX, CPC, GAME, RVN * Add support for new Ethereum tokens -Version 2.0.9 -* stable release, optional update +Version 2.0.9 [Nov 2018] * Small Monero and Segwit bugfixes -Version 2.0.8 -* stable release, optional update +Version 2.0.8 [Oct 2018] * Monero support * Cardano support * Stellar support @@ -32,25 +35,22 @@ Version 2.0.8 * Zcash sapling hardfork support * Implemented seedless setup -Version 2.0.7 -* stable release, optional update +Version 2.0.7 [Jun 2018] * Bitcoin Cash cashaddr support * Zcash Overwinter hardfork support * NEM support * Lisk support * Show warning on home screen if PIN is not set * Support for new coins: - - Bitcoin Private, Fujicoin, Vertcoin, Viacoin, Zcoin + Bitcoin Private, Fujicoin, Vertcoin, Viacoin, Zcoin * Support for new Ethereum networks: - - EOS Classic, Ethereum Social, Ellaism, Callisto, EtherGem, Wanchain + EOS Classic, Ethereum Social, Ellaism, Callisto, EtherGem, Wanchain * Support for 500+ new Ethereum tokens -Version 2.0.6 -* stable release, optional update +Version 2.0.6 [Mar 2018] * fix layout for Ethereum transactions * fix public key generation for SSH and GPG * add special characters to passphrase keyboard -Version 2.0.5 -* stable release, required update +Version 2.0.5 [Mar 2018] * first public release diff --git a/legacy/bootloader/ChangeLog b/legacy/bootloader/ChangeLog index a348378c01..2c27f6ab70 100644 --- a/legacy/bootloader/ChangeLog +++ b/legacy/bootloader/ChangeLog @@ -1,27 +1,30 @@ -Version 1.6.1 +Version 1.8.0 [Feb 2019] +* Make the update process more similar to Model T process + +Version 1.6.1 [Dec 2018] * Fix USB issue on some Windows 10 installations -Version 1.6.0 +Version 1.6.0 [Sep 2018] * Switch from HID to WebUSB -Version 1.5.1 +Version 1.5.1 [Aug 2018] * Improve MPU configuration -Version 1.5.0 +Version 1.5.0 [Jun 2018] * Make unofficial firmwares work again -Version 1.4.0 +Version 1.4.0 [Mar 2018] * More flash-write tests * Don't restore storage from unofficial firmware * Support WipeDevice message * Activate MPU and don't switch VTOR table for unofficial firmware -Version 1.3.3 +Version 1.3.3 [Aug 2017] * Add self-test * Erase metadata backup after usage * Erase SRAM on application start -Version 1.3.2 +Version 1.3.2 [Jul 2017] * Don't show recovery seed warning if firmware is flashed for the first time * Don't show fingerprint if firmware is flashed for the first time * Compute firmware hash before checking signatures @@ -29,22 +32,25 @@ Version 1.3.2 * Fix usage of RNG before setup * Fix stack protector fault -Version 1.3.1 +Version 1.3.1 [Feb 2017] * Fix button testing so it does not break USB communication -Version 1.3.0 +Version 1.3.0 [Oct 2016] * Add test for buttons * Clean USB descriptor * Return firmware_present in Features response * Don't halt on broken firware, stay in bootloader -Version 1.2.7 +Version 1.2.8 [Sep 2016] +* Don't halt on broken firmware + +Version 1.2.7 [May 2016] * Optimize speed of firmware update -Version 1.2.6 +Version 1.2.6 [Feb 2016] * Show hash of unofficial firmware * Use stack protector * Clean USB descriptor -Version 1.2.5 +Version 1.2.5 [Oct 2014] * Initial import of code diff --git a/legacy/firmware/ChangeLog b/legacy/firmware/ChangeLog index be72340c89..6d7ed6be24 100644 --- a/legacy/firmware/ChangeLog +++ b/legacy/firmware/ChangeLog @@ -1,43 +1,40 @@ -Version 1.8.0 -* Stable release, optional update +Version 1.8.1 [unreleased] +* Fix fault when using the device with no PIN +* Fix OMNI transactions parsing + +Version 1.8.0 [Feb 2019] * Security improvements * Upgraded to new storage format * Stellar and NEM fixes * New coins: ATS, KMD, XPM, XSN, ZCL * New ETH tokens -Version 1.7.3 -* Stable release, optional update +Version 1.7.3 [Dec 2018] * Fix USB issue on some Windows 10 installations -Version 1.7.2 -* Stable release, optional update +Version 1.7.2 [Dec 2018] * Add support for OMNI layer: OMNI/MAID/USDT * U2F fixes * Don't ask for PIN if it has been just set -Version 1.7.1 -* Stable release, optional update +Version 1.7.1 [Oct 2018] * Add support for Lisk * Add support for Zcash Sapling hardfork * Implement seedless setup -Version 1.7.0 -* Stable release, optional update +Version 1.7.0 [Sep 2018] * Switch from HID to WebUSB * Add support for Stellar * Included bootloader 1.6.0 -Version 1.6.3 -* Stable release, required update +Version 1.6.3 [Aug 2018] * Implement RSKIP-60 Ethereum checksum encoding * Add support for new Ethereum networks (ESN, AKA, ETHO, MUSI, PIRL, ATH, GO) * Add support for new 80 Ethereum tokens * Improve MPU configuration * Included bootloader 1.5.1 -Version 1.6.2 -* Stable release, optional update +Version 1.6.2 [Jun 2018] * Add possibility to set custom auto-lock delay * Bitcoin Cash cashaddr support * Zcash Overwinter hardfork support @@ -48,15 +45,13 @@ Version 1.6.2 * Support for 500+ new Ethereum tokens * Included bootloader 1.5.0 -Version 1.6.1 -* Stable release, required update +Version 1.6.1 [Mar 2018] * Use fixed-width font for addresses * Lots of under-the-hood improvements * Fixed issue with write-protection settings * Included bootloader 1.4.0 -Version 1.6.0 -* Stable release, optional update +Version 1.6.0 [Nov 2017] * Native SegWit (Bech32) address support * Show recognized BIP44/BIP49 paths in GetAddress dialog * NEM support @@ -64,13 +59,11 @@ Version 1.6.0 * Bitcoin Gold, DigiByte, Monacoin support * Ed25519 collective signatures (CoSi) support -Version 1.5.2 -* Stable release, required update +Version 1.5.2 [Aug 2017] * Clean memory on start * Fix storage import from older versions -Version 1.5.1 -* Stable release, optional update +Version 1.5.1 [Jul 2017] * Wipe storage after 16 wrong PIN attempts * Enable Segwit for Bitcoin * Bcash aka Bitcoin Cash support @@ -81,33 +74,28 @@ Version 1.5.1 * Allow "dry run" recovery procedure * Allow separated backup procedure -Version 1.5.0 -* Stable release, optional update +Version 1.5.0 [May 2017] * Enable Segwit for Testnet and Litecoin * Enable ERC-20 tokens for Ethereum chains -Version 1.4.2 -* Stable release, optional update +Version 1.4.2 [Jan 2017] * New Matrix-based recovery method * Minor Ethereum fixes (including EIP-155 replay protection) * Minor USB, U2F and GPG fixes -Version 1.4.1 -* Stable release, optional update +Version 1.4.1 [Oct 2016] * Support for Zcash JoinSplit transactions * Enable device lock after 10 minutes of inactivity * Enable device lock by pressing left button for 2 seconds * Confirm dialog for U2F counter change -Version 1.4.0 -* Stable release, optional update +Version 1.4.0 [Aug 2016] * U2F support * Ethereum support * GPG decryption support * Zcash support -Version 1.3.6 -* Stable release, optional update +Version 1.3.6 [Jun 2016] * Enable advanced transactions such as ones with REPLACE-BY-FEE and CHECKLOCKTIMEVERIFY * Fix message signing for altcoins * Message verification now shows address @@ -116,63 +104,53 @@ Version 1.3.6 * Use separate deterministic hierarchy for NIST256P1 and Ed25519 curves * Users using SSH already need to regenerate their keys using the new firmware!!! -Version 1.3.5 -* Stable release, optional update +Version 1.3.5 [Feb 2016] * Double size font for recovery words during the device setup * Optimizations for simultaneous access when more applications try communicate with the device -Version 1.3.4 -* Stable release, optional update +Version 1.3.4 [Aug 2015] * Screensaver active on ClearSession message * Support for NIST P-256 curve * Updated SignIdentity to v2 format * Show seconds counter during PIN lockdown * Updated maxfee per kb for coins -Version 1.3.3 -* Stable release, mandatory update +Version 1.3.3 [Apr 2015] * Ask for PIN on GetAddress and GetPublicKey * Signing speed improved -Version 1.3.2 -* Stable release, optional update +Version 1.3.2 [Mar 2015] * Fix check during transaction streaming * Login feature via SignIdentity message * GetAddress for multisig shows M of N description * PIN checking in constant time -Version 1.3.1 -* Stable release, optional update +Version 1.3.1 [Feb 2015] * Optimized signing speed * Enabled OP_RETURN * Added option to change home screen * Moved fee calculation before any signing * Made PIN delay increase immune against hardware hacking -Version 1.3.0 -* Stable release, optional update +Version 1.3.0 [Dec 2014] * Added multisig support * Added visual validation of receiving address * Added ECIES encryption capabilities -Version 1.2.1 -* Stable release, mandatory update +Version 1.2.1 [Jul 2014] * Added stack overflow protection * Added compatibility with TREZOR Bridge -Version 1.2.0 -* Stable release, optional update +Version 1.2.0 [Jul 2014] * Fix false positives for fee warning * Better UI for signing/verifying messages * Smaller firmware size -Version 1.1.0 -* Stable release, optional update +Version 1.1.0 [Jun 2014] * Minor UI fixes * Better handling of unexpected messages * Added AES support -Version 1.0.0 -* Stable release, mandatory update +Version 1.0.0 [Apr 2014] * Added support for streaming of transactions into the device * Removed all current limits on size of signed transaction From a3aae9c23425b11a3f1d2850bf4bac59a7b32e7e Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Mon, 22 Apr 2019 00:44:55 +0200 Subject: [PATCH 24/78] xmr.ui: improves UX of the monero app Based on the testing feedback I've made slight modifications to the UI. Main points: - running on the full brightness during the process (previously dimmed, felt broken) - overlaying layout fix after transaction confirmation --- core/src/apps/monero/layout/confirms.py | 53 +++++++++++-------- core/src/apps/monero/signing/state.py | 3 ++ .../signing/step_01_init_transaction.py | 9 ++-- .../apps/monero/signing/step_02_set_input.py | 4 +- .../signing/step_03_inputs_permutation.py | 2 +- .../apps/monero/signing/step_04_input_vini.py | 2 +- .../monero/signing/step_05_all_inputs_set.py | 2 +- .../apps/monero/signing/step_06_set_output.py | 5 +- .../monero/signing/step_07_all_outputs_set.py | 2 +- .../apps/monero/signing/step_09_sign_input.py | 2 +- 10 files changed, 48 insertions(+), 36 deletions(-) diff --git a/core/src/apps/monero/layout/confirms.py b/core/src/apps/monero/layout/confirms.py index b2abb37b9c..ac47f04f55 100644 --- a/core/src/apps/monero/layout/confirms.py +++ b/core/src/apps/monero/layout/confirms.py @@ -42,7 +42,7 @@ async def require_confirm_tx_key(ctx, export_key=False): return await require_confirm(ctx, content, ButtonRequestType.SignTx) -async def require_confirm_transaction(ctx, tsx_data, network_type): +async def require_confirm_transaction(ctx, state, tsx_data, network_type): """ Ask for confirmation from user. """ @@ -70,10 +70,7 @@ async def require_confirm_transaction(ctx, tsx_data, network_type): await _require_confirm_payment_id(ctx, tsx_data.payment_id) await _require_confirm_fee(ctx, tsx_data.fee) - - text = Text("Signing transaction", ui.ICON_SEND, icon_color=ui.BLUE) - text.normal("Signing...") - text.render() + await transaction_step(state, 0) async def _require_confirm_output(ctx, dst, network_type, payment_id): @@ -119,46 +116,60 @@ async def _require_confirm_fee(ctx, fee): await require_hold_to_confirm(ctx, content, ButtonRequestType.ConfirmOutput) -@ui.layout -async def transaction_step(ctx, step, sub_step=None, sub_step_total=None): +@ui.layout_no_slide +async def transaction_step(state, step, sub_step=None): info = [] - if step == 100: - info = ["Processing inputs", "%d/%d" % (sub_step + 1, sub_step_total)] + if step == 0: + info = ["Signing..."] + elif step == 100: + info = ["Processing inputs", "%d/%d" % (sub_step + 1, state.input_count)] elif step == 200: - info = ["Sorting"] + info = ["Sorting..."] elif step == 300: - info = [ - "Processing inputs", - "phase 2", - "%d/%d" % (sub_step + 1, sub_step_total), - ] + info = ["Hashing inputs", "%d/%d" % (sub_step + 1, state.input_count)] + elif step == 350: + info = ["Processing..."] elif step == 400: - info = ["Processing outputs", "%d/%d" % (sub_step + 1, sub_step_total)] + info = ["Processing outputs", "%d/%d" % (sub_step + 1, state.output_count)] elif step == 500: info = ["Postprocessing..."] elif step == 600: - info = ["Signing inputs", "%d/%d" % (sub_step + 1, sub_step_total)] + info = ["Signing inputs", "%d/%d" % (sub_step + 1, state.input_count)] else: info = ["Processing..."] + state.progress_cur += 1 + + ui.display.clear() text = Text("Signing transaction", ui.ICON_SEND, icon_color=ui.BLUE) - text.normal(*info) text.render() + p = int(1000.0 * state.progress_cur / state.progress_total) + ui.display.loader(p, -4, ui.WHITE, ui.BG) + ui.display.text_center(ui.WIDTH // 2, 210, info[0], ui.NORMAL, ui.FG, ui.BG) + if len(info) > 1: + ui.display.text_center(ui.WIDTH // 2, 235, info[1], ui.NORMAL, ui.FG, ui.BG) + ui.display.refresh() -@ui.layout + +@ui.layout_no_slide async def keyimage_sync_step(ctx, current, total_num): if current is None: return + ui.display.clear() text = Text("Syncing", ui.ICON_SEND, icon_color=ui.BLUE) - text.normal("%d/%d" % (current + 1, total_num)) text.render() + p = (int(1000.0 * (current + 1) / total_num)) if total_num > 0 else 0 + ui.display.loader(p, 18, ui.WHITE, ui.BG) + ui.display.refresh() -@ui.layout + +@ui.layout_no_slide async def live_refresh_step(ctx, current): if current is None: return + ui.display.clear() text = Text("Refreshing", ui.ICON_SEND, icon_color=ui.BLUE) text.normal("%d" % current) text.render() diff --git a/core/src/apps/monero/signing/state.py b/core/src/apps/monero/signing/state.py index d608d226a6..22c240d871 100644 --- a/core/src/apps/monero/signing/state.py +++ b/core/src/apps/monero/signing/state.py @@ -68,6 +68,9 @@ class State: self.input_count = 0 self.output_count = 0 + self.progress_total = 0 + self.progress_cur = 0 + self.output_change = None self.fee = 0 diff --git a/core/src/apps/monero/signing/step_01_init_transaction.py b/core/src/apps/monero/signing/step_01_init_transaction.py index 9778e53c27..989d16f188 100644 --- a/core/src/apps/monero/signing/step_01_init_transaction.py +++ b/core/src/apps/monero/signing/step_01_init_transaction.py @@ -36,16 +36,19 @@ async def init_transaction( state.mem_trace(1) + state.input_count = tsx_data.num_inputs + state.output_count = len(tsx_data.outputs) + state.progress_total = 4 + 3 * state.input_count + state.output_count + state.progress_cur = 0 + # Ask for confirmation await confirms.require_confirm_transaction( - state.ctx, tsx_data, state.creds.network_type + state.ctx, state, tsx_data, state.creds.network_type ) gc.collect() state.mem_trace(3) # Basic transaction parameters - state.input_count = tsx_data.num_inputs - state.output_count = len(tsx_data.outputs) state.output_change = tsx_data.change_dts state.mixin = tsx_data.mixin state.fee = tsx_data.fee diff --git a/core/src/apps/monero/signing/step_02_set_input.py b/core/src/apps/monero/signing/step_02_set_input.py index af62823264..ce408e83c1 100644 --- a/core/src/apps/monero/signing/step_02_set_input.py +++ b/core/src/apps/monero/signing/step_02_set_input.py @@ -32,9 +32,7 @@ async def set_input(state: State, src_entr: MoneroTransactionSourceEntry): state.current_input_index += 1 - await confirms.transaction_step( - state.ctx, state.STEP_INP, state.current_input_index, state.input_count - ) + await confirms.transaction_step(state, state.STEP_INP, state.current_input_index) if state.current_input_index >= state.input_count: raise ValueError("Too many inputs") diff --git a/core/src/apps/monero/signing/step_03_inputs_permutation.py b/core/src/apps/monero/signing/step_03_inputs_permutation.py index cbd19c36b0..14f538fb7e 100644 --- a/core/src/apps/monero/signing/step_03_inputs_permutation.py +++ b/core/src/apps/monero/signing/step_03_inputs_permutation.py @@ -21,7 +21,7 @@ async def tsx_inputs_permutation(state: State, permutation: list): MoneroTransactionInputsPermutationAck, ) - await transaction_step(state.ctx, state.STEP_PERM) + await transaction_step(state, state.STEP_PERM) """ Set permutation on the inputs - sorted by key image on host. diff --git a/core/src/apps/monero/signing/step_04_input_vini.py b/core/src/apps/monero/signing/step_04_input_vini.py index 874bc09e73..2c88f7965c 100644 --- a/core/src/apps/monero/signing/step_04_input_vini.py +++ b/core/src/apps/monero/signing/step_04_input_vini.py @@ -33,7 +33,7 @@ async def input_vini( ) await confirms.transaction_step( - state.ctx, state.STEP_VINI, state.current_input_index + 1, state.input_count + state, state.STEP_VINI, state.current_input_index + 1 ) if state.current_input_index >= state.input_count: raise ValueError("Too many inputs") diff --git a/core/src/apps/monero/signing/step_05_all_inputs_set.py b/core/src/apps/monero/signing/step_05_all_inputs_set.py index 86b55271c0..dac0d28ad7 100644 --- a/core/src/apps/monero/signing/step_05_all_inputs_set.py +++ b/core/src/apps/monero/signing/step_05_all_inputs_set.py @@ -12,7 +12,7 @@ from apps.monero.xmr import crypto async def all_inputs_set(state: State): state.mem_trace(0) - await confirms.transaction_step(state.ctx, state.STEP_ALL_IN) + await confirms.transaction_step(state, state.STEP_ALL_IN) from trezor.messages.MoneroTransactionAllInputsSetAck import ( MoneroTransactionAllInputsSetAck, diff --git a/core/src/apps/monero/signing/step_06_set_output.py b/core/src/apps/monero/signing/step_06_set_output.py index d0ecc55cf1..6f0e30f167 100644 --- a/core/src/apps/monero/signing/step_06_set_output.py +++ b/core/src/apps/monero/signing/step_06_set_output.py @@ -23,10 +23,7 @@ async def set_output( # Progress update only for master message (skip for offloaded BP msg) if not is_offloaded_bp: await confirms.transaction_step( - state.ctx, - state.STEP_OUT, - state.current_output_index + 1, - state.output_count, + state, state.STEP_OUT, state.current_output_index + 1 ) state.mem_trace(1, True) diff --git a/core/src/apps/monero/signing/step_07_all_outputs_set.py b/core/src/apps/monero/signing/step_07_all_outputs_set.py index e5708a7d9c..e836aaae01 100644 --- a/core/src/apps/monero/signing/step_07_all_outputs_set.py +++ b/core/src/apps/monero/signing/step_07_all_outputs_set.py @@ -18,7 +18,7 @@ from apps.monero.xmr import crypto async def all_outputs_set(state: State): state.mem_trace(0) - await confirms.transaction_step(state.ctx, state.STEP_ALL_OUT) + await confirms.transaction_step(state, state.STEP_ALL_OUT) state.mem_trace(1) _validate(state) diff --git a/core/src/apps/monero/signing/step_09_sign_input.py b/core/src/apps/monero/signing/step_09_sign_input.py index 0a7639e4e0..8b71e146cc 100644 --- a/core/src/apps/monero/signing/step_09_sign_input.py +++ b/core/src/apps/monero/signing/step_09_sign_input.py @@ -48,7 +48,7 @@ async def sign_input( :return: Generated signature MGs[i] """ await confirms.transaction_step( - state.ctx, state.STEP_SIGN, state.current_input_index + 1, state.input_count + state, state.STEP_SIGN, state.current_input_index + 1 ) state.current_input_index += 1 From 9de96f61141f29a022aa5b26a5a8e196f785abae Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Tue, 23 Apr 2019 02:06:37 +0200 Subject: [PATCH 25/78] ui.display.loader: slice_span added for indeterminate loaders --- core/embed/bootloader/bootui.c | 14 +++++----- core/embed/extmod/modtrezorui/display.c | 4 +-- core/embed/extmod/modtrezorui/display.h | 2 +- .../extmod/modtrezorui/modtrezorui-display.h | 27 ++++++++++++------- core/mocks/generated/trezorui.py | 4 ++- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/core/embed/bootloader/bootui.c b/core/embed/bootloader/bootui.c index e5e84b6a6c..8609d93fae 100644 --- a/core/embed/bootloader/bootui.c +++ b/core/embed/bootloader/bootui.c @@ -237,7 +237,7 @@ void ui_screen_install_confirm_newvendor(const vendor_header *const vhdr, void ui_screen_install(void) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); display_loader(0, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_install, - sizeof(toi_icon_install), COLOR_BLACK); + sizeof(toi_icon_install), COLOR_BLACK, 0); display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24, "Installing firmware", -1, FONT_NORMAL, COLOR_BLACK, COLOR_WHITE); @@ -245,12 +245,12 @@ void ui_screen_install(void) { void ui_screen_install_progress_erase(int pos, int len) { display_loader(250 * pos / len, -20, COLOR_BL_PROCESS, COLOR_WHITE, - toi_icon_install, sizeof(toi_icon_install), COLOR_BLACK); + toi_icon_install, sizeof(toi_icon_install), COLOR_BLACK, 0); } void ui_screen_install_progress_upload(int pos) { display_loader(pos, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_install, - sizeof(toi_icon_install), COLOR_BLACK); + sizeof(toi_icon_install), COLOR_BLACK, 0); } // wipe UI @@ -275,14 +275,14 @@ void ui_screen_wipe_confirm(void) { void ui_screen_wipe(void) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); display_loader(0, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_wipe, - sizeof(toi_icon_wipe), COLOR_BLACK); + sizeof(toi_icon_wipe), COLOR_BLACK, 0); display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24, "Wiping device", -1, FONT_NORMAL, COLOR_BLACK, COLOR_WHITE); } void ui_screen_wipe_progress(int pos, int len) { display_loader(1000 * pos / len, -20, COLOR_BL_PROCESS, COLOR_WHITE, - toi_icon_wipe, sizeof(toi_icon_wipe), COLOR_BLACK); + toi_icon_wipe, sizeof(toi_icon_wipe), COLOR_BLACK, 0); } // done UI @@ -301,7 +301,7 @@ void ui_screen_done(int restart_seconds, secbool full_redraw) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); } display_loader(1000, -20, COLOR_BL_DONE, COLOR_WHITE, toi_icon_done, - sizeof(toi_icon_done), COLOR_BLACK); + sizeof(toi_icon_done), COLOR_BLACK, 0); if (secfalse == full_redraw) { display_bar(0, DISPLAY_RESY - 24 - 18, 240, 23, COLOR_WHITE); } @@ -314,7 +314,7 @@ void ui_screen_done(int restart_seconds, secbool full_redraw) { void ui_screen_fail(void) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); display_loader(1000, -20, COLOR_BL_FAIL, COLOR_WHITE, toi_icon_fail, - sizeof(toi_icon_fail), COLOR_BLACK); + sizeof(toi_icon_fail), COLOR_BLACK, 0); display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24, "Failed! Please, reconnect.", -1, FONT_NORMAL, COLOR_BLACK, COLOR_WHITE); diff --git a/core/embed/extmod/modtrezorui/display.c b/core/embed/extmod/modtrezorui/display.c index eba7ecfd28..f7601dd8c6 100644 --- a/core/embed/extmod/modtrezorui/display.c +++ b/core/embed/extmod/modtrezorui/display.c @@ -318,7 +318,7 @@ static void inflate_callback_loader(uint8_t byte, uint32_t pos, void display_loader(uint16_t progress, int yoffset, uint16_t fgcolor, uint16_t bgcolor, const uint8_t *icon, uint32_t iconlen, - uint16_t iconfgcolor) { + uint16_t iconfgcolor, uint16_t slice_span) { #if TREZOR_MODEL == T uint16_t colortable[16], iconcolortable[16]; set_color_table(colortable, fgcolor, bgcolor); @@ -378,7 +378,7 @@ void display_loader(uint16_t progress, int yoffset, uint16_t fgcolor, PIXELDATA(iconcolortable[c]); } else { uint8_t c; - if (progress > a) { + if (progress > a && (slice_span == 0 || progress < (a + slice_span))) { c = (img_loader[my][mx] & 0x00F0) >> 4; } else { c = img_loader[my][mx] & 0x000F; diff --git a/core/embed/extmod/modtrezorui/display.h b/core/embed/extmod/modtrezorui/display.h index 578025d49f..141aba1070 100644 --- a/core/embed/extmod/modtrezorui/display.h +++ b/core/embed/extmod/modtrezorui/display.h @@ -84,7 +84,7 @@ void display_icon(int x, int y, int w, int h, const void *data, int datalen, uint16_t fgcolor, uint16_t bgcolor); void display_loader(uint16_t progress, int yoffset, uint16_t fgcolor, uint16_t bgcolor, const uint8_t *icon, uint32_t iconlen, - uint16_t iconfgcolor); + uint16_t iconfgcolor, uint16_t slice_span); #ifndef TREZOR_PRINT_DISABLE void display_print_color(uint16_t fgcolor, uint16_t bgcolor); diff --git a/core/embed/extmod/modtrezorui/modtrezorui-display.h b/core/embed/extmod/modtrezorui/modtrezorui-display.h index 862cd11991..90c78e6c12 100644 --- a/core/embed/extmod/modtrezorui/modtrezorui-display.h +++ b/core/embed/extmod/modtrezorui/modtrezorui-display.h @@ -198,7 +198,7 @@ STATIC mp_obj_t mod_trezorui_Display_icon(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_icon_obj, 6, 6, mod_trezorui_Display_icon); /// def loader(self, progress: int, yoffset: int, fgcolor: int, bgcolor: int, -/// icon: bytes = None, iconfgcolor: int = None) -> None: +/// icon: bytes = None, iconfgcolor: int = None, slice_span: int = None) -> None: /// ''' /// Renders a rotating loader graphic. /// Progress determines its position (0-1000), fgcolor is used as foreground @@ -206,6 +206,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_icon_obj, 6, 6, /// icon is drawn in the middle using the color specified in iconfgcolor. /// Icon needs to be of exactly LOADER_ICON_SIZE x LOADER_ICON_SIZE pixels /// size. +/// If slice_span is defined the progress is sliced to emulate indeterminate +/// loader. /// ''' STATIC mp_obj_t mod_trezorui_Display_loader(size_t n_args, const mp_obj_t *args) { @@ -213,8 +215,11 @@ STATIC mp_obj_t mod_trezorui_Display_loader(size_t n_args, mp_int_t yoffset = mp_obj_get_int(args[2]); mp_int_t fgcolor = mp_obj_get_int(args[3]); mp_int_t bgcolor = mp_obj_get_int(args[4]); - if (n_args > 5) { // icon provided - mp_buffer_info_t icon; + mp_buffer_info_t icon = {.buf = NULL, .len=0}; + uint16_t iconfgcolor = 0; + uint16_t slice_span = 0; + + if (n_args > 5 && args[5] != mp_const_none) { // icon provided mp_get_buffer_raise(args[5], &icon, MP_BUFFER_READ); const uint8_t *data = icon.buf; if (icon.len < 8 || memcmp(data, "TOIg", 4) != 0) { @@ -229,21 +234,25 @@ STATIC mp_obj_t mod_trezorui_Display_loader(size_t n_args, if (datalen != icon.len - 12) { mp_raise_ValueError("Invalid size of data"); } - uint16_t iconfgcolor; + if (n_args > 6) { // icon color provided iconfgcolor = mp_obj_get_int(args[6]); } else { iconfgcolor = ~bgcolor; // invert } - display_loader(progress, yoffset, fgcolor, bgcolor, icon.buf, icon.len, - iconfgcolor); - } else { - display_loader(progress, yoffset, fgcolor, bgcolor, NULL, 0, 0); } + + if (n_args > 7 && args[7] != mp_const_none) { // slice span provided + slice_span = mp_obj_get_int(args[7]); + } + + display_loader(progress, yoffset, fgcolor, bgcolor, icon.buf, icon.len, + iconfgcolor, slice_span); + return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_loader_obj, 5, - 7, mod_trezorui_Display_loader); + 8, mod_trezorui_Display_loader); /// def print(self, text: str) -> None: /// ''' diff --git a/core/mocks/generated/trezorui.py b/core/mocks/generated/trezorui.py index d42a3083c8..178902fede 100644 --- a/core/mocks/generated/trezorui.py +++ b/core/mocks/generated/trezorui.py @@ -51,12 +51,14 @@ class Display: The icon needs to be in TREZOR Optimized Image Format (TOIF) - gray-scale mode. ''' - def loader(self, progress: int, yoffset: int, fgcolor: int, bgcolor: int, icon: bytes = None, iconfgcolor: int = None) -> None: + def loader(self, progress: int, yoffset: int, fgcolor: int, bgcolor: int, icon: bytes = None, iconfgcolor: int = None, slice_span: int = None) -> None: ''' Renders a rotating loader graphic. Progress determines its position (0-1000), fgcolor is used as foreground color, bgcolor as background. When icon and iconfgcolor are provided, an icon is drawn in the middle using the color specified in iconfgcolor. Icon needs to be of exactly LOADER_ICON_SIZE x LOADER_ICON_SIZE pixels size. + If slice_span is defined the progress is sliced to emulate indeterminate + loader. ''' def print(self, text: str) -> None: From 7ea53c089e33f1b26b286098b8e14463a312d97c Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Tue, 23 Apr 2019 02:07:06 +0200 Subject: [PATCH 26/78] xmr: indeterminate progress for live refresh --- core/src/apps/monero/layout/confirms.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/apps/monero/layout/confirms.py b/core/src/apps/monero/layout/confirms.py index ac47f04f55..b02819e1df 100644 --- a/core/src/apps/monero/layout/confirms.py +++ b/core/src/apps/monero/layout/confirms.py @@ -171,5 +171,13 @@ async def live_refresh_step(ctx, current): return ui.display.clear() text = Text("Refreshing", ui.ICON_SEND, icon_color=ui.BLUE) - text.normal("%d" % current) text.render() + + step = 6 + p = int(1000.0 * (current / step)) % 1000 + if p == 0 and current > 0: + p = 1000 + + ui.display.loader(p, 18, ui.WHITE, ui.BG, None, 0, 1000 // step) + ui.display.text_center(ui.WIDTH // 2, 145, "%d" % current, ui.NORMAL, ui.FG, ui.BG) + ui.display.refresh() From b6c989226e657f125bae38691fe2aaec2f6bae89 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 23 Apr 2019 14:23:34 +0200 Subject: [PATCH 27/78] common: remove FairCoin because of an invalid picture size --- common/defs/bitcoin/faircoin.json | 39 ------------------------------ common/defs/bitcoin/faircoin.png | Bin 6023 -> 0 bytes 2 files changed, 39 deletions(-) delete mode 100644 common/defs/bitcoin/faircoin.json delete mode 100644 common/defs/bitcoin/faircoin.png diff --git a/common/defs/bitcoin/faircoin.json b/common/defs/bitcoin/faircoin.json deleted file mode 100644 index fe9a9b873f..0000000000 --- a/common/defs/bitcoin/faircoin.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "coin_name": "FairCoin", - "coin_shortcut": "FAIR", - "coin_label": "FairCoin", - "website": "https://www.faircoin.world", - "github": "https://github.com/faircoin/faircoin", - "maintainer": "Santi Noreña ", - "curve_name": "secp256k1", - "address_type": 95, - "address_type_p2sh": 36, - "maxfee_kb": 10000000, - "minfee_kb": 1000, - "signed_message_header": "FairCoin Signed Message:\n", - "hash_genesis_block": "beed44fa5e96150d95d56ebd5d2625781825a9407a5215dd7eda723373a0a1d7", - "xprv_magic": 76066276, - "xpub_magic": 76067358, - "xpub_magic_segwit_p2sh": null, - "xpub_magic_segwit_native": null, - "bech32_prefix": null, - "cashaddr_prefix": null, - "slip44": 298, - "segwit": false, - "decred": false, - "fork_id": 0, - "force_bip143": false, - "bip115": false, - "default_fee_b": { - "Normal": 800000 - }, - "dust_limit": 500000, - "blocktime_seconds": 180, - "uri_prefix": "faircoin", - "min_address_length": 27, - "max_address_length": 34, - "bitcore": [], - "blockbook": [], - "cooldown": 100, - "consensus_branch_id": null -} diff --git a/common/defs/bitcoin/faircoin.png b/common/defs/bitcoin/faircoin.png deleted file mode 100644 index f4ec63ea8da82572d61eea60ce135acaa0e11cef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6023 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 B(>VYD From 456d98ac0379a7b1f945d926eded01db30c62c8e Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 23 Apr 2019 14:41:59 +0200 Subject: [PATCH 28/78] all: style --- core/src/apps/tezos/sign_tx.py | 7 +------ crypto/tests/test_curves.py | 3 +-- crypto/tests/test_wycheproof.py | 1 - 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/core/src/apps/tezos/sign_tx.py b/core/src/apps/tezos/sign_tx.py index 04ef8d5470..a15f96853b 100644 --- a/core/src/apps/tezos/sign_tx.py +++ b/core/src/apps/tezos/sign_tx.py @@ -7,12 +7,7 @@ from trezor.messages import TezosBallotType, TezosContractType from trezor.messages.TezosSignedTx import TezosSignedTx from apps.common import paths -from apps.common.writers import ( - write_bytes, - write_uint8, - write_uint16_be, - write_uint32_be, -) +from apps.common.writers import write_bytes, write_uint8, write_uint32_be from apps.tezos import CURVE, helpers, layout PROPOSAL_LENGTH = const(32) diff --git a/crypto/tests/test_curves.py b/crypto/tests/test_curves.py index 093f95ab08..2a15bf7790 100755 --- a/crypto/tests/test_curves.py +++ b/crypto/tests/test_curves.py @@ -5,11 +5,10 @@ import hashlib import os import random +import curve25519 import ecdsa import pytest -import curve25519 - def bytes2num(s): res = 0 diff --git a/crypto/tests/test_wycheproof.py b/crypto/tests/test_wycheproof.py index 96edf3558c..c449b29c8a 100755 --- a/crypto/tests/test_wycheproof.py +++ b/crypto/tests/test_wycheproof.py @@ -5,7 +5,6 @@ import os from binascii import hexlify, unhexlify import pytest - from pyasn1.codec.ber.decoder import decode as ber_decode from pyasn1.codec.der.decoder import decode as der_decode from pyasn1.codec.der.encoder import encode as der_encode From 24bd4f84daa260705ca00f4eadb1877580295d24 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 23 Apr 2019 15:56:07 +0200 Subject: [PATCH 29/78] ci: gitlab init Lot of things are missing, this is to at least have something in master. Todo: - add legacy, python and storage tests - fix crypto - cache - pass builds from build stage to tests using artefacts and many others --- .gitlab-ci.yml | 100 +++++++++++++++++++++++++++++++ core/Dockerfile => Dockerfile | 19 ++++-- Pipfile | 32 +++++++--- Pipfile.lock | 78 ++++++++++++++---------- common/.travis.yml | 37 ------------ common/tools/requirements.txt | 16 ----- core/travis-install-libsodium.sh | 21 ------- crypto/.travis.yml | 38 ------------ 8 files changed, 182 insertions(+), 159 deletions(-) create mode 100644 .gitlab-ci.yml rename core/Dockerfile => Dockerfile (84%) delete mode 100644 common/.travis.yml delete mode 100644 common/tools/requirements.txt delete mode 100755 core/travis-install-libsodium.sh delete mode 100644 crypto/.travis.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000..6f5b532c44 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,100 @@ +variables: + GIT_SUBMODULE_STRATEGY: "recursive" + +stages: + - environment + - build + - test + +environment: + stage: environment + image: docker:latest + variables: + CONTAINER_NAME: "$CI_REGISTRY/trezor/trezor-firmware/environment" + services: + - docker:dind + before_script: + - docker login $CI_REGISTRY -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD + when: manual + script: + - docker pull $CONTAINER_NAME:latest || true + - docker build --cache-from $CONTAINER_NAME:latest --tag $CONTAINER_NAME:$CI_COMMIT_SHA --tag $CONTAINER_NAME:latest . + - docker push $CONTAINER_NAME:$CI_COMMIT_SHA + - docker push $CONTAINER_NAME:latest + +image: registry.corp.sldev.cz/trezor/trezor-firmware/environment + +before_script: + - pipenv install + +build core firmware: + stage: build + script: + - cd core + - pipenv run make build_cross + - pipenv run make build_boardloader + - pipenv run make build_bootloader + - pipenv run make build_prodtest + - pipenv run make build_firmware + # - test "$TREZOR_MODEL" = "1" || pipenv run make sizecheck + artifacts: + paths: + - core/build/firmware/firmware.bin + - core/build/bootloader/bootloader.bin + - core/build/boardloader/boardloader.bin + expire_in: 1 week + +build core unix: + stage: build + script: + - cd core + - pipenv run make build_unix_noui + +test style: + stage: test + script: + - pipenv run make style_check + - cd core && pipenv run make templates_check # TODO + +test core unix unit: + stage: test + script: + - cd core + - pipenv run make build_unix_noui + - pipenv run make test + +test core unix device: + stage: test + script: + - cd core + - pipenv run make build_unix_noui + - pipenv run make test_emu + +test core unix monero: + stage: test + script: + - cd core + - pipenv run make build_unix_noui + - pipenv run make test_emu_monero + +test common: + stage: test + script: + - cd common + - pipenv run jsonlint defs/*.json + - pipenv run jsonlint defs/*/*.json + - pipenv run python tools/cointool.py check + - pipenv run python tools/support.py check --ignore-missing + - pipenv run python protob/check.py + - pipenv run python protob/graph.py protob/*.proto # TODO: artifacts? + +test crypto: + stage: test + script: + - cd crypto + - pipenv run make + - ./tests/aestst + - ./tests/test_check + - CK_TIMEOUT_MULTIPLIER=20 valgrind -q --error-exitcode=1 ./tests/test_check + - ./tests/test_openssl 1000 + - ITERS=10 pipenv run pytest tests/ # TODO are ITERS=10 propagated? diff --git a/core/Dockerfile b/Dockerfile similarity index 84% rename from core/Dockerfile rename to Dockerfile index 3f5aa1c863..f416150c22 100644 --- a/core/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # initialize from the image -FROM debian:9 +FROM python ARG TOOLCHAIN_FLAVOR=linux ENV TOOLCHAIN_FLAVOR=$TOOLCHAIN_FLAVOR @@ -8,7 +8,10 @@ ENV TOOLCHAIN_FLAVOR=$TOOLCHAIN_FLAVOR # install build tools and dependencies RUN apt-get update && apt-get install -y \ - build-essential wget git python3-pip + build-essential wget git libsodium-dev graphviz \ + valgrind check libssl-dev libusb-1.0-0-dev libudev-dev + +# TODO are all apt packages actually needed? # install dependencies from toolchain source build @@ -57,13 +60,17 @@ RUN echo "${PROTOBUF_HASH} protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" | sha256 ENV PATH=/opt/$TOOLCHAIN_LONGVER/bin:$PATH -ENV PYTHON=python3 ENV LC_ALL=C.UTF-8 LANG=C.UTF-8 -RUN ln -s /usr/bin/python3 /usr/bin/python # use zipfile module to extract files world-readable -RUN $PYTHON -m zipfile -e "protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" /usr/local && chmod 755 /usr/local/bin/protoc +RUN python -m zipfile -e "protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" /usr/local && chmod 755 /usr/local/bin/protoc + +#ENV WORKON_HOME=/tmp/.venvs # install python dependencies -RUN $PYTHON -m pip install scons trezor +RUN pip install pipenv + +RUN python --version +RUN pip --version +RUN pipenv --version diff --git a/Pipfile b/Pipfile index ff26d37038..0147386fd3 100644 --- a/Pipfile +++ b/Pipfile @@ -4,31 +4,45 @@ name = "pypi" verify_ssl = true [packages] -trezor = {git = "https://github.com/trezor/python-trezor", editable = true, ref = "master"} +# all +trezor = {editable = true, path = "./python"} +scons = "*" protobuf = "==3.4.0" - -# python-trezor tests +pyblake2 = "*" +## tests pytest = "*" mock = "*" - -# make style +## style isort = "*" flake8 = "*" black = "*" +mako = ">=1.0.7" +munch = ">=2.3.2" -# trezor-common cointool +# common +demjson = "*" +graphviz = "*" # TODO this was '8' do we need that? +## cointool click = ">=6" "ed25519" = ">=1.4" requests = ">=2.19" -munch = ">=2.3.2" termcolor = ">=0.1.2" Pillow = ">=5.2.0" -Mako = ">=1.0.7" -# monero +# crypto +ecdsa = "*" +curve25519-donna = "*" +pyasn1 = "*" + +# core +## monero monero_agent = {version = ">=2.0.1", extras = ["tcry", "dev"]} py_trezor_crypto_ph4 = {version = ">=0.1.1"} +# legacy +setuptools = ">=24.2.0" +typing = "*" + [dev-packages] scan-build = "*" diff --git a/Pipfile.lock b/Pipfile.lock index fff4d0e178..c54a1be0a7 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "f3c2800ccd4e8ec7e07444b9bc81ce5c5f426ce15dcd6d55729ff81e7214525d" + "sha256": "4dd20b0598ed7b7cde01121a5c79aa21d756f6ca98d5a0aca1db8f57c673303c" }, "pipfile-spec": 6, "requires": {}, @@ -79,36 +79,36 @@ }, "cffi": { "hashes": [ - "sha256:00b97afa72c233495560a0793cdc86c2571721b4271c0667addc83c417f3d90f", - "sha256:0ba1b0c90f2124459f6966a10c03794082a2f3985cd699d7d63c4a8dae113e11", - "sha256:0bffb69da295a4fc3349f2ec7cbe16b8ba057b0a593a92cbe8396e535244ee9d", - "sha256:21469a2b1082088d11ccd79dd84157ba42d940064abbfa59cf5f024c19cf4891", - "sha256:2e4812f7fa984bf1ab253a40f1f4391b604f7fc424a3e21f7de542a7f8f7aedf", - "sha256:2eac2cdd07b9049dd4e68449b90d3ef1adc7c759463af5beb53a84f1db62e36c", - "sha256:2f9089979d7456c74d21303c7851f158833d48fb265876923edcb2d0194104ed", - "sha256:3dd13feff00bddb0bd2d650cdb7338f815c1789a91a6f68fdc00e5c5ed40329b", - "sha256:4065c32b52f4b142f417af6f33a5024edc1336aa845b9d5a8d86071f6fcaac5a", - "sha256:51a4ba1256e9003a3acf508e3b4f4661bebd015b8180cc31849da222426ef585", - "sha256:59888faac06403767c0cf8cfb3f4a777b2939b1fbd9f729299b5384f097f05ea", - "sha256:59c87886640574d8b14910840327f5cd15954e26ed0bbd4e7cef95fa5aef218f", - "sha256:610fc7d6db6c56a244c2701575f6851461753c60f73f2de89c79bbf1cc807f33", - "sha256:70aeadeecb281ea901bf4230c6222af0248c41044d6f57401a614ea59d96d145", - "sha256:71e1296d5e66c59cd2c0f2d72dc476d42afe02aeddc833d8e05630a0551dad7a", - "sha256:8fc7a49b440ea752cfdf1d51a586fd08d395ff7a5d555dc69e84b1939f7ddee3", - "sha256:9b5c2afd2d6e3771d516045a6cfa11a8da9a60e3d128746a7fe9ab36dfe7221f", - "sha256:9c759051ebcb244d9d55ee791259ddd158188d15adee3c152502d3b69005e6bd", - "sha256:b4d1011fec5ec12aa7cc10c05a2f2f12dfa0adfe958e56ae38dc140614035804", - "sha256:b4f1d6332339ecc61275bebd1f7b674098a66fea11a00c84d1c58851e618dc0d", - "sha256:c030cda3dc8e62b814831faa4eb93dd9a46498af8cd1d5c178c2de856972fd92", - "sha256:c2e1f2012e56d61390c0e668c20c4fb0ae667c44d6f6a2eeea5d7148dcd3df9f", - "sha256:c37c77d6562074452120fc6c02ad86ec928f5710fbc435a181d69334b4de1d84", - "sha256:c8149780c60f8fd02752d0429246088c6c04e234b895c4a42e1ea9b4de8d27fb", - "sha256:cbeeef1dc3c4299bd746b774f019de9e4672f7cc666c777cd5b409f0b746dac7", - "sha256:e113878a446c6228669144ae8a56e268c91b7f1fafae927adc4879d9849e0ea7", - "sha256:e21162bf941b85c0cda08224dade5def9360f53b09f9f259adb85fc7dd0e7b35", - "sha256:fb6934ef4744becbda3143d30c6604718871495a5e36c408431bf33d9c146889" + "sha256:041c81822e9f84b1d9c401182e174996f0bae9991f33725d059b771744290774", + "sha256:046ef9a22f5d3eed06334d01b1e836977eeef500d9b78e9ef693f9380ad0b83d", + "sha256:066bc4c7895c91812eff46f4b1c285220947d4aa46fa0a2651ff85f2afae9c90", + "sha256:066c7ff148ae33040c01058662d6752fd73fbc8e64787229ea8498c7d7f4041b", + "sha256:2444d0c61f03dcd26dbf7600cf64354376ee579acad77aef459e34efcb438c63", + "sha256:300832850b8f7967e278870c5d51e3819b9aad8f0a2c8dbe39ab11f119237f45", + "sha256:34c77afe85b6b9e967bd8154e3855e847b70ca42043db6ad17f26899a3df1b25", + "sha256:46de5fa00f7ac09f020729148ff632819649b3e05a007d286242c4882f7b1dc3", + "sha256:4aa8ee7ba27c472d429b980c51e714a24f47ca296d53f4d7868075b175866f4b", + "sha256:4d0004eb4351e35ed950c14c11e734182591465a33e960a4ab5e8d4f04d72647", + "sha256:4e3d3f31a1e202b0f5a35ba3bc4eb41e2fc2b11c1eff38b362de710bcffb5016", + "sha256:50bec6d35e6b1aaeb17f7c4e2b9374ebf95a8975d57863546fa83e8d31bdb8c4", + "sha256:55cad9a6df1e2a1d62063f79d0881a414a906a6962bc160ac968cc03ed3efcfb", + "sha256:5662ad4e4e84f1eaa8efce5da695c5d2e229c563f9d5ce5b0113f71321bcf753", + "sha256:59b4dc008f98fc6ee2bb4fd7fc786a8d70000d058c2bbe2698275bc53a8d3fa7", + "sha256:73e1ffefe05e4ccd7bcea61af76f36077b914f92b76f95ccf00b0c1b9186f3f9", + "sha256:a1f0fd46eba2d71ce1589f7e50a9e2ffaeb739fb2c11e8192aa2b45d5f6cc41f", + "sha256:a2e85dc204556657661051ff4bab75a84e968669765c8a2cd425918699c3d0e8", + "sha256:a5457d47dfff24882a21492e5815f891c0ca35fefae8aa742c6c263dac16ef1f", + "sha256:a8dccd61d52a8dae4a825cdbb7735da530179fea472903eb871a5513b5abbfdc", + "sha256:ae61af521ed676cf16ae94f30fe202781a38d7178b6b4ab622e4eec8cefaff42", + "sha256:b012a5edb48288f77a63dba0840c92d0504aa215612da4541b7b42d849bc83a3", + "sha256:d2c5cfa536227f57f97c92ac30c8109688ace8fa4ac086d19d0af47d134e2909", + "sha256:d42b5796e20aacc9d15e66befb7a345454eef794fdb0737d1af593447c6c8f45", + "sha256:dee54f5d30d775f525894d67b1495625dd9322945e7fee00731952e0368ff42d", + "sha256:e070535507bd6aa07124258171be2ee8dfc19119c28ca94c9dfb7efd23564512", + "sha256:e1ff2748c84d97b065cc95429814cdba39bcbd77c9c85c89344b317dc0d9cbff", + "sha256:ed851c75d1e0e043cbf5ca9a8e1b13c4c90f3fbd863dacb01c0808e2b5204201" ], - "version": "==1.12.2" + "version": "==1.12.3" }, "chacha20poly1305": { "hashes": [ @@ -169,6 +169,13 @@ ], "version": "==2.2.2" }, + "demjson": { + "hashes": [ + "sha256:31de2038a0fdd9c4c11f8bf3b13fe77bc2a128307f965c8d5fb4dc6d6f6beb79" + ], + "index": "pypi", + "version": "==2.2.4" + }, "ecdsa": { "hashes": [ "sha256:20c17e527e75acad8f402290e158a6ac178b91b881f941fc6ea305bfdfb9657c", @@ -205,6 +212,14 @@ "index": "pypi", "version": "==3.7.7" }, + "graphviz": { + "hashes": [ + "sha256:0e1744a45b0d707bc44f99c7b8e5f25dc22cf96b6aaf2432ac308ed9822a9cb6", + "sha256:d311be4fddfe832a56986ac5e1d6e8715d7fcb0208560da79d1bb0f72abef41f" + ], + "index": "pypi", + "version": "==0.10.1" + }, "idna": { "hashes": [ "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", @@ -580,8 +595,7 @@ }, "trezor": { "editable": true, - "git": "https://github.com/trezor/python-trezor", - "ref": "2813522b05cef4e0e545a101f8b3559a3183b45b" + "path": "./python" }, "typing-extensions": { "hashes": [ diff --git a/common/.travis.yml b/common/.travis.yml deleted file mode 100644 index 978c264a1e..0000000000 --- a/common/.travis.yml +++ /dev/null @@ -1,37 +0,0 @@ -language: python - -# Runs jobs on container based infrastructure -sudo: false - -# Saves pip downloads/wheels between builds -cache: - directories: - - $HOME/.cache/pip - -addons: - apt: - packages: - - graphviz - -python: - - "3.6" - -install: - - pip install demjson graphviz - - pip install -r tools/requirements.txt - -script: - - jsonlint defs/*.json - - jsonlint defs/*/*.json - - python tools/cointool.py check - - python tools/support.py check --ignore-missing - - python protob/check.py - - python protob/graph.py protob/*.proto - -notifications: - webhooks: - urls: - - http://ci-bot.satoshilabs.com:5000/travis - on_success: always - on_failure: always - on_start: always diff --git a/common/tools/requirements.txt b/common/tools/requirements.txt deleted file mode 100644 index 59cab2694a..0000000000 --- a/common/tools/requirements.txt +++ /dev/null @@ -1,16 +0,0 @@ -click>=6 - -# for `coin_gen coindefs` and checking icons: -ed25519>=1.4 -Pillow>=5.2.0 -trezor>=0.10 - -# for checking backends, generating coins_details.json -requests>=2.19 - -# for rendering templates: -Mako>=1.0.7 -munch>=2.3.2 - -# for pretty colors in checks -termcolor >= 0.1.2 diff --git a/core/travis-install-libsodium.sh b/core/travis-install-libsodium.sh deleted file mode 100755 index 3e73215fd3..0000000000 --- a/core/travis-install-libsodium.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -# libsodium-dev replacement -# -# The purpose of this file is to install libsodium in -# the Travis CI environment. Outside this environment, -# you would probably not want to install it like this. - -set -e -export LIBSODIUM_VER="1.0.16" - -# check if libsodium is already installed -if [ ! -d "$HOME/libsodium/lib" ]; then - wget "https://github.com/jedisct1/libsodium/releases/download/${LIBSODIUM_VER}/libsodium-${LIBSODIUM_VER}.tar.gz" - tar xvfz "libsodium-${LIBSODIUM_VER}.tar.gz" - cd "libsodium-${LIBSODIUM_VER}" - ./configure --prefix=$HOME/libsodium - make - make install -else - echo 'Using cached directory.' -fi diff --git a/crypto/.travis.yml b/crypto/.travis.yml deleted file mode 100644 index 090e8af7e2..0000000000 --- a/crypto/.travis.yml +++ /dev/null @@ -1,38 +0,0 @@ -sudo: false -dist: trusty -language: c - -compiler: - - clang - - gcc - -addons: - apt: - packages: - - check - - libssl-dev - - python3-pip - - valgrind - -env: - global: - - PYTHON=python3 - -install: - - $PYTHON -m pip install --user pytest ecdsa curve25519-donna pyasn1 - -script: - - make - - ./tests/aestst - - ./tests/test_check - - CK_TIMEOUT_MULTIPLIER=20 valgrind -q --error-exitcode=1 ./tests/test_check - - ./tests/test_openssl 1000 - - ITERS=10 $PYTHON -m pytest tests/ - -notifications: - webhooks: - urls: - - http://ci-bot.satoshilabs.com:5000/travis - on_success: always - on_failure: always - on_start: always From 8460f1b36b29ddd31019bb154f12996b90584d10 Mon Sep 17 00:00:00 2001 From: santiky Date: Tue, 23 Apr 2019 17:00:26 +0200 Subject: [PATCH 30/78] add FairCoin --- common/defs/bitcoin/faircoin.json | 39 ++++++++++++++++++++++++++++++ common/defs/bitcoin/faircoin.png | Bin 0 -> 10890 bytes 2 files changed, 39 insertions(+) create mode 100644 common/defs/bitcoin/faircoin.json create mode 100644 common/defs/bitcoin/faircoin.png diff --git a/common/defs/bitcoin/faircoin.json b/common/defs/bitcoin/faircoin.json new file mode 100644 index 0000000000..fe9a9b873f --- /dev/null +++ b/common/defs/bitcoin/faircoin.json @@ -0,0 +1,39 @@ +{ + "coin_name": "FairCoin", + "coin_shortcut": "FAIR", + "coin_label": "FairCoin", + "website": "https://www.faircoin.world", + "github": "https://github.com/faircoin/faircoin", + "maintainer": "Santi Noreña ", + "curve_name": "secp256k1", + "address_type": 95, + "address_type_p2sh": 36, + "maxfee_kb": 10000000, + "minfee_kb": 1000, + "signed_message_header": "FairCoin Signed Message:\n", + "hash_genesis_block": "beed44fa5e96150d95d56ebd5d2625781825a9407a5215dd7eda723373a0a1d7", + "xprv_magic": 76066276, + "xpub_magic": 76067358, + "xpub_magic_segwit_p2sh": null, + "xpub_magic_segwit_native": null, + "bech32_prefix": null, + "cashaddr_prefix": null, + "slip44": 298, + "segwit": false, + "decred": false, + "fork_id": 0, + "force_bip143": false, + "bip115": false, + "default_fee_b": { + "Normal": 800000 + }, + "dust_limit": 500000, + "blocktime_seconds": 180, + "uri_prefix": "faircoin", + "min_address_length": 27, + "max_address_length": 34, + "bitcore": [], + "blockbook": [], + "cooldown": 100, + "consensus_branch_id": null +} diff --git a/common/defs/bitcoin/faircoin.png b/common/defs/bitcoin/faircoin.png new file mode 100644 index 0000000000000000000000000000000000000000..f2f7c7a2fc2dfaa79ba3394bafd03409ae9ea5a2 GIT binary patch literal 10890 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 literal 0 HcmV?d00001 From 7fb1137ae4b0e53d0d4a5a3d787a4d2cc62a6407 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 23 Apr 2019 16:00:21 +0200 Subject: [PATCH 31/78] ci: test legacy --- .gitlab-ci.yml | 24 +++++ legacy/.travis.yml | 63 ----------- legacy/Pipfile | 14 --- legacy/Pipfile.lock | 254 -------------------------------------------- 4 files changed, 24 insertions(+), 331 deletions(-) delete mode 100644 legacy/.travis.yml delete mode 100644 legacy/Pipfile delete mode 100644 legacy/Pipfile.lock diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6f5b532c44..09938b50d7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -50,6 +50,17 @@ build core unix: - cd core - pipenv run make build_unix_noui +# TODO: matrix: DEBUG_LINK={0,1}, gcc vs clang +build legacy: + stage: build + variables: + HEADLESS: "1" + script: + - cd legacy + - pipenv run script/cibuild + - pipenv run make -C bootloader + - pipenv run make -C demo + test style: stage: test script: @@ -98,3 +109,16 @@ test crypto: - CK_TIMEOUT_MULTIPLIER=20 valgrind -q --error-exitcode=1 ./tests/test_check - ./tests/test_openssl 1000 - ITERS=10 pipenv run pytest tests/ # TODO are ITERS=10 propagated? + +# TODO: matrix: DEBUG_LINK={0,1}, gcc vs clang +# TODO add more based on deleted .travis.yml +test legacy: + stage: test + variables: + HEADLESS: "1" + EMULATOR: "1" + DEBUG_LINK: "1" + script: + - cd legacy + - pipenv run script/cibuild + - pipenv run script/test diff --git a/legacy/.travis.yml b/legacy/.travis.yml deleted file mode 100644 index 39adb5429c..0000000000 --- a/legacy/.travis.yml +++ /dev/null @@ -1,63 +0,0 @@ -sudo: false -dist: trusty -language: c - -addons: - apt: - sources: - - deadsnakes - packages: - - build-essential - - python3.6 - - python3.6-dev - - python3.6-venv - -env: - global: - - MAKEFLAGS=-j2 - - PYTHON=python3.6 - - PROTOBUF_VERSION=3.4.0 - - TOOLCHAIN_SHORTVER=8-2018q4 - - TOOLCHAIN_LONGVER=gcc-arm-none-eabi-8-2018-q4-major - matrix: - - DEBUG_LINK=0 - - DEBUG_LINK=1 - -matrix: - include: - - name: "Emulator GCC" - env: EMULATOR=1 HEADLESS=1 DEBUG_LINK=1 - compiler: gcc - script: pipenv run ./script/cibuild && pipenv run script/test - - name: "Emulator Clang" - env: EMULATOR=1 HEADLESS=1 DEBUG_LINK=1 - compiler: clang - script: pipenv run ./script/cibuild && pipenv run script/test - -before_install: - - $PYTHON -m ensurepip --user - - $PYTHON -m pip install --user pipenv - -install: - - wget "https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" - - unzip "protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" -d protoc - - export PATH="$(pwd)/protoc/bin:$PATH" - - pipenv install - -before_script: - - test "$EMULATOR" = "1" || wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/$TOOLCHAIN_SHORTVER/$TOOLCHAIN_LONGVER-linux.tar.bz2 - - test "$EMULATOR" = "1" || tar xfj $TOOLCHAIN_LONGVER-linux.tar.bz2 - - test "$EMULATOR" = "1" || export PATH=$PWD/$TOOLCHAIN_LONGVER/bin:$PATH - -script: - - pipenv run script/cibuild - - pipenv run make -C bootloader - - pipenv run make -C demo - -notifications: - webhooks: - urls: - - http://ci-bot.satoshilabs.com:5000/travis - on_success: always - on_failure: always - on_start: always diff --git a/legacy/Pipfile b/legacy/Pipfile deleted file mode 100644 index ba4ee323c8..0000000000 --- a/legacy/Pipfile +++ /dev/null @@ -1,14 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -name = "pypi" -verify_ssl = true - -[packages] -setuptools = ">=24.2.0" -trezor = {git = "https://github.com/trezor/python-trezor", editable = true, ref = "master"} -pytest = "*" -mock = "*" -typing = "*" -protobuf = "==3.4.0" -mako = "*" -munch = "*" diff --git a/legacy/Pipfile.lock b/legacy/Pipfile.lock deleted file mode 100644 index 6b77f6b424..0000000000 --- a/legacy/Pipfile.lock +++ /dev/null @@ -1,254 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "0c77aa21c1e385d7c3833a2f95bc6129394f6d9ce67e1181700a76a5e15074cb" - }, - "pipfile-spec": 6, - "requires": {}, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "atomicwrites": { - "hashes": [ - "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", - "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" - ], - "version": "==1.3.0" - }, - "attrs": { - "hashes": [ - "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", - "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" - ], - "version": "==19.1.0" - }, - "certifi": { - "hashes": [ - "sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5", - "sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae" - ], - "version": "==2019.3.9" - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "version": "==3.0.4" - }, - "click": { - "hashes": [ - "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", - "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" - ], - "version": "==7.0" - }, - "construct": { - "hashes": [ - "sha256:2271a0efd0798679dea825ff47e22a4c550456a5db0ba8baa82f7eae0af0118c" - ], - "version": "==2.9.45" - }, - "ecdsa": { - "hashes": [ - "sha256:20c17e527e75acad8f402290e158a6ac178b91b881f941fc6ea305bfdfb9657c", - "sha256:5c034ffa23413ac923541ceb3ac14ec15a0d2530690413bff58c12b80e56d884" - ], - "version": "==0.13.2" - }, - "idna": { - "hashes": [ - "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", - "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" - ], - "version": "==2.8" - }, - "libusb1": { - "hashes": [ - "sha256:9d4f66d2ed699986b06bc3082cd262101cb26af7a76a34bd15b7eb56cba37e0f" - ], - "version": "==1.7" - }, - "mako": { - "hashes": [ - "sha256:0728c404877cd4ca72c409c0ea372dc5f3b53fa1ad2bb434e1d216c0444ff1fd" - ], - "index": "pypi", - "version": "==1.0.9" - }, - "markupsafe": { - "hashes": [ - "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", - "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", - "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", - "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", - "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", - "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", - "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", - "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", - "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", - "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", - "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", - "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", - "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", - "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", - "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", - "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", - "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", - "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", - "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", - "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", - "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", - "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", - "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", - "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", - "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", - "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", - "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", - "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7" - ], - "version": "==1.1.1" - }, - "mnemonic": { - "hashes": [ - "sha256:02a7306a792370f4a0c106c2cf1ce5a0c84b9dbd7e71c6792fdb9ad88a727f1d" - ], - "version": "==0.18" - }, - "mock": { - "hashes": [ - "sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1", - "sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba" - ], - "index": "pypi", - "version": "==2.0.0" - }, - "more-itertools": { - "hashes": [ - "sha256:2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7", - "sha256:c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a" - ], - "markers": "python_version > '2.7'", - "version": "==7.0.0" - }, - "munch": { - "hashes": [ - "sha256:6ae3d26b837feacf732fb8aa5b842130da1daf221f5af9f9d4b2a0a6414b0d51" - ], - "index": "pypi", - "version": "==2.3.2" - }, - "pbkdf2": { - "hashes": [ - "sha256:ac6397369f128212c43064a2b4878038dab78dab41875364554aaf2a684e6979" - ], - "version": "==1.3" - }, - "pbr": { - "hashes": [ - "sha256:8257baf496c8522437e8a6cfe0f15e00aedc6c0e0e7c9d55eeeeab31e0853843", - "sha256:8c361cc353d988e4f5b998555c88098b9d5964c2e11acf7b0d21925a66bb5824" - ], - "version": "==5.1.3" - }, - "pluggy": { - "hashes": [ - "sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f", - "sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746" - ], - "version": "==0.9.0" - }, - "protobuf": { - "hashes": [ - "sha256:1fcb9b704bc2e30767352d86b2664d8f65f8ed49654d7a80e7a150739724e80a", - "sha256:41c4555d9754b985352ce5289fa3ba6b21ed715f595111e46e2b90ca53112475", - "sha256:4d4815467f8a61b06d648699842b233017b201f7a16275d680ec5480f10e30e9", - "sha256:5b816951df388f4ab2adbd3f9ae5619b9a5d7033d14b005c345dc3ee88a7faf4", - "sha256:61dbf86993a9312c3a0816b5252079a3943856003bf0380fea3098c929084ad4", - "sha256:9f3be25ad48b051186ee88f9567a3f3f548facd360e0cb62568e2736d9cfda11", - "sha256:ef02609ef445987976a3a26bff77119c518e0915c96661c3a3b17856d0ef6374" - ], - "index": "pypi", - "version": "==3.4.0" - }, - "py": { - "hashes": [ - "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", - "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" - ], - "version": "==1.8.0" - }, - "pyblake2": { - "hashes": [ - "sha256:3757f7ad709b0e1b2a6b3919fa79fe3261f166fc375cd521f2be480f8319dde9", - "sha256:407e02c7f8f36fcec1b7aa114ddca0c1060c598142ea6f6759d03710b946a7e3", - "sha256:4d47b4a2c1d292b1e460bde1dda4d13aa792ed2ed70fcc263b6bc24632c8e902", - "sha256:5ccc7eb02edb82fafb8adbb90746af71460fbc29aa0f822526fc976dff83e93f", - "sha256:8043267fbc0b2f3748c6920591cd0b8b5609dcce60c504c32858aa36206386f2", - "sha256:982295a87907d50f4723db6bc724660da76b6547826d52160171d54f95b919ac", - "sha256:baa2190bfe549e36163aa44664d4ee3a9080b236fc5d42f50dc6fd36bbdc749e", - "sha256:c53417ee0bbe77db852d5fd1036749f03696ebc2265de359fe17418d800196c4", - "sha256:fbc9fcde75713930bc2a91b149e97be2401f7c9c56d735b46a109210f58d7358" - ], - "version": "==1.1.2" - }, - "pytest": { - "hashes": [ - "sha256:3773f4c235918987d51daf1db66d51c99fac654c81d6f2f709a046ab446d5e5d", - "sha256:b7802283b70ca24d7119b32915efa7c409982f59913c1a6c0640aacf118b95f5" - ], - "index": "pypi", - "version": "==4.4.1" - }, - "requests": { - "hashes": [ - "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", - "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" - ], - "version": "==2.21.0" - }, - "six": { - "hashes": [ - "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", - "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" - ], - "version": "==1.12.0" - }, - "trezor": { - "editable": true, - "git": "https://github.com/trezor/python-trezor", - "ref": "2813522b05cef4e0e545a101f8b3559a3183b45b" - }, - "typing": { - "hashes": [ - "sha256:4027c5f6127a6267a435201981ba156de91ad0d1d98e9ddc2aa173453453492d", - "sha256:57dcf675a99b74d64dacf6fba08fb17cf7e3d5fdff53d4a30ea2a5e7e52543d4", - "sha256:a4c8473ce11a65999c8f59cb093e70686b6c84c98df58c1dae9b3b196089858a" - ], - "index": "pypi", - "version": "==3.6.6" - }, - "typing-extensions": { - "hashes": [ - "sha256:07b2c978670896022a43c4b915df8958bec4a6b84add7f2c87b2b728bda3ba64", - "sha256:f3f0e67e1d42de47b5c67c32c9b26641642e9170fe7e292991793705cd5fef7c", - "sha256:fb2cd053238d33a8ec939190f30cfd736c00653a85a2919415cecf7dc3d9da71" - ], - "version": "==3.7.2" - }, - "urllib3": { - "hashes": [ - "sha256:4c291ca23bbb55c76518905869ef34bdd5f0e46af7afe6861e8375643ffee1a0", - "sha256:9a247273df709c4fedb38c711e44292304f73f39ab01beda9f6b9fc375669ac3" - ], - "version": "==1.24.2" - } - }, - "develop": {} -} From 8c9f74d16c7af0d5966b54b0b095e141eb4190b8 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Tue, 23 Apr 2019 17:08:20 +0200 Subject: [PATCH 32/78] ci: do not pass anything to pytest globally to allow pytest in crypto/ --- setup.cfg | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/setup.cfg b/setup.cfg index 60d212f457..c7f5cdead8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,7 +29,8 @@ not_skip=__init__.py forced_separate = apps known_standard_library = micropython,ubinascii,ustruct,uctypes,utime,utimeq,trezorio,trezorui,trezorutils,trezorconfig -[tool:pytest] -addopts = --pyargs trezorlib.tests.device_tests -xfail_strict = true -run_xfail = +# TODO: ask matejcik if we can omit this completely +# [tool:pytest] +# addopts = --pyargs trezorlib.tests.device_tests +# xfail_strict = true +# run_xfail = From def96032d2b4e168a017b3f8a9c00dd5ce6a4a82 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 23 Apr 2019 18:16:37 +0200 Subject: [PATCH 33/78] core+legacy: make path checks more benevolent (#84) Non-existing BIP-48 is causing mess among implementations --- core/src/apps/wallet/sign_tx/addresses.py | 42 ++++++++++++++--------- legacy/firmware/fsm_msg_coin.h | 9 +++-- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/core/src/apps/wallet/sign_tx/addresses.py b/core/src/apps/wallet/sign_tx/addresses.py index ff6e93c3ac..d9d44d3f01 100644 --- a/core/src/apps/wallet/sign_tx/addresses.py +++ b/core/src/apps/wallet/sign_tx/addresses.py @@ -204,7 +204,7 @@ def validate_full_path( See docs/coins for what paths are allowed. Please note that this is not a comprehensive check, some nuances are omitted for simplification. """ - if len(path) != 5: + if len(path) not in (4, 5, 6): return False if not validate_purpose(path[0], coin): @@ -214,21 +214,29 @@ def validate_full_path( ): return False - if path[1] != coin.slip44 | HARDENED: + if path[1] > 20 and path[1] != coin.slip44 | HARDENED: return False - if path[2] < HARDENED or path[2] > 20 | HARDENED: + if (path[2] > 20 and path[2] < HARDENED) or path[2] > 20 | HARDENED: return False - if path[3] not in [0, 1]: + if path[3] not in (0, 1, 0 | HARDENED, 1 | HARDENED, 2 | HARDENED): return False - if path[4] > 1000000: + if len(path) > 4 and path[4] > 1000000: + return False + if len(path) > 5 and path[5] > 1000000: return False return True def validate_purpose(purpose: int, coin: CoinInfo) -> bool: - if purpose not in (44 | HARDENED, 48 | HARDENED, 49 | HARDENED, 84 | HARDENED): + if purpose not in ( + 44 | HARDENED, + 45 | HARDENED, + 48 | HARDENED, + 49 | HARDENED, + 84 | HARDENED, + ): return False - if not coin.segwit and purpose not in (44 | HARDENED, 48 | HARDENED): + if not coin.segwit and purpose not in (44 | HARDENED, 45 | HARDENED, 48 | HARDENED): return False return True @@ -239,21 +247,23 @@ def validate_purpose_against_script_type( """ Validates purpose against provided input's script type: - 44 for spending address (script_type == SPENDADDRESS) - - 48 for multisig (script_type == SPENDMULTISIG) - - 49 for p2sh-segwit spend (script_type == SPENDP2SHWITNESS) - - 84 for native segwit spend (script_type == SPENDWITNESS) + - 45, 48 for multisig (script_type == SPENDMULTISIG) + - 49 for p2wsh-nested-in-p2sh spend (script_type == SPENDP2SHWITNESS) + - 84 for p2wsh native segwit spend (script_type == SPENDWITNESS) """ if purpose == 44 | HARDENED and script_type != InputScriptType.SPENDADDRESS: return False - if purpose == 48 | HARDENED and script_type != InputScriptType.SPENDMULTISIG: + if purpose == 45 | HARDENED and script_type != InputScriptType.SPENDMULTISIG: return False - if ( # p2wsh-nested-in-p2sh - purpose == 49 | HARDENED and script_type != InputScriptType.SPENDP2SHWITNESS + if purpose == 48 | HARDENED and script_type not in ( + InputScriptType.SPENDMULTISIG, + InputScriptType.SPENDP2SHWITNESS, + InputScriptType.SPENDWITNESS, ): return False - if ( # p2wsh - purpose == 84 | HARDENED and script_type != InputScriptType.SPENDWITNESS - ): + if purpose == 49 | HARDENED and script_type != InputScriptType.SPENDP2SHWITNESS: + return False + if purpose == 84 | HARDENED and script_type != InputScriptType.SPENDWITNESS: return False return True diff --git a/legacy/firmware/fsm_msg_coin.h b/legacy/firmware/fsm_msg_coin.h index 30b5e1747e..dc3a94e526 100644 --- a/legacy/firmware/fsm_msg_coin.h +++ b/legacy/firmware/fsm_msg_coin.h @@ -147,12 +147,15 @@ static bool path_mismatched(const CoinInfo *coin, const GetAddress *msg) { // m/48' - BIP48 Copay Multisig P2SH // m / purpose' / coin_type' / account' / change / address_index + // Electrum: + // m / purpose' / coin_type' / account' / type' / change / address_index if (msg->address_n[0] == (0x80000000 + 48)) { - mismatch |= (msg->script_type != InputScriptType_SPENDMULTISIG); - mismatch |= (msg->address_n_count != 5); + mismatch |= (msg->script_type != InputScriptType_SPENDMULTISIG) && + (msg->script_type != InputScriptType_SPENDP2SHWITNESS) && + (msg->script_type != InputScriptType_SPENDWITNESS); + mismatch |= (msg->address_n_count != 5) && (msg->address_n_count != 6); mismatch |= (msg->address_n[1] != coin->coin_type); mismatch |= (msg->address_n[2] & 0x80000000) == 0; - mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; return mismatch; } From c995d9fa59472784687663c03d9ea0c9c3123584 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 23 Apr 2019 18:31:30 +0200 Subject: [PATCH 34/78] Revert "ui.display.loader: slice_span added for indeterminate loaders" This reverts commit 9de96f61141f29a022aa5b26a5a8e196f785abae. --- core/embed/bootloader/bootui.c | 14 +++++----- core/embed/extmod/modtrezorui/display.c | 4 +-- core/embed/extmod/modtrezorui/display.h | 2 +- .../extmod/modtrezorui/modtrezorui-display.h | 27 +++++++------------ core/mocks/generated/trezorui.py | 4 +-- 5 files changed, 20 insertions(+), 31 deletions(-) diff --git a/core/embed/bootloader/bootui.c b/core/embed/bootloader/bootui.c index 8609d93fae..e5e84b6a6c 100644 --- a/core/embed/bootloader/bootui.c +++ b/core/embed/bootloader/bootui.c @@ -237,7 +237,7 @@ void ui_screen_install_confirm_newvendor(const vendor_header *const vhdr, void ui_screen_install(void) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); display_loader(0, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_install, - sizeof(toi_icon_install), COLOR_BLACK, 0); + sizeof(toi_icon_install), COLOR_BLACK); display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24, "Installing firmware", -1, FONT_NORMAL, COLOR_BLACK, COLOR_WHITE); @@ -245,12 +245,12 @@ void ui_screen_install(void) { void ui_screen_install_progress_erase(int pos, int len) { display_loader(250 * pos / len, -20, COLOR_BL_PROCESS, COLOR_WHITE, - toi_icon_install, sizeof(toi_icon_install), COLOR_BLACK, 0); + toi_icon_install, sizeof(toi_icon_install), COLOR_BLACK); } void ui_screen_install_progress_upload(int pos) { display_loader(pos, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_install, - sizeof(toi_icon_install), COLOR_BLACK, 0); + sizeof(toi_icon_install), COLOR_BLACK); } // wipe UI @@ -275,14 +275,14 @@ void ui_screen_wipe_confirm(void) { void ui_screen_wipe(void) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); display_loader(0, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_wipe, - sizeof(toi_icon_wipe), COLOR_BLACK, 0); + sizeof(toi_icon_wipe), COLOR_BLACK); display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24, "Wiping device", -1, FONT_NORMAL, COLOR_BLACK, COLOR_WHITE); } void ui_screen_wipe_progress(int pos, int len) { display_loader(1000 * pos / len, -20, COLOR_BL_PROCESS, COLOR_WHITE, - toi_icon_wipe, sizeof(toi_icon_wipe), COLOR_BLACK, 0); + toi_icon_wipe, sizeof(toi_icon_wipe), COLOR_BLACK); } // done UI @@ -301,7 +301,7 @@ void ui_screen_done(int restart_seconds, secbool full_redraw) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); } display_loader(1000, -20, COLOR_BL_DONE, COLOR_WHITE, toi_icon_done, - sizeof(toi_icon_done), COLOR_BLACK, 0); + sizeof(toi_icon_done), COLOR_BLACK); if (secfalse == full_redraw) { display_bar(0, DISPLAY_RESY - 24 - 18, 240, 23, COLOR_WHITE); } @@ -314,7 +314,7 @@ void ui_screen_done(int restart_seconds, secbool full_redraw) { void ui_screen_fail(void) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); display_loader(1000, -20, COLOR_BL_FAIL, COLOR_WHITE, toi_icon_fail, - sizeof(toi_icon_fail), COLOR_BLACK, 0); + sizeof(toi_icon_fail), COLOR_BLACK); display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24, "Failed! Please, reconnect.", -1, FONT_NORMAL, COLOR_BLACK, COLOR_WHITE); diff --git a/core/embed/extmod/modtrezorui/display.c b/core/embed/extmod/modtrezorui/display.c index f7601dd8c6..eba7ecfd28 100644 --- a/core/embed/extmod/modtrezorui/display.c +++ b/core/embed/extmod/modtrezorui/display.c @@ -318,7 +318,7 @@ static void inflate_callback_loader(uint8_t byte, uint32_t pos, void display_loader(uint16_t progress, int yoffset, uint16_t fgcolor, uint16_t bgcolor, const uint8_t *icon, uint32_t iconlen, - uint16_t iconfgcolor, uint16_t slice_span) { + uint16_t iconfgcolor) { #if TREZOR_MODEL == T uint16_t colortable[16], iconcolortable[16]; set_color_table(colortable, fgcolor, bgcolor); @@ -378,7 +378,7 @@ void display_loader(uint16_t progress, int yoffset, uint16_t fgcolor, PIXELDATA(iconcolortable[c]); } else { uint8_t c; - if (progress > a && (slice_span == 0 || progress < (a + slice_span))) { + if (progress > a) { c = (img_loader[my][mx] & 0x00F0) >> 4; } else { c = img_loader[my][mx] & 0x000F; diff --git a/core/embed/extmod/modtrezorui/display.h b/core/embed/extmod/modtrezorui/display.h index 141aba1070..578025d49f 100644 --- a/core/embed/extmod/modtrezorui/display.h +++ b/core/embed/extmod/modtrezorui/display.h @@ -84,7 +84,7 @@ void display_icon(int x, int y, int w, int h, const void *data, int datalen, uint16_t fgcolor, uint16_t bgcolor); void display_loader(uint16_t progress, int yoffset, uint16_t fgcolor, uint16_t bgcolor, const uint8_t *icon, uint32_t iconlen, - uint16_t iconfgcolor, uint16_t slice_span); + uint16_t iconfgcolor); #ifndef TREZOR_PRINT_DISABLE void display_print_color(uint16_t fgcolor, uint16_t bgcolor); diff --git a/core/embed/extmod/modtrezorui/modtrezorui-display.h b/core/embed/extmod/modtrezorui/modtrezorui-display.h index 90c78e6c12..862cd11991 100644 --- a/core/embed/extmod/modtrezorui/modtrezorui-display.h +++ b/core/embed/extmod/modtrezorui/modtrezorui-display.h @@ -198,7 +198,7 @@ STATIC mp_obj_t mod_trezorui_Display_icon(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_icon_obj, 6, 6, mod_trezorui_Display_icon); /// def loader(self, progress: int, yoffset: int, fgcolor: int, bgcolor: int, -/// icon: bytes = None, iconfgcolor: int = None, slice_span: int = None) -> None: +/// icon: bytes = None, iconfgcolor: int = None) -> None: /// ''' /// Renders a rotating loader graphic. /// Progress determines its position (0-1000), fgcolor is used as foreground @@ -206,8 +206,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_icon_obj, 6, 6, /// icon is drawn in the middle using the color specified in iconfgcolor. /// Icon needs to be of exactly LOADER_ICON_SIZE x LOADER_ICON_SIZE pixels /// size. -/// If slice_span is defined the progress is sliced to emulate indeterminate -/// loader. /// ''' STATIC mp_obj_t mod_trezorui_Display_loader(size_t n_args, const mp_obj_t *args) { @@ -215,11 +213,8 @@ STATIC mp_obj_t mod_trezorui_Display_loader(size_t n_args, mp_int_t yoffset = mp_obj_get_int(args[2]); mp_int_t fgcolor = mp_obj_get_int(args[3]); mp_int_t bgcolor = mp_obj_get_int(args[4]); - mp_buffer_info_t icon = {.buf = NULL, .len=0}; - uint16_t iconfgcolor = 0; - uint16_t slice_span = 0; - - if (n_args > 5 && args[5] != mp_const_none) { // icon provided + if (n_args > 5) { // icon provided + mp_buffer_info_t icon; mp_get_buffer_raise(args[5], &icon, MP_BUFFER_READ); const uint8_t *data = icon.buf; if (icon.len < 8 || memcmp(data, "TOIg", 4) != 0) { @@ -234,25 +229,21 @@ STATIC mp_obj_t mod_trezorui_Display_loader(size_t n_args, if (datalen != icon.len - 12) { mp_raise_ValueError("Invalid size of data"); } - + uint16_t iconfgcolor; if (n_args > 6) { // icon color provided iconfgcolor = mp_obj_get_int(args[6]); } else { iconfgcolor = ~bgcolor; // invert } + display_loader(progress, yoffset, fgcolor, bgcolor, icon.buf, icon.len, + iconfgcolor); + } else { + display_loader(progress, yoffset, fgcolor, bgcolor, NULL, 0, 0); } - - if (n_args > 7 && args[7] != mp_const_none) { // slice span provided - slice_span = mp_obj_get_int(args[7]); - } - - display_loader(progress, yoffset, fgcolor, bgcolor, icon.buf, icon.len, - iconfgcolor, slice_span); - return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_loader_obj, 5, - 8, mod_trezorui_Display_loader); + 7, mod_trezorui_Display_loader); /// def print(self, text: str) -> None: /// ''' diff --git a/core/mocks/generated/trezorui.py b/core/mocks/generated/trezorui.py index 178902fede..d42a3083c8 100644 --- a/core/mocks/generated/trezorui.py +++ b/core/mocks/generated/trezorui.py @@ -51,14 +51,12 @@ class Display: The icon needs to be in TREZOR Optimized Image Format (TOIF) - gray-scale mode. ''' - def loader(self, progress: int, yoffset: int, fgcolor: int, bgcolor: int, icon: bytes = None, iconfgcolor: int = None, slice_span: int = None) -> None: + def loader(self, progress: int, yoffset: int, fgcolor: int, bgcolor: int, icon: bytes = None, iconfgcolor: int = None) -> None: ''' Renders a rotating loader graphic. Progress determines its position (0-1000), fgcolor is used as foreground color, bgcolor as background. When icon and iconfgcolor are provided, an icon is drawn in the middle using the color specified in iconfgcolor. Icon needs to be of exactly LOADER_ICON_SIZE x LOADER_ICON_SIZE pixels size. - If slice_span is defined the progress is sliced to emulate indeterminate - loader. ''' def print(self, text: str) -> None: From c761351afafc0fc0b398653858508b83e97d59c6 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 23 Apr 2019 19:15:42 +0200 Subject: [PATCH 35/78] core: add indeterminate flag to display_loader --- core/embed/bootloader/bootui.c | 16 +++++----- core/embed/extmod/modtrezorui/display.c | 22 ++++++++++---- core/embed/extmod/modtrezorui/display.h | 7 +++-- .../extmod/modtrezorui/modtrezorui-display.h | 30 ++++++++++--------- core/src/apps/cardano/layout/progress.py | 4 +-- core/src/apps/common/mnemonic.py | 4 +-- core/src/apps/monero/layout/confirms.py | 16 +++++----- core/src/apps/wallet/sign_tx/progress.py | 4 +-- core/src/trezor/pin.py | 2 +- core/src/trezor/ui/loader.py | 7 +++-- 10 files changed, 63 insertions(+), 49 deletions(-) diff --git a/core/embed/bootloader/bootui.c b/core/embed/bootloader/bootui.c index e5e84b6a6c..fc2a387739 100644 --- a/core/embed/bootloader/bootui.c +++ b/core/embed/bootloader/bootui.c @@ -236,7 +236,7 @@ void ui_screen_install_confirm_newvendor(const vendor_header *const vhdr, void ui_screen_install(void) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); - display_loader(0, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_install, + display_loader(0, false, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_install, sizeof(toi_icon_install), COLOR_BLACK); display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24, "Installing firmware", -1, FONT_NORMAL, COLOR_BLACK, @@ -244,13 +244,13 @@ void ui_screen_install(void) { } void ui_screen_install_progress_erase(int pos, int len) { - display_loader(250 * pos / len, -20, COLOR_BL_PROCESS, COLOR_WHITE, + display_loader(250 * pos / len, false, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_install, sizeof(toi_icon_install), COLOR_BLACK); } void ui_screen_install_progress_upload(int pos) { - display_loader(pos, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_install, - sizeof(toi_icon_install), COLOR_BLACK); + display_loader(pos, false, -20, COLOR_BL_PROCESS, COLOR_WHITE, + toi_icon_install, sizeof(toi_icon_install), COLOR_BLACK); } // wipe UI @@ -274,14 +274,14 @@ void ui_screen_wipe_confirm(void) { void ui_screen_wipe(void) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); - display_loader(0, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_wipe, + display_loader(0, false, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_wipe, sizeof(toi_icon_wipe), COLOR_BLACK); display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24, "Wiping device", -1, FONT_NORMAL, COLOR_BLACK, COLOR_WHITE); } void ui_screen_wipe_progress(int pos, int len) { - display_loader(1000 * pos / len, -20, COLOR_BL_PROCESS, COLOR_WHITE, + display_loader(1000 * pos / len, false, -20, COLOR_BL_PROCESS, COLOR_WHITE, toi_icon_wipe, sizeof(toi_icon_wipe), COLOR_BLACK); } @@ -300,7 +300,7 @@ void ui_screen_done(int restart_seconds, secbool full_redraw) { if (sectrue == full_redraw) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); } - display_loader(1000, -20, COLOR_BL_DONE, COLOR_WHITE, toi_icon_done, + display_loader(1000, false, -20, COLOR_BL_DONE, COLOR_WHITE, toi_icon_done, sizeof(toi_icon_done), COLOR_BLACK); if (secfalse == full_redraw) { display_bar(0, DISPLAY_RESY - 24 - 18, 240, 23, COLOR_WHITE); @@ -313,7 +313,7 @@ void ui_screen_done(int restart_seconds, secbool full_redraw) { void ui_screen_fail(void) { display_bar(0, 0, DISPLAY_RESX, DISPLAY_RESY, COLOR_WHITE); - display_loader(1000, -20, COLOR_BL_FAIL, COLOR_WHITE, toi_icon_fail, + display_loader(1000, false, -20, COLOR_BL_FAIL, COLOR_WHITE, toi_icon_fail, sizeof(toi_icon_fail), COLOR_BLACK); display_text_center(DISPLAY_RESX / 2, DISPLAY_RESY - 24, "Failed! Please, reconnect.", -1, FONT_NORMAL, diff --git a/core/embed/extmod/modtrezorui/display.c b/core/embed/extmod/modtrezorui/display.c index eba7ecfd28..db6c17898d 100644 --- a/core/embed/extmod/modtrezorui/display.c +++ b/core/embed/extmod/modtrezorui/display.c @@ -316,9 +316,9 @@ static void inflate_callback_loader(uint8_t byte, uint32_t pos, #endif -void display_loader(uint16_t progress, int yoffset, uint16_t fgcolor, - uint16_t bgcolor, const uint8_t *icon, uint32_t iconlen, - uint16_t iconfgcolor) { +void display_loader(uint16_t progress, bool indeterminate, int yoffset, + uint16_t fgcolor, uint16_t bgcolor, const uint8_t *icon, + uint32_t iconlen, uint16_t iconfgcolor) { #if TREZOR_MODEL == T uint16_t colortable[16], iconcolortable[16]; set_color_table(colortable, fgcolor, bgcolor); @@ -362,6 +362,7 @@ void display_loader(uint16_t progress, int yoffset, uint16_t fgcolor, } // inside of circle - draw glyph #define LOADER_ICON_CORNER_CUT 2 +#define LOADER_INDETERMINATE_WIDTH 125 if (icon && mx + my > (((LOADER_ICON_SIZE / 2) + LOADER_ICON_CORNER_CUT) * 2) && mx >= img_loader_size - (LOADER_ICON_SIZE / 2) && @@ -378,10 +379,19 @@ void display_loader(uint16_t progress, int yoffset, uint16_t fgcolor, PIXELDATA(iconcolortable[c]); } else { uint8_t c; - if (progress > a) { - c = (img_loader[my][mx] & 0x00F0) >> 4; + if (indeterminate) { + uint16_t diff = (progress > a) ? (progress - a) : (a - progress); + if (diff < LOADER_INDETERMINATE_WIDTH) { + c = (img_loader[my][mx] & 0x00F0) >> 4; + } else { + c = img_loader[my][mx] & 0x000F; + } } else { - c = img_loader[my][mx] & 0x000F; + if (progress > a) { + c = (img_loader[my][mx] & 0x00F0) >> 4; + } else { + c = img_loader[my][mx] & 0x000F; + } } PIXELDATA(colortable[c]); } diff --git a/core/embed/extmod/modtrezorui/display.h b/core/embed/extmod/modtrezorui/display.h index 578025d49f..baffc93001 100644 --- a/core/embed/extmod/modtrezorui/display.h +++ b/core/embed/extmod/modtrezorui/display.h @@ -21,6 +21,7 @@ #define __DISPLAY_H__ #include +#include #if TREZOR_MODEL == T @@ -82,9 +83,9 @@ void display_avatar(int x, int y, const void *data, int datalen, uint16_t fgcolor, uint16_t bgcolor); void display_icon(int x, int y, int w, int h, const void *data, int datalen, uint16_t fgcolor, uint16_t bgcolor); -void display_loader(uint16_t progress, int yoffset, uint16_t fgcolor, - uint16_t bgcolor, const uint8_t *icon, uint32_t iconlen, - uint16_t iconfgcolor); +void display_loader(uint16_t progress, bool indeterminate, int yoffset, + uint16_t fgcolor, uint16_t bgcolor, const uint8_t *icon, + uint32_t iconlen, uint16_t iconfgcolor); #ifndef TREZOR_PRINT_DISABLE void display_print_color(uint16_t fgcolor, uint16_t bgcolor); diff --git a/core/embed/extmod/modtrezorui/modtrezorui-display.h b/core/embed/extmod/modtrezorui/modtrezorui-display.h index 862cd11991..319b1308c0 100644 --- a/core/embed/extmod/modtrezorui/modtrezorui-display.h +++ b/core/embed/extmod/modtrezorui/modtrezorui-display.h @@ -197,8 +197,8 @@ STATIC mp_obj_t mod_trezorui_Display_icon(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_icon_obj, 6, 6, mod_trezorui_Display_icon); -/// def loader(self, progress: int, yoffset: int, fgcolor: int, bgcolor: int, -/// icon: bytes = None, iconfgcolor: int = None) -> None: +/// def loader(self, progress: int, indeterminate: bool, yoffset: int, fgcolor: +/// int, bgcolor: int, icon: bytes = None, iconfgcolor: int = None) -> None: /// ''' /// Renders a rotating loader graphic. /// Progress determines its position (0-1000), fgcolor is used as foreground @@ -210,12 +210,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_icon_obj, 6, 6, STATIC mp_obj_t mod_trezorui_Display_loader(size_t n_args, const mp_obj_t *args) { mp_int_t progress = mp_obj_get_int(args[1]); - mp_int_t yoffset = mp_obj_get_int(args[2]); - mp_int_t fgcolor = mp_obj_get_int(args[3]); - mp_int_t bgcolor = mp_obj_get_int(args[4]); - if (n_args > 5) { // icon provided + bool indeterminate = args[2] == mp_const_true; + mp_int_t yoffset = mp_obj_get_int(args[3]); + mp_int_t fgcolor = mp_obj_get_int(args[4]); + mp_int_t bgcolor = mp_obj_get_int(args[5]); + if (n_args > 6) { // icon provided mp_buffer_info_t icon; - mp_get_buffer_raise(args[5], &icon, MP_BUFFER_READ); + mp_get_buffer_raise(args[6], &icon, MP_BUFFER_READ); const uint8_t *data = icon.buf; if (icon.len < 8 || memcmp(data, "TOIg", 4) != 0) { mp_raise_ValueError("Invalid image format"); @@ -230,20 +231,21 @@ STATIC mp_obj_t mod_trezorui_Display_loader(size_t n_args, mp_raise_ValueError("Invalid size of data"); } uint16_t iconfgcolor; - if (n_args > 6) { // icon color provided - iconfgcolor = mp_obj_get_int(args[6]); + if (n_args > 7) { // icon color provided + iconfgcolor = mp_obj_get_int(args[7]); } else { iconfgcolor = ~bgcolor; // invert } - display_loader(progress, yoffset, fgcolor, bgcolor, icon.buf, icon.len, - iconfgcolor); + display_loader(progress, indeterminate, yoffset, fgcolor, bgcolor, icon.buf, + icon.len, iconfgcolor); } else { - display_loader(progress, yoffset, fgcolor, bgcolor, NULL, 0, 0); + display_loader(progress, indeterminate, yoffset, fgcolor, bgcolor, NULL, 0, + 0); } return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_loader_obj, 5, - 7, mod_trezorui_Display_loader); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorui_Display_loader_obj, 6, + 8, mod_trezorui_Display_loader); /// def print(self, text: str) -> None: /// ''' diff --git a/core/src/apps/cardano/layout/progress.py b/core/src/apps/cardano/layout/progress.py index 0068f90c13..fe08d6f0c9 100644 --- a/core/src/apps/cardano/layout/progress.py +++ b/core/src/apps/cardano/layout/progress.py @@ -24,5 +24,5 @@ def report_init(text): def report(): - p = int(1000 * _progress / _steps) - ui.display.loader(p, 18, ui.WHITE, ui.BG) + p = 1000 * _progress // _steps + ui.display.loader(p, False, 18, ui.WHITE, ui.BG) diff --git a/core/src/apps/common/mnemonic.py b/core/src/apps/common/mnemonic.py index fc266a0c46..80bb8f599e 100644 --- a/core/src/apps/common/mnemonic.py +++ b/core/src/apps/common/mnemonic.py @@ -44,6 +44,6 @@ def _start_progress(): def _render_progress(progress: int, total: int): - p = int(1000 * progress / total) - ui.display.loader(p, 18, ui.WHITE, ui.BG) + p = 1000 * progress // total + ui.display.loader(p, False, 18, ui.WHITE, ui.BG) ui.display.refresh() diff --git a/core/src/apps/monero/layout/confirms.py b/core/src/apps/monero/layout/confirms.py index b02819e1df..836ac12ba5 100644 --- a/core/src/apps/monero/layout/confirms.py +++ b/core/src/apps/monero/layout/confirms.py @@ -144,8 +144,8 @@ async def transaction_step(state, step, sub_step=None): text = Text("Signing transaction", ui.ICON_SEND, icon_color=ui.BLUE) text.render() - p = int(1000.0 * state.progress_cur / state.progress_total) - ui.display.loader(p, -4, ui.WHITE, ui.BG) + p = 1000 * state.progress_cur // state.progress_total + ui.display.loader(p, False, -4, ui.WHITE, ui.BG) ui.display.text_center(ui.WIDTH // 2, 210, info[0], ui.NORMAL, ui.FG, ui.BG) if len(info) > 1: ui.display.text_center(ui.WIDTH // 2, 235, info[1], ui.NORMAL, ui.FG, ui.BG) @@ -160,8 +160,8 @@ async def keyimage_sync_step(ctx, current, total_num): text = Text("Syncing", ui.ICON_SEND, icon_color=ui.BLUE) text.render() - p = (int(1000.0 * (current + 1) / total_num)) if total_num > 0 else 0 - ui.display.loader(p, 18, ui.WHITE, ui.BG) + p = (1000 * (current + 1) // total_num) if total_num > 0 else 0 + ui.display.loader(p, False, 18, ui.WHITE, ui.BG) ui.display.refresh() @@ -173,11 +173,9 @@ async def live_refresh_step(ctx, current): text = Text("Refreshing", ui.ICON_SEND, icon_color=ui.BLUE) text.render() - step = 6 - p = int(1000.0 * (current / step)) % 1000 - if p == 0 and current > 0: - p = 1000 + step = 8 + p = (1000 * current // step) % 1000 - ui.display.loader(p, 18, ui.WHITE, ui.BG, None, 0, 1000 // step) + ui.display.loader(p, True, 18, ui.WHITE, ui.BG, None, 0) ui.display.text_center(ui.WIDTH // 2, 145, "%d" % current, ui.NORMAL, ui.FG, ui.BG) ui.display.refresh() diff --git a/core/src/apps/wallet/sign_tx/progress.py b/core/src/apps/wallet/sign_tx/progress.py index 80918217fe..00c281660b 100644 --- a/core/src/apps/wallet/sign_tx/progress.py +++ b/core/src/apps/wallet/sign_tx/progress.py @@ -24,5 +24,5 @@ def report_init(): def report(): - p = int(1000 * _progress / _steps) - ui.display.loader(p, 18, ui.WHITE, ui.BG) + p = 1000 * _progress // _steps + ui.display.loader(p, False, 18, ui.WHITE, ui.BG) diff --git a/core/src/trezor/pin.py b/core/src/trezor/pin.py index e633605750..10d78cf650 100644 --- a/core/src/trezor/pin.py +++ b/core/src/trezor/pin.py @@ -18,7 +18,7 @@ def show_pin_timeout(seconds: int, progress: int, message: str) -> bool: ui.display.text_center( ui.WIDTH // 2, 37, message, ui.BOLD, ui.FG, ui.BG, ui.WIDTH ) - ui.display.loader(progress, 0, ui.FG, ui.BG) + ui.display.loader(progress, False, 0, ui.FG, ui.BG) if seconds == 0: ui.display.text_center( ui.WIDTH // 2, ui.HEIGHT - 22, "Done", ui.BOLD, ui.FG, ui.BG, ui.WIDTH diff --git a/core/src/trezor/ui/loader.py b/core/src/trezor/ui/loader.py index 6e19c8c999..9e81749fda 100644 --- a/core/src/trezor/ui/loader.py +++ b/core/src/trezor/ui/loader.py @@ -47,12 +47,15 @@ class Loader(ui.Widget): else: s = self.normal_style if s["icon"] is None: - ui.display.loader(r, -24, s["fg-color"], s["bg-color"]) + ui.display.loader(r, False, -24, s["fg-color"], s["bg-color"]) elif s["icon-fg-color"] is None: - ui.display.loader(r, -24, s["fg-color"], s["bg-color"], res.load(s["icon"])) + ui.display.loader( + r, False, -24, s["fg-color"], s["bg-color"], res.load(s["icon"]) + ) else: ui.display.loader( r, + False, -24, s["fg-color"], s["bg-color"], From a89a3bf6db30f3eea53eaf78c601b3d4b4a66a8f Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 23 Apr 2019 19:54:03 +0200 Subject: [PATCH 36/78] core: fix last commit --- core/embed/extmod/modtrezorui/display.c | 6 +++--- core/src/apps/monero/layout/confirms.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/embed/extmod/modtrezorui/display.c b/core/embed/extmod/modtrezorui/display.c index db6c17898d..0f32d91020 100644 --- a/core/embed/extmod/modtrezorui/display.c +++ b/core/embed/extmod/modtrezorui/display.c @@ -362,7 +362,7 @@ void display_loader(uint16_t progress, bool indeterminate, int yoffset, } // inside of circle - draw glyph #define LOADER_ICON_CORNER_CUT 2 -#define LOADER_INDETERMINATE_WIDTH 125 +#define LOADER_INDETERMINATE_WIDTH 100 if (icon && mx + my > (((LOADER_ICON_SIZE / 2) + LOADER_ICON_CORNER_CUT) * 2) && mx >= img_loader_size - (LOADER_ICON_SIZE / 2) && @@ -380,8 +380,8 @@ void display_loader(uint16_t progress, bool indeterminate, int yoffset, } else { uint8_t c; if (indeterminate) { - uint16_t diff = (progress > a) ? (progress - a) : (a - progress); - if (diff < LOADER_INDETERMINATE_WIDTH) { + uint16_t diff = (progress > a) ? (progress - a) : (1000 + progress - a); + if (diff < LOADER_INDETERMINATE_WIDTH || diff > 1000 - LOADER_INDETERMINATE_WIDTH) { c = (img_loader[my][mx] & 0x00F0) >> 4; } else { c = img_loader[my][mx] & 0x000F; diff --git a/core/src/apps/monero/layout/confirms.py b/core/src/apps/monero/layout/confirms.py index 836ac12ba5..64bd7ccf48 100644 --- a/core/src/apps/monero/layout/confirms.py +++ b/core/src/apps/monero/layout/confirms.py @@ -176,6 +176,6 @@ async def live_refresh_step(ctx, current): step = 8 p = (1000 * current // step) % 1000 - ui.display.loader(p, True, 18, ui.WHITE, ui.BG, None, 0) + ui.display.loader(p, True, 18, ui.WHITE, ui.BG) ui.display.text_center(ui.WIDTH // 2, 145, "%d" % current, ui.NORMAL, ui.FG, ui.BG) ui.display.refresh() From 2f77c53781b8f7eaf0e9d5b8065902559324b218 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 23 Apr 2019 19:55:18 +0200 Subject: [PATCH 37/78] core: format last change --- core/embed/extmod/modtrezorui/display.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/embed/extmod/modtrezorui/display.c b/core/embed/extmod/modtrezorui/display.c index 0f32d91020..fedda40abf 100644 --- a/core/embed/extmod/modtrezorui/display.c +++ b/core/embed/extmod/modtrezorui/display.c @@ -380,8 +380,10 @@ void display_loader(uint16_t progress, bool indeterminate, int yoffset, } else { uint8_t c; if (indeterminate) { - uint16_t diff = (progress > a) ? (progress - a) : (1000 + progress - a); - if (diff < LOADER_INDETERMINATE_WIDTH || diff > 1000 - LOADER_INDETERMINATE_WIDTH) { + uint16_t diff = + (progress > a) ? (progress - a) : (1000 + progress - a); + if (diff < LOADER_INDETERMINATE_WIDTH || + diff > 1000 - LOADER_INDETERMINATE_WIDTH) { c = (img_loader[my][mx] & 0x00F0) >> 4; } else { c = img_loader[my][mx] & 0x000F; From 914ac8a2bde9f3b2724422ec242dc10a8e15d98f Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 23 Apr 2019 20:11:59 +0200 Subject: [PATCH 38/78] core: fix unit tests --- core/tests/test_apps.wallet.address.py | 4 ++-- core/tests/test_trezor.ui.display.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/tests/test_apps.wallet.address.py b/core/tests/test_apps.wallet.address.py index 2df72c70ea..11b5e83dcf 100644 --- a/core/tests/test_apps.wallet.address.py +++ b/core/tests/test_apps.wallet.address.py @@ -130,7 +130,7 @@ class TestAddress(unittest.TestCase): ([49 | HARDENED], InputScriptType.SPENDP2SHWITNESS), # invalid length ([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0 | HARDENED], InputScriptType.SPENDP2SHWITNESS), # too many HARDENED ([49 | HARDENED, 0 | HARDENED], InputScriptType.SPENDP2SHWITNESS), # invalid length - ([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0, 0], InputScriptType.SPENDP2SHWITNESS), # invalid length + ([49 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0, 0, 0, 0], InputScriptType.SPENDP2SHWITNESS), # invalid length ([49 | HARDENED, 123 | HARDENED, 0 | HARDENED, 0, 0, 0], InputScriptType.SPENDP2SHWITNESS), # invalid slip44 ([49 | HARDENED, 0 | HARDENED, 1000 | HARDENED, 0, 0], InputScriptType.SPENDP2SHWITNESS), # account too high ([49 | HARDENED, 0 | HARDENED, 1 | HARDENED, 2, 0], InputScriptType.SPENDP2SHWITNESS), # invalid y @@ -172,7 +172,7 @@ class TestAddress(unittest.TestCase): ([49 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 0], InputScriptType.SPENDP2SHWITNESS), # bch is not segwit coin so 49' is not allowed ([84 | HARDENED, 145 | HARDENED, 1 | HARDENED, 0, 1], InputScriptType.SPENDWITNESS), # and neither is 84' ([44 | HARDENED, 145 | HARDENED], InputScriptType.SPENDADDRESS), # invalid length - ([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 0, 0], InputScriptType.SPENDADDRESS), # invalid length + ([44 | HARDENED, 145 | HARDENED, 0 | HARDENED, 0, 0, 0, 0], InputScriptType.SPENDADDRESS), # invalid length ([44 | HARDENED, 123 | HARDENED, 0 | HARDENED, 0, 0, 0], InputScriptType.SPENDADDRESS), # invalid slip44 ([44 | HARDENED, 145 | HARDENED, 1000 | HARDENED, 0, 0], InputScriptType.SPENDADDRESS), # account too high ([44 | HARDENED, 145 | HARDENED, 1 | HARDENED, 2, 0], InputScriptType.SPENDADDRESS), # invalid y diff --git a/core/tests/test_trezor.ui.display.py b/core/tests/test_trezor.ui.display.py index 87c1c27dac..d326d735fe 100644 --- a/core/tests/test_trezor.ui.display.py +++ b/core/tests/test_trezor.ui.display.py @@ -39,7 +39,7 @@ class TestDisplay(unittest.TestCase): display.qrcode(0, 0, 'Test', 4) def test_loader(self): - display.loader(333, 0, 0xFFFF, 0x0000) + display.loader(333, False, 0, 0xFFFF, 0x0000) def test_orientation(self): for o in [0, 90, 180, 270]: From 4ec3f363868cd9f585ad1d5b087f77f3e9347046 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 23 Apr 2019 20:34:00 +0200 Subject: [PATCH 39/78] core: fix multisig change device test --- .../tests/device_tests/test_multisig_change.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/python/trezorlib/tests/device_tests/test_multisig_change.py b/python/trezorlib/tests/device_tests/test_multisig_change.py index 957e09c679..d207771aed 100644 --- a/python/trezorlib/tests/device_tests/test_multisig_change.py +++ b/python/trezorlib/tests/device_tests/test_multisig_change.py @@ -127,15 +127,7 @@ class TestMultisigChange(TrezorTest): proto.TxRequest( request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=0), - ) - ] - if TREZOR_VERSION != 1: - # trezor 1 does not have UnknownDerivationPath implemented - resp.append( - proto.ButtonRequest(code=proto.ButtonRequestType.UnknownDerivationPath) - ) - - resp += [ + ), proto.TxRequest( request_type=proto.RequestType.TXMETA, details=proto.TxRequestDetailsType(tx_hash=inp1.prev_hash), @@ -162,14 +154,6 @@ class TestMultisigChange(TrezorTest): request_type=proto.RequestType.TXINPUT, details=proto.TxRequestDetailsType(request_index=1), ), - ] - if TREZOR_VERSION != 1: - # trezor 1 does not have UnknownDerivationPath implemented - resp.append( - proto.ButtonRequest(code=proto.ButtonRequestType.UnknownDerivationPath) - ) - - resp += [ proto.TxRequest( request_type=proto.RequestType.TXMETA, details=proto.TxRequestDetailsType(tx_hash=inp2.prev_hash), From e06768a7a73e7c2121081bf248c734b549aa38b0 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 23 Apr 2019 21:23:52 +0200 Subject: [PATCH 40/78] legacy: use local copy of the previously built bootloader --- legacy/firmware/Makefile | 2 +- legacy/firmware/bl_data.py | 2 +- legacy/firmware/bootloader.dat | Bin 0 -> 32768 bytes 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 legacy/firmware/bootloader.dat diff --git a/legacy/firmware/Makefile b/legacy/firmware/Makefile index a21f0cd26b..9e34ef8434 100644 --- a/legacy/firmware/Makefile +++ b/legacy/firmware/Makefile @@ -130,7 +130,7 @@ CFLAGS += -DUSE_MONERO=0 @printf " MAKO $@\n" $(Q)$(PYTHON) ../vendor/trezor-common/tools/cointool.py render $@.mako -bl_data.h: bl_data.py ../bootloader/bootloader.bin +bl_data.h: bl_data.py bootloader.dat @printf " PYTHON bl_data.py\n" $(Q)$(PYTHON) bl_data.py diff --git a/legacy/firmware/bl_data.py b/legacy/firmware/bl_data.py index 0ba5733cf1..c719d29e79 100755 --- a/legacy/firmware/bl_data.py +++ b/legacy/firmware/bl_data.py @@ -1,7 +1,7 @@ #!/usr/bin/env python from hashlib import sha256 -fn = "../bootloader/bootloader.bin" +fn = "bootloader.dat" data = open(fn, "rb").read() if len(data) > 32768: diff --git a/legacy/firmware/bootloader.dat b/legacy/firmware/bootloader.dat new file mode 100644 index 0000000000000000000000000000000000000000..334e31461fdfaf9abb83f2804a46a700e351d4a4 GIT binary patch literal 32768 zcmeFZdwi2c_CG$)CAT(h)22XaE9BBb+Z5;pQX`7;_5a~ zlp^j5ExOQp!8;d)wpv$IEGxL{bvFUoErJVP)<~-B6QJg4+9tpEJV}w={d`})&-eBF z_t)2JI+x6xIdkUBnKNh3JW`Y~v=Yq@Yt{ zLk&KtBfR*@K$V=%K>E)E#GF|5$$7r?lk@DB1m`E`$!(g-gbGevAdP9WxZ7-umlNNP zj_o-7t=L3*B+UEvTZsC8gf@grcM)}{TRUG{Jy|fhwKYu34B)V6fEC-KmInPH%YDSs z?=5Oz+RSlSk4L!<@vv3N7$~gW4>5#vCpfosWVtm}s#ap=ThzdZFuxSg#yLd9+yRAfu9KK8RSCu$Fq%-9nDeW~$>|`{ z6RU18lo&a&FU*OJ14KL+Nx>`-Lz_*9IXxcX1Vg;#XJLylF5dD}q_eBO@TYbZ9gfla z<8^fHGU}r*%(>xJ%=zK?-1`t`uO;f;h(ANPa$dedo)Ff6C2t@+qgH=U&e8dHp#DYt z7DiBD4igd55sV1>?&NCTWOgT2CRZ@63h|3b{UNP0!>x7h?kBZ7ZIAR217nDB5{D7o z6(NR@pY71a@%$Pvh=-_o1ac;s(Djj9y%pS{Y0Yttb>}#D*{<%*aWebHH<&AKZg{eR z6<-}*KklP0-cr!33`S@EBE01yZ^`LZ1fw&45pG6$T<;&i5eIFV-UGG_@0j__{xMZc zB%^LL<}4I$IK=E9g?zp)4f!9!GmuY1K2Mj7ygyvjNemj9^C`Y#Bif3PKT%#ulom3c zL&-!<@dbUq1^!l~_pC+{;v)!@=0)9Os$aRFoo{y6T=J|)5*UHm^NGYyRfroSR^ygE ztoX}et8o|NI>dVs-#Sdp1p;a>XZE}zeO>Zf>6;Q(yk_`wiO(5XhsTGL0CQxHRozL> zq{>V;(~?xBb2rz?Y7Hw&%z3(Z1 zdv{1*m==%YErm#ZS#r}je$vF=?Y3O+E}PlgW;4pPUm_WFe7}0_C{TAs_>Dtolg8|4 zX7c?RYw4_{pu8}=6y;yW%k$)N4ayDi*(i;VV$m=!&xUxIj)Bsi(w)*<)t%^M9F!K# zHl>ep938m|BcCy>lNbvrxZov*i*X9X)`-~63#Qn(_tjjwf1aU&4;oaOR#x0NWH8Od zj48zR!!drK7+3%;b@(ec*Y8=Gt!?k(I+>|V?#njrOiE$;=V=vve#~BR9#4YArFJm; zSn+1SVfL!}%5)0x+F_D+%EwIm#&0mH`W(7w=mp3hua}v67W^f~*8^R5G5$%eeh{lb zA#jkdoQbqM+q73>)q;7?!C8c1wzP|)ok%ZLw}3>Rx%N*nc26&SOn;|NY=cNFZFT{ zEkk->N@@S@tyGTdWcXI20<@y=`5|o;vtQ?4=uW$jJDOP^gGBM1`yY_*1f&r@{-+Ep z^*>>!1sK~63hRTPs z&hg?7jO87KnbK7ii z?^tke*I<$R1}R5Jeq;#W=PB|KUOWw?TxyPsB@6+#eShfS|W^r(rkat%b?k=jxntz0yw}(H5?&o%_|WK zd}pQXrk%jEOi)~%;w@J3hmENXbMv!&3meS&8NTiKKg#E87`>jcDEpWRyDDGr zO7@ToeV)mO3_>*28ez$r>NcZ;gzRM@Ym|8#&QB$V}$s@`I5Je#K;~Sg^51 zBlWw36{Hn9RI?p(&@~th-8;w$B+u^U#V>|=F%eYVKRi#^06NYa zigAuTvc~uT@-vaA95g$^PpG+c-+eWrMsrp=vms$n8GNQcyu5R8AZP{D+##`xH8NN8 z;)IBH@((^l$@~Px)`Dnq1GftgKRO1J>-S zD(oOFtMEH3+yWe~AEYC{C9=U!%*$m+%d3_{?_o=pS1kr47izko`;=j1nn5K-W)(X( z&^A>O+JCiE@i^aF?JS|1&dLSOVu5L0;#>fllmhdD!6klTTm)??IOGv(5w9Lr2M8&0 z(ed(P?Jz%qhit47RT^F#GkjL6j*tGn%cFmBaAfo^4b#yR<6Pj~KFEv3phC(p#pNHv zta+@T1qa-xlUPaK)oAN4^I~8JUnB*;zVZR^$)m-tV zKgQc!pA5xAf`IhM8s65Dn(q5Ky~b! z5Z{JC_3k~0>0bL=#3SqG8}vul&O0bOiEs|8$j2i^XMjDR)}SG$%%Pnp}U8hz|1VQvHGV zBy!}AZI)tPS#7rbxMsnQ+!`Y2Yo5mwtYL7~AOl z4?ex1!E3AQ zXTvJ&s$^a(%0H6~rj|+-WSeiGxs%S1YB<$jBQokQz-J!7J3QE7``Y&#q^1sa*v?|D zj2n8Wh8N3+c+o%jMv$cBdCy9{+cz#K60Vo{EFP%~fY5&jx)%`2tQH&+(S+LV^$UTUYEBDxCIS%-O4h!5}Lt!@D3S z&q_<5VM@0kRU^*T%tqUD9mQ_cD%xesKi(!WMDHd_Yd~)}jWV5!DdjC6AvN09iFHyF z*v^&ooRxMTq2cJoB(7RA6P(wIi(Ol~XuZ@!ke@@jRfmF%CLzcqczvZ5x}nw9c6;Mh5$GDE>yfTEx>o5LrR&v%-)12g(0&lQ0qujXJ-X(xU)iTRH{&Eb zwORqqcS#uQZ5`WM>@2BbS~+uZC292t#m?diUfe!dTxoD|;^s(ErNPaKC!$jj^P*EC zMjH1?g*8}(+i3bZnP&%ldCsf`#?s$AyMb6P_$ojXE=Ps7DcQT+hLEmbua29)pMJ{WE)<=w-`F;Z{?i((p=XAh)-%It0QlyT0Y5s_p zG0(0)?58@~RM2lK#yJ7BC`H%_I+8}BenmypD&*<;trs+)GUpWH&k%^3wW_*RPU6mV zlQ)@`m>$ibmmRPH(9^?+Zgg=T$!QjHh13Nkn5`;-<~;+e>We4|^B1qO=Ze;{3Mswz zSa6(zovJG|gcbcS1$otXKBleI{k~+VrB(`7Tpguam$-rAtLi33HS`#!g&l7wvPt~- zjkZD5r#BjzLLiT=oWwv^d*1U8W)=ar@03cY)m+-Bw#ZS?ev zFh0|5C}LWOX`#f|Y_Qn^JUig)3U3r?KUPhwr*XhaC+oSPZjzH)l`GJm)aHef*;Ol< z>;GVz+QUpB6NEsS;t@He4@OIcfP?@3II~Ode!;dwKKI6O2t}y%Bu2M&fTyjZ(98lL z)l!H|={;7B#Su{O{l3@2n?wr1s)@Bd6=6H-<84FT$qW7%BwFZdGnpMEXOWasyI9Jp z*9H`d6zes1EAq@#P2mNZ#<7tcx}xvtIIEjLT@o~~sqRG*e@!JhdeM51QMAs>w{Np8 zbY1iK)6}$XV97ORTdT*Qrz@xo)-t zn3R`E{I3_OUoK<&TGxG#ad9g~@T!(aMtNb<`HST_3pNSZe{;YOxy0NhsKzsWT=I1i z|Mblgzvb<+ZwEDHAzuz4{=N346uz`P@Sv(D@Yrv1EOUf=1iJtI)5n-uU^8A|f`;T; zX`+j^WTtDTd^@Nt`%!LNE%A*^=}Rditu=D%fBLSwg=zZ?(kw=?=&;Pwn<;LO2vj2` z6Kh0LwMKL~&q~jq+d=+;x({~LN~T(_l^eUv{XAx5l2*~zG_7grjS_!5DJ>6@r7sLB zZ)?v26@-Us-F+$*>&Pjt3A8rQ8Wn-f>Wsbwt|svN^`w>7tn5qVm{vL`pPwgNGWs}m zWsqpz=u>j5(UwA4UN+kG4F z+~n$QuoN;a1;RX`NMNqPeil_t3i*in=g=U^5tveqMZ)}y%+3Qb8p!LK+80#9Xd$Kg zF`Py#Owc&?=y5Vz7h(403sh>1ib%Y%Fwq_#V!h?G`HucI!vED+A3MT7PVi z2sMG7zjpQ|s79j(Z40V{VmwD%(H5&wzRmU}#`P10bi>>gXQFl8S|@l|neAqF@Z+zQ z_+QMozh=vI4fvQ{S#cQ@8cG!?N7P%Rp{ILB_Jug_#$%r0{rr!8^lMS%WT zd~BiaHKMf|+Fdf4kWw=Tl&O^_)N1<_{9?p>Um3p;@hw?*JFp(hY)CU#YbMtOcKjxQ zzUf(&O8Xm!sMMr<80t=0z~q#=DO5_WxZJE?<@i3UFb>09OF0lQ?*0KVsI`W|m~|P3 z2-!!iiVE1bQov{WVWm(f%oHfk{WW^KP#_e`>G{Jk+aGP6HT08R2CKL{Nhn#c16DE~ z)($;%g~LM@Az_~aGGok$HRaDM%VxovlHq2IGh7MKkSn9~|B7My{};n#Vp1?Kp++31 zxk2K$s;96%sSZ;7rRr~eR1$sR%SYePyQ9o2 zW!(G~Qw!5m`(;#q_G%k=0POsBlNd1Mb4YqUa>aLoAv z^dHp$GsaXCF$zm54$+jotoe6>!F{cyuRRL+qzJPYSbHw)(rjs(MS!%Phwy`M>>k#{ z3D7>R%cQj0cY_8-5oUr$)MPKQC20j#XzM|r8mm5|#ni%>9u}Su#D_U--V`eRu zOgfT(w-+`k0*#~KJ})sUb~ameHX=58jIUlL(AB=Q?vJopse_Di30CDpwwy3?>7LU} zoRz;wiywP# zsGAV@<}SX4Z+jT|&piaA!o5eD+QjeLZnIwe#`CzX^$g^1*>xD#Eg8U_DGL%sxn#1E z$C&NRtbh4Q!3`@me0H1DEUz*mrfy)xZ^C1RDmSI)23y!iOqnZgs7nYM5|~*xtXSt~ zitq6kxI2V5t?72`ht>D_A&aM7u(EcC#20SyJF}nmsh(Il?%8n!C+W95p9*gW$ES>W zid;P8StE{D)C6uwRSYuEkbH-?MvP7)6W7{*Yc&NF?3aW5ZJ<{2GeLu50%)oVCr2EC zWPT#FwL<&EFf%mfm84*HGU_m({2g>IrVC8b&62@|Hkxk+*#?Zifq4ow)XFduB2Ov9 znt=&nwNxI^sD5X?dTe<>tt#)+s#JNvef1nVpW0rZ?)Str=4H&1hJ@K5)j$&MPARSE z-QYNmEzuPi!ra9ld>=`MBGn#)krDqskg_Ka@ri*v<5+O@Uk7gT&kapJxKj9)Q0UhM zqs7pK4D5DLo8h*9ex7AK8P}m?9`YT1ta>>lyIsohRL~inI>x^kbDtKBmK63dIV4Ay zQP#)vcF9Ocu0ddmlV$yx1Z|Nu`#dwL_G~wwCcdmrF_>ngUJm)6Uv3rfQ@=evI`X2 zQr^cfhk~jU^7z+2-S(HMbctKxpy$lvg%lxeJ~VE10rukw<5}@H0|wD*#d=H;py9CM zy#t;u!wM=rHskd9y>F&0N!pS2dztv{AS=Ey@RQ!1DCoOY)eLs{44icWPy}-b-DBu3ko_i=+0(ldaWfaIYljt|)__eQOo}pksC9ls zPgp%bwad%$sC=383CJQ|Pzy;y7Wz3IwH`uSWf8_RK3v;L<@H<9Mb51F>S_0|V)?+g z!gOJ;kn-0(-=>h*|JiPnz#84c`_KxKf;kD%P{EK97T9QL)9@kG+J`U_Q@g2VuL*sA zh*UK6(x6G;j2g&U-O#7fYdVuqX`{IZhD^e0fuMdU4fcYtW}jKm32A#F?|~zQ|Hxo0etyV|T`6s!2ESd4-qT<) zj)txqOp{v_BVCCWX=t$yEz`L_n0E!{V2hA0L}nbKT>Ke! zq}t93r1nS3ow@OOe-QKTlxXShs8Ps}`QmKkUw&HhYBWQ(F})P|#!q)>gHVam*9Lj; zQNEC{Ma18C|!;g*(lqJ7TJ6Buq-`0nDR-=v6N4y2`R_u?3rEJFOSUTY`4XAw|~MFa|!*} zp0$92iRNjX9%?@5bMV=YLI)op3VKr41XP-`fJSky z3^v1BXi@i^_i5|W>aw!9J{xCoa8`O!m}s4ilVReDn0Cd8TXD9g9tM;b=ot)r0A8Oe zlnG;S=W+M$4Kn|w3Ran~b`QtKk#fx9o+X%F#VCh;ao{xpyrGaO`CjZlZmfbiyu~#O zG^KUU4$OjfMrrohz)$BaMs!AL4a_EyWm$PN^x>fxE}@zfVDQLp25{&aAi)a7Hemzs zh~c0L<|L*ZyIHWfuK_0WWR0g`Fy${;HwZYN3KrK)`DF6UfK9kwPCbFtOc{&c%Vo6R zXlVPuJo(qZLLQEs02wh7p)h6Y)I>vneDf#ymJYe*aefG1pL`J1QXZFH+g1q+6Z-u# z4W;hMvLvFMv~pnjK}E1adnK=xLat5UJ6Yzm#RG?+J?g#I1CtM?{59oIG+6h}cB=)G zP>S~Ufe*0znS>9ztl+4916Gtz-kX5h(a^MkkruPv3Hx-YpD&m}VPn{c6%q|O2l9o< zi1!cBd?fT-bP8He-@6faSUCi;K@A;xz>F0%PneE+im?7bEY)%uvi=HOh9xRf=~}o| z##-UkLxx%R>d>cufZ3lR=&=?fq2ERm_D@GDL&!ata3B(TF!~c^axYWnZ&LO_Xwv^d z8F;f`Mdix0c@O>Nf=}mf6x73{` z)15-if+agc!s>}p{!cPz{)x)JEV&NlI<@q+kaHmROYFbJjTRb{kh3>yPpK;s3PnwH z1WW~}6pX%&9dg_M(CZAW!AR%@NPlV};6UM7Sbub{`>RCDBB6mOoPJQ2gEBhbmGVg5 zk7mhKi-eBKzh}wPwi|6|e`lkU+9;*z`H&F{hNs99?DG1op_<28Y0noV7w1>)y|6M^ zt6u3J^#Iel!Z`*OJ3c?l&58`$+RDu0gLi0{V0I!(=i*Lc>s5AjfK@McJlCh@o^TXG zVlFLP02>!>TJHbOr>^@{dZdW-H_pqnMvJ#(E_c)h6kPF&1Rt|A8d^A5=7!aRhTw_Q#ommJ4kC{kk?S^;0_Jc zrg2w1J`j)hMM}ocKKZ90uTX~-;xdVUB=hRLu(Dra-yh@?74jKlRAxdjI_u$pikO74 z=;6(w3d4he1O>PMy^A&l8B~Bf(@=i@FloO|U}lrnV?L8mD}{=6{TKU(<6pM-g!x6yR%gt% zJ9~%(bJ!8fi%zzEP#rp3FfJGaYazTATG2*m1~=v~i*aJyN^O{l13tQ&Qx1DJauWJq z0rWouat}mD${&v?f^RUiEGwE1sz*XUM4{`pH3v8~JAq5I6cI&M{=EDx)2U%Sjl+eJd}43WGAUMg{8a^-d81+N{FR}pml z50(du0L#Pw!75?GZw#bVBs2{(%V6EF!&*!L4@E+Skr63D<-O{1xglZ_c#M{o9z!b{ zFO5=7DH7T_q{@aqKapA(W(d^Nh!H)5Wa5v0+0L*bwyr|m*)`eoXS+#WMh~TVVnedP zThfITU#d`_9SNPHni-=E8c8H{VlZX_(gD)4L3TD{N$ZW(`V+0inS|noJaA}MG~lQ7 z8L@0F>!IzapWK(e5xWuf4WjfdTX3XfC+2&UkhM=G%msyChv&_D?`oO8ZG)Pd5Hg6FMC`_lGON!J$1+MkJf zH2wPUqQ3y-lTf!kvRaN$qbGHbA~$PKg^`FA;Yo7r2)l(GXj3td+UY*5Yq=(8G? z!92hk6^6aT{HOD3^T_#)5x0bYy4%=vJMNX>7fNl5@@-0IYz_Phb#Qak7Y)4+?*9?< zbLBmfSmQm5l|>ez9Y7;B}QY zz$Q$!B6Z!SKmyMc&vEGTHGQ1wb4k0~?AR_?#Z8{-Dn(0GHWN%%D_dAGg;$Fy>dbv? z-zJ{zYgQ}Tu9X~bZ>M+AmZg&8WmU^3Qu?;ceTY6|(UlmZOw*>w$c5*<)YzU|sRoj-p zuU1z&QXMscbu+lOhXUF}qa!iEB{m0MRVxEpKGm@m<66Z^TT5YfuW#N9}3O(pW$&LH^ z*d9eh;~O!#Eq12}N@0|cSp6g{=Wf{^;~%DHF{(wXvGdWmIhqx><}l($>8iNRp57sk z$bpy?xLb-z0Z#lR4j-0bOEYeG>&wSfjHzT?V=6rGq3*d@^Jve-HII&}7*z?Y7#j`s zMbj(ND@Rpv;@8q}=$u4E^2S}S<2S+xtiTDppa7mK!GjatM%c4A!Ri+MnW91g&Ek`2 z#83U0slNc#8>qfOeVn%;o{GEh3KWbJ`4nx>4;R&*A1-R3d--r)Bi(9eQ^!vuQ25}{ zE=w_1G`-1)Nm7hP^uD~ldt&uQP-sF010P6zIJZ(K@`}Xf0^iLlb5;7uC9YBRI?j7QmV;mbgaO`}`+S`k9~Ps;fSL|H=uMe(eeW33&dUaDCz@;u}$U z+`#jBjG6lIM_Z@;s;E1&x=FqdQ5s>j0p71m#5}s{NU)L2zz(u-ke0LJeyKpf4SVnb zxwL2~4|1A_EPjQDK7MbPVtYY?Z?3t~G~IY4ct5db(X~)yCF1DAL~M}z--EpYdolFu z%^g)WJB82Br?q~5UiT)`n%*j#rUB;?aUe|3!U}H{O48&KG0e1EIx~eVBktEwZCH4t&;;DfJP5Z^Q$fk*y?su@NGxlhBRi7(x8TjPaYzZFL`M%1;Zct*)~(B9Xm9|XLp#B z@k1K;5OHVts?Ji>8$J97Kbbt>JB7LXEJEk*dC*Smo;iPJjm7<72iK~^nURP~2WB^L z7ESNjZ#i+)}iHEkjPsU!l8p4gBJMi8lgw!q2CBlyg*NtF+0ic5m!p zwrvzWY^&5{ZyngkF;6w2JjvZU;4!xjY|w8KkMJJx9N8d>%tldS57?Sevq{>>HAx%v zO_ImV$g2)`N3KPml-86-^`a+bH1?)OMS2fwX3RBJzwJnKs|5A-W@wDX;MT+zMjRLA zk%~JO^Y3Nf{j9QNlNKiiF9(mXMWGOC|bF8m4OQ~ z*by63_Vm<zXU>(%Y=_cz|pIh3tTyEe#bkM=jN<4RSnZ8)uR^G>S@PO?uh zh1Yob_9J~q?zxlPJXX<;+o*f5a?G8yr)O^NAIrv`Iba)e=7avmyP0X+)Dq!@-KTrL z?~kcj5>t=YI9xLBRG5<4_I73`w`h2)MDf;+0*G|2^*isI?Go?(crhReQ z9$%_6QPv2ud`a@|#r>smcz(%(0(WTx=DLMg z7Wj0=DHV)(GnM4xK!g|zkvkC884D2ai&~9&h+mB6Vei?Y8ag=vu}(Sksm^Fb`MfA& zG$FnQ^{+xaTZZzt$hhjW(j6YY-*a2K#EgfvQhYG_HoO5xqxE(`Nk^O}L(!qtI6z57 zoE_(MczG^2;}@K`q>{%0f)quq!c#rj;N=F`owKEWZ z8Ub{1O=NWS=o&saR!PS)17jHkV7N5kIWD3yq=A+?FzTzt?pN$`8ABe`oYSXz)YC=D#=xejVk_J zPyU!*iqZ9*iAw2Hiq~(3$)M7uaL`hce=)9)(6d%@oSMHd(t36AdI7&e_|Bgs1i(`Z zWW4BFg{>xLXC@DJFx$CpH@uqOK-ce1=qnmQ8-`&J;PT%eka0djOxoxCiiFdpJ zoVl&=kzra>8T#rXvCt)4B~U6ldoyJ!s)0G#$%v- zXJs16^FIU{$uqouNCbUuxq?30?HjM4&%*zp&qA3#f|rhDn(QASk0!rTo`>r@M`$x& z#>L}^%?>yWv>_Ip68=BYy@Jij4wSTHA0|dcy+`nY?oEnt)cB(5K~;qDt9l(S(&h z)&#ywp54prB2NTeOU9s(~ckZMg{>S!4J8d0nJEnoQK6geNWVCOWb)M#( zoV=s3Bitjp}}?4HfD$Ej4|-VUAt`K&QQxN z#(dAa{f)m#t>&gB2ifGCvlFo2d37&fEa?*Q(EUy;?{`fBHP>Y`UyJ?jngiJHV)i9A zE@Oz8_uu`OQ!l+32C%AIf5PEODQ{cu8D=7SzZbnXjGIq!;;vQ)Sv7Wb?Jk@Cj9Q?Y zXr}L7`!U^%eaF&AN&IfQH>$8VGDQUemUWdCECDt@6Ux2;HZjjKeGH$?|8px_AR*rB zD{#bL2S^X47v+ok?$r4kv3JbPgC^bkM>}IWqDyifW9VFR{jU0lt;hO3w=V1|b&xE* z#7s)C@3dvs)6<{)P8hx77FK+5U}TR5^~+zX+1N41O=)gB^B4OWJCjSfh!<770(_(h z^OtG@U#M#WUuoxB?(M1xZ;I3e-p{BBe4Jg^J2#&f$c2|YBv19(lCg@wKks3tzHA#h z{j4qf^vC^;>oq}6vu*y~K3+55(O8&t=7m0WLZ+nMCImE^1jLU?WZMFyA4JSdeHOeI zofi0vDc)+E`Z>uva^W#gW5Km&NWqZ{uX-Bu$iLpo9l#G@X=cwPy-Aias z|2z}p5v%yVM?Mp8o|}KTm*nvR#*}^Kn7U5iJRc)aHx??-9E0s5L(y7ta&lESy)Y*={ z!MSUv%;!pI-|wUEXXAYnQAL#SPPYcMBro3gdW>Lh=vN0xzKT-$@)#OJcOHz5;;R4U zD6aL#VE~s6G94O2PQakpX#bbklz{tdr4e3T41EeViprD)y6Pl%5&VjqIv%t!tqH!Q zs-zk{JoHr@V+s3axcAtKTtCA51!rv+tP!$>n1~jf;g?RXzHCsDoyKPmPR=s=PeETJ`j(b_}qPO3Vean$e2;3eXubV@~znQ=3wd6(eJ zi<|u2{nlDW+%=%XiJ6gg@K(H5qG{Mf#NQ6=Mw*p%@67|dO$u47eSV-6CrKUkpSyJ& zYMmU&#{W+Svhe>1ef6}GF|o!`$lXb?TudRPA$Kcs=N2UxQ;^;`V7;&z_`M!3!97xP zMI>}GqKSEs`l#HLDOvK~(LTB?UD*jnUkVcEb5C7~(>`5<6>S)LpKO;YZzG?q+ z3Crz946}_|Ls*gq8;_AF^?E|AqX?(;*LQO{6Dl`$B)M~|n8$J}Cd}tt6M*9c zC$K4tVWJGy-fEtoAY)^iuMF~|DPI4Mt)jnviSlP7^?xIbov)VbD}&_TG4s=|j*aO5 zI1+Qy#q?%)kbvi7CvUtf{EzYI2;S2XR!PT1$3n+38siuV z8X`UWvhB=l7U$q5Tktgfukx|&mP4_3)|`)rH(VstGaL;i#`_Vw(-t(**YmBrI`sl> zQf9Dy^Hg|sUc;8=XbNxQB(|Dwz?(;&rkU!eZ6ZA9St+$B?x`%xqiE=>_}RAzYnX8t zbtekrs$9;*?kL1%4t5Y_%L;)|dj7KgeveqAm>bKg|JsWMG`x2@WoN1WDUd*NJ85pO?4a4L}bUc;K-+erk zqDDG>4r~1$>7wqY)ssLSc8-bK#9DaE0@)I#9h*OkBY1V?=3KEEvqIB^hoSgmlo1W_ z*Ya1(+9$P89_k_Prg80EjK$v@tDom%jYqnuMXLxnSH6Le&lDi5%JGaty4?C#jw)^?|@8KrrRbGSmzUg$D5#O{}61_rJU=v_e|8xn1QIMHeU@%ILreiB~H2jO27vn#}GtFU^!34bT~ z986(loP&+KTV18D)Kgw*O@#e75q|pg?Z8hfqw8{g`gT9D!9?q}tUFZI+*pv<>y^$F zZM>&dDl)9-tZZK9{-A4TIb#6axK|%1^Ber}F`nNJ#r7zwt(Pq5l-MJs(AAS*i)UKj zhW=~vd89E%J_SUBa2j7l`BZwps0mh9KKQ=sxm(Puo?B(Ul@Io+@j_>|zH@c6LD*UT z2K-m4Zz%OIn;zHCIrq~0=~r5AbN_n%%CZ9QELcmqbKCDvDtV-g_cEnTux${-*M6q- zbQjlWXSu$OY|^YTj<4>Sxt`>|1Do*^IHPaG``4N4nbNJc?&E)#G$fNrTS^LWA}rly zlY0028S^(7lQ}-7qVDYGluD+s8NR_u?&h9s&(Q}!r^nLVzwWfU(F@P@ZQ?kGUQ*A| z*Hd}M;NCe#9ew?efE1C>vSRnK2dr+s&!OPqm7X*wWj^otU9(~R6j1#k8}s|rwv_9{TR}sbo5G}XO(BYh>32J#p_B1#hc)QBwZJXIgZ&4T z=jzk&&SG&r^904_30c1Cu>T#3w-dX^HIEY5$!KXxs&49-h_;y?=J#8gKj?yODH`+g zBt}@F+A`|#-*A2Xl`HYFI2;``BYM{XJ8^5Qtt%Q^vn=M0dJ4nG^FQj6%XO>%tM2N{ z_`Ump!_D^D6l~vvm_d_c^!!Jf$E-hznQQ8J$%b#I92s?LoN!#y6y-3PW7F2xG{0%n zp31!b;m$GjUa7Z8-&xhXrFmyLF*AlzPmF6R6{&UwDo**%s}uS`$)d}4h7{r5%HMWq z-0D^ZEH8=MqoHqx?r%0Hu$B}rXMxRp1HKy~DmD>h{Suy(*F2uXbr#+R7AtJj=Bl5wG5`ua9Hfgz@2x1DdB%sxE<-4z+$X4(^or zBAbBIVTY3c`>YP|C^-JWeTeH{f_n&xCeC)6N*4gTJ%7{W;9-;5P+s z^{x_ET0@4A*xYK%>(L8|9`T!WG0GRLp5on4{T}Af7x&*BIPZSO))@Nofc1iA-W$!$ zcujrg0A46qA>YOFL3{Qx!nB?4Qa`D$&u`ueNQnYxv3N305u297hNgLuk{Lqeg!n^>3x*njt>RWnab!fotW z-}3^s0@Pbw<;m5T{K?f95JZH3A$*4rK={^Q-AoGK_GzoX@@uQVz-Wf#b81-?-dUsX z;Yy-@66vH0qRvP9J%lAlCpn4wE|lMe{AJxX{_0xgi<&$}g)?^VOvKwETYd3+XCLsT zwq)u(n*GdZ>S=A*+;J6lUqwqav@yK0L1|HYS%V^OwZ12IBl+mc=B$JiyhNYj9PcJm zuJO)pC}=KrKiRd<#hY0vBV zzUwN_7Js(Dwcniede_sS{xzVJD}Jso!utlTGZ(w=SXb1P==2zw$LROvj^fUP?r};hs$A?((2tw$Y-T21!j0SCVZA&dk!=;AWeHBvf6j<@ zu|hL$?3yJ~ukPbWC2}mz+c!z2&3VGpkRIcO>7Kv)S;I+xBy>c!1Ka}Ku~$1c!iPa! z9rMRBy6A4Gtfbtm!wrx5ok(bJ{47iBUjw}Vdz`L+8)yF8k@Hl2&8twAg3=Z_Jn z?iw4VW5A3vhAV~jQ65x+2V>eys=N=cY|c#J3NHChcm2x}^p!R%s?s3G(#*45bk{x) z{9Q8sqHM>|3RnFb?9l@&8;lmS&(NR~$CD)AbC80geWR+1oAuCGcHve2M_s48X1Pi| ziRYL+HGWrE6tD)ZY)-rXG^C(fraILM5@l>y=vJl8%BmLN&;lGvJwE`0@$vEP!TIw> z;8BEk>&ewARn)$edUrCu9uy5dIGp5kney;uEh0V*pGC??YVgr`ya-fb#m4*}THcC; zB>4`X!pa4`D9x52=wJ)l*pW?bL1H7r^c_iMb}Yiz(CArK-4Ht;({tvj_!;w!d;Y7< z4%!NBu#?2jzf2L8r#PEl(T2|(gL4rPIdxK6I!VBn`Hoz)y6VeSv6QvGToFr=`f|=W z;OCqnKQYvoQyWP=!rch0pzfxA`lsME8TP6V^Lf^W8!20)W@cq6?E%)#q5TFuTLnCDI-{_8z}DegZ>%92dL~N$KOQCce;9in*1P=w zw^75dqoLzb7yiE&-HrcmM3*lPKamMFBA2Xi zA9LHo%1~65a`IOsIjkDO9gda>t0ek;2QBsW3#&pENYAk|SL-H!>r=Rnxga?<5Oa-~ zq7}PUbwZsj%w77YN_Fx{q75r9O$_iV25&j1*qKtbMfA~nw8qye^~ooQVGgX|{SyKT zB?D{t1k};tJ+EqoutGe{VXRh;1O1xZ?YIN~BT{zR0%%97sGE25Px6=MU0Z19o-DMR z?RvY-ooJ^PLYv65HW%k22AsM{Y@St@=b7NVtB`qv*!3>n$ya5%*kE%>CZN*uFppqn zZYyIi*NK(K@>Y39>6tl}r|0kb@{zn5oOfmZG&l4qoqzf}=EH5eWL+zt%mc)b5$`Vs zC&nowaP08Yx)P3E)LqW2yWyt*8*Lv#5aCsXL(#+Es;%Is9ztScqj-&s%^{DC+AY)w zvkS(muf&~}*LNqwgH2NnKX{ot_rjN)+7`^N$>6m%oNEFL@8pIkcDz0In&Q;HHo`FWhc!MDB{ufTSe zE8CAqXKIhSRPFdm%Vuu*VZ7*l$knt_>Ev!xI*;C}bT-{ezu;1W@3u@WmASd+y&H&n z+A^a40|K>4EUU{X*S&_6^?CR6g6?fs`Sfm*%cGa~HlAKzfKw28EE@V(lv*3|g=KDP zOZaMaR%c8KXOvSbS*-ont7GkBxtzLKj`oqYI-{J%?eTaSVqG@_X-;YLuP8BZk_wA- zhha%|Z+!IF?@q6OKJy9No}O%B!|gA2B{)jq7f0vhc=RFI6sd>$xhU0o>ksdLba&S{ z$AQfQejVEK!m0I5k8Zqumo4QuY+**cwTQAZY_t2>+fS{3?$HFtvdm{!KDQat1HV2O z-61)Oh=qD0adJJ1iKFPb&Cjj;-cLPld3>P*+Dy=pNP;ueBsd2?w$_A$ca$G;!UL%o z{&KQ@fp=(&-z;r-Oq#EIOsX~{TB*0bdfH88=Y0YHCpg`V)PAF!$-B@iw|R6%H@BJ> znpSRI|Ka*10n*jnv^g3&7A3FtHSh0y_EDp|)%L;q!ZqydHo zgK+rU8CVsYdWh*qKm8^!eVsBp%FCmead_nS>K@q8bMQh>`EzM%?Ie6tlP?^Aq@uJV zeaHAsQnhZAG~e*tEqOS>E|A9we_7auYLhkG`sVK)9VdFE3^lNS&473(wUC62*0z9 z&U&}f!V76$m1Uw=VKMrk+vDqqn$yod`ql5+GfnQ}>s#Tosg%b>?H}Lc*YIgRSj14A zCPin!?q$HYoW>&eR)jYP{1hXqf&JwjVvd!+9*J?7`Hrm4SW9ZjFyHbwe+&Z-7^u9w zB^lpLH2Y$$|Nf53OjE?<0hXzN{_H#49r{jChuK-aLnKBLx5YSpCjVd&HGPD2I0^43p& zF&H==seby5R9Fk!I{!1@TubpO ze06U%N(}AlcO_jVQaXo7uEhy1-*JiL*hIDMk4V!RN2G79sNKw>;CHbiGZ?z`PN= z#h$p3O!>g8HFJYXvia6yeu}#?IEzvA7q7BPM(apxb$`)vJzCnu^ebAv9B=vOTVL{b z0B2n{#lGfJ$x2Yp;Hz7@l;pkBvvjFd^jMeH`s$wRC-sh{hEh`aPLE@$L-aTtwXgQi zSaaTYqORuBLn|_u>kgwOIa>F{p}J0-lk#nsNZL`vON-yFt7Nt=1=jklwe{unH># zck7lG)8EYUZ@P1-xGPD6-+v=hfpSylhf67x57FwwrL@h5weQvelIU3sFWiE&Uavnv zNjadRhqgIgSKU)lOd-C%6xuXeB`UR_+B2G@uMM;o{pEC>3{@m6Vo+_OXT^YD?8fh~ z=r_Wxt(qbIsV>F!lep2JUL^qQiK>O6I=)|b%em{BVynno&n?t&WD;{GLl6&(b@)<` zlfOn=+YcY_e*g4tM(C8JTAlDD&VN_o%{<=iSjL}OBXaD1H9N>HB7IHd*-q54`hXKx zxD#KiJzdw_!_VRRJ&Uto|9!se8T`uZg~yOMeSp5DDmsk127dPOI=0`lD9w%Ui&y}` zz|TTW!(aS#bd=jatK)?wt4CcTKi7D{h2{9ppxQ^pTze_FD{ihSZsq`bg!m0V!zuZ)oy$*b@JD~K4ry8lw+PV>)1=; z=-2y?Ou=g$hV{PQPo`w}nBXkMwNh?v6}()ES3X&5bZnQt)*Y!M`PAo4J+DUO=dt}o zD>IiS*fVSCN_oBCGi4oGTc?D43(zX{ww~+fa@J3AK83A~b7YoYE18Sly_k^pJjR-c zF9=Wx{F)@`cG<2!L(C{GDp06Tn(MgEopi)2VtC)M9Nz%`Y-OwzcRnMf7HHwm$)#%3 zNEwZvtvg*u3TAlQR%4a3ZOc3S%6{D)izRJ+xkzraBmQ%p3bZP9fP>GC6Rjy>?~RhG zs6FZLlJ49)xLXN-*3NL3>eo>{b@p;H@n{`Jv-0VMN9%N@8Ey|eRd@IqL#rRX8TitX zxor2htu~>@_vBeTU3UVq)!1nhS(ZxK`f?Le1MshyaEL3Ye=Yr#HNYrdQSm~eVDeq% zp5PuU9Kb6`4!4GUkmoae{4B1(fIUs@y>{H%POc%Lp%u4zIT=c?_8csjb5kW|N-JzD zyYY=qoG9O}FTZwNpMPg2O_82W!n^?C^f&=l`mD3eC+Izy(`|QcbvL;_avyaiuI83I zSKF6wUEQ?&qt!>3C)RN*oOSjUTkD!ud{lRIMdIz;O6TqNm0NFbTKUoKM^{>lti{$@ zr;itvB>opHU6HO>H|yeYZHeLpzL7N>?@ng(vj&dyC2H?+Rdx?BolOv!6N$~7$0bnw z)CKwzTUDK29WXrLJ&I8u&pdra-RDmfOYCd|!qa|!)~OTBtPv;!#}ga4dS`>ZerrQh zJ%uK|_K-^OUOSEy%<+EM#R=@W^k8)A)hBQlYznKzQ&@XJZ>RoL)d6iGLiKZfTGkZc*tEdMX?zcD!53ZfzKd5?Y=B{@ z2Ka7pJWKkHllt-mS;zfNkA2OZ^~=|;GTgfQm$$82TfcVoFYjJQ>}!5`_v&BXvx>}H zv1aYvq;ldNcNp$oe)}rJ-R@O}wX4_NPHtIs_o|h5uexP2S@O#@x8AyXdr%gJ*ua{Jno3I8p30T7CTk*{r$RufUw8?XJ;$<-2408-=l8b->vHEPIqUlbLi#T zlUM$FzIEE&oi9ALZMj*DuGp|}+^WQ*2ZuazV${BR56!9B+kMG(*VB2?J9lMtHoi77 z>Cgf)Pv3j`t;=5~KPGSfyOr zr$@Q}!qP)S4xJx6e!FsHMD`HxX7#~3e_!g`ca(qG{Y6Fh@q_#9JzHlcoSH%Mz4y|c zIMTZZ4!E{fc7wwvQM#egKzj`1c^4y#1}~YQjkZ!=9?r`HNJzv7#~SfkjE;?FR8{$^ zr1&}`IcWe!8w{jOs9Qp|B!itz<{pp~o(rqdWUvaWOl~s3$TzEC=wBUR#{P7)%)sDy z7p|@jD1{wq&+>_;7cVRIcxfu~tE|{xEY((^RN|4mzG{C4)mBu-PJ}Ygii%Q?Ou0Um zbyAfF3As|LEneoA{62a-zYwQ@l#!7k#dEo~7^z(Iy)@0BCopH7N=rS} z)r)KRT-X&(o?O18xPmv89^z?U8@KmjoN{g$?M$jw|DpHvbXK6(>y7|j}HGaB! zhRBp3KWCPCYN>F}jKZmNZslP9thw{178Km7X=eQVzPc*K{tDkRx^At;ar>m|rHDhu z_sk^~OET#3D&Mjt*G^ig+`~@&3>vo%?|IldeZ;JuwP_zYq?)9s7VZ3CPH=MQ>-GAl z?iIhiF)Hz#V3C>Ssl7HEv5QlwuL@D|Uo&QKYh{(M)KgZ2%N&aekM}Z3JK7n*S+sw^w*?^gqAwdGh;N`Y5AykTv`9 z4Thh+dtgv)`;KAlD~a}hZ2yToO&tH?)cNW6C4D3Gwc74<_MRJZYW~ce)?+VcW#<-CIpFEZPdh;IZNyEa`N$qRLryYGh zVZQ&#_A2d-ZspgO&$ju`Z##UrV%rz%9%?_=@$%us-@lTO<{SIfxIJt94}bPr+oDqq z->#T5+ts|ZV|;7ZXj5Bf@QHUW4pKWF`o*t5nO*vxWyyW_4P86%?a|H=_hdwuj(udv z9nQJ)st0-w4t!=s#<3%-){d=edgbgpJAb-(cx&axpB~vY+uJ$pY||goEHD0P?c~9i zDqEhh-+g%ZFq`**l!Ds4(ffYeHtF9IlBYbp=j5zM-+O7{MPEjFPiTwZSNdw*(7LWQ zHe=_eD|&n5e|ZacU*7d?`-QsY9ZUW);F-01mmI9RxGX38zaM=yIA`$eP36M2*FRk? z4*Nh|i1+N_i{Oa%exkI>ACdcop%T5)bt-Aq@lVg6cJKa|e?{9|e{t!vEdP;7xM1cbfwuMti6`Y0q2yd)28Rnm;gI8vQ2tzK+8iL`&w2s-1U|R7e;(I3!6c}g|PtzmI-4h zmprtBDyS4XpO%pyx{4N4F}FNK^I^@UYV<7x&*5vDCQuelq!OA;<53<*PW+UjK8_~A zG7+ugXbL1Nk3+9gw0ls?gtd&97sL8IW@B5D=@FVsGie_0kx3(9!|OkkfX@-~4nItU z@wx*U)D$LrU2KdVy)J{1>t${?L|7ku?n)&RZXre5?RlgjoWt8(~8Y2|hU?_=Pj=zB8W%JxXa_I{g<#c2{g zb9tNQ8_iB$LQC1;njGIKBJ=n(GNnJR2i07v|_Old4xoM z^kO_t%%hlJaUW&wsf@b>q8xTE#*gWi!D4Y*TQV~<~z7V*rE)u?~^BxEXVh#sPH=?N1y9Hza=6@<)|DH&u@9ITaJp6#9ph)`hDE5Ur^J} z>Hnd>^Q{~SM)i8Ff4^wFUrJq0&dTiB5KNNDiAIgNgxzf3OspD-jVwkBAU@M1;^IB7`0hA@qm{p+`hGVEso(9Htl% z*+E4_T|q*Kx@u*T{aSS`sH?~omMuY@or@xzg{!&_hhs?;kEaMT8dL}}C`_`At;5Is zfBf-kT*TI)5(!PDu%{e&{u3s`lM)Fp@e?6wkXev+n;lijZevXnzRH#)w-M5<81Q`O z2TL)m>!t|i!@ReZvaUyMb~s@EM@R?mS!{L-9!PC=mfY~O$U-&;Iq+*COA!?@Ga_5W z@4Q%lUO<(bF%qL;g&7S+OkQ6H9^^0!GXC~U_(K8LT#?DZ2a<_mPe@JpGZ))ThEZG# zq_`!N1WIjZJp`j+L_5=(ZD=aAiI5lA=Ha~`w_^XwTib8d_h#Gg{+DA3!DHafeOxoUT_ISS(*6|KrDa*NFW_#0w-{TJWvSm?si)l@B%*wfHj~2G=iJg zt$+Rha{Zg(OHse|Z$Z5kl*85ry&r^u9mie?8(aT6=q}h!fRjK4XF)HB0NkkCY`_j2 zKtcZoAfeod@#)Z+zzN(S4-|q0pa_(MN>B$B&;S}iGiU*=pbhK?VbBFmfRjK4XF)HB z0K#X(Y`_lK`a7W6`b*G_@F^WS6F7kz{?*W?Fl#oJ-MuGymEyEuZAC7GbZRi z(+hBY3U?$|cHp|UNPAN&NFhTQ*AZtR#QK{;0VmUf*Qq(#7`r{d?jwvQ3z_ZM&}Ot) z$TZlg@_qp=qy;d}t?>(yuz7pag%XSlH8t^lI@0r4i}3l*o$blc0WGGL&F#Hm)V*4) zU*(kzY#w7)78xgIVP8&f2X^-XibyYIAeP|9&6rd=WUvnoRx}) zX_rf4zi1{QUSRX_`DHmR2aj@sBPUJ9?XB76vS*^7d3bilSZGsJ!whK8aT#4~Kbbj$ zbI`%+bXk%uu2^nmxooa1v^gIXxG$qJvL|4a*&gT#VXP6ihvraEz>YHR2l!osspv`( zsw5owy?yaI=tVoGEzNo^qbsL&W zP|rnzviwGVHfYe7nwpxha6$mO09sKtw_ygsi#qF9290^l_H@2Ob0{~R`6sxQtu1co zKxpe8tiMp-*ogBO@r{oQARXb4fVAO$FgrXJH1t!T(cZL()p2eyKagEKf02}-D7W(+ z3h20T8pdh@T?aWcPS$HUSQ`G{%QysE?EP z*E`bf={Vtpu#_T&p%JE?M-WHu4U2CGdxJfeddRymW5%SQuBE1?CPODf^Spy*ekG$F z>m%6f8x=RSW8}z@E@;gzNo<^=z!PW}_Q&e3xSh?z=9AOX(s-UhBhPp{0xl$B-ZW@Q zil(MQ=fEL0Z)Soq0eOacf-wg=HOt0wS#S!1z}izsjbS+^IB-LYel~nToKawT!Vbzz z%F$y6TK`Maj~0>b!6)(lTd?XWvK<2ppN0*r1|NXwMfe Date: Tue, 23 Apr 2019 21:28:47 +0200 Subject: [PATCH 41/78] legacy: bump versions to 1.8.1 --- legacy/bootloader/bootloader.h | 2 +- legacy/firmware/bl_check.c | 2 +- legacy/firmware/version.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/legacy/bootloader/bootloader.h b/legacy/bootloader/bootloader.h index b967c7f0b9..98267c9849 100644 --- a/legacy/bootloader/bootloader.h +++ b/legacy/bootloader/bootloader.h @@ -22,7 +22,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 8 -#define VERSION_PATCH 0 +#define VERSION_PATCH 1 #define STR(X) #X #define VERSTR(X) STR(X) diff --git a/legacy/firmware/bl_check.c b/legacy/firmware/bl_check.c index 68728902f9..3080c6b367 100644 --- a/legacy/firmware/bl_check.c +++ b/legacy/firmware/bl_check.c @@ -123,7 +123,7 @@ static int known_bootloader(int r, const uint8_t *hash) { "\xf7\xfa\x16\x5b\xe6\xd7\x80\xf3\xe1\xaf\x00\xab\xc0\x7d\xf8\xb3" "\x07\x6b\xcd\xad\x72\xd7\x0d\xa2\x2a\x63\xd8\x89\x6b\x63\x91\xd8", 32)) - return 1; // 1.8.0 shipped with fw 1.8.0 + return 1; // 1.8.0 shipped with fw 1.8.0 and 1.8.1 return 0; } diff --git a/legacy/firmware/version.h b/legacy/firmware/version.h index 1202e1b44f..a0ce369d9f 100644 --- a/legacy/firmware/version.h +++ b/legacy/firmware/version.h @@ -1,6 +1,6 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 8 -#define VERSION_PATCH 0 +#define VERSION_PATCH 1 #define FIX_VERSION_MAJOR 1 #define FIX_VERSION_MINOR 8 From 1c00d868a9762dd5b4f2b13a1c977cf36ad4dcb1 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 23 Apr 2019 21:31:00 +0200 Subject: [PATCH 42/78] core: bump versions of firmware to 2.1.1, bootloader to 2.0.4 --- core/embed/bootloader/version.h | 2 +- core/embed/firmware/version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/embed/bootloader/version.h b/core/embed/bootloader/version.h index 5cbcb75b50..05d8755e76 100644 --- a/core/embed/bootloader/version.h +++ b/core/embed/bootloader/version.h @@ -1,6 +1,6 @@ #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_PATCH 3 +#define VERSION_PATCH 4 #define VERSION_BUILD 0 #define VERSION_UINT32 \ (VERSION_MAJOR | (VERSION_MINOR << 8) | (VERSION_PATCH << 16) | \ diff --git a/core/embed/firmware/version.h b/core/embed/firmware/version.h index 0e57ccee0a..1ed18ec6e4 100644 --- a/core/embed/firmware/version.h +++ b/core/embed/firmware/version.h @@ -1,6 +1,6 @@ #define VERSION_MAJOR 2 #define VERSION_MINOR 1 -#define VERSION_PATCH 0 +#define VERSION_PATCH 1 #define VERSION_BUILD 0 #define FIX_VERSION_MAJOR 2 From 8ec40bdccb2ddc3403f150ece0e41a7e2faf04a2 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Wed, 24 Apr 2019 11:27:21 +0200 Subject: [PATCH 43/78] ci: use artifacts to pass builds to testing --- .gitlab-ci.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 09938b50d7..bc758ebdc5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,10 +38,10 @@ build core firmware: - pipenv run make build_firmware # - test "$TREZOR_MODEL" = "1" || pipenv run make sizecheck artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" paths: - core/build/firmware/firmware.bin - core/build/bootloader/bootloader.bin - - core/build/boardloader/boardloader.bin expire_in: 1 week build core unix: @@ -49,6 +49,10 @@ build core unix: script: - cd core - pipenv run make build_unix_noui + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + untracked: true + expire_in: 1 day # TODO: matrix: DEBUG_LINK={0,1}, gcc vs clang build legacy: @@ -69,23 +73,26 @@ test style: test core unix unit: stage: test + dependencies: + - build core unix script: - cd core - - pipenv run make build_unix_noui - pipenv run make test test core unix device: stage: test + dependencies: + - build core unix script: - cd core - - pipenv run make build_unix_noui - pipenv run make test_emu test core unix monero: stage: test + dependencies: + - build core unix script: - cd core - - pipenv run make build_unix_noui - pipenv run make test_emu_monero test common: From f3924138fcb85ad01fef6ebd0db3d8df5ee026bd Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Wed, 24 Apr 2019 15:05:48 +0200 Subject: [PATCH 44/78] ci: run make in paralel; use git depth --- .gitlab-ci.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bc758ebdc5..83719a924b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,16 @@ variables: + # Init submodules. + # See https://docs.gitlab.com/ee/ci/yaml/#git-submodule-strategy GIT_SUBMODULE_STRATEGY: "recursive" + # Use shallow cloning to speed up git clone. This can fail, if retrying an older build on CI + # and the old commit is not in the shallow history any more. + # See https://docs.gitlab.com/ee/ci/yaml/#shallow-cloning + GIT_DEPTH: "50" + + # run make paralel + MAKEFLAGS: "-j10" + stages: - environment - build From 30ae11f22e9d5b24d50c498e08771043be309c2e Mon Sep 17 00:00:00 2001 From: Roman Zeyde Date: Wed, 24 Apr 2019 22:38:51 +0300 Subject: [PATCH 45/78] core: allow running 'build_protobuf' from any directory (#106) --- core/tools/build_protobuf | 1 + 1 file changed, 1 insertion(+) diff --git a/core/tools/build_protobuf b/core/tools/build_protobuf index 921fc95a1d..0df512e436 100755 --- a/core/tools/build_protobuf +++ b/core/tools/build_protobuf @@ -1,5 +1,6 @@ #!/bin/bash set -e +cd $(dirname $0) rm -f ../src/trezor/messages/[A-Z]*.py ../vendor/trezor-common/protob/pb2py \ From 62707a062a2285f93fc2795a1caed0bbcd5906da Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Wed, 24 Apr 2019 22:55:34 +0200 Subject: [PATCH 46/78] core+legacy: reintroduce USB 2.1 landing page for firmware --- core/src/usb.py | 1 - legacy/firmware/usb.c | 2 +- legacy/webusb.c | 11 ----------- legacy/webusb.h | 2 -- 4 files changed, 1 insertion(+), 15 deletions(-) diff --git a/core/src/usb.py b/core/src/usb.py index a878b5fca9..06132785ea 100644 --- a/core/src/usb.py +++ b/core/src/usb.py @@ -62,7 +62,6 @@ bus = io.USB( product="TREZOR", interface="TREZOR Interface", serial_number=get_device_id(), - usb21_landing=False, ) bus.add(iface_wire) if __debug__: diff --git a/legacy/firmware/usb.c b/legacy/firmware/usb.c index 8d80dbed92..c55947e6c1 100644 --- a/legacy/firmware/usb.c +++ b/legacy/firmware/usb.c @@ -344,7 +344,7 @@ static uint8_t usbd_control_buffer[256] __attribute__((aligned(2))); static const struct usb_device_capability_descriptor *capabilities[] = { (const struct usb_device_capability_descriptor - *)&webusb_platform_capability_descriptor_no_landing_page, + *)&webusb_platform_capability_descriptor, }; static const struct usb_bos_descriptor bos_descriptor = { diff --git a/legacy/webusb.c b/legacy/webusb.c index 24f28a4ca5..b4f2677e2d 100644 --- a/legacy/webusb.c +++ b/legacy/webusb.c @@ -32,17 +32,6 @@ const struct webusb_platform_descriptor webusb_platform_capability_descriptor = .bVendorCode = WEBUSB_VENDOR_CODE, .iLandingPage = 1}; -const struct webusb_platform_descriptor - webusb_platform_capability_descriptor_no_landing_page = { - .bLength = WEBUSB_PLATFORM_DESCRIPTOR_SIZE, - .bDescriptorType = USB_DT_DEVICE_CAPABILITY, - .bDevCapabilityType = USB_DC_PLATFORM, - .bReserved = 0, - .platformCapabilityUUID = WEBUSB_UUID, - .bcdVersion = 0x0100, - .bVendorCode = WEBUSB_VENDOR_CODE, - .iLandingPage = 0}; - static const char* webusb_https_url; static enum usbd_request_return_codes webusb_control_vendor_request( diff --git a/legacy/webusb.h b/legacy/webusb.h index 01feda4e5b..5835e3ca4e 100644 --- a/legacy/webusb.h +++ b/legacy/webusb.h @@ -27,8 +27,6 @@ extern const struct webusb_platform_descriptor webusb_platform_capability_descriptor; -extern const struct webusb_platform_descriptor - webusb_platform_capability_descriptor_no_landing_page; extern void webusb_setup(usbd_device* usbd_dev, const char* https_url); From aae1be49140863934b2763aa5f6dcd39745ef08f Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 25 Apr 2019 12:54:41 +0200 Subject: [PATCH 47/78] legacy: add symlinks to Pipfile and Pipfile.lock --- legacy/Pipfile | 1 + legacy/Pipfile.lock | 1 + 2 files changed, 2 insertions(+) create mode 120000 legacy/Pipfile create mode 120000 legacy/Pipfile.lock diff --git a/legacy/Pipfile b/legacy/Pipfile new file mode 120000 index 0000000000..4dc2112428 --- /dev/null +++ b/legacy/Pipfile @@ -0,0 +1 @@ +../Pipfile \ No newline at end of file diff --git a/legacy/Pipfile.lock b/legacy/Pipfile.lock new file mode 120000 index 0000000000..7dfd73a6ed --- /dev/null +++ b/legacy/Pipfile.lock @@ -0,0 +1 @@ +../Pipfile.lock \ No newline at end of file From 659feca7e1a14fb849527d41c608d511f433c98b Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 25 Apr 2019 12:59:44 +0200 Subject: [PATCH 48/78] legacy: simplify pipenv install in script/fullbuild --- legacy/script/fullbuild | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/legacy/script/fullbuild b/legacy/script/fullbuild index 193d52d548..bef2f3b91c 100755 --- a/legacy/script/fullbuild +++ b/legacy/script/fullbuild @@ -50,11 +50,7 @@ worktree_build() { fi pushd $path/$MCU_ROOT - if ! pipenv install; then - # older tags can fail because they don't have protobuf in Pipfile - pipenv run pip install "protobuf==3.4.0" - pipenv install - fi + pipenv install pipenv run script/cibuild popd } From 9abe9d66987cc546ebc79510db48dab66c05fb7a Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 25 Apr 2019 13:08:33 +0200 Subject: [PATCH 49/78] legacy: fix docker build --- Dockerfile | 2 +- legacy/Dockerfile | 77 ----------------------------------------- legacy/Pipfile | 1 - legacy/Pipfile.lock | 1 - legacy/script/fullbuild | 6 ++-- 5 files changed, 4 insertions(+), 83 deletions(-) delete mode 100644 legacy/Dockerfile delete mode 120000 legacy/Pipfile delete mode 120000 legacy/Pipfile.lock diff --git a/Dockerfile b/Dockerfile index f416150c22..f18f3c0ea1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -65,7 +65,7 @@ ENV LC_ALL=C.UTF-8 LANG=C.UTF-8 # use zipfile module to extract files world-readable RUN python -m zipfile -e "protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" /usr/local && chmod 755 /usr/local/bin/protoc -#ENV WORKON_HOME=/tmp/.venvs +ENV WORKON_HOME=/tmp/.venvs # install python dependencies diff --git a/legacy/Dockerfile b/legacy/Dockerfile deleted file mode 100644 index 720c210761..0000000000 --- a/legacy/Dockerfile +++ /dev/null @@ -1,77 +0,0 @@ -# initialize from the image - -FROM debian:9 - -ARG TOOLCHAIN_FLAVOR=linux -ENV TOOLCHAIN_FLAVOR=$TOOLCHAIN_FLAVOR - -# install build tools and dependencies - -ARG EMULATOR=0 -ENV EMULATOR=$EMULATOR - -RUN apt-get update && apt-get install -y \ - build-essential wget git python3-pip - -# install dependencies from toolchain source build - -RUN if [ "$TOOLCHAIN_FLAVOR" = "src" ]; then \ - apt-get install -y autoconf autogen bison dejagnu \ - flex flip gawk git gperf gzip nsis \ - openssh-client p7zip-full perl python-dev \ - libisl-dev tcl tofrodos zip \ - texinfo texlive texlive-extra-utils; \ - fi - -# download toolchain - -ENV TOOLCHAIN_SHORTVER=8-2018q4 -ENV TOOLCHAIN_LONGVER=gcc-arm-none-eabi-8-2018-q4-major -ENV TOOLCHAIN_URL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/$TOOLCHAIN_SHORTVER/$TOOLCHAIN_LONGVER-$TOOLCHAIN_FLAVOR.tar.bz2 -ENV TOOLCHAIN_HASH_linux=fb31fbdfe08406ece43eef5df623c0b2deb8b53e405e2c878300f7a1f303ee52 -ENV TOOLCHAIN_HASH_src=bc228325dbbfaf643f2ee5d19e01d8b1873fcb9c31781b5e1355d40a68704ce7 - -RUN if [ "$EMULATOR" = 1 ]; then \ - apt-get install -y libsdl2-dev libsdl2-image-dev; \ - fi - -# extract toolchain - -RUN cd /opt && wget $TOOLCHAIN_URL - -RUN cd /opt && echo "$TOOLCHAIN_HASH_linux $TOOLCHAIN_LONGVER-linux.tar.bz2\n$TOOLCHAIN_HASH_src $TOOLCHAIN_LONGVER-src.tar.bz2" | sha256sum -c --ignore-missing - -RUN cd /opt && tar xfj $TOOLCHAIN_LONGVER-$TOOLCHAIN_FLAVOR.tar.bz2 - -# build toolchain (if required) - -RUN if [ "$TOOLCHAIN_FLAVOR" = "src" ]; then \ - pushd /opt/$TOOLCHAIN_LONGVER ; \ - ./install-sources.sh --skip_steps=mingw32 ; \ - ./build-prerequisites.sh --skip_steps=mingw32 ; \ - ./build-toolchain.sh --skip_steps=mingw32,manual ; \ - popd ; \ - fi - -# download protobuf - -ENV PROTOBUF_VERSION=3.4.0 -ENV PROTOBUF_HASH=e4b51de1b75813e62d6ecdde582efa798586e09b5beaebfb866ae7c9eaadace4 -RUN wget "https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" -RUN echo "${PROTOBUF_HASH} protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" | sha256sum -c - -# setup toolchain - -ENV PATH=/opt/$TOOLCHAIN_LONGVER/bin:$PATH - -ENV PYTHON=python3 -ENV LC_ALL=C.UTF-8 LANG=C.UTF-8 - -# use zipfile module to extract files world-readable -RUN $PYTHON -m zipfile -e "protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" /usr/local && chmod 755 /usr/local/bin/protoc - -ENV WORKON_HOME=/tmp/.venvs - -# install python dependencies - -RUN $PYTHON -m pip install pipenv diff --git a/legacy/Pipfile b/legacy/Pipfile deleted file mode 120000 index 4dc2112428..0000000000 --- a/legacy/Pipfile +++ /dev/null @@ -1 +0,0 @@ -../Pipfile \ No newline at end of file diff --git a/legacy/Pipfile.lock b/legacy/Pipfile.lock deleted file mode 120000 index 7dfd73a6ed..0000000000 --- a/legacy/Pipfile.lock +++ /dev/null @@ -1 +0,0 @@ -../Pipfile.lock \ No newline at end of file diff --git a/legacy/script/fullbuild b/legacy/script/fullbuild index bef2f3b91c..8771a31290 100755 --- a/legacy/script/fullbuild +++ b/legacy/script/fullbuild @@ -44,14 +44,14 @@ worktree_setup() { worktree_build() { local path="$1" - if [ ! -e "$path/$MCU_ROOT/Pipfile" ]; then + if [ ! -e "$path/Pipfile" ]; then echo "Can't handle pre-monorepo tags properly. You will have to check out manually" exit 1 fi - pushd $path/$MCU_ROOT + pushd $path pipenv install - pipenv run script/cibuild + pipenv run $MCU_ROOT/script/cibuild popd } From 5507688ce6226845fa75de88afeb7686e025df05 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 25 Apr 2019 14:11:37 +0200 Subject: [PATCH 50/78] legacy: more build script fixing --- legacy/build.sh | 13 ++----------- legacy/script/fullbuild | 2 ++ 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/legacy/build.sh b/legacy/build.sh index 02a3e6f46a..67013873f6 100755 --- a/legacy/build.sh +++ b/legacy/build.sh @@ -5,20 +5,11 @@ cd "$(dirname $0)/.." BOOTLOADER_COMMIT=${1:-HEAD} FIRMWARE_COMMIT=${2:-HEAD} - -if [ "$BOOTLOADER_COMMIT" = "EMU" ]; then - export EMULATOR=1 -fi - -if [ "$EMULATOR" = 1 ]; then - IMAGE=trezor-mcu-emulator -else - IMAGE=trezor-mcu-build -fi +IMAGE=trezor-mcu-build USER=$(ls -lnd . | awk '{ print $3 }') GROUP=$(ls -lnd . | awk '{ print $4 }') -docker build -t "$IMAGE" --build-arg EMULATOR=$EMULATOR legacy +docker build -t "$IMAGE" . docker run -it -v $(pwd):/src:z --user="$USER:$GROUP" "$IMAGE" \ /src/legacy/script/fullbuild "$BOOTLOADER_COMMIT" "$FIRMWARE_COMMIT" diff --git a/legacy/script/fullbuild b/legacy/script/fullbuild index 8771a31290..bf887a962e 100755 --- a/legacy/script/fullbuild +++ b/legacy/script/fullbuild @@ -11,6 +11,8 @@ set -eu cd "$(dirname "$0")/../.." +EMULATOR=${EMULATOR:-0} + readonly MCU_ROOT=legacy readonly ARTIFACT_EXTENSIONS=(bin elf) From 7a42acc08ef66f2c0fa4dd13e52e707235aefa0d Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 25 Apr 2019 15:13:33 +0200 Subject: [PATCH 51/78] legacy: fix missing homedir glitch in fullbuild --- Pipfile.lock | 42 +++++++++++++++++++++++++++++++++++++---- legacy/script/fullbuild | 1 + 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index c54a1be0a7..1ab45b8fd6 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "4dd20b0598ed7b7cde01121a5c79aa21d756f6ca98d5a0aca1db8f57c673303c" + "sha256": "1d18173266c4e9735f105b869b0a9ec701400ca360b8a7a13e400ce644b006dd" }, "pipfile-spec": 6, "requires": {}, @@ -169,6 +169,13 @@ ], "version": "==2.2.2" }, + "curve25519-donna": { + "hashes": [ + "sha256:1818a9d5356a05c022cd504f44fe1d2f641a5c020f8a4c51b2294e02bd9c1bf0" + ], + "index": "pypi", + "version": "==1.3" + }, "demjson": { "hashes": [ "sha256:31de2038a0fdd9c4c11f8bf3b13fe77bc2a128307f965c8d5fb4dc6d6f6beb79" @@ -181,6 +188,7 @@ "sha256:20c17e527e75acad8f402290e158a6ac178b91b881f941fc6ea305bfdfb9657c", "sha256:5c034ffa23413ac923541ceb3ac14ec15a0d2530690413bff58c12b80e56d884" ], + "index": "pypi", "version": "==0.13.2" }, "ed25519": { @@ -456,6 +464,14 @@ "index": "pypi", "version": "==0.1.1" }, + "pyasn1": { + "hashes": [ + "sha256:da2420fe13a9452d8ae97a0e478adde1dee153b11ba832a95b223a2ba01c10f7", + "sha256:da6b43a8c9ae93bc80e2739efb38cc776ba74a886e3e9318d65fe81a8b8a2c6e" + ], + "index": "pypi", + "version": "==0.4.5" + }, "pyblake2": { "hashes": [ "sha256:3757f7ad709b0e1b2a6b3919fa79fe3261f166fc375cd521f2be480f8319dde9", @@ -559,6 +575,14 @@ "index": "pypi", "version": "==2.21.0" }, + "scons": { + "hashes": [ + "sha256:8c2353ae3ce559e9361baa254c0c85d3eb099d5f96e44b5bc586801b7c756a06", + "sha256:e95eaae17d9e490cf12cd37f091a6cbee8a628b5c8dbd3cab1f348f602f46462" + ], + "index": "pypi", + "version": "==3.0.5" + }, "shlib": { "hashes": [ "sha256:f9798b0a3e37407171f06efca7c213269f034eee2c94dd9933a819730a6d528b" @@ -597,6 +621,15 @@ "editable": true, "path": "./python" }, + "typing": { + "hashes": [ + "sha256:4027c5f6127a6267a435201981ba156de91ad0d1d98e9ddc2aa173453453492d", + "sha256:57dcf675a99b74d64dacf6fba08fb17cf7e3d5fdff53d4a30ea2a5e7e52543d4", + "sha256:a4c8473ce11a65999c8f59cb093e70686b6c84c98df58c1dae9b3b196089858a" + ], + "index": "pypi", + "version": "==3.6.6" + }, "typing-extensions": { "hashes": [ "sha256:07b2c978670896022a43c4b915df8958bec4a6b84add7f2c87b2b728bda3ba64", @@ -614,10 +647,10 @@ }, "virtualenv": { "hashes": [ - "sha256:6aebaf4dd2568a0094225ebbca987859e369e3e5c22dc7d52e5406d504890417", - "sha256:984d7e607b0a5d1329425dd8845bd971b957424b5ba664729fab51ab8c11bc39" + "sha256:15ee248d13e4001a691d9583948ad3947bcb8a289775102e4c4aa98a8b7a6d73", + "sha256:bfc98bb9b42a3029ee41b96dc00a34c2f254cbf7716bec824477b2c82741a5c4" ], - "version": "==16.4.3" + "version": "==16.5.0" }, "wheel": { "hashes": [ @@ -641,6 +674,7 @@ "sha256:57dcf675a99b74d64dacf6fba08fb17cf7e3d5fdff53d4a30ea2a5e7e52543d4", "sha256:a4c8473ce11a65999c8f59cb093e70686b6c84c98df58c1dae9b3b196089858a" ], + "index": "pypi", "version": "==3.6.6" } } diff --git a/legacy/script/fullbuild b/legacy/script/fullbuild index bf887a962e..4d3c38002c 100755 --- a/legacy/script/fullbuild +++ b/legacy/script/fullbuild @@ -52,6 +52,7 @@ worktree_build() { fi pushd $path + export HOME=/tmp pipenv install pipenv run $MCU_ROOT/script/cibuild popd From 16dbe5f6f6944c27bbc04d3d280027312fd3a6f0 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 25 Apr 2019 15:20:28 +0200 Subject: [PATCH 52/78] core+python: remove unnecessary .travis.yml --- core/.travis.yml | 78 ---------------------------------------------- python/.travis.yml | 57 --------------------------------- 2 files changed, 135 deletions(-) delete mode 100644 core/.travis.yml delete mode 100644 python/.travis.yml diff --git a/core/.travis.yml b/core/.travis.yml deleted file mode 100644 index 10b250a848..0000000000 --- a/core/.travis.yml +++ /dev/null @@ -1,78 +0,0 @@ -sudo: false -dist: trusty -language: c - -addons: - apt: - sources: - - deadsnakes - packages: - - build-essential - - python3.6 - - python3.6-dev - - python3.6-venv - - libusb-1.0-0-dev - - libudev-dev - -env: - global: - - MAKEFLAGS=-j2 - - PYTHON=python3.6 - - PROTOBUF_VERSION=3.4.0 - - TOOLCHAIN_SHORTVER=8-2018q4 - - TOOLCHAIN_LONGVER=gcc-arm-none-eabi-8-2018-q4-major - matrix: - - GOAL=stm32 - - GOAL=unix - - GOAL=src - -matrix: - include: - - compiler: clang - env: GOAL=unix - -cache: - directories: - - $HOME/libsodium - -before_install: - - $PYTHON -m ensurepip --user - - $PYTHON -m pip install --user pipenv - -install: - - ./travis-install-libsodium.sh - - export PKG_CONFIG_PATH=$HOME/libsodium/lib/pkgconfig:$PKG_CONFIG_PATH - - export LD_LIBRARY_PATH=$HOME/libsodium/lib:$LD_LIBRARY_PATH - - wget "https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" - - unzip "protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" -d protoc - - export PATH="$(pwd)/protoc/bin:$PATH" - - pipenv install - -before_script: - - test "$GOAL" != "stm32" || wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/$TOOLCHAIN_SHORTVER/$TOOLCHAIN_LONGVER-linux.tar.bz2 - - test "$GOAL" != "stm32" || tar xfj $TOOLCHAIN_LONGVER-linux.tar.bz2 - - test "$GOAL" != "stm32" || export PATH=$PWD/$TOOLCHAIN_LONGVER/bin:$PATH - -script: - - test "$GOAL" != "src" || pipenv run make style_check - - test "$GOAL" != "src" || pipenv run make templates_check - - - test "$GOAL" != "stm32" || pipenv run make build_cross - - test "$GOAL" != "stm32" || pipenv run make build_boardloader - - test "$GOAL" != "stm32" || pipenv run make build_bootloader - - test "$GOAL" != "stm32" || pipenv run make build_prodtest - - test "$GOAL" != "stm32" || pipenv run make build_firmware - - test "$GOAL" != "stm32" || test "$TREZOR_MODEL" = "1" || pipenv run make sizecheck - - - test "$GOAL" != "unix" || pipenv run make build_unix_noui - - test "$GOAL" != "unix" || pipenv run make test - - test "$GOAL" != "unix" || test "$TREZOR_MODEL" = "1" || pipenv run make test_emu - - test "$GOAL" != "unix" || test "$TREZOR_MODEL" = "1" || pipenv run make test_emu_monero - -notifications: - webhooks: - urls: - - http://ci-bot.satoshilabs.com:5000/travis - on_success: always - on_failure: always - on_start: always diff --git a/python/.travis.yml b/python/.travis.yml deleted file mode 100644 index c7c009af12..0000000000 --- a/python/.travis.yml +++ /dev/null @@ -1,57 +0,0 @@ -language: python - -# Runs jobs on container based infrastructure -sudo: false - -# Saves pip downloads/wheels between builds -cache: - directories: - - $HOME/.cache/pip - -addons: - apt: - packages: - - libudev-dev - - libusb-1.0-0-dev - -env: - global: - PROTOBUF_VERSION=3.4.0 - -python: - - "3.5" - - "3.6" - -# workaround for https://github.com/travis-ci/travis-ci/issues/9815 -matrix: - include: - - python: 3.7 - dist: xenial - sudo: true - -install: - # Optimisation: build requirements as wheels, which get cached by Travis - - pip install "pip>=9.0" wheel # pip 9.0 understands `python_requires` constraints - - pip install "setuptools>=38" # setuptools >= 38 are capable of using prebuilt wheels - - pip install tox-travis - - pip install -r requirements-dev.txt - # protobuf-related dependencies - - curl -LO "https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" - - unzip "protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" -d protoc - - export PATH="$(pwd)/protoc/bin:$PATH" - -before_script: - - ./trigger-travis.sh - -script: - - python setup.py install - - if [ $TRAVIS_PYTHON_VERSION != 3.5 ]; then make style_check; fi - - tox - -notifications: - webhooks: - urls: - - http://ci-bot.satoshilabs.com:5000/travis - on_success: always - on_failure: always - on_start: always From 0b9f4654bd1049a4cc02393acb5b61dc548929f5 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 25 Apr 2019 15:21:42 +0200 Subject: [PATCH 53/78] docker: use $PYTHON variable --- Dockerfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index f18f3c0ea1..f0fe5966c1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -63,7 +63,9 @@ ENV PATH=/opt/$TOOLCHAIN_LONGVER/bin:$PATH ENV LC_ALL=C.UTF-8 LANG=C.UTF-8 # use zipfile module to extract files world-readable -RUN python -m zipfile -e "protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" /usr/local && chmod 755 /usr/local/bin/protoc +ENV PYTHON=python + +RUN $PYTHON -m zipfile -e "protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" /usr/local && chmod 755 /usr/local/bin/protoc ENV WORKON_HOME=/tmp/.venvs @@ -71,6 +73,6 @@ ENV WORKON_HOME=/tmp/.venvs RUN pip install pipenv -RUN python --version +RUN $PYTHON --version RUN pip --version RUN pipenv --version From 8f0f6e4aed128a5039747968e394ff093a8b88a3 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 25 Apr 2019 15:53:13 +0200 Subject: [PATCH 54/78] ci: do not define dependencies; skip them if artifacts are not needed Artifacts are passed on by default, so we do not have to define them in 'dependencies' when we need them. On the contrary, we define them in case we do not need them. --- .gitlab-ci.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 83719a924b..af0f6ca289 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -77,36 +77,32 @@ build legacy: test style: stage: test + dependencies: [] # no need to download artifacts script: - pipenv run make style_check - cd core && pipenv run make templates_check # TODO test core unix unit: stage: test - dependencies: - - build core unix script: - cd core - pipenv run make test test core unix device: stage: test - dependencies: - - build core unix script: - cd core - pipenv run make test_emu test core unix monero: stage: test - dependencies: - - build core unix script: - cd core - pipenv run make test_emu_monero test common: stage: test + dependencies: [] # no need to download artifacts script: - cd common - pipenv run jsonlint defs/*.json @@ -118,6 +114,7 @@ test common: test crypto: stage: test + dependencies: [] # no need to download artifacts script: - cd crypto - pipenv run make From 81b6010b0f0f3282a8e4a8c0db986e8ac998a2d0 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 25 Apr 2019 15:32:48 +0200 Subject: [PATCH 55/78] legacy: remove unnecessary shell.nix --- legacy/shell.nix | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 legacy/shell.nix diff --git a/legacy/shell.nix b/legacy/shell.nix deleted file mode 100644 index 87f5ce3442..0000000000 --- a/legacy/shell.nix +++ /dev/null @@ -1,9 +0,0 @@ -with import {}; - -let - myPython = python3.withPackages(p: [p.trezor p.Mako p.munch p.pillow]); -in - stdenv.mkDerivation { - name = "trezor-mcu-dev"; - buildInputs = [ myPython protobuf gnumake gcc gcc-arm-embedded pkgconfig SDL2 SDL2_image clang-tools ]; - } From 662537095ef8d9815b6fc06489861fd69528f702 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 25 Apr 2019 16:38:25 +0200 Subject: [PATCH 56/78] storage: add tests dependencies to Pipfile --- Pipfile | 4 ++++ Pipfile.lock | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Pipfile b/Pipfile index 0147386fd3..2ee9dd6826 100644 --- a/Pipfile +++ b/Pipfile @@ -43,6 +43,10 @@ py_trezor_crypto_ph4 = {version = ">=0.1.1"} setuptools = ">=24.2.0" typing = "*" +# storage +cryptography = "*" +hypothesis = "*" + [dev-packages] scan-build = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 1ab45b8fd6..9f9cf4f756 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "1d18173266c4e9735f105b869b0a9ec701400ca360b8a7a13e400ce644b006dd" + "sha256": "4c4b090a727ea8f5db592444bf5930bb9ec06898d5b58d7c4eba4fd9a1317ebb" }, "pipfile-spec": 6, "requires": {}, @@ -160,6 +160,7 @@ "sha256:d9ed28030797c00f4bc43c86bf819266c76a5ea61d006cd4078a93ebf7da6bfd", "sha256:e603aa7bb52e4e8ed4119a58a03b60323918467ef209e6ff9db3ac382e5cf2c6" ], + "index": "pypi", "version": "==2.6.1" }, "ctypeslib2": { @@ -228,6 +229,15 @@ "index": "pypi", "version": "==0.10.1" }, + "hypothesis": { + "hashes": [ + "sha256:4e1378aec40b109f2212f8416a0ef27635b40fdc22a7a8116cd5527776ce1f9e", + "sha256:63d33b2394410ab09a05a19354d826e2aa7814288b0800eb5e89857181def40a", + "sha256:905395b9da7fe04e5ce32b41eace83b613586a104db4b4b0a2552db980a40dd2" + ], + "index": "pypi", + "version": "==4.18.0" + }, "idna": { "hashes": [ "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", From 99329fb30d392fdd4542b72c24f32bd7292e9a26 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 25 Apr 2019 16:38:56 +0200 Subject: [PATCH 57/78] storage: add tests from https://github.com/trezor/trezor-storage-test --- storage/tests/Makefile | 12 + storage/tests/README.md | 12 + storage/tests/c/Makefile | 25 ++ storage/tests/c/common.c | 57 ++++ storage/tests/c/common.h | 32 +++ storage/tests/c/flash.c | 129 +++++++++ storage/tests/c/flash.h | 41 +++ storage/tests/c/libtrezor-storage.so | Bin 0 -> 86616 bytes storage/tests/c/norcow_config.h | 43 +++ storage/tests/c/secbool.h | 33 +++ storage/tests/c/storage.py | 72 +++++ storage/tests/c0/Makefile | 14 + storage/tests/c0/common.h | 29 ++ storage/tests/c0/flash.c | 130 +++++++++ storage/tests/c0/flash.h | 41 +++ storage/tests/c0/libtrezor-storage0.so | Bin 0 -> 21928 bytes storage/tests/c0/norcow.c | 305 +++++++++++++++++++++ storage/tests/c0/norcow.h | 58 ++++ storage/tests/c0/norcow_config.h | 29 ++ storage/tests/c0/secbool.h | 33 +++ storage/tests/c0/storage.c | 238 ++++++++++++++++ storage/tests/c0/storage.h | 38 +++ storage/tests/c0/storage.py | 54 ++++ storage/tests/python/__init__.py | 0 storage/tests/python/src/__init__.py | 0 storage/tests/python/src/consts.py | 152 ++++++++++ storage/tests/python/src/crypto.py | 112 ++++++++ storage/tests/python/src/helpers.py | 45 +++ storage/tests/python/src/norcow.py | 174 ++++++++++++ storage/tests/python/src/pin_log.py | 123 +++++++++ storage/tests/python/src/prng.py | 34 +++ storage/tests/python/src/storage.py | 237 ++++++++++++++++ storage/tests/python/tests/__init__.py | 0 storage/tests/python/tests/common.py | 2 + storage/tests/python/tests/conftest.py | 8 + storage/tests/python/tests/test_helpers.py | 13 + storage/tests/python/tests/test_norcow.py | 155 +++++++++++ storage/tests/python/tests/test_pin.py | 28 ++ storage/tests/python/tests/test_pin_log.py | 12 + storage/tests/python/tests/test_prng.py | 11 + storage/tests/test.py | 45 +++ storage/tests/tests/__init__.py | 0 storage/tests/tests/common.py | 23 ++ storage/tests/tests/storage_model.py | 68 +++++ storage/tests/tests/test_compact.py | 32 +++ storage/tests/tests/test_pin.py | 66 +++++ storage/tests/tests/test_random.py | 83 ++++++ storage/tests/tests/test_random_upgrade.py | 73 +++++ storage/tests/tests/test_set_get.py | 178 ++++++++++++ storage/tests/tests/test_upgrade.py | 75 +++++ 50 files changed, 3174 insertions(+) create mode 100644 storage/tests/Makefile create mode 100644 storage/tests/README.md create mode 100644 storage/tests/c/Makefile create mode 100644 storage/tests/c/common.c create mode 100644 storage/tests/c/common.h create mode 100644 storage/tests/c/flash.c create mode 100644 storage/tests/c/flash.h create mode 100755 storage/tests/c/libtrezor-storage.so create mode 100644 storage/tests/c/norcow_config.h create mode 100644 storage/tests/c/secbool.h create mode 100644 storage/tests/c/storage.py create mode 100644 storage/tests/c0/Makefile create mode 100644 storage/tests/c0/common.h create mode 100644 storage/tests/c0/flash.c create mode 100644 storage/tests/c0/flash.h create mode 100755 storage/tests/c0/libtrezor-storage0.so create mode 100644 storage/tests/c0/norcow.c create mode 100644 storage/tests/c0/norcow.h create mode 100644 storage/tests/c0/norcow_config.h create mode 100644 storage/tests/c0/secbool.h create mode 100644 storage/tests/c0/storage.c create mode 100644 storage/tests/c0/storage.h create mode 100644 storage/tests/c0/storage.py create mode 100644 storage/tests/python/__init__.py create mode 100644 storage/tests/python/src/__init__.py create mode 100644 storage/tests/python/src/consts.py create mode 100644 storage/tests/python/src/crypto.py create mode 100644 storage/tests/python/src/helpers.py create mode 100644 storage/tests/python/src/norcow.py create mode 100644 storage/tests/python/src/pin_log.py create mode 100644 storage/tests/python/src/prng.py create mode 100644 storage/tests/python/src/storage.py create mode 100644 storage/tests/python/tests/__init__.py create mode 100644 storage/tests/python/tests/common.py create mode 100644 storage/tests/python/tests/conftest.py create mode 100644 storage/tests/python/tests/test_helpers.py create mode 100644 storage/tests/python/tests/test_norcow.py create mode 100644 storage/tests/python/tests/test_pin.py create mode 100644 storage/tests/python/tests/test_pin_log.py create mode 100644 storage/tests/python/tests/test_prng.py create mode 100755 storage/tests/test.py create mode 100644 storage/tests/tests/__init__.py create mode 100644 storage/tests/tests/common.py create mode 100644 storage/tests/tests/storage_model.py create mode 100644 storage/tests/tests/test_compact.py create mode 100644 storage/tests/tests/test_pin.py create mode 100644 storage/tests/tests/test_random.py create mode 100644 storage/tests/tests/test_random_upgrade.py create mode 100644 storage/tests/tests/test_set_get.py create mode 100644 storage/tests/tests/test_upgrade.py diff --git a/storage/tests/Makefile b/storage/tests/Makefile new file mode 100644 index 0000000000..16cc25552c --- /dev/null +++ b/storage/tests/Makefile @@ -0,0 +1,12 @@ +.PHONY: tests + +build: + $(MAKE) -C c + $(MAKE) -C c0 + +## tests commands: +tests: + pytest -k "not hypothesis" + +tests_all: + pytest diff --git a/storage/tests/README.md b/storage/tests/README.md new file mode 100644 index 0000000000..3fd1eacc8a --- /dev/null +++ b/storage/tests/README.md @@ -0,0 +1,12 @@ +# Trezor Storage tests + +This repository contains all the necessary files to properly test Trezor's internal storage, which is implemented in the [trezor-storage](https://github.com/trezor/trezor-storage) repository. + +The CI is available on the internal GitLab. + +This repository consists of: + +- `c`: The actual C version is implemented in [trezor-storage](https://github.com/trezor/trezor-storage), however we need some other accompanying files to build it on PC. +- `c0`: This is the older version of Trezor storage. It is used to test upgrades from the older format to the newer one. +- `python`: Python version. Serves as a reference implementation and is implemented purely for the goal of properly testing the C version. +- `tests`: Most of the tests run the two implementations against each other. Uses Pytest and [hypothesis](https://hypothesis.works) for random tests. diff --git a/storage/tests/c/Makefile b/storage/tests/c/Makefile new file mode 100644 index 0000000000..a1fc96c2e3 --- /dev/null +++ b/storage/tests/c/Makefile @@ -0,0 +1,25 @@ +CC = gcc +CFLAGS = -Wall -Wshadow -Wextra -Wpedantic -Werror -fPIC -DTREZOR_STORAGE_TEST +LIBS = +INC = -I ../../../crypto -I ../../../storage -I . +OBJ = flash.o common.o +OBJ += ../../../storage/storage.o ../../../storage/norcow.o +OBJ += ../../../crypto/pbkdf2.o +OBJ += ../../../crypto/rand.o +OBJ += ../../../crypto/chacha20poly1305/rfc7539.o +OBJ += ../../../crypto/chacha20poly1305/chacha20poly1305.o +OBJ += ../../../crypto/chacha20poly1305/poly1305-donna.o +OBJ += ../../../crypto/chacha20poly1305/chacha_merged.o +OBJ += ../../../crypto/hmac.o +OBJ += ../../../crypto/sha2.o +OBJ += ../../../crypto/memzero.o +OUT = libtrezor-storage.so + +$(OUT): $(OBJ) + $(CC) $(CFLAGS) $(LIBS) $(OBJ) -shared -o $(OUT) + +%.o: %.c %.h + $(CC) $(CFLAGS) $(INC) -c $< -o $@ + +clean: + rm -f $(OUT) $(OBJ) diff --git a/storage/tests/c/common.c b/storage/tests/c/common.c new file mode 100644 index 0000000000..faa109c07c --- /dev/null +++ b/storage/tests/c/common.c @@ -0,0 +1,57 @@ +/* + * 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 . + */ + +#include +#include +#include + +#include "common.h" + +void __shutdown(void) +{ + printf("SHUTDOWN\n"); + exit(3); +} + +void __fatal_error(const char *expr, const char *msg, const char *file, int line, const char *func) +{ + printf("\nFATAL ERROR:\n"); + if (expr) { + printf("expr: %s\n", expr); + } + if (msg) { + printf("msg : %s\n", msg); + } + if (file) { + printf("file: %s:%d\n", file, line); + } + if (func) { + printf("func: %s\n", func); + } + __shutdown(); +} + +void error_shutdown(const char *line1, const char *line2, const char *line3, const char *line4) { + // For testing do not treat pin_fails_check_max as a fatal error. + (void) line1; + (void) line2; + (void) line3; + (void) line4; + return; +} diff --git a/storage/tests/c/common.h b/storage/tests/c/common.h new file mode 100644 index 0000000000..e58a1b0077 --- /dev/null +++ b/storage/tests/c/common.h @@ -0,0 +1,32 @@ +/* + * 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 . + */ + +#ifndef __TREZORHAL_COMMON_H__ +#define __TREZORHAL_COMMON_H__ + +#include "secbool.h" + +void __fatal_error(const char *expr, const char *msg, const char *file, int line, const char *func); +void error_shutdown(const char *line1, const char *line2, const char *line3, const char *line4); + +#define ensure(expr, msg) (((expr) == sectrue) ? (void)0 : __fatal_error(#expr, msg, __FILE__, __LINE__, __func__)) + +#define hal_delay(ms) (void)ms; + +#endif diff --git a/storage/tests/c/flash.c b/storage/tests/c/flash.c new file mode 100644 index 0000000000..6282e3997d --- /dev/null +++ b/storage/tests/c/flash.c @@ -0,0 +1,129 @@ +/* + * 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 . + */ + +#include +#include +#include + +#include "common.h" +#include "flash.h" + +static const uint32_t FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT + 1] = { + [ 0] = 0x08000000, // - 0x08003FFF | 16 KiB + [ 1] = 0x08004000, // - 0x08007FFF | 16 KiB + [ 2] = 0x08008000, // - 0x0800BFFF | 16 KiB + [ 3] = 0x0800C000, // - 0x0800FFFF | 16 KiB + [ 4] = 0x08010000, // - 0x0801FFFF | 64 KiB + [ 5] = 0x08020000, // - 0x0803FFFF | 128 KiB + [ 6] = 0x08040000, // - 0x0805FFFF | 128 KiB + [ 7] = 0x08060000, // - 0x0807FFFF | 128 KiB + [ 8] = 0x08080000, // - 0x0809FFFF | 128 KiB + [ 9] = 0x080A0000, // - 0x080BFFFF | 128 KiB + [10] = 0x080C0000, // - 0x080DFFFF | 128 KiB + [11] = 0x080E0000, // - 0x080FFFFF | 128 KiB + [12] = 0x08100000, // - 0x08103FFF | 16 KiB + [13] = 0x08104000, // - 0x08107FFF | 16 KiB + [14] = 0x08108000, // - 0x0810BFFF | 16 KiB + [15] = 0x0810C000, // - 0x0810FFFF | 16 KiB + [16] = 0x08110000, // - 0x0811FFFF | 64 KiB + [17] = 0x08120000, // - 0x0813FFFF | 128 KiB + [18] = 0x08140000, // - 0x0815FFFF | 128 KiB + [19] = 0x08160000, // - 0x0817FFFF | 128 KiB + [20] = 0x08180000, // - 0x0819FFFF | 128 KiB + [21] = 0x081A0000, // - 0x081BFFFF | 128 KiB + [22] = 0x081C0000, // - 0x081DFFFF | 128 KiB + [23] = 0x081E0000, // - 0x081FFFFF | 128 KiB + [24] = 0x08200000, // last element - not a valid sector +}; +const uint32_t FLASH_SIZE = 0x200000; +uint8_t *FLASH_BUFFER = NULL; + +void flash_init(void) +{ + assert(FLASH_SIZE == FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT] - FLASH_SECTOR_TABLE[0]); +} + +secbool flash_unlock_write(void) +{ + return sectrue; +} + +secbool flash_lock_write(void) +{ + return sectrue; +} + +const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size) +{ + if (sector >= FLASH_SECTOR_COUNT) { + return NULL; + } + const uint32_t addr = FLASH_SECTOR_TABLE[sector] + offset; + const uint32_t next = FLASH_SECTOR_TABLE[sector + 1]; + if (addr + size > next) { + return NULL; + } + return FLASH_BUFFER + addr - FLASH_SECTOR_TABLE[0]; +} + +secbool flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(int pos, int len)) +{ + if (progress) { + progress(0, len); + } + for (int i = 0; i < len; i++) { + const uint8_t sector = sectors[i]; + const uint32_t offset = FLASH_SECTOR_TABLE[sector] - FLASH_SECTOR_TABLE[0]; + const uint32_t size = FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector]; + memset(FLASH_BUFFER + offset, 0xFF, size); + if (progress) { + progress(i + 1, len); + } + } + return sectrue; +} + +secbool flash_write_byte(uint8_t sector, uint32_t offset, uint8_t data) +{ + uint8_t *flash = (uint8_t *)flash_get_address(sector, offset, 1); + if (!flash) { + return secfalse; + } + if ((flash[0] & data) != data) { + return secfalse; // we cannot change zeroes to ones + } + flash[0] = data; + return sectrue; +} + +secbool flash_write_word(uint8_t sector, uint32_t offset, uint32_t data) +{ + if (offset % 4) { // we write only at 4-byte boundary + return secfalse; + } + uint32_t *flash = (uint32_t *)flash_get_address(sector, offset, sizeof(data)); + if (!flash) { + return secfalse; + } + if ((flash[0] & data) != data) { + return secfalse; // we cannot change zeroes to ones + } + flash[0] = data; + return sectrue; +} diff --git a/storage/tests/c/flash.h b/storage/tests/c/flash.h new file mode 100644 index 0000000000..102c9e1904 --- /dev/null +++ b/storage/tests/c/flash.h @@ -0,0 +1,41 @@ +/* + * 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 . + */ + +#ifndef FLASH_H +#define FLASH_H + +#include +#include +#include "secbool.h" + +#define FLASH_SECTOR_COUNT 24 + +void flash_init(void); + +secbool __wur flash_unlock_write(void); +secbool __wur flash_lock_write(void); + +const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size); + +secbool __wur flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(int pos, int len)); +static inline secbool flash_erase(uint8_t sector) { return flash_erase_sectors(§or, 1, NULL); } +secbool __wur flash_write_byte(uint8_t sector, uint32_t offset, uint8_t data); +secbool __wur flash_write_word(uint8_t sector, uint32_t offset, uint32_t data); + +#endif diff --git a/storage/tests/c/libtrezor-storage.so b/storage/tests/c/libtrezor-storage.so new file mode 100755 index 0000000000000000000000000000000000000000..83aa0728477018e3cca9ef144fa3e5f7cf5b70a9 GIT binary patch literal 86616 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 literal 0 HcmV?d00001 diff --git a/storage/tests/c/norcow_config.h b/storage/tests/c/norcow_config.h new file mode 100644 index 0000000000..04e278c762 --- /dev/null +++ b/storage/tests/c/norcow_config.h @@ -0,0 +1,43 @@ +/* + * 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 . + */ + +#ifndef __NORCOW_CONFIG_H__ +#define __NORCOW_CONFIG_H__ + +#include "flash.h" + +#define NORCOW_SECTOR_COUNT 2 +#define NORCOW_SECTOR_SIZE (64*1024) +#define NORCOW_SECTORS {4, 16} + +/* + * The length of the sector header in bytes. The header is preserved between sector erasures. + */ +#if TREZOR_MODEL == 1 +#define NORCOW_HEADER_LEN (0x100) +#else +#define NORCOW_HEADER_LEN 0 +#endif + +/* + * Current storage version. + */ +#define NORCOW_VERSION ((uint32_t)0x00000001) + +#endif diff --git a/storage/tests/c/secbool.h b/storage/tests/c/secbool.h new file mode 100644 index 0000000000..76dfb38dc1 --- /dev/null +++ b/storage/tests/c/secbool.h @@ -0,0 +1,33 @@ +/* + * 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 . + */ + +#ifndef TREZORHAL_SECBOOL_H +#define TREZORHAL_SECBOOL_H + +#include + +typedef uint32_t secbool; +#define sectrue 0xAAAAAAAAU +#define secfalse 0x00000000U + +#ifndef __wur +#define __wur __attribute__ ((warn_unused_result)) +#endif + +#endif diff --git a/storage/tests/c/storage.py b/storage/tests/c/storage.py new file mode 100644 index 0000000000..f9a1a6fa53 --- /dev/null +++ b/storage/tests/c/storage.py @@ -0,0 +1,72 @@ +import ctypes as c +import os + +sectrue = -1431655766 # 0xAAAAAAAAA +fname = os.path.join(os.path.dirname(__file__), "libtrezor-storage.so") + + +class Storage: + def __init__(self) -> None: + self.lib = c.cdll.LoadLibrary(fname) + self.flash_size = c.cast(self.lib.FLASH_SIZE, c.POINTER(c.c_uint32))[0] + self.flash_buffer = c.create_string_buffer(self.flash_size) + c.cast(self.lib.FLASH_BUFFER, c.POINTER(c.c_void_p))[0] = c.addressof(self.flash_buffer) + + def init(self, salt: bytes) -> None: + self.lib.storage_init(0, salt, c.c_uint16(len(salt))) + + def wipe(self) -> None: + self.lib.storage_wipe() + + def unlock(self, pin: int) -> bool: + return sectrue == self.lib.storage_unlock(c.c_uint32(pin)) + + def lock(self) -> None: + self.lib.storage_lock() + + def has_pin(self) -> bool: + return sectrue == self.lib.storage_has_pin() + + def get_pin_rem(self) -> int: + return self.lib.storage_get_pin_rem() + + def change_pin(self, oldpin: int, newpin: int) -> bool: + return sectrue == self.lib.storage_change_pin(c.c_uint32(oldpin), c.c_uint32(newpin)) + + def get(self, key: int) -> bytes: + val_len = c.c_uint16() + if sectrue != self.lib.storage_get(c.c_uint16(key), None, 0, c.byref(val_len)): + raise RuntimeError("Failed to find key in storage.") + s = c.create_string_buffer(val_len.value) + if sectrue != self.lib.storage_get(c.c_uint16(key), s, val_len, c.byref(val_len)): + raise RuntimeError("Failed to get value from storage.") + return s.raw + + def set(self, key: int, val: bytes) -> None: + if sectrue != self.lib.storage_set(c.c_uint16(key), val, c.c_uint16(len(val))): + raise RuntimeError("Failed to set value in storage.") + + def set_counter(self, key: int, count: int) -> bool: + return sectrue == self.lib.storage_set_counter(c.c_uint16(key), c.c_uint32(count)) + + def next_counter(self, key: int) -> int: + count = c.c_uint32() + if sectrue == self.lib.storage_next_counter(c.c_uint16(key), c.byref(count)): + return count.value + else: + return None + + def delete(self, key: int) -> bool: + return sectrue == self.lib.storage_delete(c.c_uint16(key)) + + def _dump(self) -> bytes: + # return just sectors 4 and 16 of the whole flash + return [self.flash_buffer[0x010000:0x010000 + 0x10000], self.flash_buffer[0x110000:0x110000 + 0x10000]] + + def _get_flash_buffer(self) -> bytes: + return bytes(self.flash_buffer) + + def _set_flash_buffer(self, buf: bytes) -> None: + if len(buf) != self.flash_size: + raise RuntimeError("Failed to set flash buffer due to length mismatch.") + self.flash_buffer.value = buf diff --git a/storage/tests/c0/Makefile b/storage/tests/c0/Makefile new file mode 100644 index 0000000000..854ffa6931 --- /dev/null +++ b/storage/tests/c0/Makefile @@ -0,0 +1,14 @@ +CC=gcc +CFLAGS=-Wall -fPIC +LIBS= +OBJ=storage.o norcow.o flash.o +OUT=libtrezor-storage0.so + +$(OUT): $(OBJ) + $(CC) $(CFLAGS) $(LIBS) $(OBJ) -shared -o $(OUT) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f $(OUT) $(OBJ) diff --git a/storage/tests/c0/common.h b/storage/tests/c0/common.h new file mode 100644 index 0000000000..6f2b178c82 --- /dev/null +++ b/storage/tests/c0/common.h @@ -0,0 +1,29 @@ +/* + * 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 . + */ + +#ifndef __TREZORHAL_COMMON_H__ +#define __TREZORHAL_COMMON_H__ + +#include "secbool.h" + +#define ensure(expr, msg) (((expr) == sectrue) ? (void)0 : (void)1) + +#define hal_delay(ms) (void)ms; + +#endif diff --git a/storage/tests/c0/flash.c b/storage/tests/c0/flash.c new file mode 100644 index 0000000000..3a0a143f2f --- /dev/null +++ b/storage/tests/c0/flash.c @@ -0,0 +1,130 @@ +/* + * 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 . + */ + +#include +#include +#include + +#include "common.h" +#include "flash.h" + +static const uint32_t FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT + 1] = { + [ 0] = 0x08000000, // - 0x08003FFF | 16 KiB + [ 1] = 0x08004000, // - 0x08007FFF | 16 KiB + [ 2] = 0x08008000, // - 0x0800BFFF | 16 KiB + [ 3] = 0x0800C000, // - 0x0800FFFF | 16 KiB + [ 4] = 0x08010000, // - 0x0801FFFF | 64 KiB + [ 5] = 0x08020000, // - 0x0803FFFF | 128 KiB + [ 6] = 0x08040000, // - 0x0805FFFF | 128 KiB + [ 7] = 0x08060000, // - 0x0807FFFF | 128 KiB + [ 8] = 0x08080000, // - 0x0809FFFF | 128 KiB + [ 9] = 0x080A0000, // - 0x080BFFFF | 128 KiB + [10] = 0x080C0000, // - 0x080DFFFF | 128 KiB + [11] = 0x080E0000, // - 0x080FFFFF | 128 KiB + [12] = 0x08100000, // - 0x08103FFF | 16 KiB + [13] = 0x08104000, // - 0x08107FFF | 16 KiB + [14] = 0x08108000, // - 0x0810BFFF | 16 KiB + [15] = 0x0810C000, // - 0x0810FFFF | 16 KiB + [16] = 0x08110000, // - 0x0811FFFF | 64 KiB + [17] = 0x08120000, // - 0x0813FFFF | 128 KiB + [18] = 0x08140000, // - 0x0815FFFF | 128 KiB + [19] = 0x08160000, // - 0x0817FFFF | 128 KiB + [20] = 0x08180000, // - 0x0819FFFF | 128 KiB + [21] = 0x081A0000, // - 0x081BFFFF | 128 KiB + [22] = 0x081C0000, // - 0x081DFFFF | 128 KiB + [23] = 0x081E0000, // - 0x081FFFFF | 128 KiB + [24] = 0x08200000, // last element - not a valid sector +}; + +const uint32_t FLASH_SIZE = 0x200000; +uint8_t *FLASH_BUFFER = NULL; + +void flash_init(void) +{ + assert(FLASH_SIZE == FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT] - FLASH_SECTOR_TABLE[0]); +} + +secbool flash_unlock(void) +{ + return sectrue; +} + +secbool flash_lock(void) +{ + return sectrue; +} + +const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size) +{ + if (sector >= FLASH_SECTOR_COUNT) { + return NULL; + } + const uint32_t addr = FLASH_SECTOR_TABLE[sector] + offset; + const uint32_t next = FLASH_SECTOR_TABLE[sector + 1]; + if (addr + size > next) { + return NULL; + } + return FLASH_BUFFER + addr - FLASH_SECTOR_TABLE[0]; +} + +secbool flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(int pos, int len)) +{ + if (progress) { + progress(0, len); + } + for (int i = 0; i < len; i++) { + const uint8_t sector = sectors[i]; + const uint32_t offset = FLASH_SECTOR_TABLE[sector] - FLASH_SECTOR_TABLE[0]; + const uint32_t size = FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector]; + memset(FLASH_BUFFER + offset, 0xFF, size); + if (progress) { + progress(i + 1, len); + } + } + return sectrue; +} + +secbool flash_write_byte(uint8_t sector, uint32_t offset, uint8_t data) +{ + uint8_t *flash = (uint8_t *)flash_get_address(sector, offset, 1); + if (!flash) { + return secfalse; + } + if ((flash[0] & data) != data) { + return secfalse; // we cannot change zeroes to ones + } + flash[0] = data; + return sectrue; +} + +secbool flash_write_word(uint8_t sector, uint32_t offset, uint32_t data) +{ + if (offset % 4) { // we write only at 4-byte boundary + return secfalse; + } + uint32_t *flash = (uint32_t *)flash_get_address(sector, offset, sizeof(data)); + if (!flash) { + return secfalse; + } + if ((flash[0] & data) != data) { + return secfalse; // we cannot change zeroes to ones + } + flash[0] = data; + return sectrue; +} diff --git a/storage/tests/c0/flash.h b/storage/tests/c0/flash.h new file mode 100644 index 0000000000..436dceb429 --- /dev/null +++ b/storage/tests/c0/flash.h @@ -0,0 +1,41 @@ +/* + * 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 . + */ + +#ifndef FLASH_H +#define FLASH_H + +#include +#include +#include "secbool.h" + +#define FLASH_SECTOR_COUNT 24 + +void flash_init(void); + +secbool __wur flash_unlock(void); +secbool __wur flash_lock(void); + +const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size); + +secbool __wur flash_erase_sectors(const uint8_t *sectors, int len, void (*progress)(int pos, int len)); +static inline secbool flash_erase_sector(uint8_t sector) { return flash_erase_sectors(§or, 1, NULL); } +secbool __wur flash_write_byte(uint8_t sector, uint32_t offset, uint8_t data); +secbool __wur flash_write_word(uint8_t sector, uint32_t offset, uint32_t data); + +#endif diff --git a/storage/tests/c0/libtrezor-storage0.so b/storage/tests/c0/libtrezor-storage0.so new file mode 100755 index 0000000000000000000000000000000000000000..e03da87a7d60a1b8c634beab479835dada4ae0c3 GIT binary patch literal 21928 zcmeHP3wTu3oxhW0Fd#5dfdbYtYG}dLG`s{vG(c{+)C3TjT5IVr$xI+MFOwM_Zlloz zo7-zFcBN(8x@teR&uz>4;PtIEJW3)(jgQjkrfaGxo!LYi7sO~Iv%mj2=YQwUC3JVc z{dV{J_RHnF`QP*VKhFRB&+{JUp8NFTq708m<116UNuyN$B@!u6jQC3R08*eW&_?6$ z<=Rvso0Vdn_f$JZLQJ(BnWj9v^r+HqddUPS$CUY`C-Tdee9c9_H%j76oqEK|zDe@4 zRe6|xFGJ!Tin1OhNpG^!o2>MhwyDrD&6Z|_kL2e9WzS7>l^j!1CSa@o5Dn9FzbcoT z=BoUdvfi!GQ$<&n|1#mQuTlA3C@zC4Kc*@v%12d{iF^6VfVTWQ&;RqP;NzPfT=&Og z8ILW^{PgXe50D4cj3_1)hKq0)EzS65{>u|*Y@9pg`Nr&)ERGjg+Qqni^ni=}yA=0j zlyF>>=Oo-yabK?R1HMT^|1vuN>FJNY-!%D$4?VEE?FaS8LMukUSu^9Yf6RLB>AcW2 zuRZs{SHJZ7l&9)ve(R;Jx%;2Hwq*Le2RAi*{F~yAt6uwL*-Kl0b^7hbM;<%+bnY7u zJ#b>)4TtU=bIWLDepo{O`G?{B4KVWCeSIH+8xG$E!QpU1!{57XiO&82LW| zyA+ZAyH7~wd?iz@@KV17&MLe^;SW-fap{t1zXc^bzgBpS!plUtF4pp{mOz=p?^E~} zu9U!Hh3g96p>V2mx=L}A{l`BglG>w^XqPJaxsxR@R^fXjPV9#qU2By5Bg&sz z32Q$ECHei+B=BQ}PgC;qRPa7j_}777qK(s<^CiIjY>txu>6H@yx{?oolKl_WxXI;h zQFyb;cb<|@D*QL9UV;k$s>0_e`CAqKw!&{v@?nMV5PnY5I#x@7`%_f;`GoS5(umgZ0r+T{IZff-BaR1jCWK$cBpgXryj!$+F6t>d4wqX=Oz6hUWy!wuFM^71g22 zihCkjeYB=7v>_6#sIG`=)irfxHJj6(W3}N>G{T(C6}9&BhDa11!I`qVf@K@;3YLc| zDz&OeRXuQJh#onEvW*c4)>c&8NUXZDrtB^|N%mOSskkvzpQcu}F;oqQotA`K<&~lO zjloD=s6G;`kCdTY^_s+BIT#9u>mv1xsH`P>b6rI=65L!<7gq8TDBX&3O9|npd=zmi zDqeD%5xi~17j;cOE)5hF>1z;gor@K~3L7KCaX#w;&aIrp6_TmowlL{j;;J}^p-=G7hd55pb${)g< zbBo`BE7yg}39r`IL&Q*EpXs!XcDXf2QKSMY8Vb&4XuK)+JU12?CT2-oW~?$ zu6N+hiEE<+_t{o7t=567p;d6VIPfbR@{JCByaR7>;4~-mwcUXaH@~sm2px&QNCZYA zFcN|Pn+Tlted;5lsXyDWyf5FZX~wp;XhyQrXgZpGL^zb3_ZHyf^j`e+O)J0?r8g2U z(UZhA{S8Xf6rSi5=~pOCQ(~e`q>odYro6;%kv>dmnz|BCi1YzU(^Q|>F49j?nx^tZ zqe%aR(llKsYDN0{l&0lJV!cRzhtf3VC02{{Hz`fK4v7Mh{yL>;`J2cW=?5rHQ+dKK z()UoBrr1P|NY_!CrnrPA(qEx8O`VB>bIGL9y7+DYMyr=_W8q+QB0~2tb_XbQqmw

Zft> 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 r3^(V`;|H. + */ + +#include + +#include "norcow.h" +#include "flash.h" +#include "common.h" + +// NRCW = 4e524357 +#define NORCOW_MAGIC ((uint32_t)0x5743524e) +#define NORCOW_MAGIC_LEN (sizeof(uint32_t)) + +static const uint8_t norcow_sectors[NORCOW_SECTOR_COUNT] = NORCOW_SECTORS; +static uint8_t norcow_active_sector = 0; +static uint32_t norcow_active_offset = NORCOW_MAGIC_LEN; + +/* + * Returns pointer to sector, starting with offset + * Fails when there is not enough space for data of given size + */ +static const void *norcow_ptr(uint8_t sector, uint32_t offset, uint32_t size) +{ + ensure(sectrue * (sector <= NORCOW_SECTOR_COUNT), "invalid sector"); + return flash_get_address(norcow_sectors[sector], offset, size); +} + +/* + * Writes data to given sector, starting from offset + */ +static secbool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix, const uint8_t *data, uint16_t len) +{ + if (sector >= NORCOW_SECTOR_COUNT) { + return secfalse; + } + ensure(flash_unlock(), NULL); + + // write prefix + ensure(flash_write_word(norcow_sectors[sector], offset, prefix), NULL); + + if (len > 0) { + offset += sizeof(uint32_t); + // write data + for (uint16_t i = 0; i < len; i++, offset++) { + ensure(flash_write_byte(norcow_sectors[sector], offset, data[i]), NULL); + } + // pad with zeroes + for (; offset % 4; offset++) { + ensure(flash_write_byte(norcow_sectors[sector], offset, 0x00), NULL); + } + } + ensure(flash_lock(), NULL); + return sectrue; +} + +/* + * Erases sector (and sets a magic) + */ +static void norcow_erase(uint8_t sector, secbool set_magic) +{ + ensure(sectrue * (sector <= NORCOW_SECTOR_COUNT), "invalid sector"); + ensure(flash_erase_sector(norcow_sectors[sector]), "erase failed"); + if (sectrue == set_magic) { + ensure(norcow_write(sector, 0, NORCOW_MAGIC, NULL, 0), "set magic failed"); + } +} + +#define ALIGN4(X) (X) = ((X) + 3) & ~3 + +/* + * Reads one item starting from offset + */ +static secbool read_item(uint8_t sector, uint32_t offset, uint16_t *key, const void **val, uint16_t *len, uint32_t *pos) +{ + *pos = offset; + + const void *k = norcow_ptr(sector, *pos, 2); + if (k == NULL) return secfalse; + *pos += 2; + memcpy(key, k, sizeof(uint16_t)); + if (*key == 0xFFFF) { + return secfalse; + } + + const void *l = norcow_ptr(sector, *pos, 2); + if (l == NULL) return secfalse; + *pos += 2; + memcpy(len, l, sizeof(uint16_t)); + + *val = norcow_ptr(sector, *pos, *len); + if (*val == NULL) return secfalse; + *pos += *len; + ALIGN4(*pos); + return sectrue; +} + +/* + * Writes one item starting from offset + */ +static secbool write_item(uint8_t sector, uint32_t offset, uint16_t key, const void *val, uint16_t len, uint32_t *pos) +{ + uint32_t prefix = (len << 16) | key; + *pos = offset + sizeof(uint32_t) + len; + ALIGN4(*pos); + return norcow_write(sector, offset, prefix, val, len); +} + +/* + * Finds item in given sector + */ +static secbool find_item(uint8_t sector, uint16_t key, const void **val, uint16_t *len) +{ + *val = 0; + *len = 0; + uint32_t offset = NORCOW_MAGIC_LEN; + for (;;) { + uint16_t k, l; + const void *v; + uint32_t pos; + if (sectrue != read_item(sector, offset, &k, &v, &l, &pos)) { + break; + } + if (key == k) { + *val = v; + *len = l; + } + offset = pos; + } + return sectrue * (*val != NULL); +} + +/* + * Finds first unused offset in given sector + */ +static uint32_t find_free_offset(uint8_t sector) +{ + uint32_t offset = NORCOW_MAGIC_LEN; + for (;;) { + uint16_t key, len; + const void *val; + uint32_t pos; + if (sectrue != read_item(sector, offset, &key, &val, &len, &pos)) { + break; + } + offset = pos; + } + return offset; +} + +/* + * Compacts active sector and sets new active sector + */ +static void compact() +{ + uint8_t norcow_next_sector = (norcow_active_sector + 1) % NORCOW_SECTOR_COUNT; + norcow_erase(norcow_next_sector, sectrue); + + uint32_t offset = NORCOW_MAGIC_LEN, offsetw = NORCOW_MAGIC_LEN; + + for (;;) { + // read item + uint16_t k, l; + const void *v; + uint32_t pos; + secbool r = read_item(norcow_active_sector, offset, &k, &v, &l, &pos); + if (sectrue != r) { + break; + } + offset = pos; + + // check if not already saved + const void *v2; + uint16_t l2; + r = find_item(norcow_next_sector, k, &v2, &l2); + if (sectrue == r) { + continue; + } + + // scan for latest instance + uint32_t offsetr = offset; + for (;;) { + uint16_t k2; + uint32_t posr; + r = read_item(norcow_active_sector, offsetr, &k2, &v2, &l2, &posr); + if (sectrue != r) { + break; + } + if (k == k2) { + v = v2; + l = l2; + } + offsetr = posr; + } + + // copy the last item + uint32_t posw; + ensure(write_item(norcow_next_sector, offsetw, k, v, l, &posw), "compaction write failed"); + offsetw = posw; + } + + norcow_erase(norcow_active_sector, secfalse); + norcow_active_sector = norcow_next_sector; + norcow_active_offset = find_free_offset(norcow_active_sector); +} + +/* + * Initializes storage + */ +void norcow_init(void) +{ + flash_init(); + secbool found = secfalse; + // detect active sector - starts with magic + for (uint8_t i = 0; i < NORCOW_SECTOR_COUNT; i++) { + const uint32_t *magic = norcow_ptr(i, 0, NORCOW_MAGIC_LEN); + if (magic != NULL && *magic == NORCOW_MAGIC) { + found = sectrue; + norcow_active_sector = i; + break; + } + } + // no active sectors found - let's erase + if (sectrue == found) { + norcow_active_offset = find_free_offset(norcow_active_sector); + } else { + norcow_wipe(); + } +} + +/* + * Wipe the storage + */ +void norcow_wipe(void) +{ + norcow_erase(0, sectrue); + for (uint8_t i = 1; i < NORCOW_SECTOR_COUNT; i++) { + norcow_erase(i, secfalse); + } + norcow_active_sector = 0; + norcow_active_offset = NORCOW_MAGIC_LEN; +} + +/* + * Looks for the given key, returns status of the operation + */ +secbool norcow_get(uint16_t key, const void **val, uint16_t *len) +{ + return find_item(norcow_active_sector, key, val, len); +} + +/* + * Sets the given key, returns status of the operation + */ +secbool norcow_set(uint16_t key, const void *val, uint16_t len) +{ + // check whether there is enough free space + // and compact if full + if (norcow_active_offset + sizeof(uint32_t) + len > NORCOW_SECTOR_SIZE) { + compact(); + } + // write item + uint32_t pos; + secbool r = write_item(norcow_active_sector, norcow_active_offset, key, val, len, &pos); + if (sectrue == r) { + norcow_active_offset = pos; + } + return r; +} + +/* + * Update a word in flash at the given pointer. The pointer must point + * into the NORCOW area. + */ +secbool norcow_update(uint16_t key, uint16_t offset, uint32_t value) +{ + const void *ptr; + uint16_t len; + if (sectrue != find_item(norcow_active_sector, key, &ptr, &len)) { + return secfalse; + } + if ((offset & 3) != 0 || offset >= len) { + return secfalse; + } + uint32_t sector_offset = (const uint8_t*) ptr - (const uint8_t *)norcow_ptr(norcow_active_sector, 0, NORCOW_SECTOR_SIZE) + offset; + ensure(flash_unlock(), NULL); + ensure(flash_write_word(norcow_sectors[norcow_active_sector], sector_offset, value), NULL); + ensure(flash_lock(), NULL); + return sectrue; +} diff --git a/storage/tests/c0/norcow.h b/storage/tests/c0/norcow.h new file mode 100644 index 0000000000..00bab4d0ae --- /dev/null +++ b/storage/tests/c0/norcow.h @@ -0,0 +1,58 @@ +/* + * 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 . + */ + +#ifndef __NORCOW_H__ +#define __NORCOW_H__ + +#include +#include "secbool.h" + +/* + * Storage parameters + */ + +#include "norcow_config.h" + +/* + * Initialize storage + */ +void norcow_init(void); + +/* + * Wipe the storage + */ +void norcow_wipe(void); + +/* + * Looks for the given key, returns status of the operation + */ +secbool norcow_get(uint16_t key, const void **val, uint16_t *len); + +/* + * Sets the given key, returns status of the operation + */ +secbool norcow_set(uint16_t key, const void *val, uint16_t len); + +/* + * Update a word in flash in the given key at the given offset. + * Note that you can only change bits from 1 to 0. + */ +secbool norcow_update(uint16_t key, uint16_t offset, uint32_t value); + +#endif diff --git a/storage/tests/c0/norcow_config.h b/storage/tests/c0/norcow_config.h new file mode 100644 index 0000000000..ff7b1eb0be --- /dev/null +++ b/storage/tests/c0/norcow_config.h @@ -0,0 +1,29 @@ +/* + * 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 . + */ + +#ifndef __NORCOW_CONFIG_H__ +#define __NORCOW_CONFIG_H__ + +#include "flash.h" + +#define NORCOW_SECTOR_COUNT 2 +#define NORCOW_SECTOR_SIZE (64*1024) +#define NORCOW_SECTORS {4, 16} + +#endif diff --git a/storage/tests/c0/secbool.h b/storage/tests/c0/secbool.h new file mode 100644 index 0000000000..76dfb38dc1 --- /dev/null +++ b/storage/tests/c0/secbool.h @@ -0,0 +1,33 @@ +/* + * 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 . + */ + +#ifndef TREZORHAL_SECBOOL_H +#define TREZORHAL_SECBOOL_H + +#include + +typedef uint32_t secbool; +#define sectrue 0xAAAAAAAAU +#define secfalse 0x00000000U + +#ifndef __wur +#define __wur __attribute__ ((warn_unused_result)) +#endif + +#endif diff --git a/storage/tests/c0/storage.c b/storage/tests/c0/storage.c new file mode 100644 index 0000000000..5e0f8343bb --- /dev/null +++ b/storage/tests/c0/storage.c @@ -0,0 +1,238 @@ +/* + * 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 . + */ + +#include + +#include "common.h" +#include "norcow.h" +#include "storage.h" + +// Norcow storage key of configured PIN. +#define PIN_KEY 0x0000 + +// Maximum PIN length. +#define PIN_MAXLEN 32 + +// Byte-length of flash section containing fail counters. +#define PIN_FAIL_KEY 0x0001 +#define PIN_FAIL_SECTOR_SIZE 32 + +// Maximum number of failed unlock attempts. +#define PIN_MAX_TRIES 15 + +static secbool initialized = secfalse; +static secbool unlocked = secfalse; +static PIN_UI_WAIT_CALLBACK ui_callback = NULL; + +void storage_init(PIN_UI_WAIT_CALLBACK callback) +{ + initialized = secfalse; + unlocked = secfalse; + norcow_init(); + initialized = sectrue; + ui_callback = callback; +} + +static secbool pin_fails_reset(uint16_t ofs) +{ + return norcow_update(PIN_FAIL_KEY, ofs, 0); +} + +static secbool pin_fails_increase(const uint32_t *ptr, uint16_t ofs) +{ + uint32_t ctr = *ptr; + ctr = ctr << 1; + + if (sectrue != norcow_update(PIN_FAIL_KEY, ofs, ctr)) { + return secfalse; + } + + uint32_t check = *ptr; + if (ctr != check) { + return secfalse; + } + return sectrue; +} + +static void pin_fails_check_max(uint32_t ctr) +{ + if (~ctr >= (1 << PIN_MAX_TRIES)) { + norcow_wipe(); + ensure(secfalse, "pin_fails_check_max"); + } +} + +static secbool pin_cmp(const uint32_t pin) +{ + const void *spin = NULL; + uint16_t spinlen = 0; + norcow_get(PIN_KEY, &spin, &spinlen); + if (NULL != spin && spinlen == sizeof(uint32_t)) { + return sectrue * (pin == *(const uint32_t*)spin); + } else { + return sectrue * (1 == pin); + } +} + +static secbool pin_get_fails(const uint32_t **pinfail, uint32_t *pofs) +{ + const void *vpinfail; + uint16_t pinfaillen; + unsigned int ofs; + // The PIN_FAIL_KEY points to an area of words, initialized to + // 0xffffffff (meaning no pin failures). The first non-zero word + // in this area is the current pin failure counter. If PIN_FAIL_KEY + // has no configuration or is empty, the pin failure counter is 0. + // We rely on the fact that flash allows to clear bits and we clear one + // bit to indicate pin failure. On success, the word is set to 0, + // indicating that the next word is the pin failure counter. + + // Find the current pin failure counter + if (secfalse != norcow_get(PIN_FAIL_KEY, &vpinfail, &pinfaillen)) { + *pinfail = vpinfail; + for (ofs = 0; ofs < pinfaillen / sizeof(uint32_t); ofs++) { + if (((const uint32_t *) vpinfail)[ofs]) { + *pinfail = vpinfail; + *pofs = ofs; + return sectrue; + } + } + } + + // No pin failure section, or all entries used -> create a new one. + uint32_t pinarea[PIN_FAIL_SECTOR_SIZE]; + memset(pinarea, 0xff, sizeof(pinarea)); + if (sectrue != norcow_set(PIN_FAIL_KEY, pinarea, sizeof(pinarea))) { + return secfalse; + } + if (sectrue != norcow_get(PIN_FAIL_KEY, &vpinfail, &pinfaillen)) { + return secfalse; + } + *pinfail = vpinfail; + *pofs = 0; + return sectrue; +} + +secbool storage_check_pin(const uint32_t pin) +{ + const uint32_t *pinfail = NULL; + uint32_t ofs; + uint32_t ctr; + + // Get the pin failure counter + if (pin_get_fails(&pinfail, &ofs) != sectrue) { + return secfalse; + } + + // Read current failure counter + ctr = pinfail[ofs]; + // Wipe storage if too many failures + pin_fails_check_max(ctr); + + // Sleep for ~ctr seconds before checking the PIN. + uint32_t progress; + for (uint32_t wait = ~ctr; wait > 0; wait--) { + for (int i = 0; i < 10; i++) { + if (ui_callback) { + if ((~ctr) > 1000000) { // precise enough + progress = (~ctr - wait) / ((~ctr) / 1000); + } else { + progress = ((~ctr - wait) * 10 + i) * 100 / (~ctr); + } + ui_callback(wait, progress); + } + hal_delay(100); + } + } + // Show last frame if we were waiting + if ((~ctr > 0) && ui_callback) { + ui_callback(0, 1000); + } + + // First, we increase PIN fail counter in storage, even before checking the + // PIN. If the PIN is correct, we reset the counter afterwards. If not, we + // check if this is the last allowed attempt. + if (sectrue != pin_fails_increase(pinfail + ofs, ofs * sizeof(uint32_t))) { + return secfalse; + } + if (sectrue != pin_cmp(pin)) { + // Wipe storage if too many failures + pin_fails_check_max(ctr << 1); + return secfalse; + } + // Finally set the counter to 0 to indicate success. + return pin_fails_reset(ofs * sizeof(uint32_t)); +} + +secbool storage_unlock(const uint32_t pin) +{ + unlocked = secfalse; + if (sectrue == initialized && sectrue == storage_check_pin(pin)) { + unlocked = sectrue; + } + return unlocked; +} + +secbool storage_get(const uint16_t key, const void **val, uint16_t *len) +{ + const uint8_t app = key >> 8; + // APP == 0 is reserved for PIN related values + if (sectrue != initialized || app == 0) { + return secfalse; + } + // top bit of APP set indicates the value can be read from unlocked device + if (sectrue != unlocked && ((app & 0x80) == 0)) { + return secfalse; + } + return norcow_get(key, val, len); +} + +secbool storage_set(const uint16_t key, const void *val, uint16_t len) +{ + const uint8_t app = key >> 8; + // APP == 0 is reserved for PIN related values + if (sectrue != initialized || sectrue != unlocked || app == 0) { + return secfalse; + } + return norcow_set(key, val, len); +} + +secbool storage_has_pin(void) +{ + if (sectrue != initialized) { + return secfalse; + } + return sectrue == pin_cmp(1) ? secfalse : sectrue; +} + +secbool storage_change_pin(const uint32_t oldpin, const uint32_t newpin) +{ + if (sectrue != initialized || sectrue != unlocked) { + return secfalse; + } + if (sectrue != storage_check_pin(oldpin)) { + return secfalse; + } + return norcow_set(PIN_KEY, &newpin, sizeof(uint32_t)); +} + +void storage_wipe(void) +{ + norcow_wipe(); +} diff --git a/storage/tests/c0/storage.h b/storage/tests/c0/storage.h new file mode 100644 index 0000000000..7975281756 --- /dev/null +++ b/storage/tests/c0/storage.h @@ -0,0 +1,38 @@ +/* + * 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 . + */ + +#ifndef __STORAGE_H__ +#define __STORAGE_H__ + +#include +#include +#include "secbool.h" + +typedef void (*PIN_UI_WAIT_CALLBACK)(uint32_t wait, uint32_t progress); + +void storage_init(PIN_UI_WAIT_CALLBACK callback); +void storage_wipe(void); +secbool storage_check_pin(const uint32_t pin); +secbool storage_unlock(const uint32_t pin); +secbool storage_has_pin(void); +secbool storage_change_pin(const uint32_t oldpin, const uint32_t newpin); +secbool storage_get(const uint16_t key, const void **val, uint16_t *len); +secbool storage_set(const uint16_t key, const void *val, uint16_t len); + +#endif diff --git a/storage/tests/c0/storage.py b/storage/tests/c0/storage.py new file mode 100644 index 0000000000..64c9bf8bbc --- /dev/null +++ b/storage/tests/c0/storage.py @@ -0,0 +1,54 @@ +import ctypes as c +import os + +sectrue = -1431655766 # 0xAAAAAAAAA +fname = os.path.join(os.path.dirname(__file__), "libtrezor-storage0.so") + +class Storage: + + def __init__(self) -> None: + self.lib = c.cdll.LoadLibrary(fname) + self.flash_size = c.cast(self.lib.FLASH_SIZE, c.POINTER(c.c_uint32))[0] + self.flash_buffer = c.create_string_buffer(self.flash_size) + c.cast(self.lib.FLASH_BUFFER, c.POINTER(c.c_void_p))[0] = c.addressof(self.flash_buffer) + + def init(self) -> None: + self.lib.storage_init(0) + + def wipe(self) -> None: + self.lib.storage_wipe() + + def check_pin(self, pin: int) -> bool: + return sectrue == self.lib.storage_check_pin(c.c_uint32(pin)) + + def unlock(self, pin: int) -> bool: + return sectrue == self.lib.storage_unlock(c.c_uint32(pin)) + + def has_pin(self) -> bool: + return sectrue == self.lib.storage_has_pin() + + def change_pin(self, oldpin: int, newpin: int) -> bool: + return sectrue == self.lib.storage_change_pin(c.c_uint32(oldpin), c.c_uint32(newpin)) + + def get(self, key: int) -> bytes: + val_ptr = c.c_void_p() + val_len = c.c_uint16() + if sectrue != self.lib.storage_get(c.c_uint16(key), c.byref(val_ptr), c.byref(val_len)): + raise RuntimeError("Failed to find key in storage.") + return c.string_at(val_ptr, size=val_len.value) + + def set(self, key: int, val: bytes) -> None: + if sectrue != self.lib.storage_set(c.c_uint16(key), val, c.c_uint16(len(val))): + raise RuntimeError("Failed to set value in storage.") + + def _dump(self) -> bytes: + # return just sectors 4 and 16 of the whole flash + return [self.flash_buffer[0x010000:0x010000 + 0x10000], self.flash_buffer[0x110000:0x110000 + 0x10000]] + + def _get_flash_buffer(self) -> bytes: + return bytes(self.flash_buffer) + + def _set_flash_buffer(self, buf: bytes) -> None: + if len(buf) != self.flash_size: + raise RuntimeError("Failed to set flash buffer due to length mismatch.") + self.flash_buffer = buf diff --git a/storage/tests/python/__init__.py b/storage/tests/python/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/storage/tests/python/src/__init__.py b/storage/tests/python/src/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/storage/tests/python/src/consts.py b/storage/tests/python/src/consts.py new file mode 100644 index 0000000000..1785c36239 --- /dev/null +++ b/storage/tests/python/src/consts.py @@ -0,0 +1,152 @@ +# ----- PIN and encryption related ----- # + +# App ID where PIN log is stored. +PIN_APP_ID = 0x00 + +# Storage key of the combined salt, EDEK, ESEK and PIN verification code entry. +EDEK_ESEK_PVC_KEY = (PIN_APP_ID << 8) | 0x02 + +# Storage key of the PIN set flag. +PIN_NOT_SET_KEY = (PIN_APP_ID << 8) | 0x03 + +# Norcow storage key of the storage version. +VERSION_KEY = (PIN_APP_ID << 8) | 0x04 + +# Norcow storage key of the storage authentication tag. +SAT_KEY = (PIN_APP_ID << 8) | 0x05 + +# The PIN value corresponding to an empty PIN. +PIN_EMPTY = 1 + +# Maximum number of failed unlock attempts. +PIN_MAX_TRIES = 16 + +# The total number of iterations to use in PBKDF2. +PIN_ITER_COUNT = 20000 + +# The length of the data encryption key in bytes. +DEK_SIZE = 32 + +# The length of the storage authentication key in bytes. +SAK_SIZE = 16 + +# The length of the storage authentication tag in bytes. +SAT_SIZE = 16 + +# The length of the random salt in bytes. +PIN_SALT_SIZE = 4 +PIN_HARDWARE_SALT_SIZE = 32 + +# The length of the PIN verification code in bytes. +PVC_SIZE = 8 + +# The length of KEK in bytes. +KEK_SIZE = 32 + +# The length of KEIV in bytes. +KEIV_SIZE = 12 + +# Size of counter. 4B integer and 8B tail. +COUNTER_TAIL = 12 +COUNTER_TAIL_SIZE = 8 +COUNTER_MAX_TAIL = 64 + +# ----- PIN logs ----- # + +# Storage key of the PIN entry log and PIN success log. +PIN_LOG_KEY = (PIN_APP_ID << 8) | 0x01 + +# Length of items in the PIN entry log +PIN_LOG_GUARD_KEY_SIZE = 4 + +# Values used for the guard key integrity check. +GUARD_KEY_MODULUS = 6311 +GUARD_KEY_REMAINDER = 15 +GUARD_KEY_RANDOM_MAX = (0xFFFFFFFF // GUARD_KEY_MODULUS) + 1 + +# Length of both success log and entry log +PIN_LOG_SIZE = 64 + +# Used for in guard bits operations. +LOW_MASK = 0x55555555 + +# Log initialized to all FFs. +ALL_FF_LOG = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + +# ----- Bytes ----- + +# If the top bit of APP is set, then the value is not encrypted. +FLAG_PUBLIC = 0x80 + +# If the top two bits of APP are set, then the value is not encrypted and it +# can be written even when the storage is locked. +FLAG_WRITE = 0xC0 + +# Length of word in bytes. +WORD_SIZE = 4 + +# Boolean values are stored as a simple 0/1 int. +TRUE_BYTE = b"\x01" +FALSE_BYTE = b"\x00" + +# ----- Crypto ----- # + +# The length of the Poly1305 MAC in bytes. +POLY1305_MAC_SIZE = 16 + +# The length of the ChaCha20 IV (aka nonce) in bytes as per RFC 7539. +CHACHA_IV_SIZE = 12 + +# ----- Norcow ----- # + +NORCOW_SECTOR_COUNT = 2 +NORCOW_SECTOR_SIZE = 64 * 1024 + +# Magic flag at the beggining of an active sector. +NORCOW_MAGIC = b"NRC2" + +# Norcow version, set in the storage header, but also as an encrypted item. +NORCOW_VERSION = b"\x01\x00\x00\x00" + +# Norcow magic combined with the version, which is stored as its negation. +NORCOW_MAGIC_AND_VERSION = NORCOW_MAGIC + bytes( + [ + ~NORCOW_VERSION[0] & 0xFF, + ~NORCOW_VERSION[1] & 0xFF, + ~NORCOW_VERSION[2] & 0xFF, + ~NORCOW_VERSION[3] & 0xFF, + ] +) + +# Signalizes free storage. +NORCOW_KEY_FREE = 0xFFFF + + +# |-----------|-------------------| +# | Private | APP = 0 | +# | Protected | 1 <= APP <= 127 | +# | Public | 128 <= APP <= 255 | + + +def is_app_public(app: int): + if app & FLAG_PUBLIC: + return True + return False + + +def is_app_protected(app: int): + if is_app_public(app): + return False + if is_app_private(app): + return False + return True + + +def is_app_private(app: int): + return app == PIN_APP_ID + + +def is_app_lock_writable(app: int): + if app & FLAG_WRITE == FLAG_WRITE: + return True + return False diff --git a/storage/tests/python/src/crypto.py b/storage/tests/python/src/crypto.py new file mode 100644 index 0000000000..037d82a0e5 --- /dev/null +++ b/storage/tests/python/src/crypto.py @@ -0,0 +1,112 @@ +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, hmac +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms +from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305 +from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC + +from . import consts, prng + + +def derive_kek_keiv(salt: bytes, pin: int) -> (bytes, bytes): + kdf = PBKDF2HMAC( + algorithm=hashes.SHA256(), + length=consts.KEK_SIZE + consts.KEIV_SIZE, + salt=bytes(salt), + iterations=10000, + backend=default_backend(), + ) + pbkdf_output = kdf.derive(pin.to_bytes(4, "little")) + # the first 256b is Key Encryption Key + kek = pbkdf_output[: consts.KEK_SIZE] + # following with 96b of Initialization Vector + keiv = pbkdf_output[consts.KEK_SIZE :] + + return kek, keiv + + +def chacha_poly_encrypt( + key: bytes, iv: bytes, data: bytes, additional_data: bytes = None +) -> (bytes, bytes): + chacha = ChaCha20Poly1305(key) + chacha_output = chacha.encrypt(iv, bytes(data), additional_data) + # cipher text and 128b authentication tag + return chacha_output[: len(data)], chacha_output[len(data) :] + + +def chacha_poly_decrypt( + key: bytes, app_key: int, iv: bytes, data: bytes, additional_data: bytes = None +) -> bytes: + chacha = ChaCha20Poly1305(key) + chacha_output = chacha.decrypt(bytes(iv), bytes(data), additional_data) + return chacha_output + + +def decrypt_edek_esak( + pin: int, salt: bytes, edek_esak: bytes, pvc: bytes +) -> (bytes, bytes): + """ + Decrypts EDEK, ESAK to DEK, SAK and checks PIN in the process. + Raises: + InvalidPinError: if PIN is invalid + """ + kek, keiv = derive_kek_keiv(salt, pin) + + algorithm = algorithms.ChaCha20(kek, (1).to_bytes(4, "little") + keiv) + cipher = Cipher(algorithm, mode=None, backend=default_backend()) + decryptor = cipher.decryptor() + dek_sak = decryptor.update(bytes(edek_esak)) + dek = dek_sak[: consts.DEK_SIZE] + sak = dek_sak[consts.DEK_SIZE :] + + if not validate_pin(kek, keiv, dek_sak, pvc): + raise InvalidPinError("Invalid PIN") + + return dek, sak + + +def validate_pin(kek: bytes, keiv: bytes, dek_sak: bytes, pvc: bytes) -> bool: + """ + This a little bit hackish. We do not store the whole + authentication tag so we can't decrypt using ChaCha20Poly1305 + because it obviously checks the tag first and fails. + So we are using the sole ChaCha20 cipher to decipher and then encrypt + again with Chacha20Poly1305 here to get the tag and compare it to PVC. + """ + _, tag = chacha_poly_encrypt(kek, keiv, dek_sak) + prng.random32() + prng.random32() + return tag[: consts.PVC_SIZE] == pvc + + +def calculate_hmacs(sak: bytes, keys: bytes) -> bytes: + """ + This calculates HMAC-SHA-256(SAK, (XOR_i) HMAC-SHA-256(SAK, KEY_i)). + In other words, it does HMAC for every KEY and XORs it all together. + One more final HMAC is then performed on the result. + """ + hmacs = _hmac(sak, keys[0]) + for key in keys[1:]: + hmacs = _xor(hmacs, _hmac(sak, key)) + return _final_hmac(sak, hmacs) + + +def init_hmacs(sak: bytes) -> bytes: + return _final_hmac(sak, b"\x00" * hashes.SHA256.digest_size) + + +def _final_hmac(sak: bytes, data: bytes) -> bytes: + return _hmac(sak, data)[: consts.SAT_SIZE] + + +def _hmac(key: bytes, data: bytes) -> bytes: + h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend()) + h.update(data) + return h.finalize() + + +def _xor(first: bytes, second: bytes) -> bytes: + return bytes(a ^ b for a, b in zip(first, second)) + + +class InvalidPinError(ValueError): + pass diff --git a/storage/tests/python/src/helpers.py b/storage/tests/python/src/helpers.py new file mode 100644 index 0000000000..83ec34e2ef --- /dev/null +++ b/storage/tests/python/src/helpers.py @@ -0,0 +1,45 @@ +import sys + +from . import consts + + +def expand_to_log_size(value: int) -> int: + result = 0 + for i in range(0, consts.PIN_LOG_SIZE, 4): + result = result | (value << i * 8) + return result + + +def to_int_by_words(array: bytes) -> int: + """ + Converts array of bytes into an int by reading word size + of bytes then converted to int using the system's endianness. + """ + assert len(array) % consts.WORD_SIZE == 0 + n = 0 + for i in range(0, len(array), consts.WORD_SIZE): + n = (n << (consts.WORD_SIZE * 8)) + int.from_bytes( + array[i : i + consts.WORD_SIZE], sys.byteorder + ) + return n + + +def to_bytes_by_words(n: int, length: int) -> bytes: + """ + Converting int back to bytes by words. + """ + mask = (1 << (consts.WORD_SIZE * 8)) - 1 + array = bytes() + for i in reversed(range(0, length, consts.WORD_SIZE)): + array = array + ((n >> (i * 8)) & mask).to_bytes( + consts.WORD_SIZE, sys.byteorder + ) + return array + + +def int_to_word(n: int) -> bytes: + return n.to_bytes(consts.WORD_SIZE, sys.byteorder) + + +def word_to_int(b: bytes) -> int: + return int.from_bytes(b, sys.byteorder) diff --git a/storage/tests/python/src/norcow.py b/storage/tests/python/src/norcow.py new file mode 100644 index 0000000000..3a8baca33f --- /dev/null +++ b/storage/tests/python/src/norcow.py @@ -0,0 +1,174 @@ +import sys +from struct import pack + +from . import consts + + +def align4_int(i: int): + return (4 - i) % 4 + + +def align4_data(data): + return data + b"\x00" * align4_int(len(data)) + + +class Norcow: + def __init__(self): + self.sectors = None + + def init(self): + if self.sectors: + for sector in range(consts.NORCOW_SECTOR_COUNT): + if self.sectors[sector][:8] == consts.NORCOW_MAGIC_AND_VERSION: + self.active_sector = sector + self.active_offset = len(consts.NORCOW_MAGIC_AND_VERSION) + break + else: + self.wipe() + + def wipe(self, sector: int = 0): + self.sectors = [ + bytearray([0xFF] * consts.NORCOW_SECTOR_SIZE) + for _ in range(consts.NORCOW_SECTOR_COUNT) + ] + self.sectors[sector][:8] = consts.NORCOW_MAGIC_AND_VERSION + self.active_sector = sector + self.active_offset = len(consts.NORCOW_MAGIC_AND_VERSION) + + def get(self, key: int) -> bytes: + value, _ = self._find_item(key) + return value + + def set(self, key: int, val: bytes): + if key == consts.NORCOW_KEY_FREE: + raise RuntimeError("Norcow: key 0xFFFF is not allowed") + + found_value, pos = self._find_item(key) + if found_value is not False: + if self._is_updatable(found_value, val): + self._write(pos, key, val) + return + else: + self._delete_old(pos, found_value) + + if self.active_offset + 4 + len(val) > consts.NORCOW_SECTOR_SIZE: + self._compact() + + self._append(key, val) + + def delete(self, key: int): + if key == consts.NORCOW_KEY_FREE: + raise RuntimeError("Norcow: key 0xFFFF is not allowed") + + found_value, pos = self._find_item(key) + if found_value is False: + return False + self._delete_old(pos, found_value) + return True + + def replace(self, key: int, new_value: bytes) -> bool: + old_value, offset = self._find_item(key) + if not old_value: + raise RuntimeError("Norcow: key not found") + if len(old_value) != len(new_value): + raise RuntimeError( + "Norcow: replace works only with items of the same length" + ) + self._write(offset, key, new_value) + + def _is_updatable(self, old: bytes, new: bytes) -> bool: + """ + Item is updatable if the new value is the same or + it changes 1 to 0 only (the flash memory does not + allow to flip 0 to 1 unless you wipe it). + """ + if len(old) != len(new): + return False + if old == new: + return True + for a, b in zip(old, new): + if a & b != b: + return False + return True + + def _delete_old(self, pos: int, value: bytes): + wiped_data = b"\x00" * len(value) + self._write(pos, 0x0000, wiped_data) + + def _append(self, key: int, value: bytes): + self.active_offset += self._write(self.active_offset, key, value) + + def _write(self, pos: int, key: int, new_value: bytes) -> int: + data = pack(" consts.NORCOW_SECTOR_SIZE: + raise RuntimeError("Norcow: item too big") + self.sectors[self.active_sector][pos : pos + len(data)] = data + return len(data) + + def _find_item(self, key: int) -> (bytes, int): + offset = len(consts.NORCOW_MAGIC_AND_VERSION) + value = False + pos = offset + while True: + try: + k, v = self._read_item(offset) + if k == key: + value = v + pos = offset + except ValueError: + break + offset = offset + self._norcow_item_length(v) + return value, pos + + def _get_all_keys(self) -> (bytes, int): + offset = len(consts.NORCOW_MAGIC_AND_VERSION) + keys = set() + while True: + try: + k, v = self._read_item(offset) + keys.add(k) + except ValueError: + break + offset = offset + self._norcow_item_length(v) + return keys + + def _norcow_item_length(self, data: bytes) -> int: + # APP_ID, KEY_ID, LENGTH, DATA, ALIGNMENT + return 1 + 1 + 2 + len(data) + align4_int(len(data)) + + def _read_item(self, offset: int) -> (int, bytes): + key = self.sectors[self.active_sector][offset : offset + 2] + key = int.from_bytes(key, sys.byteorder) + if key == consts.NORCOW_KEY_FREE: + raise ValueError("Norcow: no data on this offset") + length = self.sectors[self.active_sector][offset + 2 : offset + 4] + length = int.from_bytes(length, sys.byteorder) + value = self.sectors[self.active_sector][offset + 4 : offset + 4 + length] + return key, value + + def _compact(self): + offset = len(consts.NORCOW_MAGIC_AND_VERSION) + data = list() + while True: + try: + k, v = self._read_item(offset) + if k != 0x00: + data.append((k, v)) + except ValueError: + break + offset = offset + self._norcow_item_length(v) + sector = self.active_sector + self.wipe((sector + 1) % consts.NORCOW_SECTOR_COUNT) + for key, value in data: + self._append(key, value) + + def _set_sectors(self, data): + if list(map(len, data)) != [ + consts.NORCOW_SECTOR_SIZE, + consts.NORCOW_SECTOR_SIZE, + ]: + raise RuntimeError("Norcow: set_sectors called with invalid data length") + self.sectors = [bytearray(sector) for sector in data] + + def _dump(self): + return [bytes(sector) for sector in self.sectors] diff --git a/storage/tests/python/src/pin_log.py b/storage/tests/python/src/pin_log.py new file mode 100644 index 0000000000..0416baaeb6 --- /dev/null +++ b/storage/tests/python/src/pin_log.py @@ -0,0 +1,123 @@ +from . import consts, helpers, prng + + +class PinLog: + def __init__(self, norcow): + self.norcow = norcow + + def init(self): + guard_key = self._generate_guard_key() + guard_mask, guard = self.derive_guard_mask_and_value(guard_key) + + pin_success_log = (~guard_mask & consts.ALL_FF_LOG) | guard + pin_entry_log = (~guard_mask & consts.ALL_FF_LOG) | guard + self._write_log(guard_key, pin_success_log, pin_entry_log) + + def derive_guard_mask_and_value(self, guard_key: int) -> (int, int): + if guard_key > 0xFFFFFFFF: + raise ValueError("Invalid guard key") + + guard_mask = ((guard_key & consts.LOW_MASK) << 1) | ( + (~guard_key & 0xFFFFFFFF) & consts.LOW_MASK + ) + guard = (((guard_key & consts.LOW_MASK) << 1) & guard_key) | ( + ((~guard_key & 0xFFFFFFFF) & consts.LOW_MASK) & (guard_key >> 1) + ) + return helpers.expand_to_log_size(guard_mask), helpers.expand_to_log_size(guard) + + def write_attempt(self): + guard_key, pin_success_log, pin_entry_log = self._get_logs() + guard_mask, guard = self.derive_guard_mask_and_value(guard_key) + assert (pin_entry_log & guard_mask) == guard + + clean_pin_entry_log = self.remove_guard_bits(guard_mask, pin_entry_log) + clean_pin_entry_log = clean_pin_entry_log >> 2 # set 11 to 00 + pin_entry_log = ( + clean_pin_entry_log & (~guard_mask & consts.ALL_FF_LOG) + ) | guard + + self._write_log(guard_key, pin_success_log, pin_entry_log) + + def write_success(self): + guard_key, pin_success_log, pin_entry_log = self._get_logs() + pin_success_log = pin_entry_log + + self._write_log(guard_key, pin_success_log, pin_entry_log) + + def get_failures_count(self) -> int: + guard_key, pin_succes_log, pin_entry_log = self._get_logs() + guard_mask, _ = self.derive_guard_mask_and_value(guard_key) + + pin_succes_log = self.remove_guard_bits(guard_mask, pin_succes_log) + pin_entry_log = self.remove_guard_bits(guard_mask, pin_entry_log) + + # divide by two because bits are doubled after remove_guard_bits() + return bin(pin_succes_log - pin_entry_log).count("1") // 2 + + def remove_guard_bits(self, guard_mask: int, log: int) -> int: + """ + Removes all guard bits and replaces each guard bit + with its neighbour value. + Example: 0g0gg1 -> 000011 + """ + log = log & (~guard_mask & consts.ALL_FF_LOG) + log = ((log >> 1) | log) & helpers.expand_to_log_size(consts.LOW_MASK) + log = log | (log << 1) + return log + + def _generate_guard_key(self) -> int: + while True: + r = prng.random_uniform(consts.GUARD_KEY_RANDOM_MAX) + r = (r * consts.GUARD_KEY_MODULUS + consts.GUARD_KEY_REMAINDER) & 0xFFFFFFFF + if self._check_guard_key(r): + return r + + def _check_guard_key(self, guard_key: int) -> bool: + """ + Checks if guard_key is congruent to 15 modulo 6311 and + some other conditions, see the docs. + """ + count = (guard_key & 0x22222222) + ((guard_key >> 2) & 0x22222222) + count = count + (count >> 4) + + zero_runs = ~guard_key & 0xFFFFFFFF + zero_runs = zero_runs & (zero_runs >> 2) + zero_runs = zero_runs & (zero_runs >> 1) + zero_runs = zero_runs & (zero_runs >> 1) + one_runs = guard_key + one_runs = one_runs & (one_runs >> 2) + one_runs = one_runs & (one_runs >> 1) + one_runs = one_runs & (one_runs >> 1) + + return ( + ((count & 0x0E0E0E0E) == 0x04040404) + & (one_runs == 0) + & (zero_runs == 0) + & (guard_key % consts.GUARD_KEY_MODULUS == consts.GUARD_KEY_REMAINDER) + ) + + def _get_logs(self) -> (int, int, int): + pin_log = self.norcow.get(consts.PIN_LOG_KEY) + guard_key = pin_log[: consts.PIN_LOG_GUARD_KEY_SIZE] + guard_key = helpers.word_to_int(guard_key) + guard_mask, guard = self.derive_guard_mask_and_value(guard_key) + pin_entry_log = pin_log[consts.PIN_LOG_GUARD_KEY_SIZE + consts.PIN_LOG_SIZE :] + pin_entry_log = helpers.to_int_by_words(pin_entry_log) + pin_success_log = pin_log[ + consts.PIN_LOG_GUARD_KEY_SIZE : consts.PIN_LOG_GUARD_KEY_SIZE + + consts.PIN_LOG_SIZE + ] + pin_success_log = helpers.to_int_by_words(pin_success_log) + + return guard_key, pin_success_log, pin_entry_log + + def _write_log(self, guard_key: int, pin_success_log: int, pin_entry_log: int): + pin_log = ( + helpers.int_to_word(guard_key) + + helpers.to_bytes_by_words(pin_success_log, consts.PIN_LOG_SIZE) + + helpers.to_bytes_by_words(pin_entry_log, consts.PIN_LOG_SIZE) + ) + try: + self.norcow.replace(consts.PIN_LOG_KEY, pin_log) + except RuntimeError: + self.norcow.set(consts.PIN_LOG_KEY, pin_log) diff --git a/storage/tests/python/src/prng.py b/storage/tests/python/src/prng.py new file mode 100644 index 0000000000..6461bfcccb --- /dev/null +++ b/storage/tests/python/src/prng.py @@ -0,0 +1,34 @@ +import sys + +seed = 0 + + +def random_buffer(length: int) -> bytes: + length = length + if length % 4 != 0: + raise ValueError("Use only for whole words (multiples of 4 bytes)") + b = bytearray(length) + for i in range(length): + if i % 4 == 0: + rand = random32().to_bytes(4, sys.byteorder) + b[i] = rand[i % 4] + return bytes(b) + + +def random_reseed(reseed: int = 0): + global seed + seed = reseed + + +def random32(): + global seed + seed = (1664525 * seed + 1013904223) & 0xFFFFFFFF + return seed + + +def random_uniform(n: int): + max = 0xFFFFFFFF - (0xFFFFFFFF % n) + while True: + x = random32() + if x < max: + return x // (max // n) diff --git a/storage/tests/python/src/storage.py b/storage/tests/python/src/storage.py new file mode 100644 index 0000000000..e223d287f2 --- /dev/null +++ b/storage/tests/python/src/storage.py @@ -0,0 +1,237 @@ +import hashlib +import sys + +from . import consts, crypto, helpers, prng +from .norcow import Norcow +from .pin_log import PinLog + + +class Storage: + def __init__(self): + self.initialized = False + self.unlocked = False + self.dek = None + self.sak = None + self.nc = Norcow() + self.pin_log = PinLog(self.nc) + + def init(self, hardware_salt: bytes = b""): + """ + Initializes storage. Normally we would check if EDEK is already present, + but we simplify things in the python version and suppose we are starting + a new storage each time. + """ + self.nc.init() + self.initialized = True + self.hw_salt_hash = hashlib.sha256(hardware_salt).digest() + + edek_esak_pvc = self.nc.get(consts.EDEK_ESEK_PVC_KEY) + if not edek_esak_pvc: + self._init_pin() + + def _init_pin(self): + """ + Initalizes PIN counters, generates random + Data Encryption Key and Storage Authentication Key + """ + self.dek = prng.random_buffer(consts.DEK_SIZE) + self.sak = prng.random_buffer(consts.SAK_SIZE) + + self.nc.set(consts.SAT_KEY, crypto.init_hmacs(self.sak)) + self._set_encrypt(consts.VERSION_KEY, b"\x01\x00\x00\x00") + self.pin_log.init() + self._set_pin(consts.PIN_EMPTY) + self.unlocked = False + + def _set_pin(self, pin: int): + random_salt = prng.random_buffer(consts.PIN_SALT_SIZE) + salt = self.hw_salt_hash + random_salt + kek, keiv = crypto.derive_kek_keiv(salt, pin) + + # Encrypted Data Encryption Key and Encrypted Storage Authentication Key + edek_esak, tag = crypto.chacha_poly_encrypt(kek, keiv, self.dek + self.sak) + # Pin Verification Code + pvc = tag[: consts.PVC_SIZE] + + self.nc.set(consts.EDEK_ESEK_PVC_KEY, random_salt + edek_esak + pvc) + if pin == consts.PIN_EMPTY: + self._set_bool(consts.PIN_NOT_SET_KEY, True) + else: + self._set_bool(consts.PIN_NOT_SET_KEY, False) + + def wipe(self): + self.nc.wipe() + self._init_pin() + + def check_pin(self, pin: int) -> bool: + self.pin_log.write_attempt() + + data = self.nc.get(consts.EDEK_ESEK_PVC_KEY) + salt = self.hw_salt_hash + data[: consts.PIN_SALT_SIZE] + edek_esak = data[consts.PIN_SALT_SIZE : -consts.PVC_SIZE] + pvc = data[-consts.PVC_SIZE :] + + try: + dek, sak = crypto.decrypt_edek_esak(pin, salt, edek_esak, pvc) + self.pin_log.write_success() + self.dek = dek + self.sak = sak + return True + except crypto.InvalidPinError: + fails = self.pin_log.get_failures_count() + if fails >= consts.PIN_MAX_TRIES: + self.wipe() + return False + + def lock(self) -> None: + self.unlocked = False + + def unlock(self, pin: int) -> bool: + if not self.initialized or not self.check_pin(pin): + return False + + version = self._decrypt(consts.VERSION_KEY) + if version != consts.NORCOW_VERSION: + return False + + self.unlocked = True + return True + + def has_pin(self) -> bool: + val = self.nc.get(consts.PIN_NOT_SET_KEY) + return val != consts.TRUE_BYTE + + def get_pin_rem(self) -> int: + return consts.PIN_MAX_TRIES - self.pin_log.get_failures_count() + + def change_pin(self, oldpin: int, newpin: int) -> bool: + if not self.initialized or not self.unlocked: + return False + if not self.check_pin(oldpin): + return False + self._set_pin(newpin) + return True + + def get(self, key: int) -> bytes: + app = key >> 8 + if not self.initialized or consts.is_app_private(app): + raise RuntimeError("Storage not initialized or app is private") + if not self.unlocked and not consts.is_app_public(app): + # public fields can be read from an unlocked device + raise RuntimeError("Storage locked") + if consts.is_app_public(app): + return self.nc.get(key) + return self._get_encrypted(key) + + def set(self, key: int, val: bytes) -> bool: + app = key >> 8 + self._check_lock(app) + if consts.is_app_public(app): + return self.nc.set(key, val) + return self._set_encrypt(key, val) + + def set_counter(self, key: int, val: int): + app = key >> 8 + if not consts.is_app_public(app): + raise RuntimeError("Counter can be set only for public items") + counter = val.to_bytes(4, sys.byteorder) + bytearray( + b"\xFF" * consts.COUNTER_TAIL_SIZE + ) + self.set(key, counter) + + def next_counter(self, key: int) -> int: + app = key >> 8 + self._check_lock(app) + + current = self.get(key) + if current is False: + self.set_counter(key, 0) + return 0 + + base = int.from_bytes(current[:4], sys.byteorder) + tail = helpers.to_int_by_words(current[4:]) + tail_count = "{0:064b}".format(tail).count("0") + increased_count = base + tail_count + 1 + + if tail_count == consts.COUNTER_MAX_TAIL: + self.set_counter(key, increased_count) + return increased_count + + self.set( + key, + current[:4] + + helpers.to_bytes_by_words(tail >> 1, consts.COUNTER_TAIL_SIZE), + ) + return increased_count + + def delete(self, key: int) -> bool: + app = key >> 8 + self._check_lock(app) + ret = self.nc.delete(key) + if consts.is_app_protected(app): + sat = self._calculate_authentication_tag() + self.nc.set(consts.SAT_KEY, sat) + return ret + + def _check_lock(self, app: int): + if not self.initialized or consts.is_app_private(app): + raise RuntimeError("Storage not initialized or app is private") + if not self.unlocked and not consts.is_app_lock_writable(app): + raise RuntimeError("Storage locked and app is not public-writable") + + def _get_encrypted(self, key: int) -> bytes: + if not consts.is_app_protected(key): + raise RuntimeError("Only protected values are encrypted") + sat = self.nc.get(consts.SAT_KEY) + if not sat: + raise RuntimeError("SAT not found") + if sat != self._calculate_authentication_tag(): + raise RuntimeError("Storage authentication tag mismatch") + return self._decrypt(key) + + def _decrypt(self, key: int) -> bytes: + data = self.nc.get(key) + iv = data[: consts.CHACHA_IV_SIZE] + # cipher text with MAC + tag = data[ + consts.CHACHA_IV_SIZE : consts.CHACHA_IV_SIZE + consts.POLY1305_MAC_SIZE + ] + ciphertext = data[consts.CHACHA_IV_SIZE + consts.POLY1305_MAC_SIZE :] + return crypto.chacha_poly_decrypt( + self.dek, key, iv, ciphertext + tag, key.to_bytes(2, sys.byteorder) + ) + + def _set_encrypt(self, key: int, val: bytes): + # In C, data are preallocated beforehand for encrypted values, + # to match the behaviour we do the same. + preallocate = b"\xFF" * ( + consts.CHACHA_IV_SIZE + len(val) + consts.POLY1305_MAC_SIZE + ) + self.nc.set(key, preallocate) + if consts.is_app_protected(key >> 8): + sat = self._calculate_authentication_tag() + self.nc.set(consts.SAT_KEY, sat) + + iv = prng.random_buffer(consts.CHACHA_IV_SIZE) + cipher_text, tag = crypto.chacha_poly_encrypt( + self.dek, iv, val, key.to_bytes(2, sys.byteorder) + ) + return self.nc.replace(key, iv + tag + cipher_text) + + def _calculate_authentication_tag(self) -> bytes: + keys = [] + for key in self.nc._get_all_keys(): + if consts.is_app_protected(key >> 8): + keys.append(key.to_bytes(2, sys.byteorder)) + if not keys: + return crypto.init_hmacs(self.sak) + return crypto.calculate_hmacs(self.sak, keys) + + def _set_bool(self, key: int, val: bool) -> bool: + if val: + return self.nc.set(key, consts.TRUE_BYTE) + # False is stored as an empty value + return self.nc.set(key, consts.FALSE_BYTE) + + def _dump(self) -> bytes: + return self.nc._dump() diff --git a/storage/tests/python/tests/__init__.py b/storage/tests/python/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/storage/tests/python/tests/common.py b/storage/tests/python/tests/common.py new file mode 100644 index 0000000000..e1497054a0 --- /dev/null +++ b/storage/tests/python/tests/common.py @@ -0,0 +1,2 @@ +def all_ff_bytes(data: bytes): + return all(i == 0xFF for i in data) diff --git a/storage/tests/python/tests/conftest.py b/storage/tests/python/tests/conftest.py new file mode 100644 index 0000000000..9c908e2d1d --- /dev/null +++ b/storage/tests/python/tests/conftest.py @@ -0,0 +1,8 @@ +from ..src import prng + + +def pytest_runtest_teardown(item): + """ + Called after each test ran to reset the PRNG + """ + prng.seed = 0 diff --git a/storage/tests/python/tests/test_helpers.py b/storage/tests/python/tests/test_helpers.py new file mode 100644 index 0000000000..4e5a58f712 --- /dev/null +++ b/storage/tests/python/tests/test_helpers.py @@ -0,0 +1,13 @@ +from ..src import consts, helpers + + +def test_read_bytes_by_words(): + array = b"\x04\x03\x02\x01\x08\x07\x06\x05" + n = helpers.to_int_by_words(array) + assert n == 0x0102030405060708 + assert array == helpers.to_bytes_by_words(n, consts.PIN_LOG_SIZE)[56:] + + array = b"\xFF\xFF\xFF\x01\x01\x05\x09\x01" + n = helpers.to_int_by_words(array) + assert n == 0x01FFFFFF01090501 + assert array == helpers.to_bytes_by_words(n, consts.PIN_LOG_SIZE)[56:] diff --git a/storage/tests/python/tests/test_norcow.py b/storage/tests/python/tests/test_norcow.py new file mode 100644 index 0000000000..78a6819c7f --- /dev/null +++ b/storage/tests/python/tests/test_norcow.py @@ -0,0 +1,155 @@ +import pytest + +from . import common +from ..src import consts, norcow + + +def test_norcow_set(): + n = norcow.Norcow() + n.init() + n.set(0x0001, b"123") + data = n._dump()[0][:256] + assert data[:8] == b"NRC2\xFE\xFF\xFF\xFF" + assert data[8:10] == b"\x01\x00" # app + key + assert data[10:12] == b"\x03\x00" # length + assert data[12:15] == b"123" # data + assert common.all_ff_bytes(data[16:]) + + n.wipe() + n.set(0x0901, b"hello") + data = n._dump()[0][:256] + assert data[:8] == b"NRC2\xFE\xFF\xFF\xFF" + assert data[8:10] == b"\x01\x09" # app + key + assert data[10:12] == b"\x05\x00" # length + assert data[12:17] == b"hello" # data + assert data[17:20] == b"\x00\x00\x00" # alignment + assert common.all_ff_bytes(data[20:]) + + offset = 20 + n.set(0x0102, b"world!") + data = n._dump()[0][:256] + assert data[offset : offset + 2] == b"\x02\x01" # app + key + assert data[offset + 2 : offset + 4] == b"\x06\x00" # length + assert data[offset + 4 : offset + 10] == b"world!" # data + assert data[offset + 10 : offset + 12] == b"\x00\x00" # alignment + assert common.all_ff_bytes(data[offset + 12 :]) + + +def test_norcow_read_item(): + n = norcow.Norcow() + n.init() + n.set(0x0001, b"123") + n.set(0x0002, b"456") + n.set(0x0101, b"789") + key, value = n._read_item(16) + assert key == 0x0002 + assert value == b"456" + key, value = n._read_item(24) + assert key == 0x0101 + assert value == b"789" + + with pytest.raises(ValueError) as e: + key, value = n._read_item(204) + assert "no data" in str(e) + + +def test_norcow_get_item(): + n = norcow.Norcow() + n.init() + n.set(0x0001, b"123") + n.set(0x0002, b"456") + n.set(0x0101, b"789") + value = n.get(0x0001) + assert value == b"123" + assert ( + n._dump()[0][:40].hex() + == "4e524332feffffff010003003132330002000300343536000101030037383900ffffffffffffffff" + ) + + # replacing item with the same value (update) + n.set(0x0101, b"789") + value = n.get(0x0101) + assert value == b"789" + assert ( + n._dump()[0][:40].hex() + == "4e524332feffffff010003003132330002000300343536000101030037383900ffffffffffffffff" + ) + + # replacing item with value with less 1 bits than before (update) + n.set(0x0101, b"788") + value = n.get(0x0101) + assert value == b"788" + assert ( + n._dump()[0][:40].hex() + == "4e524332feffffff010003003132330002000300343536000101030037383800ffffffffffffffff" + ) + + # replacing item with value with more 1 bits than before (wipe and new entry) + n.set(0x0101, b"787") + value = n.get(0x0101) + assert value == b"787" + assert ( + n._dump()[0][:44].hex() + == "4e524332feffffff0100030031323300020003003435360000000300000000000101030037383700ffffffff" + ) + + n.set(0x0002, b"world") + n.set(0x0002, b"earth") + value = n.get(0x0002) + assert value == b"earth" + + +def test_norcow_replace_item(): + n = norcow.Norcow() + n.init() + n.set(0x0001, b"123") + n.set(0x0002, b"456") + n.set(0x0101, b"789") + value = n.get(0x0002) + assert value == b"456" + + n.replace(0x0001, b"000") + value = n.get(0x0001) + assert value == b"000" + + n.replace(0x0002, b"111") + value = n.get(0x0002) + assert value == b"111" + value = n.get(0x0001) + assert value == b"000" + value = n.get(0x0101) + assert value == b"789" + + with pytest.raises(RuntimeError) as e: + n.replace(0x0001, b"00000") + assert "same length" in str(e) + + +def test_norcow_compact(): + n = norcow.Norcow() + n.init() + n.set(0x0101, b"ahoj") + n.set(0x0101, b"a" * (consts.NORCOW_SECTOR_SIZE - 100)) + n.set(0x0101, b"hello") + + n.set(0x0103, b"123456789x") + n.set(0x0104, b"123456789x") + n.set(0x0105, b"123456789x") + n.set(0x0106, b"123456789x") + mem = n._dump() + assert mem[0][:8] == consts.NORCOW_MAGIC_AND_VERSION + assert mem[0][200:300] == b"\x00" * 100 + + # compact is triggered + n.set(0x0107, b"123456789x") + mem = n._dump() + # assert the other sector is active + assert mem[1][:8] == consts.NORCOW_MAGIC_AND_VERSION + # assert the deleted item was not copied + assert mem[0][200:300] == b"\xff" * 100 + + n.set(0x0108, b"123456789x") + n.set(0x0109, b"123456789x") + + assert n.get(0x0101) == b"hello" + assert n.get(0x0103) == b"123456789x" diff --git a/storage/tests/python/tests/test_pin.py b/storage/tests/python/tests/test_pin.py new file mode 100644 index 0000000000..a6f517922d --- /dev/null +++ b/storage/tests/python/tests/test_pin.py @@ -0,0 +1,28 @@ +from ..src.storage import Storage + + +def test_set_pin_success(): + s = Storage() + hw_salt = b"\x00\x00\x00\x00\x00\x00" + s.init(hw_salt) + s._set_pin(1) + assert s.unlock(1) + + s = Storage() + s.init(hw_salt) + s._set_pin(229922) + assert s.unlock(229922) + + +def test_set_pin_failure(): + s = Storage() + hw_salt = b"\x00\x00\x00\x00\x00\x00" + s.init(hw_salt) + s._set_pin(1) + assert s.unlock(1) + assert not s.unlock(1234) + + s = Storage() + s.init(hw_salt) + s._set_pin(229922) + assert not s.unlock(1122992211) diff --git a/storage/tests/python/tests/test_pin_log.py b/storage/tests/python/tests/test_pin_log.py new file mode 100644 index 0000000000..5ef72c521e --- /dev/null +++ b/storage/tests/python/tests/test_pin_log.py @@ -0,0 +1,12 @@ +from ..src import pin_log + + +def test_generate_guard_key(): + p = pin_log.PinLog(None) + + assert p._generate_guard_key() == 2267428717 + assert p._generate_guard_key() == 1653399972 + assert p._check_guard_key(2267428717) + assert p._check_guard_key(1653399972) + assert p._check_guard_key(3706993061) + assert p._check_guard_key(3593237286) diff --git a/storage/tests/python/tests/test_prng.py b/storage/tests/python/tests/test_prng.py new file mode 100644 index 0000000000..5d2ae8e43c --- /dev/null +++ b/storage/tests/python/tests/test_prng.py @@ -0,0 +1,11 @@ +from ..src import prng + + +def test_prng(): + buf = prng.random_buffer(4) + assert buf == b"\x5f\xf3\x6e\x3c" + buf = prng.random_buffer(4) + assert buf == b"\x32\x29\x50\x47" + + buf = prng.random_buffer(8) + assert buf == b"\xe9\xf6\xcc\xd1\x34\x53\xf9\xaa" diff --git a/storage/tests/test.py b/storage/tests/test.py new file mode 100755 index 0000000000..982766ba48 --- /dev/null +++ b/storage/tests/test.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +from c.storage import Storage as StorageC +from c0.storage import Storage as StorageC0 +from python.src.storage import Storage as StoragePy + +from hashlib import sha256 + + +def hash(data): + return sha256(data).hexdigest()[:16] + + +# Strings for testing ChaCha20 encryption. +test_strings = [b"Short string.", b"", b"Although ChaCha20 is a stream cipher, it operates on blocks of 64 bytes. This string is over 152 bytes in length so that we test multi-block encryption.", b"This string is exactly 64 bytes long, that is exactly one block."] + +# Unique device ID for testing. +uid = b"\x67\xce\x6a\xe8\xf7\x9b\x73\x96\x83\x88\x21\x5e" + +sc = StorageC() +sp = StoragePy() +a = [] + +for s in [sc, sp]: + print(s.__class__) + s.init(uid) + assert s.unlock(3) == False + assert s.unlock(1) == True + s.set(0xbeef, b"hello") + s.set(0x03fe, b"world!") + s.set(0xbeef, b"satoshi") + s.set(0xbeef, b"Satoshi") + for value in test_strings: + s.set(0x0301, value) + assert s.get(0x0301) == value + d = s._dump() + print(d[0][:512].hex()) + h = [hash(x) for x in d] + print(h) + a.append(h[0]) + a.append(h[1]) + print() + +print("-------------") +print("Equals:", a[0] == a[2] and a[1] == a[3]) diff --git a/storage/tests/tests/__init__.py b/storage/tests/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/storage/tests/tests/common.py b/storage/tests/tests/common.py new file mode 100644 index 0000000000..c66ea8091e --- /dev/null +++ b/storage/tests/tests/common.py @@ -0,0 +1,23 @@ +from c.storage import Storage as StorageC +from python.src import prng +from python.src.storage import Storage as StoragePy + +test_uid = b"\x67\xce\x6a\xe8\xf7\x9b\x73\x96\x83\x88\x21\x5e" + + +def init( + unlock: bool = False, reseed: int = 0, uid: int = test_uid +) -> (StorageC, StoragePy): + sc = StorageC() + sp = StoragePy() + sc.lib.random_reseed(reseed) + prng.random_reseed(reseed) + for s in (sc, sp): + s.init(uid) + if unlock: + assert s.unlock(1) + return sc, sp + + +def memory_equals(sc, sp) -> bool: + return sc._dump() == sp._dump() diff --git a/storage/tests/tests/storage_model.py b/storage/tests/tests/storage_model.py new file mode 100644 index 0000000000..06eedc4600 --- /dev/null +++ b/storage/tests/tests/storage_model.py @@ -0,0 +1,68 @@ +# Logical storage model used for testing. + + +class StorageModel: + _EMPTY_PIN = 1 + _PIN_MAX_TRIES = 16 + + def __init__(self) -> None: + self.wipe() + + def init(self, salt: bytes) -> None: + self.unlocked = False + + def wipe(self) -> None: + self.unlocked = False + self.pin = 1 + self.pin_rem = self._PIN_MAX_TRIES + self.dict = {} + + def lock(self) -> None: + self.unlocked = False + + def unlock(self, pin: int) -> bool: + if pin == self.pin: + self.pin_rem = self._PIN_MAX_TRIES + self.unlocked = True + return True + else: + self.pin_rem -= 1 + if self.pin_rem <= 0: + self.wipe() + return False + + def has_pin(self) -> bool: + return self.pin != self._EMPTY_PIN + + def get_pin_rem(self) -> int: + return self.pin_rem + + def change_pin(self, oldpin: int, newpin: int) -> bool: + if self.unlocked and self.unlock(oldpin): + self.pin = newpin + return True + else: + return False + + def get(self, key: int) -> bytes: + if (key & 0x8000 != 0 or self.unlocked) and self.dict.get(key) is not None: + return self.dict[key] + raise RuntimeError("Failed to find key in storage.") + + def set(self, key: int, val: bytes) -> None: + if self.unlocked: + self.dict[key] = val + else: + raise RuntimeError("Failed to set value in storage.") + + def delete(self, key: int) -> bool: + if not self.unlocked: + return False + try: + self.dict.pop(key) + except KeyError: + return False + return True + + def __iter__(self): + return iter(self.dict.items()) diff --git a/storage/tests/tests/test_compact.py b/storage/tests/tests/test_compact.py new file mode 100644 index 0000000000..671b5d6595 --- /dev/null +++ b/storage/tests/tests/test_compact.py @@ -0,0 +1,32 @@ +import pytest + +from python.src import consts + +from . import common + + +def test_compact(): + sc, sp = common.init(unlock=True) + for s in (sc, sp): + s.set(0xBEEF, b"hello") + s.set(0xBEEF, b"asdasdasdasd") + s.set(0xBEEF, b"fsdasdasdasdasdsadasdsadasdasd") + s.set(0x0101, b"a" * (consts.NORCOW_SECTOR_SIZE - 600)) + s.set(0x03FE, b"world!") + s.set(0x04FE, b"world!xfffffffffffffffffffffffffffff") + s.set(0x05FE, b"world!affffffffffffffffffffffffffffff") + s.set(0x0101, b"s") + s.set(0x06FE, b"world!aaaaaaaaaaaaaaaaaaaaaaaaab") + s.set(0x07FE, b"worxxxxxxxxxxxxxxxxxx") + s.set(0x09EE, b"worxxxxxxxxxxxxxxxxxx") + assert common.memory_equals(sc, sp) + + sc, sp = common.init(unlock=True) + for s in (sc, sp): + s.set(0xBEEF, b"asdasdasdasd") + s.set(0xBEEF, b"fsdasdasdasdasdsadasdsadasdasd") + s.set(0x8101, b"a" * (consts.NORCOW_SECTOR_SIZE - 1000)) + with pytest.raises(RuntimeError): + s.set(0x0101, b"a" * (consts.NORCOW_SECTOR_SIZE - 100)) + s.set(0x0101, b"hello") + assert common.memory_equals(sc, sp) diff --git a/storage/tests/tests/test_pin.py b/storage/tests/tests/test_pin.py new file mode 100644 index 0000000000..915deb4b31 --- /dev/null +++ b/storage/tests/tests/test_pin.py @@ -0,0 +1,66 @@ +import pytest + +from python.src import consts + +from . import common + + +def test_init_pin(): + sc, sp = common.init(uid=b"\x00\x00\x00\x00\x00\x00") + assert common.memory_equals(sc, sp) + + sc, sp = common.init(uid=b"\x22\x00\xDD\x00\x00\xBE") + assert common.memory_equals(sc, sp) + + +def test_change_pin(): + sc, sp = common.init(unlock=True) + for s in (sc, sp): + assert s.change_pin(1, 2221) + # invalid PIN + assert not s.change_pin(99991, 1) + assert s.unlock(2221) + assert s.change_pin(2221, 999991) + assert s.change_pin(999991, 991) + assert s.unlock(991) + assert not s.unlock(99991) + + assert common.memory_equals(sc, sp) + + +def test_has_pin(): + sc, sp = common.init() + for s in (sc, sp): + assert not s.has_pin() + assert s.unlock(1) + assert not s.has_pin() + assert s.change_pin(1, 221) + assert s.has_pin() + assert s.change_pin(221, 1) + assert not s.has_pin() + + +def test_wipe_after_max_pin(): + sc, sp = common.init(unlock=True) + for s in (sc, sp): + assert s.change_pin(1, 2221) + assert s.unlock(2221) + s.set(0x0202, b"Hello") + + # try an invalid PIN MAX - 1 times + for i in range(consts.PIN_MAX_TRIES - 1): + assert not s.unlock(99991) + # this should pass + assert s.unlock(2221) + assert s.get(0x0202) == b"Hello" + + # try an invalid PIN MAX times, the storage should get wiped + for i in range(consts.PIN_MAX_TRIES): + assert not s.unlock(99991) + assert i == consts.PIN_MAX_TRIES - 1 + # this should return False and raise an exception, the storage is wiped + assert not s.unlock(2221) + with pytest.raises(RuntimeError): + assert s.get(0x0202) == b"Hello" + + assert common.memory_equals(sc, sp) diff --git a/storage/tests/tests/test_random.py b/storage/tests/tests/test_random.py new file mode 100644 index 0000000000..01f4f71c64 --- /dev/null +++ b/storage/tests/tests/test_random.py @@ -0,0 +1,83 @@ +import hypothesis.strategies as st +from hypothesis import assume, settings +from hypothesis.stateful import Bundle, RuleBasedStateMachine, invariant, rule + +from . import common +from .storage_model import StorageModel + + +class StorageComparison(RuleBasedStateMachine): + def __init__(self): + super(StorageComparison, self).__init__() + self.sc, self.sp = common.init(unlock=True) + self.sm = StorageModel() + self.sm.init(b"") + self.sm.unlock(1) + self.storages = (self.sc, self.sp, self.sm) + + keys = Bundle("keys") + values = Bundle("values") + pins = Bundle("pins") + + @rule(target=keys, app=st.integers(1, 0xFF), key=st.integers(0, 0xFF)) + def k(self, app, key): + return (app << 8) | key + + @rule(target=values, v=st.binary(min_size=0, max_size=10000)) + def v(self, v): + return v + + @rule(target=pins, p=st.integers(1, 3)) + def p(self, p): + return p + + @rule(k=keys, v=values) + def set(self, k, v): + assume(k != 0xFFFF) + for s in self.storages: + s.set(k, v) + + @rule(k=keys) + def delete(self, k): + assume(k != 0xFFFF) + assert len(set(s.delete(k) for s in self.storages)) == 1 + + @rule(p=pins) + def check_pin(self, p): + assert len(set(s.unlock(p) for s in self.storages)) == 1 + self.ensure_unlocked() + + @rule(oldpin=pins, newpin=pins) + def change_pin(self, oldpin, newpin): + assert len(set(s.change_pin(oldpin, newpin) for s in self.storages)) == 1 + self.ensure_unlocked() + + @rule() + def lock(self): + for s in self.storages: + s.lock() + self.ensure_unlocked() + + @invariant() + def values_agree(self): + for k, v in self.sm: + assert self.sc.get(k) == v + + @invariant() + def dumps_agree(self): + assert self.sc._dump() == self.sp._dump() + + @invariant() + def pin_counters_agree(self): + assert len(set(s.get_pin_rem() for s in self.storages)) == 1 + + def ensure_unlocked(self): + if not self.sm.unlocked: + for s in self.storages: + assert s.unlock(self.sm.pin) + + +TestStorageComparison = StorageComparison.TestCase +TestStorageComparison.settings = settings( + deadline=2000, max_examples=30, stateful_step_count=50 +) diff --git a/storage/tests/tests/test_random_upgrade.py b/storage/tests/tests/test_random_upgrade.py new file mode 100644 index 0000000000..4aaa1a507b --- /dev/null +++ b/storage/tests/tests/test_random_upgrade.py @@ -0,0 +1,73 @@ +import hypothesis.strategies as st +from hypothesis import assume, settings +from hypothesis.stateful import Bundle, RuleBasedStateMachine, invariant, rule + +from c0.storage import Storage as StorageC0 +from c.storage import Storage as StorageC + +from . import common +from .storage_model import StorageModel + + +class StorageUpgrade(RuleBasedStateMachine): + def __init__(self): + super(StorageUpgrade, self).__init__() + self.sc = StorageC0() + self.sc.init() + self.sm = StorageModel() + self.sm.init(common.test_uid) + self.storages = (self.sc, self.sm) + self.ensure_unlocked() + + keys = Bundle("keys") + values = Bundle("values") + pins = Bundle("pins") + + @rule(target=keys, app=st.integers(1, 0xFF), key=st.integers(0, 0xFF)) + def k(self, app, key): + return (app << 8) | key + + @rule(target=values, v=st.binary(min_size=0, max_size=10000)) + def v(self, v): + return v + + @rule(target=pins, p=st.integers(1, 3)) + def p(self, p): + return p + + @rule(k=keys, v=values) + def set(self, k, v): + assume(k != 0xFFFF) + for s in self.storages: + s.set(k, v) + + @rule(p=pins) + def check_pin(self, p): + assert self.sm.unlock(p) == self.sc.check_pin(p) + self.ensure_unlocked() + + @rule(oldpin=pins, newpin=pins) + def change_pin(self, oldpin, newpin): + assert self.sm.change_pin(oldpin, newpin) == self.sc.change_pin(oldpin, newpin) + self.ensure_unlocked() + + @invariant() + def check_upgrade(self): + sc1 = StorageC() + sc1._set_flash_buffer(self.sc._get_flash_buffer()) + sc1.init(common.test_uid) + assert self.sm.get_pin_rem() == sc1.get_pin_rem() + assert sc1.unlock(self.sm.pin) + for k, v in self.sm: + assert sc1.get(k) == v + + def ensure_unlocked(self): + if not self.sm.unlocked: + for s in self.storages: + assert s.unlock(self.sm.pin) + + +TestStorageUpgrade = StorageUpgrade.TestCase +TestStorageUpgrade.settings = settings( + deadline=None, max_examples=30, stateful_step_count=50 +) diff --git a/storage/tests/tests/test_set_get.py b/storage/tests/tests/test_set_get.py new file mode 100644 index 0000000000..727bc20a27 --- /dev/null +++ b/storage/tests/tests/test_set_get.py @@ -0,0 +1,178 @@ +import pytest + +from . import common + +# Strings for testing ChaCha20 encryption. +chacha_strings = [ + b"Short string.", + b"", + b"Although ChaCha20 is a stream cipher, it operates on blocks of 64 bytes. This string is over 152 bytes in length so that we test multi-block encryption.", + b"This string is exactly 64 bytes long, that is exactly one block.", +] + + +def test_set_get(): + sc, sp = common.init(unlock=True) + for s in (sc, sp): + s.set(0xBEEF, b"Hello") + s.set(0xCAFE, b"world! ") + s.set(0xDEAD, b"How\n") + s.set(0xAAAA, b"are") + s.set(0x0901, b"you?") + s.set(0x0902, b"Lorem") + s.set(0x0903, b"ipsum") + s.set(0xDEAD, b"A\n") + s.set(0xDEAD, b"AAAAAAAAAAA") + s.set(0x2200, b"BBBB") + assert common.memory_equals(sc, sp) + + for s in (sc, sp): + s.change_pin(1, 2221) + s.change_pin(2221, 991) + s.set(0xAAAA, b"something else") + assert common.memory_equals(sc, sp) + + # check data are not changed by gets + datasc = sc._dump() + datasp = sp._dump() + + for s in (sc, sp): + assert s.get(0xAAAA) == b"something else" + assert s.get(0x0901) == b"you?" + assert s.get(0x0902) == b"Lorem" + assert s.get(0x0903) == b"ipsum" + assert s.get(0xDEAD) == b"AAAAAAAAAAA" + assert s.get(0x2200) == b"BBBB" + + assert datasc == sc._dump() + assert datasp == sp._dump() + + # test locked storage + for s in (sc, sp): + s.lock() + with pytest.raises(RuntimeError): + s.set(0xAAAA, b"test public") + with pytest.raises(RuntimeError): + s.set(0x0901, b"test protected") + with pytest.raises(RuntimeError): + s.get(0x0901) + assert s.get(0xAAAA) == b"something else" + + # check that storage functions after unlock + for s in (sc, sp): + s.unlock(991) + s.set(0xAAAA, b"public") + s.set(0x0902, b"protected") + assert s.get(0xAAAA) == b"public" + assert s.get(0x0902) == b"protected" + + # test delete + for s in (sc, sp): + assert s.delete(0x0902) + assert common.memory_equals(sc, sp) + + for s in (sc, sp): + assert not s.delete(0x7777) + assert not s.delete(0x0902) + assert common.memory_equals(sc, sp) + + +def test_invalid_key(): + for s in common.init(unlock=True): + with pytest.raises(RuntimeError): + s.set(0xFFFF, b"Hello") + + +def test_chacha_strings(): + sc, sp = common.init(unlock=True) + for s in (sc, sp): + for i, string in enumerate(chacha_strings): + s.set(0x0301 + i, string) + assert common.memory_equals(sc, sp) + + for s in (sc, sp): + for i, string in enumerate(chacha_strings): + assert s.get(0x0301 + i) == string + + +def test_set_repeated(): + test_strings = [[0x0501, b""], [0x0502, b"test"], [0x8501, b""], [0x8502, b"test"]] + sc, sp = common.init(unlock=True) + for s in (sc, sp): + for key, val in test_strings: + s.set(key, val) + s.set(key, val) + assert common.memory_equals(sc, sp) + + for s in (sc, sp): + for key, val in test_strings: + s.set(key, val) + assert common.memory_equals(sc, sp) + + for key, val in test_strings: + for s in (sc, sp): + assert s.delete(key) + assert common.memory_equals(sc, sp) + + +def test_set_similar(): + sc, sp = common.init(unlock=True) + for s in (sc, sp): + s.set(0xBEEF, b"Satoshi") + s.set(0xBEEF, b"satoshi") + assert common.memory_equals(sc, sp) + + for s in (sc, sp): + s.wipe() + s.unlock(1) + s.set(0xBEEF, b"satoshi") + s.set(0xBEEF, b"Satoshi") + assert common.memory_equals(sc, sp) + + for s in (sc, sp): + s.wipe() + s.unlock(1) + s.set(0xBEEF, b"satoshi") + s.set(0xBEEF, b"Satoshi") + s.set(0xBEEF, b"Satoshi") + s.set(0xBEEF, b"SatosHi") + s.set(0xBEEF, b"satoshi") + s.set(0xBEEF, b"satoshi\x00") + assert common.memory_equals(sc, sp) + + +def test_set_locked(): + sc, sp = common.init() + for s in (sc, sp): + with pytest.raises(RuntimeError): + s.set(0x0303, b"test") + with pytest.raises(RuntimeError): + s.set(0x8003, b"test") + assert common.memory_equals(sc, sp) + + for s in (sc, sp): + s.set(0xC001, b"Ahoj") + s.set(0xC003, b"test") + assert common.memory_equals(sc, sp) + + for s in (sc, sp): + s.get(0xC001) == b"Ahoj" + s.get(0xC003) == b"test" + + +def test_counter(): + sc, sp = common.init(unlock=True) + for i in range(0, 200): + for s in (sc, sp): + assert i == s.next_counter(0xC001) + assert common.memory_equals(sc, sp) + + for s in (sc, sp): + s.lock() + s.set_counter(0xC001, 500) + assert common.memory_equals(sc, sp) + + for i in range(501, 700): + for s in (sc, sp): + assert i == s.next_counter(0xC001) + assert common.memory_equals(sc, sp) diff --git a/storage/tests/tests/test_upgrade.py b/storage/tests/tests/test_upgrade.py new file mode 100644 index 0000000000..a089438ca8 --- /dev/null +++ b/storage/tests/tests/test_upgrade.py @@ -0,0 +1,75 @@ +from c0.storage import Storage as StorageC0 +from c.storage import Storage as StorageC +from python.src.storage import Storage as StoragePy + +from . import common + +# Strings for testing ChaCha20 encryption. +chacha_strings = [ + b"Short string.", + b"", + b"Although ChaCha20 is a stream cipher, it operates on blocks of 64 bytes. This string is over 152 bytes in length so that we test multi-block encryption.", + b"This string is exactly 64 bytes long, that is exactly one block.", +] + + +def set_values(s): + s.set(0xBEEF, b"Hello") + s.set(0xCAFE, b"world! ") + s.set(0xDEAD, b"How\n") + s.change_pin(1, 1222) + s.set(0xAAAA, b"are") + s.set(0x0901, b"you?") + s.set(0x0902, b"Lorem") + s.set(0x0903, b"ipsum") + s.change_pin(1222, 199) + s.set(0xDEAD, b"A\n") + s.set(0xDEAD, b"AAAAAAAAAAA") + s.set(0x2200, b"BBBB") + for i, string in enumerate(chacha_strings): + s.set(0x0301 + i, string) + + +def check_values(s): + assert s.unlock(199) + assert s.get(0xAAAA) == b"are" + assert s.get(0x0901) == b"you?" + assert s.get(0x0902) == b"Lorem" + assert s.get(0x0903) == b"ipsum" + assert s.get(0xDEAD) == b"AAAAAAAAAAA" + assert s.get(0x2200) == b"BBBB" + for i, string in enumerate(chacha_strings): + assert s.get(0x0301 + i) == string + + +def test_upgrade(): + sc0 = StorageC0() + sc0.init() + assert sc0.unlock(1) + set_values(sc0) + for _ in range(10): + assert not sc0.unlock(3) + + sc1 = StorageC() + sc1._set_flash_buffer(sc0._get_flash_buffer()) + sc1.init(common.test_uid) + assert sc1.get_pin_rem() == 6 + check_values(sc1) + + +def test_python_set_sectors(): + sp0 = StoragePy() + sp0.init(common.test_uid) + assert sp0.unlock(1) + set_values(sp0) + for _ in range(10): + assert not sp0.unlock(3) + assert sp0.get_pin_rem() == 6 + + sp1 = StoragePy() + sp1.nc._set_sectors(sp0._dump()) + sp1.init(common.test_uid) + common.memory_equals(sp0, sp1) + + assert sp1.get_pin_rem() == 6 + check_values(sp1) From 18482a9c3704839b49c7339dbdf1ee6a4fbdb3c1 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 25 Apr 2019 11:14:42 +0200 Subject: [PATCH 58/78] storage: style --- storage/tests/c/storage.py | 21 +++++++++++++----- storage/tests/c0/storage.py | 19 ++++++++++++----- storage/tests/python/tests/test_norcow.py | 2 +- storage/tests/test.py | 26 +++++++++++++---------- tools/style.py.include | 1 + 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/storage/tests/c/storage.py b/storage/tests/c/storage.py index f9a1a6fa53..d446b0dcf4 100644 --- a/storage/tests/c/storage.py +++ b/storage/tests/c/storage.py @@ -10,7 +10,9 @@ class Storage: self.lib = c.cdll.LoadLibrary(fname) self.flash_size = c.cast(self.lib.FLASH_SIZE, c.POINTER(c.c_uint32))[0] self.flash_buffer = c.create_string_buffer(self.flash_size) - c.cast(self.lib.FLASH_BUFFER, c.POINTER(c.c_void_p))[0] = c.addressof(self.flash_buffer) + c.cast(self.lib.FLASH_BUFFER, c.POINTER(c.c_void_p))[0] = c.addressof( + self.flash_buffer + ) def init(self, salt: bytes) -> None: self.lib.storage_init(0, salt, c.c_uint16(len(salt))) @@ -31,14 +33,18 @@ class Storage: return self.lib.storage_get_pin_rem() def change_pin(self, oldpin: int, newpin: int) -> bool: - return sectrue == self.lib.storage_change_pin(c.c_uint32(oldpin), c.c_uint32(newpin)) + return sectrue == self.lib.storage_change_pin( + c.c_uint32(oldpin), c.c_uint32(newpin) + ) def get(self, key: int) -> bytes: val_len = c.c_uint16() if sectrue != self.lib.storage_get(c.c_uint16(key), None, 0, c.byref(val_len)): raise RuntimeError("Failed to find key in storage.") s = c.create_string_buffer(val_len.value) - if sectrue != self.lib.storage_get(c.c_uint16(key), s, val_len, c.byref(val_len)): + if sectrue != self.lib.storage_get( + c.c_uint16(key), s, val_len, c.byref(val_len) + ): raise RuntimeError("Failed to get value from storage.") return s.raw @@ -47,7 +53,9 @@ class Storage: raise RuntimeError("Failed to set value in storage.") def set_counter(self, key: int, count: int) -> bool: - return sectrue == self.lib.storage_set_counter(c.c_uint16(key), c.c_uint32(count)) + return sectrue == self.lib.storage_set_counter( + c.c_uint16(key), c.c_uint32(count) + ) def next_counter(self, key: int) -> int: count = c.c_uint32() @@ -61,7 +69,10 @@ class Storage: def _dump(self) -> bytes: # return just sectors 4 and 16 of the whole flash - return [self.flash_buffer[0x010000:0x010000 + 0x10000], self.flash_buffer[0x110000:0x110000 + 0x10000]] + return [ + self.flash_buffer[0x010000 : 0x010000 + 0x10000], + self.flash_buffer[0x110000 : 0x110000 + 0x10000], + ] def _get_flash_buffer(self) -> bytes: return bytes(self.flash_buffer) diff --git a/storage/tests/c0/storage.py b/storage/tests/c0/storage.py index 64c9bf8bbc..235695c63f 100644 --- a/storage/tests/c0/storage.py +++ b/storage/tests/c0/storage.py @@ -4,13 +4,15 @@ import os sectrue = -1431655766 # 0xAAAAAAAAA fname = os.path.join(os.path.dirname(__file__), "libtrezor-storage0.so") -class Storage: +class Storage: def __init__(self) -> None: self.lib = c.cdll.LoadLibrary(fname) self.flash_size = c.cast(self.lib.FLASH_SIZE, c.POINTER(c.c_uint32))[0] self.flash_buffer = c.create_string_buffer(self.flash_size) - c.cast(self.lib.FLASH_BUFFER, c.POINTER(c.c_void_p))[0] = c.addressof(self.flash_buffer) + c.cast(self.lib.FLASH_BUFFER, c.POINTER(c.c_void_p))[0] = c.addressof( + self.flash_buffer + ) def init(self) -> None: self.lib.storage_init(0) @@ -28,12 +30,16 @@ class Storage: return sectrue == self.lib.storage_has_pin() def change_pin(self, oldpin: int, newpin: int) -> bool: - return sectrue == self.lib.storage_change_pin(c.c_uint32(oldpin), c.c_uint32(newpin)) + return sectrue == self.lib.storage_change_pin( + c.c_uint32(oldpin), c.c_uint32(newpin) + ) def get(self, key: int) -> bytes: val_ptr = c.c_void_p() val_len = c.c_uint16() - if sectrue != self.lib.storage_get(c.c_uint16(key), c.byref(val_ptr), c.byref(val_len)): + if sectrue != self.lib.storage_get( + c.c_uint16(key), c.byref(val_ptr), c.byref(val_len) + ): raise RuntimeError("Failed to find key in storage.") return c.string_at(val_ptr, size=val_len.value) @@ -43,7 +49,10 @@ class Storage: def _dump(self) -> bytes: # return just sectors 4 and 16 of the whole flash - return [self.flash_buffer[0x010000:0x010000 + 0x10000], self.flash_buffer[0x110000:0x110000 + 0x10000]] + return [ + self.flash_buffer[0x010000 : 0x010000 + 0x10000], + self.flash_buffer[0x110000 : 0x110000 + 0x10000], + ] def _get_flash_buffer(self) -> bytes: return bytes(self.flash_buffer) diff --git a/storage/tests/python/tests/test_norcow.py b/storage/tests/python/tests/test_norcow.py index 78a6819c7f..38b9369c8e 100644 --- a/storage/tests/python/tests/test_norcow.py +++ b/storage/tests/python/tests/test_norcow.py @@ -1,7 +1,7 @@ import pytest -from . import common from ..src import consts, norcow +from . import common def test_norcow_set(): diff --git a/storage/tests/test.py b/storage/tests/test.py index 982766ba48..0ee5f4a0e4 100755 --- a/storage/tests/test.py +++ b/storage/tests/test.py @@ -1,18 +1,22 @@ #!/usr/bin/env python3 -from c.storage import Storage as StorageC -from c0.storage import Storage as StorageC0 -from python.src.storage import Storage as StoragePy - from hashlib import sha256 +from c.storage import Storage as StorageC +from python.src.storage import Storage as StoragePy + def hash(data): return sha256(data).hexdigest()[:16] # Strings for testing ChaCha20 encryption. -test_strings = [b"Short string.", b"", b"Although ChaCha20 is a stream cipher, it operates on blocks of 64 bytes. This string is over 152 bytes in length so that we test multi-block encryption.", b"This string is exactly 64 bytes long, that is exactly one block."] +test_strings = [ + b"Short string.", + b"", + b"Although ChaCha20 is a stream cipher, it operates on blocks of 64 bytes. This string is over 152 bytes in length so that we test multi-block encryption.", + b"This string is exactly 64 bytes long, that is exactly one block.", +] # Unique device ID for testing. uid = b"\x67\xce\x6a\xe8\xf7\x9b\x73\x96\x83\x88\x21\x5e" @@ -24,12 +28,12 @@ a = [] for s in [sc, sp]: print(s.__class__) s.init(uid) - assert s.unlock(3) == False - assert s.unlock(1) == True - s.set(0xbeef, b"hello") - s.set(0x03fe, b"world!") - s.set(0xbeef, b"satoshi") - s.set(0xbeef, b"Satoshi") + assert s.unlock(3) is False + assert s.unlock(1) is True + s.set(0xBEEF, b"hello") + s.set(0x03FE, b"world!") + s.set(0xBEEF, b"satoshi") + s.set(0xBEEF, b"Satoshi") for value in test_strings: s.set(0x0301, value) assert s.get(0x0301) == value diff --git a/tools/style.py.include b/tools/style.py.include index 0e2706842e..07bba6d035 100644 --- a/tools/style.py.include +++ b/tools/style.py.include @@ -2,3 +2,4 @@ ^\./core/src/ ^\./crypto/ ^\./legacy/ +^\./storage/ From 3854fa38cc80a1a506b5a04043d6352f42dad3cd Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 25 Apr 2019 11:17:54 +0200 Subject: [PATCH 59/78] ci: test storage --- .gitlab-ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index af0f6ca289..efccbd7acd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -136,3 +136,11 @@ test legacy: - cd legacy - pipenv run script/cibuild - pipenv run script/test + +test storage: + stage: test + dependencies: [] + script: + - cd storage/tests + - pipenv run make build + - pipenv run make tests_all From 6657134bd83a8aeda6a9083da5e1dd9c3d26de27 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 25 Apr 2019 16:45:58 +0200 Subject: [PATCH 60/78] storage: alter README to reflect monorepo --- storage/README.md | 2 +- storage/tests/README.md | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/storage/README.md b/storage/README.md index f44352a283..5e1bacc556 100644 --- a/storage/README.md +++ b/storage/README.md @@ -2,7 +2,7 @@ This repository contains the implementation of Trezor's internal storage, which is common for both trezor-mcu (Trezor One) and trezor-core (Trezor T). This README also contains a detailed description of the cryptographic design. -All tests are located in the [trezor-storage-test](https://github.com/trezor/trezor-storage-test) repository, which also includes a Python implementation to run tests against this C production version and the Python one. +All tests are located in the `tests` subdirectory, which also includes a Python implementation to run tests against this C production version and the Python one. ## Summary diff --git a/storage/tests/README.md b/storage/tests/README.md index 3fd1eacc8a..72f9b83e1c 100644 --- a/storage/tests/README.md +++ b/storage/tests/README.md @@ -1,12 +1,10 @@ # Trezor Storage tests -This repository contains all the necessary files to properly test Trezor's internal storage, which is implemented in the [trezor-storage](https://github.com/trezor/trezor-storage) repository. - -The CI is available on the internal GitLab. +This repository contains all the necessary files to properly test Trezor's internal storage. This repository consists of: -- `c`: The actual C version is implemented in [trezor-storage](https://github.com/trezor/trezor-storage), however we need some other accompanying files to build it on PC. +- `c`: The actual C version is implemented in the main `storage` folder, however we need some other accompanying files to build it on computer. - `c0`: This is the older version of Trezor storage. It is used to test upgrades from the older format to the newer one. - `python`: Python version. Serves as a reference implementation and is implemented purely for the goal of properly testing the C version. - `tests`: Most of the tests run the two implementations against each other. Uses Pytest and [hypothesis](https://hypothesis.works) for random tests. From 1532c96b0e41a1cc23eb282665ca577ae7a0f367 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 25 Apr 2019 16:56:54 +0200 Subject: [PATCH 61/78] core+legacy: remove tag automagic from build scripts --- core/build-docker.sh | 6 +----- legacy/script/fullbuild | 7 ------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/core/build-docker.sh b/core/build-docker.sh index 2543b645a5..18fb852d5f 100755 --- a/core/build-docker.sh +++ b/core/build-docker.sh @@ -11,11 +11,7 @@ else fi IMAGE=trezor-core-build.$TOOLCHAIN_FLAVOR -if [ -z "$1" ]; then - TAG=master -else - TAG=core/${1} -fi +TAG=${1:-master} REPOSITORY=${2:-trezor} PRODUCTION=${PRODUCTION:-0} diff --git a/legacy/script/fullbuild b/legacy/script/fullbuild index 4d3c38002c..30fce963ff 100755 --- a/legacy/script/fullbuild +++ b/legacy/script/fullbuild @@ -88,13 +88,6 @@ main() { local bootloader_commit="$1" local firmware_commit="$2" - if [ "$bootloader_commit" != "HEAD" ]; then - bootloader_commit="legacy/$bootloader_commit" - fi - if [ "$firmware_commit" != "HEAD" ]; then - firmware_commit="legacy/$firmware_commit" - fi - worktree_setup "$FIRMWARE_DIR" "$firmware_commit" if [ "$EMULATOR" != 1 ]; then From 9ed77afd12c5d49f74e8d0287c2f4a884234313e Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 26 Apr 2019 12:00:20 +0200 Subject: [PATCH 62/78] core: build protobuf --- core/src/trezor/messages/MoneroTransactionData.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/trezor/messages/MoneroTransactionData.py b/core/src/trezor/messages/MoneroTransactionData.py index a05cb49da7..a2b3918b6b 100644 --- a/core/src/trezor/messages/MoneroTransactionData.py +++ b/core/src/trezor/messages/MoneroTransactionData.py @@ -29,6 +29,8 @@ class MoneroTransactionData(p.MessageType): rsig_data: MoneroTransactionRsigData = None, integrated_indices: List[int] = None, client_version: int = None, + hard_fork: int = None, + monero_version: bytes = None, ) -> None: self.version = version self.payment_id = payment_id @@ -43,6 +45,8 @@ class MoneroTransactionData(p.MessageType): self.rsig_data = rsig_data self.integrated_indices = integrated_indices if integrated_indices is not None else [] self.client_version = client_version + self.hard_fork = hard_fork + self.monero_version = monero_version @classmethod def get_fields(cls): @@ -60,4 +64,6 @@ class MoneroTransactionData(p.MessageType): 11: ('rsig_data', MoneroTransactionRsigData, 0), 12: ('integrated_indices', p.UVarintType, p.FLAG_REPEATED), 13: ('client_version', p.UVarintType, 0), + 14: ('hard_fork', p.UVarintType, 0), + 15: ('monero_version', p.BytesType, 0), } From 2b1f591cb79fada2be74a14e14927d720bfd4ac3 Mon Sep 17 00:00:00 2001 From: kuchtak Date: Fri, 26 Apr 2019 15:40:52 +0200 Subject: [PATCH 63/78] common/defs: add BST (#110) --- common/defs/bitcoin/blockstamp.json | 42 ++++++++++++++++++++++++++++ common/defs/bitcoin/blockstamp.png | Bin 0 -> 9023 bytes 2 files changed, 42 insertions(+) create mode 100644 common/defs/bitcoin/blockstamp.json create mode 100644 common/defs/bitcoin/blockstamp.png diff --git a/common/defs/bitcoin/blockstamp.json b/common/defs/bitcoin/blockstamp.json new file mode 100644 index 0000000000..11c0975b7f --- /dev/null +++ b/common/defs/bitcoin/blockstamp.json @@ -0,0 +1,42 @@ +{ + "coin_name": "BlockStamp", + "coin_shortcut": "BST", + "coin_label": "BlockStamp", + "website": "https://blockstamp.info", + "github": "https://github.com/BlockStamp/bst", + "maintainer": "Krzysztof Kuchta ", + "curve_name": "secp256k1", + "address_type": 26, + "address_type_p2sh": 5, + "maxfee_kb": 2000000, + "minfee_kb": 1000, + "signed_message_header": "BST Signed Message:\n", + "hash_genesis_block": "8000000049a2e26b0185be50b4b8ed58b707c8893762959f0b1673641cae1828", + "xprv_magic": 76066276, + "xpub_magic": 76067358, + "xpub_magic_segwit_p2sh": 77429938, + "xpub_magic_segwit_native": 78792518, + "bech32_prefix": "bst", + "cashaddr_prefix": null, + "slip44": 254, + "segwit": true, + "decred": false, + "fork_id": null, + "force_bip143": false, + "bip115": false, + "default_fee_b": { + "Low": 10, + "Economy": 70, + "Normal": 140, + "High": 200 + }, + "dust_limit": 546, + "blocktime_seconds": 60, + "uri_prefix": "blockstamp", + "min_address_length": 27, + "max_address_length": 34, + "bitcore": [], + "blockbook": [], + "cooldown": 1000, + "consensus_branch_id": null +} diff --git a/common/defs/bitcoin/blockstamp.png b/common/defs/bitcoin/blockstamp.png new file mode 100644 index 0000000000000000000000000000000000000000..a92dc339d5021acc4916ba3fc19ea4c4d126e472 GIT binary patch literal 9023 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 Date: Fri, 26 Apr 2019 15:33:25 +0200 Subject: [PATCH 64/78] ci: add prebuild stage --- .gitlab-ci.yml | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index efccbd7acd..8b4f848619 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,6 +13,7 @@ variables: stages: - environment + - prebuild - build - test @@ -37,6 +38,23 @@ image: registry.corp.sldev.cz/trezor/trezor-firmware/environment before_script: - pipenv install +style: + stage: prebuild + script: + - pipenv run make style_check + - cd core && pipenv run make templates_check # TODO + +common: + stage: prebuild + script: + - cd common + - pipenv run jsonlint defs/*.json + - pipenv run jsonlint defs/*/*.json + - pipenv run python tools/cointool.py check + - pipenv run python tools/support.py check --ignore-missing + - pipenv run python protob/check.py + - pipenv run python protob/graph.py protob/*.proto # TODO: artifacts? + build core firmware: stage: build script: @@ -75,13 +93,6 @@ build legacy: - pipenv run make -C bootloader - pipenv run make -C demo -test style: - stage: test - dependencies: [] # no need to download artifacts - script: - - pipenv run make style_check - - cd core && pipenv run make templates_check # TODO - test core unix unit: stage: test script: @@ -100,18 +111,6 @@ test core unix monero: - cd core - pipenv run make test_emu_monero -test common: - stage: test - dependencies: [] # no need to download artifacts - script: - - cd common - - pipenv run jsonlint defs/*.json - - pipenv run jsonlint defs/*/*.json - - pipenv run python tools/cointool.py check - - pipenv run python tools/support.py check --ignore-missing - - pipenv run python protob/check.py - - pipenv run python protob/graph.py protob/*.proto # TODO: artifacts? - test crypto: stage: test dependencies: [] # no need to download artifacts From ae1a721030d8423a64f6435064a4dca5511d534d Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Fri, 26 Apr 2019 16:24:28 +0200 Subject: [PATCH 65/78] common/defs: update support for new coins --- common/defs/support.json | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/common/defs/support.json b/common/defs/support.json index 4bd81189ee..b619ae08f9 100644 --- a/common/defs/support.json +++ b/common/defs/support.json @@ -96,6 +96,7 @@ "bitcoin:BCH": "1.6.2", "bitcoin:BITC": "1.7.2", "bitcoin:BSD": "1.7.2", + "bitcoin:BST": "soon", "bitcoin:BTC": "1.5.2", "bitcoin:BTCP": "1.6.2", "bitcoin:BTDX": "1.7.2", @@ -106,6 +107,7 @@ "bitcoin:DGB": "1.6.3", "bitcoin:DNR": "1.7.1", "bitcoin:DOGE": "1.5.2", + "bitcoin:FAIR": "soon", "bitcoin:FJC": "1.6.1", "bitcoin:FLASH": "1.7.1", "bitcoin:FLO": "1.7.2", @@ -122,8 +124,10 @@ "bitcoin:NIX": "1.7.2", "bitcoin:NMC": "1.5.2", "bitcoin:PIVX": "1.8.0", + "bitcoin:POLIS": "soon", "bitcoin:PTC": "1.7.1", "bitcoin:QTUM": "1.8.1", + "bitcoin:REGTEST": "soon", "bitcoin:RVN": "1.7.2", "bitcoin:SMART": "1.7.1", "bitcoin:TAZ": "1.6.2", @@ -135,6 +139,7 @@ "bitcoin:VTC": "1.6.1", "bitcoin:XMY": "1.7.1", "bitcoin:XPM": "1.8.0", + "bitcoin:XRC": "soon", "bitcoin:XSN": "1.8.0", "bitcoin:XZC": "1.6.2", "bitcoin:ZCL": "1.8.0", @@ -1232,9 +1237,11 @@ "eth:ETC": "1.6.2", "eth:ETH": "1.6.2", "eth:ETHO": "1.6.3", + "eth:ETI": "soon", "eth:ETSC": "1.6.2", "eth:EXP": "1.6.2", "eth:GO": "1.6.2", + "eth:META": "soon", "eth:MIX": "1.7.2", "eth:MUSIC": "1.6.3", "eth:PIRL": "1.6.3", @@ -1261,6 +1268,7 @@ "nem:XEM": "1.6.2" }, "unsupported": { + "bitcoin:CPC": "not implemented", "bitcoin:CRW": "address_type collides with Bitcoin", "bitcoin:TRC": "address_type collides with Bitcoin", "bitcoin:ZEN": "not implemented", @@ -1417,12 +1425,14 @@ "erc20:eth:YEED:6f7a": "(AUTO) duplicate key", "erc20:eth:YEED:ca27": "(AUTO) duplicate key", "misc:ADA": "not implemented", + "misc:BNB": "not implemented", "misc:EOS": "not implemented", "misc:ONT": "not implemented", "misc:TRX": "not implemented", "misc:XMR": "not implemented", "misc:XRP": "not implemented", - "misc:XTZ": "not implemented" + "misc:XTZ": "not implemented", + "misc:tXRP": "not implemented" } }, "trezor2": { @@ -1432,6 +1442,7 @@ "bitcoin:BCH": "2.0.7", "bitcoin:BITC": "2.0.10", "bitcoin:BSD": "2.0.10", + "bitcoin:BST": "soon", "bitcoin:BTC": "2.0.5", "bitcoin:BTCP": "2.0.7", "bitcoin:BTDX": "2.0.10", @@ -1443,6 +1454,7 @@ "bitcoin:DGB": "2.0.7", "bitcoin:DNR": "2.0.8", "bitcoin:DOGE": "2.0.5", + "bitcoin:FAIR": "soon", "bitcoin:FJC": "2.0.5", "bitcoin:FLASH": "2.0.8", "bitcoin:FLO": "2.0.11", @@ -1459,8 +1471,10 @@ "bitcoin:NIX": "2.0.11", "bitcoin:NMC": "2.0.5", "bitcoin:PIVX": "2.0.11", + "bitcoin:POLIS": "soon", "bitcoin:PTC": "2.0.8", "bitcoin:QTUM": "2.1.1", + "bitcoin:REGTEST": "soon", "bitcoin:RVN": "2.0.10", "bitcoin:SMART": "2.0.8", "bitcoin:TAZ": "2.0.7", @@ -1472,6 +1486,7 @@ "bitcoin:VTC": "2.0.5", "bitcoin:XMY": "2.0.8", "bitcoin:XPM": "2.0.11", + "bitcoin:XRC": "soon", "bitcoin:XSN": "2.0.11", "bitcoin:XZC": "2.0.7", "bitcoin:ZCL": "2.0.11", @@ -2570,9 +2585,11 @@ "eth:ETC": "2.0.7", "eth:ETH": "2.0.7", "eth:ETHO": "2.0.8", + "eth:ETI": "soon", "eth:ETSC": "2.0.7", "eth:EXP": "2.0.7", "eth:GO": "2.0.7", + "eth:META": "soon", "eth:MIX": "2.0.10", "eth:MUSIC": "2.0.8", "eth:PIRL": "2.0.8", From 48abff67df4997625e785645cd2a334e4b93de55 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 26 Apr 2019 15:31:09 +0200 Subject: [PATCH 66/78] ci: build legacy emulator and pass to tests updates #108 --- .gitlab-ci.yml | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8b4f848619..5c62f50256 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -82,17 +82,29 @@ build core unix: untracked: true expire_in: 1 day -# TODO: matrix: DEBUG_LINK={0,1}, gcc vs clang -build legacy: +# TODO: matrix: gcc vs clang +build legacy firmware: stage: build - variables: - HEADLESS: "1" script: - cd legacy - pipenv run script/cibuild - pipenv run make -C bootloader - pipenv run make -C demo +build legacy emu: + stage: build + variables: + HEADLESS: "1" + EMULATOR: "1" + DEBUG_LINK: "1" + script: + - cd legacy + - pipenv run script/cibuild + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + untracked: true + expire_in: 1 day + test core unix unit: stage: test script: @@ -111,6 +123,18 @@ test core unix monero: - cd core - pipenv run make test_emu_monero +# TODO: matrix: do we need to test gcc vs clang as well? or build is enough? +# TODO: aren't some tests from .travis.yml missing? +test legacy emu: + stage: test + variables: + EMULATOR: "1" + dependencies: + - build legacy emu + script: + - cd legacy + - pipenv run script/test + test crypto: stage: test dependencies: [] # no need to download artifacts @@ -123,19 +147,6 @@ test crypto: - ./tests/test_openssl 1000 - ITERS=10 pipenv run pytest tests/ # TODO are ITERS=10 propagated? -# TODO: matrix: DEBUG_LINK={0,1}, gcc vs clang -# TODO add more based on deleted .travis.yml -test legacy: - stage: test - variables: - HEADLESS: "1" - EMULATOR: "1" - DEBUG_LINK: "1" - script: - - cd legacy - - pipenv run script/cibuild - - pipenv run script/test - test storage: stage: test dependencies: [] From 34fa641a9bb873ffb72ebabbb57e4e5e94d25999 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 26 Apr 2019 16:23:41 +0200 Subject: [PATCH 67/78] ci: define dependencies in core tests to skip artifacts that are not needed --- .gitlab-ci.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5c62f50256..962ca9459e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -107,18 +107,24 @@ build legacy emu: test core unix unit: stage: test + dependencies: + - build core unix script: - cd core - pipenv run make test test core unix device: stage: test + dependencies: + - build core unix script: - cd core - pipenv run make test_emu test core unix monero: stage: test + dependencies: + - build core unix script: - cd core - pipenv run make test_emu_monero @@ -127,10 +133,10 @@ test core unix monero: # TODO: aren't some tests from .travis.yml missing? test legacy emu: stage: test - variables: - EMULATOR: "1" dependencies: - build legacy emu + variables: + EMULATOR: "1" script: - cd legacy - pipenv run script/test From ee6a06a51e7864180513c4d4a1794dca5d3ea186 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Fri, 26 Apr 2019 17:40:07 +0200 Subject: [PATCH 68/78] ci: split crypto into build and test stages --- .gitlab-ci.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 962ca9459e..4e01980e4d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -105,6 +105,16 @@ build legacy emu: untracked: true expire_in: 1 day +build crypto: + stage: build + script: + - cd crypto + - pipenv run make + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + untracked: true + expire_in: 1 day + test core unix unit: stage: test dependencies: @@ -143,10 +153,10 @@ test legacy emu: test crypto: stage: test - dependencies: [] # no need to download artifacts + dependencies: + - build crypto script: - cd crypto - - pipenv run make - ./tests/aestst - ./tests/test_check - CK_TIMEOUT_MULTIPLIER=20 valgrind -q --error-exitcode=1 ./tests/test_check From 7cf22c5227e6911cee49bbbbeab1df27062fa59f Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Fri, 26 Apr 2019 17:55:49 +0200 Subject: [PATCH 69/78] common/defs: fix naming collision --- common/defs/support.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/defs/support.json b/common/defs/support.json index b619ae08f9..ec5beba310 100644 --- a/common/defs/support.json +++ b/common/defs/support.json @@ -289,7 +289,6 @@ "erc20:eth:BRD": "1.6.2", "erc20:eth:BRLN": "1.8.0", "erc20:eth:BSDC": "1.6.2", - "erc20:eth:BST": "1.6.2", "erc20:eth:BTCA": "1.8.0", "erc20:eth:BTCE": "1.6.2", "erc20:eth:BTCL": "1.6.2", @@ -1286,6 +1285,7 @@ "erc20:eth:BNC:ef51": "(AUTO) duplicate key", "erc20:eth:BOX:63f5": "(AUTO) duplicate key", "erc20:eth:BOX:e1a1": "(AUTO) duplicate key", + "erc20:eth:BST": "(AUTO) duplicate key", "erc20:eth:BTL (Battle)": "(AUTO) duplicate key", "erc20:eth:BTL (Bitlle)": "(AUTO) duplicate key", "erc20:eth:BTR:499a": "(AUTO) duplicate key", @@ -1637,7 +1637,6 @@ "erc20:eth:BRD": "2.0.7", "erc20:eth:BRLN": "2.0.10", "erc20:eth:BSDC": "2.0.7", - "erc20:eth:BST": "2.0.7", "erc20:eth:BTCA": "2.0.10", "erc20:eth:BTCE": "2.0.7", "erc20:eth:BTCL": "2.0.7", @@ -2637,6 +2636,7 @@ "erc20:eth:BNC:ef51": "(AUTO) duplicate key", "erc20:eth:BOX:63f5": "(AUTO) duplicate key", "erc20:eth:BOX:e1a1": "(AUTO) duplicate key", + "erc20:eth:BST": "(AUTO) duplicate key", "erc20:eth:BTL (Battle)": "(AUTO) duplicate key", "erc20:eth:BTL (Bitlle)": "(AUTO) duplicate key", "erc20:eth:BTR:499a": "(AUTO) duplicate key", From 9f7228a763b0a5810b91a727caf7377f013f08e3 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Fri, 26 Apr 2019 18:06:10 +0200 Subject: [PATCH 70/78] gitlab: use prefix for prebuild targets --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4e01980e4d..1b4f2f1983 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,13 +38,13 @@ image: registry.corp.sldev.cz/trezor/trezor-firmware/environment before_script: - pipenv install -style: +prebuild style: stage: prebuild script: - pipenv run make style_check - cd core && pipenv run make templates_check # TODO -common: +prebuild common: stage: prebuild script: - cd common From 719d54d97d934057ee5c569c3ecbb72a60778ec0 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Fri, 26 Apr 2019 18:06:36 +0200 Subject: [PATCH 71/78] core: regenerate templates --- core/src/apps/ethereum/tokens.py | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/apps/ethereum/tokens.py b/core/src/apps/ethereum/tokens.py index 96df77aa19..1d9ed2e86c 100644 --- a/core/src/apps/ethereum/tokens.py +++ b/core/src/apps/ethereum/tokens.py @@ -159,7 +159,6 @@ tokens = [ (1, b"\x80\x04\x63\x05\xaa\xab\x08\xf6\x03\x3b\x56\xa3\x60\xc1\x84\x39\x11\x65\xdc\x2d", "BRLN", 18), # eth / Berlin Coin (1, b"\xb2\x2c\x27\x86\xa5\x49\xb0\x08\x51\x7b\x67\x62\x5f\x52\x96\xe8\xfa\xf9\x58\x9e", "BRP", 18), # eth / Rental Processor Token (1, b"\xf2\x6e\xf5\xe0\x54\x53\x84\xb7\xdc\xc0\xf2\x97\xf2\x67\x41\x89\x58\x68\x30\xdf", "BSDC", 18), # eth / BSDC - (1, b"\x50\x9a\x38\xb7\xa1\xcc\x0d\xcd\x83\xaa\x9d\x06\x21\x46\x63\xd9\xec\x7c\x7f\x4a", "BST", 18), # eth / BlocksquareToken (1, b"\x02\x72\x58\x36\xeb\xf3\xec\xdb\x1c\xdf\x1c\x7b\x02\xfc\xbb\xfa\xa2\x73\x6a\xf8", "BTCA", 8), # eth / BitAir (1, b"\x08\x86\x94\x9c\x1b\x8c\x41\x28\x60\xc4\x26\x4c\xeb\x80\x83\xd1\x36\x5e\x86\xcf", "BTCE", 8), # eth / EthereumBitcoin (1, b"\x5a\xcd\x19\xb9\xc9\x1e\x59\x6b\x1f\x06\x2f\x18\xe3\xd0\x2d\xa7\xed\x8d\x1e\x50", "BTCL", 8), # eth / BTC Lite From 13195831d5a1a25c1b58aee954b28665fbe941d3 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Fri, 26 Apr 2019 18:41:01 +0200 Subject: [PATCH 72/78] gitlab: build also legacy firmware with debug link --- .gitlab-ci.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1b4f2f1983..87cc868eec 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -82,7 +82,6 @@ build core unix: untracked: true expire_in: 1 day -# TODO: matrix: gcc vs clang build legacy firmware: stage: build script: @@ -91,6 +90,16 @@ build legacy firmware: - pipenv run make -C bootloader - pipenv run make -C demo +build legacy firmware debug: + stage: build + variables: + DEBUG_LINK: "1" + script: + - cd legacy + - pipenv run script/cibuild + - pipenv run make -C bootloader + - pipenv run make -C demo + build legacy emu: stage: build variables: @@ -139,7 +148,6 @@ test core unix monero: - cd core - pipenv run make test_emu_monero -# TODO: matrix: do we need to test gcc vs clang as well? or build is enough? # TODO: aren't some tests from .travis.yml missing? test legacy emu: stage: test From b76fb0905d726944621a01e57f97c36e4d098d42 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Fri, 26 Apr 2019 22:52:00 +0200 Subject: [PATCH 73/78] core: remove flamegraph --- core/emu.sh | 9 - core/vendor/flamegraph/flamegraph.pl | 1077 ------------------ core/vendor/flamegraph/stackcollapse-perf.pl | 257 ----- 3 files changed, 1343 deletions(-) delete mode 100755 core/vendor/flamegraph/flamegraph.pl delete mode 100755 core/vendor/flamegraph/stackcollapse-perf.pl diff --git a/core/emu.sh b/core/emu.sh index d6fca1ee44..eddb3000a3 100755 --- a/core/emu.sh +++ b/core/emu.sh @@ -5,7 +5,6 @@ source emu.config 2>/dev/null EXE=build/unix/micropython PYOPT="${PYOPT:-1}" MAIN="${MAIN:-${PWD}/src/main.py}" -BROWSER="${BROWSER:-chromium}" HEAPSIZE="${HEAPSIZE:-50M}" SOURCE_PY_DIR="${SOURCE_PY_DIR:-src}" @@ -33,14 +32,6 @@ case "$1" in kill $UPY_PID done ;; - "-p") - shift - ../$EXE $ARGS $* $MAIN & - perf record -F 100 -p $! -g -- sleep 600 - perf script > perf.trace - ../vendor/flamegraph/stackcollapse-perf.pl perf.trace | ../vendor/flamegraph/flamegraph.pl > perf.svg - $BROWSER perf.svg - ;; *) ../$EXE $ARGS $* $MAIN esac diff --git a/core/vendor/flamegraph/flamegraph.pl b/core/vendor/flamegraph/flamegraph.pl deleted file mode 100755 index 5286d9f859..0000000000 --- a/core/vendor/flamegraph/flamegraph.pl +++ /dev/null @@ -1,1077 +0,0 @@ -#!/usr/bin/perl -w -# -# flamegraph.pl flame stack grapher. -# -# This takes stack samples and renders a call graph, allowing hot functions -# and codepaths to be quickly identified. Stack samples can be generated using -# tools such as DTrace, perf, SystemTap, and Instruments. -# -# USAGE: ./flamegraph.pl [options] input.txt > graph.svg -# -# grep funcA input.txt | ./flamegraph.pl [options] > graph.svg -# -# Then open the resulting .svg in a web browser, for interactivity: mouse-over -# frames for info, click to zoom, and ctrl-F to search. -# -# Options are listed in the usage message (--help). -# -# The input is stack frames and sample counts formatted as single lines. Each -# frame in the stack is semicolon separated, with a space and count at the end -# of the line. These can be generated using DTrace with stackcollapse.pl, -# and other tools using the stackcollapse variants. -# -# An optional extra column of counts can be provided to generate a differential -# flame graph of the counts, colored red for more, and blue for less. This -# can be useful when using flame graphs for non-regression testing. -# See the header comment in the difffolded.pl program for instructions. -# -# The output graph shows relative presence of functions in stack samples. The -# ordering on the x-axis has no meaning; since the data is samples, time order -# of events is not known. The order used sorts function names alphabetically. -# -# While intended to process stack samples, this can also process stack traces. -# For example, tracing stacks for memory allocation, or resource usage. You -# can use --title to set the title to reflect the content, and --countname -# to change "samples" to "bytes" etc. -# -# There are a few different palettes, selectable using --color. By default, -# the colors are selected at random (except for differentials). Functions -# called "-" will be printed gray, which can be used for stack separators (eg, -# between user and kernel stacks). -# -# HISTORY -# -# This was inspired by Neelakanth Nadgir's excellent function_call_graph.rb -# program, which visualized function entry and return trace events. As Neel -# wrote: "The output displayed is inspired by Roch's CallStackAnalyzer which -# was in turn inspired by the work on vftrace by Jan Boerhout". See: -# https://blogs.oracle.com/realneel/entry/visualizing_callstacks_via_dtrace_and -# -# Copyright 2016 Netflix, Inc. -# Copyright 2011 Joyent, Inc. All rights reserved. -# Copyright 2011 Brendan Gregg. All rights reserved. -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at docs/cddl1.txt or -# http://opensource.org/licenses/CDDL-1.0. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at docs/cddl1.txt. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# 11-Oct-2014 Adrien Mahieux Added zoom. -# 21-Nov-2013 Shawn Sterling Added consistent palette file option -# 17-Mar-2013 Tim Bunce Added options and more tunables. -# 15-Dec-2011 Dave Pacheco Support for frames with whitespace. -# 10-Sep-2011 Brendan Gregg Created this. - -use strict; - -use Getopt::Long; - -# tunables -my $encoding; -my $fonttype = "Verdana"; -my $imagewidth = 1200; # max width, pixels -my $frameheight = 16; # max height is dynamic -my $fontsize = 12; # base text size -my $fontwidth = 0.59; # avg width relative to fontsize -my $minwidth = 0.1; # min function width, pixels -my $nametype = "Function:"; # what are the names in the data? -my $countname = "samples"; # what are the counts in the data? -my $colors = "hot"; # color theme -my $bgcolor1 = "#eeeeee"; # background color gradient start -my $bgcolor2 = "#eeeeb0"; # background color gradient stop -my $nameattrfile; # file holding function attributes -my $timemax; # (override the) sum of the counts -my $factor = 1; # factor to scale counts by -my $hash = 0; # color by function name -my $palette = 0; # if we use consistent palettes (default off) -my %palette_map; # palette map hash -my $pal_file = "palette.map"; # palette map file name -my $stackreverse = 0; # reverse stack order, switching merge end -my $inverted = 0; # icicle graph -my $negate = 0; # switch differential hues -my $titletext = ""; # centered heading -my $titledefault = "Flame Graph"; # overwritten by --title -my $titleinverted = "Icicle Graph"; # " " -my $searchcolor = "rgb(230,0,230)"; # color for search highlighting -my $help = 0; - -sub usage { - die < outfile.svg\n - --title # change title text - --width # width of image (default 1200) - --height # height of each frame (default 16) - --minwidth # omit smaller functions (default 0.1 pixels) - --fonttype # font type (default "Verdana") - --fontsize # font size (default 12) - --countname # count type label (default "samples") - --nametype # name type label (default "Function:") - --colors # set color palette. choices are: hot (default), mem, io, - # wakeup, chain, java, js, perl, red, green, blue, aqua, - # yellow, purple, orange - --hash # colors are keyed by function name hash - --cp # use consistent palette (palette.map) - --reverse # generate stack-reversed flame graph - --inverted # icicle graph - --negate # switch differential hues (blue<->red) - --help # this message - - eg, - $0 --title="Flame Graph: malloc()" trace.txt > graph.svg -USAGE_END -} - -GetOptions( - 'fonttype=s' => \$fonttype, - 'width=i' => \$imagewidth, - 'height=i' => \$frameheight, - 'encoding=s' => \$encoding, - 'fontsize=f' => \$fontsize, - 'fontwidth=f' => \$fontwidth, - 'minwidth=f' => \$minwidth, - 'title=s' => \$titletext, - 'nametype=s' => \$nametype, - 'countname=s' => \$countname, - 'nameattr=s' => \$nameattrfile, - 'total=s' => \$timemax, - 'factor=f' => \$factor, - 'colors=s' => \$colors, - 'hash' => \$hash, - 'cp' => \$palette, - 'reverse' => \$stackreverse, - 'inverted' => \$inverted, - 'negate' => \$negate, - 'help' => \$help, -) or usage(); -$help && usage(); - -# internals -my $ypad1 = $fontsize * 4; # pad top, include title -my $ypad2 = $fontsize * 2 + 10; # pad bottom, include labels -my $xpad = 10; # pad lefm and right -my $framepad = 1; # vertical padding for frames -my $depthmax = 0; -my %Events; -my %nameattr; - -if ($titletext eq "") { - unless ($inverted) { - $titletext = $titledefault; - } else { - $titletext = $titleinverted; - } -} - -if ($nameattrfile) { - # The name-attribute file format is a function name followed by a tab then - # a sequence of tab separated name=value pairs. - open my $attrfh, $nameattrfile or die "Can't read $nameattrfile: $!\n"; - while (<$attrfh>) { - chomp; - my ($funcname, $attrstr) = split /\t/, $_, 2; - die "Invalid format in $nameattrfile" unless defined $attrstr; - $nameattr{$funcname} = { map { split /=/, $_, 2 } split /\t/, $attrstr }; - } -} - -# background colors: -# - yellow gradient: default (hot, java, js, perl) -# - blue gradient: mem, chain -# - gray gradient: io, wakeup, flat colors (red, green, blue, ...) -if ($colors eq "mem" or $colors eq "chain") { - $bgcolor1 = "#eeeeee"; $bgcolor2 = "#e0e0ff"; -} -if ($colors =~ /^(io|wakeup|red|green|blue|aqua|yellow|purple|orange)$/) { - $bgcolor1 = "#f8f8f8"; $bgcolor2 = "#e8e8e8"; -} - -# SVG functions -{ package SVG; - sub new { - my $class = shift; - my $self = {}; - bless ($self, $class); - return $self; - } - - sub header { - my ($self, $w, $h) = @_; - my $enc_attr = ''; - if (defined $encoding) { - $enc_attr = qq{ encoding="$encoding"}; - } - $self->{svg} .= < - - - -SVG - } - - sub include { - my ($self, $content) = @_; - $self->{svg} .= $content; - } - - sub colorAllocate { - my ($self, $r, $g, $b) = @_; - return "rgb($r,$g,$b)"; - } - - sub group_start { - my ($self, $attr) = @_; - - my @g_attr = map { - exists $attr->{$_} ? sprintf(qq/$_="%s"/, $attr->{$_}) : () - } qw(class style onmouseover onmouseout onclick); - push @g_attr, $attr->{g_extra} if $attr->{g_extra}; - $self->{svg} .= sprintf qq/\n/, join(' ', @g_attr); - - $self->{svg} .= sprintf qq/%s<\/title>/, $attr->{title} - if $attr->{title}; # should be first element within g container - - if ($attr->{href}) { - my @a_attr; - push @a_attr, sprintf qq/xlink:href="%s"/, $attr->{href} if $attr->{href}; - # default target=_top else links will open within SVG - push @a_attr, sprintf qq/target="%s"/, $attr->{target} || "_top"; - push @a_attr, $attr->{a_extra} if $attr->{a_extra}; - $self->{svg} .= sprintf qq//, join(' ', @a_attr); - } - } - - sub group_end { - my ($self, $attr) = @_; - $self->{svg} .= qq/<\/a>\n/ if $attr->{href}; - $self->{svg} .= qq/<\/g>\n/; - } - - sub filledRectangle { - my ($self, $x1, $y1, $x2, $y2, $fill, $extra) = @_; - $x1 = sprintf "%0.1f", $x1; - $x2 = sprintf "%0.1f", $x2; - my $w = sprintf "%0.1f", $x2 - $x1; - my $h = sprintf "%0.1f", $y2 - $y1; - $extra = defined $extra ? $extra : ""; - $self->{svg} .= qq/\n/; - } - - sub stringTTF { - my ($self, $color, $font, $size, $angle, $x, $y, $str, $loc, $extra) = @_; - $x = sprintf "%0.2f", $x; - $loc = defined $loc ? $loc : "left"; - $extra = defined $extra ? $extra : ""; - $self->{svg} .= qq/$str<\/text>\n/; - } - - sub svg { - my $self = shift; - return "$self->{svg}\n"; - } - 1; -} - -sub namehash { - # Generate a vector hash for the name string, weighting early over - # later characters. We want to pick the same colors for function - # names across different flame graphs. - my $name = shift; - my $vector = 0; - my $weight = 1; - my $max = 1; - my $mod = 10; - # if module name present, trunc to 1st char - $name =~ s/.(.*?)`//; - foreach my $c (split //, $name) { - my $i = (ord $c) % $mod; - $vector += ($i / ($mod++ - 1)) * $weight; - $max += 1 * $weight; - $weight *= 0.70; - last if $mod > 12; - } - return (1 - $vector / $max) -} - -sub color { - my ($type, $hash, $name) = @_; - my ($v1, $v2, $v3); - - if ($hash) { - $v1 = namehash($name); - $v2 = $v3 = namehash(scalar reverse $name); - } else { - $v1 = rand(1); - $v2 = rand(1); - $v3 = rand(1); - } - - # theme palettes - if (defined $type and $type eq "hot") { - my $r = 205 + int(50 * $v3); - my $g = 0 + int(230 * $v1); - my $b = 0 + int(55 * $v2); - return "rgb($r,$g,$b)"; - } - if (defined $type and $type eq "mem") { - my $r = 0; - my $g = 190 + int(50 * $v2); - my $b = 0 + int(210 * $v1); - return "rgb($r,$g,$b)"; - } - if (defined $type and $type eq "io") { - my $r = 80 + int(60 * $v1); - my $g = $r; - my $b = 190 + int(55 * $v2); - return "rgb($r,$g,$b)"; - } - - # multi palettes - if (defined $type and $type eq "java") { - if ($name =~ m:(/|\.):) { # Java (match "/" in path) - $type = "green"; - $type = "aqua" if $name =~ m/_\[i\]/; #inline - } elsif ($name =~ /::/) { # C++ - $type = "yellow"; - } elsif ($name =~ m:_\[k\]:) { # kernel - $type = "orange" - } else { # system - $type = "red"; - } - # fall-through to color palettes - } - if (defined $type and $type eq "perl") { - if ($name =~ /::/) { # C++ - $type = "yellow"; - } elsif ($name =~ m:Perl: or $name =~ m:\.pl:) { # Perl - $type = "green"; - } elsif ($name =~ m:_\[k\]:) { # kernel - $type = "orange" - } else { # system - $type = "red"; - } - # fall-through to color palettes - } - if (defined $type and $type eq "js") { - if ($name =~ /::/) { # C++ - $type = "yellow"; - } elsif ($name =~ m:/:) { # JavaScript (match "/" in path) - $type = "green" - } elsif ($name =~ m/:/) { # JavaScript (match ":" in builtin) - $type = "aqua" - } elsif ($name =~ m/^ $/) { # Missing symbol - $type = "green" - } elsif ($name =~ m:_\[k\]:) { # kernel - $type = "orange" - } else { # system - $type = "red"; - } - # fall-through to color palettes - } - if (defined $type and $type eq "wakeup") { - $type = "aqua"; - # fall-through to color palettes - } - if (defined $type and $type eq "chain") { - if ($name =~ m:_\[w\]:) { # waker - $type = "aqua" - } else { # off-CPU - $type = "blue"; - } - # fall-through to color palettes - } - - # color palettes - if (defined $type and $type eq "red") { - my $r = 200 + int(55 * $v1); - my $x = 50 + int(80 * $v1); - return "rgb($r,$x,$x)"; - } - if (defined $type and $type eq "green") { - my $g = 200 + int(55 * $v1); - my $x = 50 + int(60 * $v1); - return "rgb($x,$g,$x)"; - } - if (defined $type and $type eq "blue") { - my $b = 205 + int(50 * $v1); - my $x = 80 + int(60 * $v1); - return "rgb($x,$x,$b)"; - } - if (defined $type and $type eq "yellow") { - my $x = 175 + int(55 * $v1); - my $b = 50 + int(20 * $v1); - return "rgb($x,$x,$b)"; - } - if (defined $type and $type eq "purple") { - my $x = 190 + int(65 * $v1); - my $g = 80 + int(60 * $v1); - return "rgb($x,$g,$x)"; - } - if (defined $type and $type eq "aqua") { - my $r = 50 + int(60 * $v1); - my $g = 165 + int(55 * $v1); - my $b = 165 + int(55 * $v1); - return "rgb($r,$g,$b)"; - } - if (defined $type and $type eq "orange") { - my $r = 190 + int(65 * $v1); - my $g = 90 + int(65 * $v1); - return "rgb($r,$g,0)"; - } - - return "rgb(0,0,0)"; -} - -sub color_scale { - my ($value, $max) = @_; - my ($r, $g, $b) = (255, 255, 255); - $value = -$value if $negate; - if ($value > 0) { - $g = $b = int(210 * ($max - $value) / $max); - } elsif ($value < 0) { - $r = $g = int(210 * ($max + $value) / $max); - } - return "rgb($r,$g,$b)"; -} - -sub color_map { - my ($colors, $func) = @_; - if (exists $palette_map{$func}) { - return $palette_map{$func}; - } else { - $palette_map{$func} = color($colors, $hash, $func); - return $palette_map{$func}; - } -} - -sub write_palette { - open(FILE, ">$pal_file"); - foreach my $key (sort keys %palette_map) { - print FILE $key."->".$palette_map{$key}."\n"; - } - close(FILE); -} - -sub read_palette { - if (-e $pal_file) { - open(FILE, $pal_file) or die "can't open file $pal_file: $!"; - while ( my $line = ) { - chomp($line); - (my $key, my $value) = split("->",$line); - $palette_map{$key}=$value; - } - close(FILE) - } -} - -my %Node; # Hash of merged frame data -my %Tmp; - -# flow() merges two stacks, storing the merged frames and value data in %Node. -sub flow { - my ($last, $this, $v, $d) = @_; - - my $len_a = @$last - 1; - my $len_b = @$this - 1; - - my $i = 0; - my $len_same; - for (; $i <= $len_a; $i++) { - last if $i > $len_b; - last if $last->[$i] ne $this->[$i]; - } - $len_same = $i; - - for ($i = $len_a; $i >= $len_same; $i--) { - my $k = "$last->[$i];$i"; - # a unique ID is constructed from "func;depth;etime"; - # func-depth isn't unique, it may be repeated later. - $Node{"$k;$v"}->{stime} = delete $Tmp{$k}->{stime}; - if (defined $Tmp{$k}->{delta}) { - $Node{"$k;$v"}->{delta} = delete $Tmp{$k}->{delta}; - } - delete $Tmp{$k}; - } - - for ($i = $len_same; $i <= $len_b; $i++) { - my $k = "$this->[$i];$i"; - $Tmp{$k}->{stime} = $v; - if (defined $d) { - $Tmp{$k}->{delta} += $i == $len_b ? $d : 0; - } - } - - return $this; -} - -# parse input -my @Data; -my $last = []; -my $time = 0; -my $delta = undef; -my $ignored = 0; -my $line; -my $maxdelta = 1; - -# reverse if needed -foreach (<>) { - chomp; - $line = $_; - if ($stackreverse) { - # there may be an extra samples column for differentials - # XXX todo: redo these REs as one. It's repeated below. - my($stack, $samples) = (/^(.*)\s+?(\d+(?:\.\d*)?)$/); - my $samples2 = undef; - if ($stack =~ /^(.*)\s+?(\d+(?:\.\d*)?)$/) { - $samples2 = $samples; - ($stack, $samples) = $stack =~ (/^(.*)\s+?(\d+(?:\.\d*)?)$/); - unshift @Data, join(";", reverse split(";", $stack)) . " $samples $samples2"; - } else { - unshift @Data, join(";", reverse split(";", $stack)) . " $samples"; - } - } else { - unshift @Data, $line; - } -} - -# process and merge frames -foreach (sort @Data) { - chomp; - # process: folded_stack count - # eg: func_a;func_b;func_c 31 - my ($stack, $samples) = (/^(.*)\s+?(\d+(?:\.\d*)?)$/); - unless (defined $samples and defined $stack) { - ++$ignored; - next; - } - - # there may be an extra samples column for differentials: - my $samples2 = undef; - if ($stack =~ /^(.*)\s+?(\d+(?:\.\d*)?)$/) { - $samples2 = $samples; - ($stack, $samples) = $stack =~ (/^(.*)\s+?(\d+(?:\.\d*)?)$/); - } - $delta = undef; - if (defined $samples2) { - $delta = $samples2 - $samples; - $maxdelta = abs($delta) if abs($delta) > $maxdelta; - } - - # clean up SVG breaking characters: - $stack =~ tr/<>/()/; - - # for chain graphs, annotate waker frames with "_[w]", for later - # coloring. This is a hack, but has a precedent ("_[k]" from perf). - if ($colors eq "chain") { - my @parts = split ";-;", $stack; - my @newparts = (); - $stack = shift @parts; - $stack .= ";-;"; - foreach my $part (@parts) { - $part =~ s/;/_[w];/g; - $part .= "_[w]"; - push @newparts, $part; - } - $stack .= join ";-;", @parts; - } - - # merge frames and populate %Node: - $last = flow($last, [ '', split ";", $stack ], $time, $delta); - - if (defined $samples2) { - $time += $samples2; - } else { - $time += $samples; - } -} -flow($last, [], $time, $delta); - -warn "Ignored $ignored lines with invalid format\n" if $ignored; -unless ($time) { - warn "ERROR: No stack counts found\n"; - my $im = SVG->new(); - # emit an error message SVG, for tools automating flamegraph use - my $imageheight = $fontsize * 5; - $im->header($imagewidth, $imageheight); - $im->stringTTF($im->colorAllocate(0, 0, 0), $fonttype, $fontsize + 2, - 0.0, int($imagewidth / 2), $fontsize * 2, - "ERROR: No valid input provided to flamegraph.pl.", "middle"); - print $im->svg; - exit 2; -} -if ($timemax and $timemax < $time) { - warn "Specified --total $timemax is less than actual total $time, so ignored\n" - if $timemax/$time > 0.02; # only warn is significant (e.g., not rounding etc) - undef $timemax; -} -$timemax ||= $time; - -my $widthpertime = ($imagewidth - 2 * $xpad) / $timemax; -my $minwidth_time = $minwidth / $widthpertime; - -# prune blocks that are too narrow and determine max depth -while (my ($id, $node) = each %Node) { - my ($func, $depth, $etime) = split ";", $id; - my $stime = $node->{stime}; - die "missing start for $id" if not defined $stime; - - if (($etime-$stime) < $minwidth_time) { - delete $Node{$id}; - next; - } - $depthmax = $depth if $depth > $depthmax; -} - -# draw canvas, and embed interactive JavaScript program -my $imageheight = ($depthmax * $frameheight) + $ypad1 + $ypad2; -my $im = SVG->new(); -$im->header($imagewidth, $imageheight); -my $inc = < - - - - - - - -INC -$im->include($inc); -$im->filledRectangle(0, 0, $imagewidth, $imageheight, 'url(#background)'); -my ($white, $black, $vvdgrey, $vdgrey) = ( - $im->colorAllocate(255, 255, 255), - $im->colorAllocate(0, 0, 0), - $im->colorAllocate(40, 40, 40), - $im->colorAllocate(160, 160, 160), - ); -$im->stringTTF($black, $fonttype, $fontsize + 5, 0.0, int($imagewidth / 2), $fontsize * 2, $titletext, "middle"); -$im->stringTTF($black, $fonttype, $fontsize, 0.0, $xpad, $imageheight - ($ypad2 / 2), " ", "", 'id="details"'); -$im->stringTTF($black, $fonttype, $fontsize, 0.0, $xpad, $fontsize * 2, - "Reset Zoom", "", 'id="unzoom" onclick="unzoom()" style="opacity:0.0;cursor:pointer"'); -$im->stringTTF($black, $fonttype, $fontsize, 0.0, $imagewidth - $xpad - 100, - $fontsize * 2, "Search", "", 'id="search" onmouseover="searchover()" onmouseout="searchout()" onclick="search_prompt()" style="opacity:0.1;cursor:pointer"'); -$im->stringTTF($black, $fonttype, $fontsize, 0.0, $imagewidth - $xpad - 100, $imageheight - ($ypad2 / 2), " ", "", 'id="matched"'); - -if ($palette) { - read_palette(); -} - -# draw frames -while (my ($id, $node) = each %Node) { - my ($func, $depth, $etime) = split ";", $id; - my $stime = $node->{stime}; - my $delta = $node->{delta}; - - $etime = $timemax if $func eq "" and $depth == 0; - - my $x1 = $xpad + $stime * $widthpertime; - my $x2 = $xpad + $etime * $widthpertime; - my ($y1, $y2); - unless ($inverted) { - $y1 = $imageheight - $ypad2 - ($depth + 1) * $frameheight + $framepad; - $y2 = $imageheight - $ypad2 - $depth * $frameheight; - } else { - $y1 = $ypad1 + $depth * $frameheight; - $y2 = $ypad1 + ($depth + 1) * $frameheight - $framepad; - } - - my $samples = sprintf "%.0f", ($etime - $stime) * $factor; - (my $samples_txt = $samples) # add commas per perlfaq5 - =~ s/(^[-+]?\d+?(?=(?>(?:\d{3})+)(?!\d))|\G\d{3}(?=\d))/$1,/g; - - my $info; - if ($func eq "" and $depth == 0) { - $info = "all ($samples_txt $countname, 100%)"; - } else { - my $pct = sprintf "%.2f", ((100 * $samples) / ($timemax * $factor)); - my $escaped_func = $func; - $escaped_func =~ s/&/&/g; - $escaped_func =~ s//>/g; - $escaped_func =~ s/"/"/g; - $escaped_func =~ s/_\[[kwi]\]$//; # strip any annotation - unless (defined $delta) { - $info = "$escaped_func ($samples_txt $countname, $pct%)"; - } else { - my $d = $negate ? -$delta : $delta; - my $deltapct = sprintf "%.2f", ((100 * $d) / ($timemax * $factor)); - $deltapct = $d > 0 ? "+$deltapct" : $deltapct; - $info = "$escaped_func ($samples_txt $countname, $pct%; $deltapct%)"; - } - } - - my $nameattr = { %{ $nameattr{$func}||{} } }; # shallow clone - $nameattr->{class} ||= "func_g"; - $nameattr->{onmouseover} ||= "s(this)"; - $nameattr->{onmouseout} ||= "c()"; - $nameattr->{onclick} ||= "zoom(this)"; - $nameattr->{title} ||= $info; - $im->group_start($nameattr); - - my $color; - if ($func eq "-") { - $color = $vdgrey; - } elsif (defined $delta) { - $color = color_scale($delta, $maxdelta); - } elsif ($palette) { - $color = color_map($colors, $func); - } else { - $color = color($colors, $hash, $func); - } - $im->filledRectangle($x1, $y1, $x2, $y2, $color, 'rx="2" ry="2"'); - - my $chars = int( ($x2 - $x1) / ($fontsize * $fontwidth)); - my $text = ""; - if ($chars >= 3) { # room for one char plus two dots - $func =~ s/_\[[kwi]\]$//; # strip any annotation - $text = substr $func, 0, $chars; - substr($text, -2, 2) = ".." if $chars < length $func; - $text =~ s/&/&/g; - $text =~ s//>/g; - } - $im->stringTTF($black, $fonttype, $fontsize, 0.0, $x1 + 3, 3 + ($y1 + $y2) / 2, $text, ""); - - $im->group_end($nameattr); -} - -print $im->svg; - -if ($palette) { - write_palette(); -} - -# vim: ts=8 sts=8 sw=8 noexpandtab diff --git a/core/vendor/flamegraph/stackcollapse-perf.pl b/core/vendor/flamegraph/stackcollapse-perf.pl deleted file mode 100755 index dfd6da47bc..0000000000 --- a/core/vendor/flamegraph/stackcollapse-perf.pl +++ /dev/null @@ -1,257 +0,0 @@ -#!/usr/bin/perl -w -# -# stackcolllapse-perf.pl collapse perf samples into single lines. -# -# Parses a list of multiline stacks generated by "perf script", and -# outputs a semicolon separated stack followed by a space and a count. -# If memory addresses (+0xd) are present, they are stripped, and resulting -# identical stacks are colased with their counts summed. -# -# USAGE: ./stackcollapse-perf.pl [options] infile > outfile -# -# Run "./stackcollapse-perf.pl -h" to list options. -# -# Example input: -# -# swapper 0 [000] 158665.570607: cpu-clock: -# ffffffff8103ce3b native_safe_halt ([kernel.kallsyms]) -# ffffffff8101c6a3 default_idle ([kernel.kallsyms]) -# ffffffff81013236 cpu_idle ([kernel.kallsyms]) -# ffffffff815bf03e rest_init ([kernel.kallsyms]) -# ffffffff81aebbfe start_kernel ([kernel.kallsyms].init.text) -# [...] -# -# Example output: -# -# swapper;start_kernel;rest_init;cpu_idle;default_idle;native_safe_halt 1 -# -# Input may be created and processed using: -# -# perf record -a -g -F 997 sleep 60 -# perf script | ./stackcollapse-perf.pl > out.stacks-folded -# -# The output of "perf script" should include stack traces. If these are missing -# for you, try manually selecting the perf script output; eg: -# -# perf script -f comm,pid,tid,cpu,time,event,ip,sym,dso,trace | ... -# -# This is also required for the --pid or --tid options, so that the output has -# both the PID and TID. -# -# Copyright 2012 Joyent, Inc. All rights reserved. -# Copyright 2012 Brendan Gregg. All rights reserved. -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at docs/cddl1.txt or -# http://opensource.org/licenses/CDDL-1.0. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at docs/cddl1.txt. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# 02-Mar-2012 Brendan Gregg Created this. -# 02-Jul-2014 " " Added process name to stacks. - -use strict; -use Getopt::Long; - -my %collapsed; - -sub remember_stack { - my ($stack, $count) = @_; - $collapsed{$stack} += $count; -} -my $annotate_kernel = 0; # put an annotation on kernel function -my $include_pname = 1; # include process names in stacks -my $include_pid = 0; # include process ID with process name -my $include_tid = 0; # include process & thread ID with process name -my $tidy_java = 1; # condense Java signatures -my $tidy_generic = 1; # clean up function names a little -my $target_pname; # target process name from perf invocation - -my $show_inline = 0; -my $show_context = 0; -GetOptions('inline' => \$show_inline, - 'context' => \$show_context, - 'pid' => \$include_pid, - 'kernel' => \$annotate_kernel, - 'tid' => \$include_tid) -or die < outfile\n - --pid # include PID with process names [1] - --tid # include TID and PID with process names [1] - --inline # un-inline using addr2line - --kernel # annotate kernel functions with a _[k] - --context # include source context from addr2line\n -[1] perf script must emit both PID and TIDs for these to work; eg: - perf script -f comm,pid,tid,cpu,time,event,ip,sym,dso,trace -USAGE_END - -# for the --inline option -sub inline { - my ($pc, $mod) = @_; - - # capture addr2line output - my $a2l_output = `addr2line -a $pc -e $mod -i -f -s -C`; - - # remove first line - $a2l_output =~ s/^(.*\n){1}//; - - my @fullfunc; - my $one_item = ""; - for (split /^/, $a2l_output) { - chomp $_; - - # remove discriminator info if exists - $_ =~ s/ \(discriminator \S+\)//; - - if ($one_item eq "") { - $one_item = $_; - } else { - if ($show_context == 1) { - unshift @fullfunc, $one_item . ":$_"; - } else { - unshift @fullfunc, $one_item; - } - $one_item = ""; - } - } - - return join(";", @fullfunc); -} - -my @stack; -my $pname; - -# -# Main loop -# -while (defined($_ = <>)) { - - # find the name of the process launched by perf, by stepping backwards - # over the args to find the first non-option (no dash): - if (/^# cmdline/) { - my @args = split ' ', $_; - foreach my $arg (reverse @args) { - if ($arg !~ /^-/) { - $target_pname = $arg; - $target_pname =~ s:.*/::; # strip pathname - last; - } - } - } - - # skip remaining comments - next if m/^#/; - chomp; - - # end of stack. save cached data. - if (m/^$/) { - if ($include_pname) { - if (defined $pname) { - unshift @stack, $pname; - } else { - unshift @stack, ""; - } - } - remember_stack(join(";", @stack), 1) if @stack; - undef @stack; - undef $pname; - next; - } - - # event record start - if (/^(\S+\s*?\S*?)\s+(\d+)\s/) { - # default "perf script" output has TID but not PID - # eg, "java 25607 4794564.109216: cycles:" - # eg, "java 12688 [002] 6544038.708352: cpu-clock:" - # eg, "V8 WorkerThread 25607 4794564.109216: cycles:" - # other combinations possible - if ($include_tid) { - $pname = "$1-?/$2"; - } elsif ($include_pid) { - $pname = "$1-?"; - } else { - $pname = $1; - } - $pname =~ tr/ /_/; - } elsif (/^(\S+\s*?\S*?)\s+(\d+)\/(\d+)/) { - # eg, "java 24636/25607 [000] 4794564.109216: cycles:" - # eg, "java 12688/12764 6544038.708352: cpu-clock:" - # eg, "V8 WorkerThread 24636/25607 [000] 94564.109216: cycles:" - # other combinations possible - if ($include_tid) { - $pname = "$1-$2/$3"; - } elsif ($include_pid) { - $pname = "$1-$2"; - } else { - $pname = $1; - } - $pname =~ tr/ /_/; - - # stack line - } elsif (/^\s*(\w+)\s*(.+) \((\S*)\)/) { - my ($pc, $rawfunc, $mod) = ($1, $2, $3); - $rawfunc.="_[k]" if ($annotate_kernel == 1 && $mod =~ m/kernel\./); - if ($show_inline == 1 && $mod !~ m/(perf-\d+.map|kernel\.|\[[^\]]+\])/) { - unshift @stack, inline($pc, $mod); - next; - } - - next if $rawfunc =~ /^\(/; # skip process names - - my @inline; - for (split /\->/, $rawfunc) { - my $func = $_; - - if ($tidy_generic) { - $func =~ s/;/:/g; - $func =~ tr/<>//d; - if ($func !~ m/\.\(.*\)\./) { - # This doesn't look like a Go method name (such as - # "net/http.(*Client).Do"), so everything after the first open - # paren is just noise. - $func =~ s/\(.*//; - } - # now tidy this horrible thing: - # 13a80b608e0a RegExp:[&<>\"\'] (/tmp/perf-7539.map) - $func =~ tr/"\'//d; - # fall through to $tidy_java - } - - if ($tidy_java and $pname eq "java") { - # along with $tidy_generic, converts the following: - # Lorg/mozilla/javascript/ContextFactory;.call(Lorg/mozilla/javascript/ContextAction;)Ljava/lang/Object; - # Lorg/mozilla/javascript/ContextFactory;.call(Lorg/mozilla/javascript/C - # Lorg/mozilla/javascript/MemberBox;.(Ljava/lang/reflect/Method;)V - # into: - # org/mozilla/javascript/ContextFactory:.call - # org/mozilla/javascript/ContextFactory:.call - # org/mozilla/javascript/MemberBox:.init - $func =~ s/^L// if $func =~ m:/:; - } - - $func .= "_[i]" if scalar(@inline) > 0; #inlined - push @inline, $func; - } - - unshift @stack, @inline; - } else { - warn "Unrecognized line: $_"; - } -} - -foreach my $k (sort { $a cmp $b } keys %collapsed) { - print "$k $collapsed{$k}\n"; -} From 236ca1ae830d3c0b7fd9755a1286141484e0b567 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Sat, 27 Apr 2019 16:15:35 +0200 Subject: [PATCH 74/78] legacy: enable Bitcoin only firmware --- .gitlab-ci.yml | 10 +++++++ common/defs/support.json | 4 +-- common/tools/cointool.py | 19 ++++++++++-- legacy/Makefile.include | 8 +++++ legacy/firmware/Makefile | 27 +++++++++++++---- legacy/firmware/fsm.c | 18 ++++++++---- legacy/firmware/fsm_msg_common.h | 2 ++ legacy/firmware/layout2.c | 8 +++++ legacy/firmware/protob/Makefile | 8 ++++- legacy/firmware/usb.c | 50 +++++++++++++++++++++++++------- 10 files changed, 127 insertions(+), 27 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 87cc868eec..793a997aa5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -100,6 +100,16 @@ build legacy firmware debug: - pipenv run make -C bootloader - pipenv run make -C demo +build legacy firmware bitcoinonly: + stage: build + variables: + BITCOIN_ONLY: "1" + script: + - cd legacy + - pipenv run script/cibuild + - pipenv run make -C bootloader + - pipenv run make -C demo + build legacy emu: stage: build variables: diff --git a/common/defs/support.json b/common/defs/support.json index ec5beba310..65de9178b5 100644 --- a/common/defs/support.json +++ b/common/defs/support.json @@ -127,7 +127,7 @@ "bitcoin:POLIS": "soon", "bitcoin:PTC": "1.7.1", "bitcoin:QTUM": "1.8.1", - "bitcoin:REGTEST": "soon", + "bitcoin:REGTEST": "1.8.2", "bitcoin:RVN": "1.7.2", "bitcoin:SMART": "1.7.1", "bitcoin:TAZ": "1.6.2", @@ -1474,7 +1474,7 @@ "bitcoin:POLIS": "soon", "bitcoin:PTC": "2.0.8", "bitcoin:QTUM": "2.1.1", - "bitcoin:REGTEST": "soon", + "bitcoin:REGTEST": "2.1.2", "bitcoin:RVN": "2.0.10", "bitcoin:SMART": "2.0.8", "bitcoin:TAZ": "2.0.7", diff --git a/common/tools/cointool.py b/common/tools/cointool.py index 98ba220767..706d1bf724 100755 --- a/common/tools/cointool.py +++ b/common/tools/cointool.py @@ -209,7 +209,7 @@ def check_btc(coins): for coin in bucket: name = coin["name"] prefix = "" - if name.endswith("Testnet"): + if name.endswith("Testnet") or name.endswith("Regtest"): color = "green" elif name == "Bitcoin": color = "red" @@ -232,7 +232,12 @@ def check_btc(coins): """ failed = False for key, bucket in buckets.items(): - mainnets = [c for c in bucket if not c["name"].endswith("Testnet")] + mainnets = [ + c + for c in bucket + if not c["name"].endswith("Testnet") + and not c["name"].endswith("Regtest") + ] have_bitcoin = False for coin in mainnets: @@ -778,8 +783,9 @@ def coindefs(outfile): @click.argument("paths", metavar="[path]...", nargs=-1) @click.option("-o", "--outfile", type=click.File("w"), help="Alternate output file") @click.option("-v", "--verbose", is_flag=True, help="Print rendered file names") +@click.option("-b", "--bitcoin-only", is_flag=True, help="Accept only Bitcoin coins") # fmt: on -def render(paths, outfile, verbose): +def render(paths, outfile, verbose, bitcoin_only): """Generate source code from Mako templates. For every "foo.bar.mako" filename passed, runs the template and @@ -800,6 +806,13 @@ def render(paths, outfile, verbose): defs = coin_info.coin_info() support_info = coin_info.support_info(defs) + if bitcoin_only: + defs["bitcoin"] = [ + x + for x in defs["bitcoin"] + if x["coin_name"] in ("Bitcoin", "Testnet", "Regtest") + ] + # munch dicts - make them attribute-accessible for key, value in defs.items(): defs[key] = [Munch(coin) for coin in value] diff --git a/legacy/Makefile.include b/legacy/Makefile.include index 029dddae7d..c2a96e254a 100644 --- a/legacy/Makefile.include +++ b/legacy/Makefile.include @@ -132,6 +132,14 @@ LDLIBS += -lopencm3_stm32f2 LIBDEPS += $(TOOLCHAIN_DIR)/lib/libopencm3_stm32f2.a endif +ifeq ($(BITCOIN_ONLY), 1) +CFLAGS += -DBITCOIN_ONLY=1 +CFLAGS += -DU2F_ENABLED=0 +else +CFLAGS += -DBITCOIN_ONLY=0 +CFLAGS += -DU2F_ENABLED=1 +endif + ifeq ($(MEMORY_PROTECT), 0) CFLAGS += -DMEMORY_PROTECT=0 $(info MEMORY_PROTECT=0) diff --git a/legacy/firmware/Makefile b/legacy/firmware/Makefile index 9e34ef8434..41ac8fb99a 100644 --- a/legacy/firmware/Makefile +++ b/legacy/firmware/Makefile @@ -11,7 +11,6 @@ OBJS += otp.o OBJS += header.o endif -OBJS += u2f.o OBJS += messages.o OBJS += config.o OBJS += trezor.o @@ -26,12 +25,19 @@ OBJS += recovery.o OBJS += reset.o OBJS += signing.o OBJS += crypto.o + +ifeq ($(U2F_ENABLED),1) +OBJS += u2f.o +endif + +ifeq ($(BITCOIN_ONLY),0) OBJS += ethereum.o OBJS += ethereum_tokens.o +OBJS += lisk.o OBJS += nem2.o OBJS += nem_mosaics.o OBJS += stellar.o -OBJS += lisk.o +endif OBJS += debug.o @@ -98,11 +104,13 @@ OBJS += protob/messages-bitcoin.pb.o OBJS += protob/messages-common.pb.o OBJS += protob/messages-crypto.pb.o OBJS += protob/messages-debug.pb.o -OBJS += protob/messages-ethereum.pb.o OBJS += protob/messages-management.pb.o +ifeq ($(BITCOIN_ONLY),0) +OBJS += protob/messages-ethereum.pb.o +OBJS += protob/messages-lisk.pb.o OBJS += protob/messages-nem.pb.o OBJS += protob/messages-stellar.pb.o -OBJS += protob/messages-lisk.pb.o +endif OPTFLAGS ?= -Os @@ -122,13 +130,20 @@ CFLAGS += -I../vendor/nanopb -Iprotob -DPB_FIELD_16BIT=1 CFLAGS += -DDEBUG_LINK=$(DEBUG_LINK) CFLAGS += -DDEBUG_LOG=$(DEBUG_LOG) CFLAGS += -DSCM_REVISION='"$(shell git rev-parse HEAD | sed 's:\(..\):\\x\1:g')"' +CFLAGS += -DUSE_MONERO=0 +ifeq ($(BITCOIN_ONLY),0) CFLAGS += -DUSE_ETHEREUM=1 CFLAGS += -DUSE_NEM=1 -CFLAGS += -DUSE_MONERO=0 +MAKO_RENDER_FLAG = +else +CFLAGS += -DUSE_ETHEREUM=0 +CFLAGS += -DUSE_NEM=0 +MAKO_RENDER_FLAG = --bitcoin-only +endif %:: %.mako defs @printf " MAKO $@\n" - $(Q)$(PYTHON) ../vendor/trezor-common/tools/cointool.py render $@.mako + $(Q)$(PYTHON) ../vendor/trezor-common/tools/cointool.py render $(MAKO_RENDER_FLAG) $@.mako bl_data.h: bl_data.py bootloader.dat @printf " PYTHON bl_data.py\n" diff --git a/legacy/firmware/fsm.c b/legacy/firmware/fsm.c index 59f93774f3..59b98a3797 100644 --- a/legacy/firmware/fsm.c +++ b/legacy/firmware/fsm.c @@ -30,18 +30,14 @@ #include "curves.h" #include "debug.h" #include "ecdsa.h" -#include "ethereum.h" #include "fsm.h" #include "gettext.h" #include "hmac.h" #include "layout2.h" -#include "lisk.h" #include "memory.h" #include "memzero.h" #include "messages.h" #include "messages.pb.h" -#include "nem.h" -#include "nem2.h" #include "oled.h" #include "pinmatrix.h" #include "protect.h" @@ -51,13 +47,20 @@ #include "rng.h" #include "secp256k1.h" #include "signing.h" -#include "stellar.h" #include "supervise.h" #include "transaction.h" #include "trezor.h" #include "usb.h" #include "util.h" +#if !BITCOIN_ONLY +#include "ethereum.h" +#include "lisk.h" +#include "nem.h" +#include "nem2.h" +#include "stellar.h" +#endif + // message methods static uint8_t msg_resp[MSG_OUT_SIZE] __attribute__((aligned)); @@ -254,7 +257,12 @@ static bool fsm_layoutAddress(const char *address, const char *desc, #include "fsm_msg_common.h" #include "fsm_msg_crypto.h" #include "fsm_msg_debug.h" + +#if !BITCOIN_ONLY + #include "fsm_msg_ethereum.h" #include "fsm_msg_lisk.h" #include "fsm_msg_nem.h" #include "fsm_msg_stellar.h" + +#endif diff --git a/legacy/firmware/fsm_msg_common.h b/legacy/firmware/fsm_msg_common.h index e8c4c447d3..44dece9919 100644 --- a/legacy/firmware/fsm_msg_common.h +++ b/legacy/firmware/fsm_msg_common.h @@ -273,7 +273,9 @@ void fsm_msgCancel(const Cancel *msg) { (void)msg; recovery_abort(); signing_abort(); +#if !BITCOIN_ONLY ethereum_signing_abort(); +#endif fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); } diff --git a/legacy/firmware/layout2.c b/legacy/firmware/layout2.c index 61510249e8..9b7d961b18 100644 --- a/legacy/firmware/layout2.c +++ b/legacy/firmware/layout2.c @@ -745,11 +745,17 @@ void layoutDecryptIdentity(const IdentityType *identity) { row_user[0] ? row_user : NULL, NULL, NULL, NULL); } +#if U2F_ENABLED + void layoutU2FDialog(const char *verb, const char *appname) { layoutDialog(&bmp_webauthn, NULL, verb, NULL, verb, _("U2F security key?"), NULL, appname, NULL, NULL); } +#endif + +#if !BITCOIN_ONLY + void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *address) { static char first_third[NEM_ADDRESS_SIZE / 3 + 1]; @@ -908,6 +914,8 @@ void layoutNEMLevy(const NEMMosaicDefinition *definition, uint8_t network) { } } +#endif + static inline bool is_slip18(const uint32_t *address_n, size_t address_n_count) { return address_n_count == 2 && address_n[0] == (0x80000000 + 10018) && diff --git a/legacy/firmware/protob/Makefile b/legacy/firmware/protob/Makefile index e456572d5f..e1f3daced0 100644 --- a/legacy/firmware/protob/Makefile +++ b/legacy/firmware/protob/Makefile @@ -2,6 +2,12 @@ ifneq ($(V),1) Q := @ endif +ifeq ($(BITCOIN_ONLY), 1) +SKIPPED_MESSAGES := Cardano Tezos Ripple Monero DebugMonero Ontology Tron Eos Binance Ethereum Lisk NEM Stellar +else +SKIPPED_MESSAGES := Cardano Tezos Ripple Monero DebugMonero Ontology Tron Eos Binance +endif + all: messages_map.h messages_map_limits.h messages-bitcoin.pb.c messages-common.pb.c messages-crypto.pb.c messages-debug.pb.c messages-ethereum.pb.c messages-management.pb.c messages-nem.pb.c messages.pb.c messages-stellar.pb.c messages-lisk.pb.c messages_nem_pb2.py PYTHON ?= python @@ -26,7 +32,7 @@ messages_%_pb2.py: messages-%.proto $(Q)protoc -I/usr/include -I. $< --python_out=. messages_map.h messages_map_limits.h: messages_map.py messages_pb2.py - $(Q)$(PYTHON) $< Cardano Tezos Ripple Monero DebugMonero Ontology Tron Eos Binance + $(Q)$(PYTHON) $< ${SKIPPED_MESSAGES} clean: rm -f *.pb *.o *.d *.pb.c *.pb.h *_pb2.py messages_map.h messages_map_limits.h diff --git a/legacy/firmware/usb.c b/legacy/firmware/usb.c index c55947e6c1..a5b990d012 100644 --- a/legacy/firmware/usb.c +++ b/legacy/firmware/usb.c @@ -25,7 +25,9 @@ #include "messages.h" #include "timer.h" #include "trezor.h" +#if U2F_ENABLED #include "u2f.h" +#endif #include "usb.h" #include "util.h" @@ -36,11 +38,19 @@ #define USB_INTERFACE_INDEX_MAIN 0 #if DEBUG_LINK #define USB_INTERFACE_INDEX_DEBUG 1 +#if U2F_ENABLED #define USB_INTERFACE_INDEX_U2F 2 #define USB_INTERFACE_COUNT 3 #else +#define USB_INTERFACE_COUNT 2 +#endif +#else +#if U2F_ENABLED #define USB_INTERFACE_INDEX_U2F 1 #define USB_INTERFACE_COUNT 2 +#else +#define USB_INTERFACE_COUNT 1 +#endif #endif #define ENDPOINT_ADDRESS_MAIN_IN (0x81) @@ -49,8 +59,10 @@ #define ENDPOINT_ADDRESS_DEBUG_IN (0x82) #define ENDPOINT_ADDRESS_DEBUG_OUT (0x02) #endif +#if U2F_ENABLED #define ENDPOINT_ADDRESS_U2F_IN (0x83) #define ENDPOINT_ADDRESS_U2F_OUT (0x03) +#endif #define USB_STRINGS \ X(MANUFACTURER, "SatoshiLabs") \ @@ -88,6 +100,8 @@ static const struct usb_device_descriptor dev_descr = { .bNumConfigurations = 1, }; +#if U2F_ENABLED + static const uint8_t hid_report_descriptor_u2f[] = { 0x06, 0xd0, 0xf1, // USAGE_PAGE (FIDO Alliance) 0x09, 0x01, // USAGE (U2F HID Authenticator Device) @@ -160,7 +174,10 @@ static const struct usb_interface_descriptor hid_iface_u2f[] = {{ .extralen = sizeof(hid_function_u2f), }}; +#endif + #if DEBUG_LINK + static const struct usb_endpoint_descriptor webusb_endpoints_debug[2] = { { .bLength = USB_DT_ENDPOINT_SIZE, @@ -241,10 +258,12 @@ static const struct usb_interface ifaces[] = { .num_altsetting = 1, .altsetting = webusb_iface_debug, #endif +#if U2F_ENABLED }, { .num_altsetting = 1, .altsetting = hid_iface_u2f, +#endif }}; static const struct usb_config_descriptor config = { @@ -259,6 +278,8 @@ static const struct usb_config_descriptor config = { .interface = ifaces, }; +#if U2F_ENABLED + static enum usbd_request_return_codes hid_control_request( usbd_device *dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, usbd_control_complete_callback *complete) { @@ -277,6 +298,17 @@ static enum usbd_request_return_codes hid_control_request( return 1; } +static void u2f_rx_callback(usbd_device *dev, uint8_t ep) { + (void)ep; + static CONFIDENTIAL uint8_t buf[64] __attribute__((aligned(4))); + + debugLog(0, "", "u2f_rx_callback"); + if (usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_U2F_OUT, buf, 64) != 64) return; + u2fhid_read(tiny, (const U2FHID_FRAME *)(void *)buf); +} + +#endif + static volatile char tiny = 0; static void main_rx_callback(usbd_device *dev, uint8_t ep) { @@ -292,16 +324,8 @@ static void main_rx_callback(usbd_device *dev, uint8_t ep) { } } -static void u2f_rx_callback(usbd_device *dev, uint8_t ep) { - (void)ep; - static CONFIDENTIAL uint8_t buf[64] __attribute__((aligned(4))); - - debugLog(0, "", "u2f_rx_callback"); - if (usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_U2F_OUT, buf, 64) != 64) return; - u2fhid_read(tiny, (const U2FHID_FRAME *)(void *)buf); -} - #if DEBUG_LINK + static void debug_rx_callback(usbd_device *dev, uint8_t ep) { (void)ep; static uint8_t buf[64] __attribute__((aligned(4))); @@ -314,6 +338,7 @@ static void debug_rx_callback(usbd_device *dev, uint8_t ep) { msg_read_tiny(buf, 64); } } + #endif static void set_config(usbd_device *dev, uint16_t wValue) { @@ -323,20 +348,23 @@ static void set_config(usbd_device *dev, uint16_t wValue) { 0); usbd_ep_setup(dev, ENDPOINT_ADDRESS_MAIN_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, main_rx_callback); +#if U2F_ENABLED usbd_ep_setup(dev, ENDPOINT_ADDRESS_U2F_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0); usbd_ep_setup(dev, ENDPOINT_ADDRESS_U2F_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, u2f_rx_callback); +#endif #if DEBUG_LINK usbd_ep_setup(dev, ENDPOINT_ADDRESS_DEBUG_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0); usbd_ep_setup(dev, ENDPOINT_ADDRESS_DEBUG_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, debug_rx_callback); #endif - +#if U2F_ENABLED usbd_register_control_callback( dev, USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_INTERFACE, USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, hid_control_request); +#endif } static usbd_device *usbd_dev = NULL; @@ -381,12 +409,14 @@ void usbPoll(void) { 64) { } } +#if U2F_ENABLED data = u2f_out_data(); if (data) { while (usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_U2F_IN, data, 64) != 64) { } } +#endif #if DEBUG_LINK // write pending debug data data = msg_debug_out_data(); From 32c5f58782685685c4815a42827e940882650470 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Sat, 27 Apr 2019 16:23:17 +0200 Subject: [PATCH 75/78] core: regenerate templates --- core/src/apps/common/coininfo.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/src/apps/common/coininfo.py b/core/src/apps/common/coininfo.py index aafca7a645..a695b1eddb 100644 --- a/core/src/apps/common/coininfo.py +++ b/core/src/apps/common/coininfo.py @@ -984,6 +984,26 @@ COINS = [ decred=False, curve_name='secp256k1', ), + CoinInfo( + coin_name="Regtest", + coin_shortcut="REGTEST", + address_type=111, + address_type_p2sh=196, + maxfee_kb=10000000, + signed_message_header="Bitcoin Signed Message:\n", + xpub_magic=0x043587cf, + xpub_magic_segwit_p2sh=0x044a5262, + xpub_magic_segwit_native=0x045f1cf6, + bech32_prefix="bcrt", + cashaddr_prefix=None, + slip44=1, + segwit=True, + fork_id=None, + force_bip143=False, + bip115=False, + decred=False, + curve_name='secp256k1', + ), CoinInfo( coin_name="SmartCash", coin_shortcut="SMART", From 597b9fabd1f4c35c35bd24dbf588048ac5cd4cac Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Sat, 27 Apr 2019 16:38:08 +0200 Subject: [PATCH 76/78] legacy: fix build parameters --- legacy/firmware/Makefile | 8 ++++---- legacy/firmware/usb.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/legacy/firmware/Makefile b/legacy/firmware/Makefile index 41ac8fb99a..85e759fea1 100644 --- a/legacy/firmware/Makefile +++ b/legacy/firmware/Makefile @@ -26,11 +26,11 @@ OBJS += reset.o OBJS += signing.o OBJS += crypto.o -ifeq ($(U2F_ENABLED),1) +ifneq ($(U2F_ENABLED),0) OBJS += u2f.o endif -ifeq ($(BITCOIN_ONLY),0) +ifneq ($(BITCOIN_ONLY),1) OBJS += ethereum.o OBJS += ethereum_tokens.o OBJS += lisk.o @@ -105,7 +105,7 @@ OBJS += protob/messages-common.pb.o OBJS += protob/messages-crypto.pb.o OBJS += protob/messages-debug.pb.o OBJS += protob/messages-management.pb.o -ifeq ($(BITCOIN_ONLY),0) +ifneq ($(BITCOIN_ONLY),1) OBJS += protob/messages-ethereum.pb.o OBJS += protob/messages-lisk.pb.o OBJS += protob/messages-nem.pb.o @@ -131,7 +131,7 @@ CFLAGS += -DDEBUG_LINK=$(DEBUG_LINK) CFLAGS += -DDEBUG_LOG=$(DEBUG_LOG) CFLAGS += -DSCM_REVISION='"$(shell git rev-parse HEAD | sed 's:\(..\):\\x\1:g')"' CFLAGS += -DUSE_MONERO=0 -ifeq ($(BITCOIN_ONLY),0) +ifneq ($(BITCOIN_ONLY),1) CFLAGS += -DUSE_ETHEREUM=1 CFLAGS += -DUSE_NEM=1 MAKO_RENDER_FLAG = diff --git a/legacy/firmware/usb.c b/legacy/firmware/usb.c index a5b990d012..45ecb9ac21 100644 --- a/legacy/firmware/usb.c +++ b/legacy/firmware/usb.c @@ -278,6 +278,8 @@ static const struct usb_config_descriptor config = { .interface = ifaces, }; +static volatile char tiny = 0; + #if U2F_ENABLED static enum usbd_request_return_codes hid_control_request( @@ -309,8 +311,6 @@ static void u2f_rx_callback(usbd_device *dev, uint8_t ep) { #endif -static volatile char tiny = 0; - static void main_rx_callback(usbd_device *dev, uint8_t ep) { (void)ep; static CONFIDENTIAL uint8_t buf[64] __attribute__((aligned(4))); From 7c58d7d24ed70b5839da27bf875763d2099d78db Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Sat, 27 Apr 2019 19:16:50 +0200 Subject: [PATCH 77/78] ci: split gitlab configuration files and store them in a 'ci' folder The core/build-docker.sh si broken, but so it was already in master and is tracked to be fixed in #108. --- .gitlab-ci.yml | 176 ++---------------------------------- Dockerfile => ci/Dockerfile | 0 ci/core.yml | 52 +++++++++++ ci/crypto.yml | 23 +++++ ci/environment.yml | 15 +++ ci/legacy.yml | 54 +++++++++++ ci/prebuild.yml | 18 ++++ ci/storage.yml | 9 ++ core/build-docker.sh | 2 +- legacy/build.sh | 2 +- 10 files changed, 181 insertions(+), 170 deletions(-) rename Dockerfile => ci/Dockerfile (100%) create mode 100644 ci/core.yml create mode 100644 ci/crypto.yml create mode 100644 ci/environment.yml create mode 100644 ci/legacy.yml create mode 100644 ci/prebuild.yml create mode 100644 ci/storage.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 793a997aa5..96441b85ee 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,174 +17,14 @@ stages: - build - test -environment: - stage: environment - image: docker:latest - variables: - CONTAINER_NAME: "$CI_REGISTRY/trezor/trezor-firmware/environment" - services: - - docker:dind - before_script: - - docker login $CI_REGISTRY -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD - when: manual - script: - - docker pull $CONTAINER_NAME:latest || true - - docker build --cache-from $CONTAINER_NAME:latest --tag $CONTAINER_NAME:$CI_COMMIT_SHA --tag $CONTAINER_NAME:latest . - - docker push $CONTAINER_NAME:$CI_COMMIT_SHA - - docker push $CONTAINER_NAME:latest - -image: registry.corp.sldev.cz/trezor/trezor-firmware/environment - before_script: - pipenv install -prebuild style: - stage: prebuild - script: - - pipenv run make style_check - - cd core && pipenv run make templates_check # TODO - -prebuild common: - stage: prebuild - script: - - cd common - - pipenv run jsonlint defs/*.json - - pipenv run jsonlint defs/*/*.json - - pipenv run python tools/cointool.py check - - pipenv run python tools/support.py check --ignore-missing - - pipenv run python protob/check.py - - pipenv run python protob/graph.py protob/*.proto # TODO: artifacts? - -build core firmware: - stage: build - script: - - cd core - - pipenv run make build_cross - - pipenv run make build_boardloader - - pipenv run make build_bootloader - - pipenv run make build_prodtest - - pipenv run make build_firmware - # - test "$TREZOR_MODEL" = "1" || pipenv run make sizecheck - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - core/build/firmware/firmware.bin - - core/build/bootloader/bootloader.bin - expire_in: 1 week - -build core unix: - stage: build - script: - - cd core - - pipenv run make build_unix_noui - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" - untracked: true - expire_in: 1 day - -build legacy firmware: - stage: build - script: - - cd legacy - - pipenv run script/cibuild - - pipenv run make -C bootloader - - pipenv run make -C demo - -build legacy firmware debug: - stage: build - variables: - DEBUG_LINK: "1" - script: - - cd legacy - - pipenv run script/cibuild - - pipenv run make -C bootloader - - pipenv run make -C demo - -build legacy firmware bitcoinonly: - stage: build - variables: - BITCOIN_ONLY: "1" - script: - - cd legacy - - pipenv run script/cibuild - - pipenv run make -C bootloader - - pipenv run make -C demo - -build legacy emu: - stage: build - variables: - HEADLESS: "1" - EMULATOR: "1" - DEBUG_LINK: "1" - script: - - cd legacy - - pipenv run script/cibuild - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" - untracked: true - expire_in: 1 day - -build crypto: - stage: build - script: - - cd crypto - - pipenv run make - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" - untracked: true - expire_in: 1 day - -test core unix unit: - stage: test - dependencies: - - build core unix - script: - - cd core - - pipenv run make test - -test core unix device: - stage: test - dependencies: - - build core unix - script: - - cd core - - pipenv run make test_emu - -test core unix monero: - stage: test - dependencies: - - build core unix - script: - - cd core - - pipenv run make test_emu_monero - -# TODO: aren't some tests from .travis.yml missing? -test legacy emu: - stage: test - dependencies: - - build legacy emu - variables: - EMULATOR: "1" - script: - - cd legacy - - pipenv run script/test - -test crypto: - stage: test - dependencies: - - build crypto - script: - - cd crypto - - ./tests/aestst - - ./tests/test_check - - CK_TIMEOUT_MULTIPLIER=20 valgrind -q --error-exitcode=1 ./tests/test_check - - ./tests/test_openssl 1000 - - ITERS=10 pipenv run pytest tests/ # TODO are ITERS=10 propagated? - -test storage: - stage: test - dependencies: [] - script: - - cd storage/tests - - pipenv run make build - - pipenv run make tests_all +include: + - ci/environment.yml + - ci/prebuild.yml # common, style + - ci/core.yml + - ci/legacy.yml + # - ci/python.yml TODO + - ci/crypto.yml + - ci/storage.yml diff --git a/Dockerfile b/ci/Dockerfile similarity index 100% rename from Dockerfile rename to ci/Dockerfile diff --git a/ci/core.yml b/ci/core.yml new file mode 100644 index 0000000000..d62cb029e8 --- /dev/null +++ b/ci/core.yml @@ -0,0 +1,52 @@ +image: registry.corp.sldev.cz/trezor/trezor-firmware/environment + +build core firmware: + stage: build + script: + - cd core + - pipenv run make build_cross + - pipenv run make build_boardloader + - pipenv run make build_bootloader + - pipenv run make build_prodtest + - pipenv run make build_firmware + # - test "$TREZOR_MODEL" = "1" || pipenv run make sizecheck + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + paths: + - core/build/firmware/firmware.bin + - core/build/bootloader/bootloader.bin + expire_in: 1 week + +build core unix: + stage: build + script: + - cd core + - pipenv run make build_unix_noui + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + untracked: true + expire_in: 1 day + +test core unix unit: + stage: test + dependencies: + - build core unix + script: + - cd core + - pipenv run make test + +test core unix device: + stage: test + dependencies: + - build core unix + script: + - cd core + - pipenv run make test_emu + +test core unix monero: + stage: test + dependencies: + - build core unix + script: + - cd core + - pipenv run make test_emu_monero diff --git a/ci/crypto.yml b/ci/crypto.yml new file mode 100644 index 0000000000..5a95b2a5f2 --- /dev/null +++ b/ci/crypto.yml @@ -0,0 +1,23 @@ +image: registry.corp.sldev.cz/trezor/trezor-firmware/environment + +build crypto: + stage: build + script: + - cd crypto + - pipenv run make + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + untracked: true + expire_in: 1 day + +test crypto: + stage: test + dependencies: + - build crypto + script: + - cd crypto + - ./tests/aestst + - ./tests/test_check + - CK_TIMEOUT_MULTIPLIER=20 valgrind -q --error-exitcode=1 ./tests/test_check + - ./tests/test_openssl 1000 + - ITERS=10 pipenv run pytest tests/ # TODO are ITERS=10 propagated? diff --git a/ci/environment.yml b/ci/environment.yml new file mode 100644 index 0000000000..a3f3f4520c --- /dev/null +++ b/ci/environment.yml @@ -0,0 +1,15 @@ +environment: + stage: environment + image: docker:latest + variables: + CONTAINER_NAME: "$CI_REGISTRY/trezor/trezor-firmware/environment" + services: + - docker:dind + before_script: + - docker login $CI_REGISTRY -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD + when: manual + script: + - docker pull $CONTAINER_NAME:latest || true + - docker build --cache-from $CONTAINER_NAME:latest --tag $CONTAINER_NAME:$CI_COMMIT_SHA --tag $CONTAINER_NAME:latest ci/ + - docker push $CONTAINER_NAME:$CI_COMMIT_SHA + - docker push $CONTAINER_NAME:latest diff --git a/ci/legacy.yml b/ci/legacy.yml new file mode 100644 index 0000000000..0480208836 --- /dev/null +++ b/ci/legacy.yml @@ -0,0 +1,54 @@ +image: registry.corp.sldev.cz/trezor/trezor-firmware/environment + +build legacy firmware: + stage: build + script: + - cd legacy + - pipenv run script/cibuild + - pipenv run make -C bootloader + - pipenv run make -C demo + +build legacy firmware debug: + stage: build + variables: + DEBUG_LINK: "1" + script: + - cd legacy + - pipenv run script/cibuild + - pipenv run make -C bootloader + - pipenv run make -C demo + +build legacy firmware bitcoinonly: + stage: build + variables: + BITCOIN_ONLY: "1" + script: + - cd legacy + - pipenv run script/cibuild + - pipenv run make -C bootloader + - pipenv run make -C demo + +build legacy emu: + stage: build + variables: + HEADLESS: "1" + EMULATOR: "1" + DEBUG_LINK: "1" + script: + - cd legacy + - pipenv run script/cibuild + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA" + untracked: true + expire_in: 1 day + +# TODO: aren't some tests from .travis.yml missing? +test legacy emu: + stage: test + dependencies: + - build legacy emu + variables: + EMULATOR: "1" + script: + - cd legacy + - pipenv run script/test \ No newline at end of file diff --git a/ci/prebuild.yml b/ci/prebuild.yml new file mode 100644 index 0000000000..ac83fb64c1 --- /dev/null +++ b/ci/prebuild.yml @@ -0,0 +1,18 @@ +image: registry.corp.sldev.cz/trezor/trezor-firmware/environment + +prebuild style: + stage: prebuild + script: + - pipenv run make style_check + - cd core && pipenv run make templates_check # TODO + +prebuild common: + stage: prebuild + script: + - cd common + - pipenv run jsonlint defs/*.json + - pipenv run jsonlint defs/*/*.json + - pipenv run python tools/cointool.py check + - pipenv run python tools/support.py check --ignore-missing + - pipenv run python protob/check.py + - pipenv run python protob/graph.py protob/*.proto # TODO: artifacts? diff --git a/ci/storage.yml b/ci/storage.yml new file mode 100644 index 0000000000..57b046c5b5 --- /dev/null +++ b/ci/storage.yml @@ -0,0 +1,9 @@ +image: registry.corp.sldev.cz/trezor/trezor-firmware/environment + +test storage: + stage: test + dependencies: [] + script: + - cd storage/tests + - pipenv run make build + - pipenv run make tests_all diff --git a/core/build-docker.sh b/core/build-docker.sh index 18fb852d5f..52843e93e1 100755 --- a/core/build-docker.sh +++ b/core/build-docker.sh @@ -21,7 +21,7 @@ else REPOSITORY=https://github.com/$REPOSITORY/trezor-firmware.git fi -docker build -t $IMAGE --build-arg TOOLCHAIN_FLAVOR=$TOOLCHAIN_FLAVOR core +docker build -t $IMAGE --build-arg TOOLCHAIN_FLAVOR=$TOOLCHAIN_FLAVOR ci/ USER=$(ls -lnd . | awk '{ print $3 }') GROUP=$(ls -lnd . | awk '{ print $4 }') diff --git a/legacy/build.sh b/legacy/build.sh index 67013873f6..63f3fe241f 100755 --- a/legacy/build.sh +++ b/legacy/build.sh @@ -10,6 +10,6 @@ IMAGE=trezor-mcu-build USER=$(ls -lnd . | awk '{ print $3 }') GROUP=$(ls -lnd . | awk '{ print $4 }') -docker build -t "$IMAGE" . +docker build -t "$IMAGE" ci/ docker run -it -v $(pwd):/src:z --user="$USER:$GROUP" "$IMAGE" \ /src/legacy/script/fullbuild "$BOOTLOADER_COMMIT" "$FIRMWARE_COMMIT" From fe3d67e259003a0edf676f36acbdca7c82f33098 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Sun, 28 Apr 2019 12:15:24 +0200 Subject: [PATCH 78/78] ci: set GIT_SUBMODULE_STRATEGY to none where not submodules are not needed I've decided to omit some places (e.g. build crypto) where it might be confusing if some submodules dependency is introduced. Well, that concerns tests stages as well, but I guess it is more likely for the build stage. Updates #108 --- ci/core.yml | 6 ++++++ ci/environment.yml | 1 + ci/legacy.yml | 2 ++ ci/storage.yml | 2 ++ 4 files changed, 11 insertions(+) diff --git a/ci/core.yml b/ci/core.yml index d62cb029e8..d4e6d53633 100644 --- a/ci/core.yml +++ b/ci/core.yml @@ -29,6 +29,8 @@ build core unix: test core unix unit: stage: test + variables: + GIT_SUBMODULE_STRATEGY: none # no need to fetch submodules dependencies: - build core unix script: @@ -37,6 +39,8 @@ test core unix unit: test core unix device: stage: test + variables: + GIT_SUBMODULE_STRATEGY: none # no need to fetch submodules dependencies: - build core unix script: @@ -45,6 +49,8 @@ test core unix device: test core unix monero: stage: test + variables: + GIT_SUBMODULE_STRATEGY: none # no need to fetch submodules dependencies: - build core unix script: diff --git a/ci/environment.yml b/ci/environment.yml index a3f3f4520c..604587736f 100644 --- a/ci/environment.yml +++ b/ci/environment.yml @@ -2,6 +2,7 @@ environment: stage: environment image: docker:latest variables: + GIT_SUBMODULE_STRATEGY: none # no need to fetch submodules CONTAINER_NAME: "$CI_REGISTRY/trezor/trezor-firmware/environment" services: - docker:dind diff --git a/ci/legacy.yml b/ci/legacy.yml index 0480208836..8af8aebb0d 100644 --- a/ci/legacy.yml +++ b/ci/legacy.yml @@ -44,6 +44,8 @@ build legacy emu: # TODO: aren't some tests from .travis.yml missing? test legacy emu: + variables: + GIT_SUBMODULE_STRATEGY: none # no need to fetch submodules stage: test dependencies: - build legacy emu diff --git a/ci/storage.yml b/ci/storage.yml index 57b046c5b5..6d6a93785e 100644 --- a/ci/storage.yml +++ b/ci/storage.yml @@ -1,6 +1,8 @@ image: registry.corp.sldev.cz/trezor/trezor-firmware/environment test storage: + variables: + GIT_SUBMODULE_STRATEGY: none # no need to fetch submodules stage: test dependencies: [] script: