Skip to content

HW wallet design

valdok edited this page Jun 8, 2020 · 6 revisions

Beam vs other blockchains

Beam HW wallet supports the functionality similar to other blockchain protocols, such as keeping all the secret keys within the device, and signing transactions authorized by the users.

Since Beam is based in MW, the design of such a wallet is more complex, because of the following:

  • Transactions are built interactively
  • No addresses

Assumptions

  1. The HW wallet is a minimalistic stateless device. It should not handle blockchain events (blocks, headers, reorgs), handle BBS traffic, fully negotiate transactions, and etc.
  2. The host may be compromised, yet no spend should be possible without user's permission.
  3. Once the user activates the HW wallet (enters the pin-code), the host can view all the user information (balance, UTXOs), but not spend funds.
  4. Every spend (transaction that assumes user spends funds) needs a user authorization. The user sees the following:
    1. The amount being-sent
    2. Asset type (beam or CA).
    3. Recipient identifier.
    4. Transaction kernel ID.
    5. Height range (min/max height) during which the transaction is valid.
    • All this information is cryptographically verified, and there should be no feasible way for the host to tamper with it.

Note: Because in MW transactions are essentially (sort of) Schnorr's multisignatures, the (1) is actually tricky to implement. To create a Schnorr's signature the signer generates a nonce, which must be retained for the duration of the multi-signature ritual.

We solve this by assuming the device has a limited non-volatile memory that can be used to store nonce preimage (source material). We make sure that neither the nonce nor the appropriate secret key can be extracted by malicious host (such as attempt to use the same nonce to sign different things).

The HW wallet device should have several 256-bit slots for nonce preimages. The more slots it has - the more ongoing transactions the HW wallet may handle at the same time.

(There is also an option of using VRFs, but it's too complex for HW wallets.)

Terminology

  • CoinID - full UTXO identifier. Consists of the following:
    1. SubIdx 32-bit child key identifier
    2. Idx 64-bit coin number
    3. Value (64 bits)
    4. AssetID (32-bit number)
    • For each CoinID the HW wallet generates a unique blinding factor using the master secret.
  • OwnerKey - derived from the master secret, used to recognize the owned UTXOs.
  • PublicKeyGen - key generator which for a CoinID generates an appropriate public key (EC point) that corresponds to its blinding factor.

Note: the actual key generation is according to HKDF (rfc-5869). The generated blinding factor depends in a non-transparent way on all the fields of CoinID, thus there's no feasible way to tamper with them and adjust the resulting blinding factor accordingly.

In addition to HKDF, each computed key is multiplied by a fixed secret scalar, so-called co-factor. The PublicKeyGen contains the same seed HKDF data, but the co-factor is replaced by its image (EC point).

Note (2): To denote the UTXO Beam actually uses so-called switch commitments, which are compressed el-Gamal commitments. So that in practice the obtained blidning factor gets an addition, which depends on the committed value and asset type. The host can calculate this addition alone (without the HW wallet).

Functionality high-level

All the HW wallet functionality comes down to the following methods.

The following is returned without user permission

  • OwnerKey
  • PublicKeyGen for arbitrary child key
  • Rangeproof for arbitrary CoinID
  • signature for receive transaction kernel (i.e. transaction in which no funds are lost).

The following needs user permission

  • signature for send transaction kernel
  • signature for split transaction kernel (i.e. only transaction fee is lost)

Why this design is secure?

MW transaction consists of 3 things: inputs, outputs, and transaction kernels.

  • Input is denoted by its Pedersen (switch) commitment. The host can create it for any CoinID, since only blinding factor image is needed, and PublicKeyGen is exported freely.
  • Output consists of Pedersen (switch) commitment and the rangeproof. The commitment is calculated by the host (as for inputs), and the rangeproof is calculated by the HW wallet without any restriction.

So, both inputs and outputs can be created by the host without any restrictions. But to build a valid (balanced) MW transaction the corresponding transaction kernel is required, and this is where HW wallet restrictions come into play. As we mentioned, HW wallet requests verifiable recipient ID and user permission for any spend transaction.

How do we verify the transaction is indeed spending/receiving, or splitting?

For all transactions the host specifies the input/output CoinIDs that belong to the user before/after the transaction. Based on them the HW wallet deduces:

  1. Overall value and asset which is obtained/lost in a transaction
  2. excess blinding factor (diff of input/output blinding factors).

The transaction type is verified according to (1), whereas the kernel signature balances the transaction according to (2). As we mentioned, by blinding factor depends on the CoinID in an opaque way, hence there's no feasible way to substitute different inputs/outputs in the transaction after it was signed.

Clone this wiki locally