\pagebreak
\pagebreak
Author: Cédric Mesnil <cslashm@gmail.com>
License:
Copyright 2017-2019 Cédric Mesnil <cslashm@gmail.com>, Ledger SASLicensed under the Apache License, Version 2.0 (the "License");you may not use this file except in compliance with the License.You may obtain a copy of the License atUnless required by applicable law or agreed to in writing, softwaredistributed under the License is distributed on an "AS IS" BASIS,WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the License for the specific language governing permissions andlimitations under the License.
\pagebreak
We want to enforce key protection, transaction confidentiality and transaction integrity against potential malware on the Host. To achieve that we propose to use a Ledger Nano S/X as a 2nd factor trusted device. Such a device has small amount of memory and is not capable of holding the entire transaction or building the required proofs in RAM. So we need to split the process between the host and the NanoS. This draft note explain how.
To summarize, the signature process is:
. Generate a TX key pair (\mathit{r, R})
. Process Stealth Payment ID
. For each input \mathit{T_{in}} to spend:
- Compute the input public derivation data \mathfrak{D}_\mathrm{in}
- Compute the spend key (\mathit{x_{in}, P_{in}}) from \mathit{R_{in}} and \mathit{b}
- Compute the key image \mathit{I_{in}} of \mathit{x_{in}}
. For each output \mathit{T_{out}} :
- Compute the output secret derivation data \mathfrak{D}_\mathrm{out}
- Compute the output public key \mathit{P_{out}}
. For each output \mathit{T_{out}} :
- compute the range proof
- blind the amount
- compute the view tag
. Compute the final confidential ring signature
. Return TX
\pagebreak
Elliptic curve points, such as pubic keys, are written in italic upper case, and scalars, such as private keys, are written in italic lower case:
- \mathit{spk} : protection key
- (\mathit{r, R}) : transaction key pair
- (\mathit{a, A}) (\mathit{b, B}) : sender main view/spend key pair
- (\mathit{c, C}) (\mathit{d, D}) : sender sub view/spend key pair
- \mathit{A_{out}} \mathit{B_{out}} : receiver main view/spend public keys
- \mathit{C_{out}} \mathit{D_{out}} : receiver sub view/spend public key
- \mathit{H} : 2nd group generator, such \mathit{H = h.G} and \mathit{h} is unknown
- \mathcal{\mathrm{amount}} : amount to send/spend
- \mathcal{\mathrm{mask}} : secret amount mask factor
- \mathit{C_v} : commitment to a with v such \mathit{C_v = k.G + v.H}
- \mathit{\alpha_{in}} : secret co-signing key for ith input
- \mathit{x_{in}} : secret signing key for ith input
- \mathit{P_{in}} : public key of ith input
- \mathit{P_{out}} : public key of ith output
- \mathfrak{D}_\mathrm{out} \mathfrak{D}_\mathrm{in} : first level derivation data
Hash and encryption function:
- \mathtt{AES} : [k](m) AES encryption of m with key k
- \mathtt{AES^{-1}} : [k](c) AES decryption of c with key k
Others:
- \mathit{PayID} : Stealth payment ID
- \mathtt{ENC\_PAYMENT\_ID\_TAIL} : 0x82
\pagebreak
Hereafter are the code integration and application specification.
The commands are divided in three sets:
- Provisioning
- Low level crypto command
- High level transaction command
The low level set is a direct mapping of some crypto Monero function. For such command the Monero function will be referenced.
The high level set encompasses functions that handle the confidential/sensitive part of full transaction
All command follow the generic ISO7816 command format, with the following meaning:
| byte | length | description |
|---|---|---|
| CLA | 01 | Protocol version |
| INS | 01 | Command |
| P1 | 01 | Sub command |
| P2 | 01 | Command/Sub command counter |
| LC | 01 | byte length of data |
| data | 01 var |
options additional data |
When a command/sub-command can be sent repeatedly, the counter must be increased
by one at each command. The flag last sub command indicator must be set
to indicate another command will be sent.
Common option encoding
|
Last sub command indicator More identical subcommand forthcoming Last sub command |
\pagebreak
There is no provisioning in a standard setup. Both key pairs (\mathit{a, A}) and (\mathit{b, B}) should be derived under BIP44 path.
The general BIP44 path is :
/ purpose' / coin_type' / account' / change / address_index
and is defined as follow for any Monero main address:
`` /44'/128'/account'/0/0``
so in hexa:
/0x8000002C/0x80000080/0x8......./0x00000000/0x00000000
The address_index is set to 0 for the main address and will be used as sub-address index according to kenshi84 fork.
In case an already existing key needs to be transferred, an optional dedicated command may be provided. As there is no secure messaging for now, this transfer shall be done from a trusted Host. Moreover, as provisioning is not handled by Monero client, a separate tool must be provided.
Description
Restart the application and check client/application versions compatibility.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 02 | 00 | 00 | ll |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| var | string version, without trailing null byte |
Response data
| Length | Value |
|---|---|
| 01 | Application major version |
| 01 | Application minor version |
| 01 | Application micro version |
Description
Put sender key pairs.
This command allows to set specific key on the device and should only be used for testing purpose.
The application shall:
check \mathit{A} == \mathit{a}.|G|check \mathit{B} == \mathit{b}.|G|store \mathit{a}, \mathit{A}, \mathit{b}, \mathit{B}
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 22 | 00 | 00 | e0 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | \mathit{a} |
| 20 | \mathit{A} |
| 20 | \mathit{b} |
| 20 | \mathit{B} |
| 5f | Base58 encoded public key |
Response data
| Length | Value |
|---|---|
Description
Retrieves public base58 encoded public key.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 20 | 01 | 00 | 01 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
Response data
| Length | Value |
|---|---|
| 20 | "A" view public key |
| 20 | "B" view spend key |
| 5f | Base58 encoded public key |
Description
Retrieves the private view key in order to accelerate the blockchain scan.
The device should ask the user to accept or reject this export. If rejected the client will use the device for scanning the blockchain.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 20 | 02 | 00 | 01 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
Response data
| Length | Value |
|---|---|
| 20 | "a" secret view key |
Monero
Description
Display requested main address, sub address or integrated address.
compute \mathit{x} = |dec|[|spk|](\widetilde{\mathit{x}})
if payment ID is provided:
compute \mathit{xP} = \mathit{x}.|G|check \mathit{xP} == \mathit{P}
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 21 | xx | 00 | 11 |
if P1 is '00' display non-integrated address.
if P1 is '01' display integrated address.
Any other value will be rejected.
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 08 | index (Major.minor) \mathit{index} |
| 08 | Payment ID, (or '0000000000000000') |
Response data
| Length | Value |
|---|---|
\pagebreak
This section describe lowlevel commands that can be used in a transaction or not.
Monero
device_default::verify_keys.
Description
Verify that the provided private key and public key match.
compute \mathit{x} = |dec|[|spk|](\widetilde{\mathit{x}})compute \mathit{xP} = \mathit{x}.|G|check \mathit{xP} == \mathit{P}
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 26 | xx | 00 | 41 |
if P1 is '00' the provided public key will be used.
if P1 is '01' the public view is key will be used and the provided private key will be 'ignored'
if P1 is '02' the public spend is key will be used and the provided private key will be 'ignored'
Any other value will be rejected.
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | secret key \widetilde{\mathit{x}} |
| 20 | public key or '00'*32 \mathit{P} |
Response data
| Length | Value |
|---|---|
Monero
Description
compute \mathit{s} = |H|(|a| | \mathit{B} | \mathtt{ENC\_PAYMENT\_ID\_TAIL})
return the full internal state (200 bytes) of Keccak.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 24 | 00 | 00 | 00 |
Command data
| Length | Value |
|---|---|
Response data
| Length | Value |
|---|---|
| C8 | ChaCha8 prekey |
Monero
crypto::generate_key_derivation.
Description
Compute the secret key derivation and return it encrypted.
compute \mathit{x} = |dec|[|spk|](\widetilde{\mathit{x}})compute \mathfrak{D}_\mathrm{in} = |keyDrv|(|x|,|P|)compute \widetilde{\mathfrak{D}_\mathrm{in}} = |enc|[|spk|](\mathfrak{D}_\mathrm{in})
return \widetilde{\mathfrak{D}_\mathrm{in}}.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 32 | 00 | 00 | 41 or 61 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | public key \mathit{P} |
| 20 | secret key \widetilde{\mathit{x}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
Response data
| Length | Value |
|---|---|
| 20 | encrypted key derivation \widetilde{\mathfrak{D}_\mathrm{in}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
Monero
crypto::derivation_to_scalar.
Description
Transform a secret derivation data to a secret scalar according to its index.
compute \mathfrak{D}_\mathrm{in} = |dec|[|spk|](\widetilde{\mathfrak{D}_\mathrm{in}})compute \mathit{s} = |Hps|(|Drv|, \mathit{index})compute \widetilde{\mathit{s}} = |enc|[|spk|](\mathit{s})
return \widetilde{\mathit{s}}.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 34 | 00 | 00 | 25 or 45 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | encrypted key derivation \widetilde{\mathfrak{D}_\mathrm{in}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
| 04 | index |
Response data
| Length | Value |
|---|---|
| 20 | encrypted scalar \widetilde{\mathit{s}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
Monero
crypto::derive_public_key.
Description
Compute a new public key from some secret derivation data, a parent public key and its index.
compute \widetilde{\mathfrak{D}_\mathrm{in}} = |dec|[|spk|](\widetilde{\mathfrak{D}_\mathrm{in}})
derivation_to_scalar:
compute \mathit{s} = |Hps|(|Drv|, \mathit{index})
then:
compute \mathit{P}' = |P|+|s|.|G|
return \mathit{P}'.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 36 | 00 | 00 | 25 or 45 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | encrypted key derivation \widetilde{\mathfrak{D}_\mathrm{in}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
| 04 | index |
| 20 | public key \mathit{P} |
Response data
| Length | Value |
|---|---|
| 20 | public key \mathit{P}' |
Monero
crypto::derive_secret_key.
Description
Compute a new secret key from some secret derivation data, a parent secret key and its index.
compute \widetilde{\mathfrak{D}_\mathrm{in}} = |dec|[|spk|](\widetilde{\mathfrak{D}_\mathrm{in}})compute \mathit{x} = |dec|[|spk|](\widetilde{\mathit{x}})
derivation_to_scalar:
compute \mathit{s} = |Hps|(|Drv|, \mathit{index})
then:
compute \mathit{x}' = (|x|+|s|) % \mathtt{\#n}compute \widetilde{\mathit{x}}' = |enc|[|spk|](\mathit{x})
return \widetilde{\mathit{x}}.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 38 | 00 | 00 | 65 or 85 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | encrypted key derivation \widetilde{\mathfrak{D}_\mathrm{in}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
| 04 | index |
| 20 | encrypted secret key \widetilde{\mathit{x}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
Response data
| Length | Value |
|---|---|
| 20 | encrypted derived secret key \widetilde{\mathit{x}}' |
| 20 | ephemeral hmac (optional, only during active transaction) |
Monero
crypto_ops::derive_subaddress_public_key.
Description
compute \widetilde{\mathfrak{D}_\mathrm{in}} = |dec|[|spk|](\widetilde{\mathfrak{D}_\mathrm{in}})compute \mathit{s} = |Hps|(|Drv|, \mathit{index})compute \mathit{P}' = \mathit{P} - \mathit{s}.|G|
return \mathit{P}'
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 46 | 00 | 00 | 45 or 65 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | public key \mathit{P} |
| 20 | encrypted derivation key \widetilde{\mathfrak{D}_\mathrm{in}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
| 04 | index \mathit{index} |
Response data
| Length | Value |
|---|---|
| 20 | sub public key \mathit{P}' |
Monero
device_default::get_subaddress_spend_public_key.
Description
get_subaddress_secret_key:
compute \mathit{s} = |H|("SubAddr" \| |a| | \mathit{index} )compute \mathit{x} = \mathit{s} % \mathtt{\#n}
then:
compute \mathit{D = \mathit{B} + \mathit{x}.|G|
return \mathit{D
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 4A | 00 | 00 | 09 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 08 | index (Major.minor) \mathit{index} |
Response data
| Length | Value |
|---|---|
| 20 | sub spend public key \mathit{D |
Monero
get_subaddress_secret_key
Description
compute \mathit{x} = |dec|[|spk|](\widetilde{\mathit{x}})compute \mathit{s} = |H|("SubAddr" \| |x| | \mathit{index} )compute \mathit{d} = \mathit{s} % \mathtt{\#n}compute \widetilde{\mathit{d_{i}}} = |dec|[|spk|](\mathit{d})
return \widetilde{\mathit{d_{i}}}
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 4C | 00 | 00 | 39 or 59 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | secret key \widetilde{\mathit{x}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
| 08 | index (Major.minor) \mathit{index} |
Response data
| Length | Value |
|---|---|
| 20 | sub secret key \widetilde{\mathit{d_{i}}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
Monero
device_default::get_subaddress_secret_key.
Description
compute \mathit{s} = |H|("SubAddr" \| |a| | \mathit{index} )compute \mathit{x} = \mathit{s} % \mathtt{\#n}
then:
compute \mathit{D = \mathit{B} + \mathit{x}.|G|compute \mathit{C} = \mathit{A}.|D|
return \mathit{C}, \mathit{D
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 48 | 00 | 00 | 09 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 08 | index (Major.minor) \mathit{index} |
Response data
Monero
crypto::generate_key_image.
Description
Compute the key image of a key pair.
compute \mathit{x} = |dec|[|spk|](\widetilde{\mathit{x}})compute \mathit{P}' = |Hp|(|P|)compute \mathit{Img(P)} = \mathit{x}.|P|'
return \mathit{Img(P)}.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 3A | 00 | 00 | 41 or 61 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | public key \mathit{P} |
| 20 | secret key \widetilde{\mathit{x}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
Response data
| Length | Value |
|---|---|
| 20 | key image \mathit{Img(P)} |
Monero
crypto::derive_view_tag.
Description
Derive the view tag of an output.
compute \mathfrak{D}_\mathrm{in} = |dec|[|spk|](\widetilde{\mathfrak{D}_\mathrm{in}})compute \mathit{view\_tag\_full} = |Hs|("view_tag" \|, |Drv|, \mathit{index})compute \mathit{view\_tag} = |vtf|[0:1]
return \mathit{view\_tag}.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 3B | 00 | 00 | 25 or 45 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | encrypted key derivation \widetilde{\mathfrak{D}_\mathrm{in}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
| 04 | index |
Response data
| Length | Value |
|---|---|
| 01 | view tag \mathit{view\_tag} |
Monero
crypto::generate_keys.
Description
Generate a new keypair and return it. The secret key is returned encrypted.
generate \mathit{x}compute \mathit{xP} = \mathit{x}.|P|compute \widetilde{\mathit{x}} = |enc|[|spk|](\mathit{x})
return \mathit{P}, \widetilde{\mathit{x}}.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 40 | 00 | 00 | 01 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
Response data
| Length | Value |
|---|---|
| 20 | public key \mathit{P} |
| 20 | encrypted secret key \widetilde{\mathit{x}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
Monero
crypto::secret_key_to_public_key.
Description
Compute a public key from secret a secret key.
compute \mathit{x} = |dec|[|spk|](\widetilde{\mathit{x}})compute \mathit{P} = \mathit{x}.|G|
return \mathit{P}.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 30 | 00 | 00 | 21 or 41 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | encrypted secret key \widetilde{\mathit{x}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
Response data
| Length | Value |
|---|---|
| 20 | public key \mathit{P} |
Monero
sc_add
Description
compute \mathit{x_1} = |dec|[|spk|](\widetilde{\mathit{x_1}})compute \mathit{x_2} = |dec|[|spk|](\widetilde{\mathit{x_2}})compute \mathit{x} = \mathit{x_1} + \mathit{x_2}compute \widetilde{\mathit{x}} = |enc|[|spk|](\mathit{x})
return \widetilde{\mathit{x}}.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 3C | 00 | 00 | 41 or 61 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | secret key \widetilde{\mathit{x_1}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
| 20 | secret key \widetilde{\mathit{x_2}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
Response data
| Length | Value |
|---|---|
| 20 | secret key \widetilde{\mathit{x}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
Monero
rct::scalarmultKey.
Description
Multiply a secret scalar with a public key.
compute \mathit{x} = |dec|[|spk|](\widetilde{\mathit{x}})compute \mathit{xP} = \mathit{x}.|P|
return \mathit{xP}
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 42 | 00 | 00 | 41 or 61 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | public key \mathit{P} |
| 20 | secret key \widetilde{\mathit{x}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
Response data
| Length | Value |
|---|---|
| 20 | new public key \mathit{xP} |
Monero
rct::scalarmultBase.
Description
Multiply a secret scalar with the publis base point \mathit{G}.
compute \mathit{x} = |dec|[|spk|](\widetilde{\mathit{x}})compute \mathit{xG} = \mathit{x}.|G|
return \mathit{xG}
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 44 | 00 | 00 | 21 or 41 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | secret key \widetilde{\mathit{x}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
Response data
| Length | Value |
|---|---|
| 00 | |
| 20 | new public key \mathit{xG} |
Monero
Description
Encrypt payment ID
compute \mathit{x} = |dec|[|spk|](\widetilde{\mathit{x}})compute \mathfrak{D}_\mathrm{in} = |keyDrv|(|P|, \mathit{x})compute \mathit{s} = |Hs|( |DRV| | \mathtt{ENC\_PAYMENT\_ID\_TAIL})compute \mathit{PayID} = |ePayID|^|s|
return \mathit{PayID}
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 76 | 00 | 00 | 61 or 81 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | public key \mathit{P} |
| 20 | encryped secret key \widetilde{\mathit{x}} |
| 20 | ephemeral hmac (optional, only during active transaction) |
| 20 | encryped payment ID \widetilde{\mathit{PayID}} |
Response data
Monero
Description
Unblind amount and his mask.
First:
compute \mathcal{AK}_\mathrm{amount} = |dec|[|spk|](\widetilde{\mathcal{AK}_\mathrm{amount}})
If blind V1:
compute \mathit{s} = |Hs|(|AKout|)compute \widetilde{\mathcal{\mathrm{mask}}} = \mathcal{\mathrm{mask}}-\mathit{s}compute \mathit{s} = |Hs|(|a|)compute \widetilde{\mathcal{\mathrm{amount}}} = \mathcal{\mathrm{amount}}-\mathit{s}
- If blind V2:
- compute \mathcal{\mathrm{mask}} = |Hs|("commitment_mask" \| |Akout|) % \mathtt{\#n}compute \mathit{s} = |Hs|("amount" \| |Akout|)
return \widetilde{\mathcal{\mathrm{mask}}},|ev|
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 7A | 00 | 00 | 61 or 81 |
specific options
|
Commitment scheme version Blind V2 Blind V1 |
Command data
| Length | Value |
|---|---|
| 01 | xx |
| 20 | encryped blinding factor \mathcal{AK}_\mathrm{amount} |
| 20 | ephemeral hmac (optional, only during active transaction) |
| 20 | blinded mask \widetilde{\mathcal{\mathrm{mask}}} |
| 20 | blinded amount \widetilde{\mathcal{\mathrm{amount}}} |
Response data
| Length | Value |
|---|---|
| 20 | mask \widetilde{\mathcal{\mathrm{mask}}} |
| 20 | amount \widetilde{\mathcal{\mathrm{amount}}} |
The transaction is mainly generated in construct_tx_and_get_tx_key (or construct_tx) and construct_tx_with_tx_key functions.
First, a new transaction keypair (\mathit{r, R}) is generated.
Then, the stealth payment id is processed if any.
Then, for each input transaction to spend, the input key image is retrieved.
Then, for each output transaction, the epehemeral destination key and the blinding key amount \mathcal{AK}_\mathrm{amount} are computed.
Once \mathit{T_{in}} and \mathit{T_{out}} keys are set up, the genRCT/genRctSimple function is called.
First a commitment \mathit{C_v} to each \mathcal{\mathrm{amount}} amount and its associated range proof are computed to ensure the \mathcal{\mathrm{amount}} amount confidentiality. The commitment and its range proof do not imply any secret and generate \mathit{C_v}, \mathcal{\mathrm{mask}} such \mathit{C_v = k.G + v.H}.
Then \mathcal{\mathrm{mask}} and \mathcal{\mathrm{amount}} are blinded by using the \mathcal{AK}_\mathrm{amount} which is only known in an encrypted form by the host.
After all commitments have been setup, the confidential ring signature happens. This signature is performed by calling proveRctMG which then calls MLSAG_Gen.
At this point the amounts and destination keys must be validated on the NanoS. This information is embedded in the message to sign by calling get_pre_mlsag_hash, prior to calling ProveRctMG. So the get_pre_mlsag_hash function will have to be modified to serialize the rv transaction to NanoS which will validate the tuple <amount,dest> and compute the prehash. The prehash will be kept inside NanoS to ensure its integrity. Any further access to the prehash will be delegated.
Once the prehash is computed, the proveRctMG is called. This function only builds some matrix and vectors to prepare the signature which is performed by the final call MLSAG_Gen.
During this last step some ephemeral key pairs are generated : \mathit{\alpha_{in}}, \mathit{\alpha_{in}.G}. All \mathit{\alpha_{in}} must be kept secret to protect the \mathit{x_{in}} keys. Moreover we must avoid signing arbitrary values during the final loop.
In order to achieve this validation, we need to approve the original destination address |Aout||Bout|, which is not recoverable from P out . Here the only solution is to pass the original destination with the \mathcal{\mathrm{mask}}, \mathcal{\mathrm{amount}}, \mathcal{AK}_\mathrm{amount}.
Unblind \mathcal{\mathrm{mask}} and \mathcal{\mathrm{amount}} and then verify the commitment \mathit{C_v = k.G + v.H}. If \mathit{C_v} is verified and user validate \mathit{A_{out}},|Bout| and \mathcal{\mathrm{amount}}, continue.
\pagebreak
During a transaction the following state machine is enforced:
OPEN_TX{1} -----------------------------------------------------
|
----------------------------------------------------------------
|
----> STEALTH{1} -----------------------------------------------
|
----------------------------------------------------------------
|
----> GEN_TXOUT_KEYS{*} ---------------------------------------
|
------------------------------------------------------------ ---
|
----> PREFIX_HASH{1} ---> PREFIX_HASH{*} ---> PREFIX_HASH{1} ---
(ph_init) (ph_update) (ph_finalize) |
|
----------------------------------------------------------------
|
----> GEN_COMMITMENT_MASK{*} -----------------------------------
only for real TX |
|
----------------------------------------------------------------
|
----> BLIND ----------------------------------------------------
|
----------------------------------------------------------------
|
----> VALIDATE{1} ---> VALIDATE{*} --- VALIDATE{*} <------------
mlsag_ph_init mlsag__update mlsag__finalize |
|
---------------------------------------------------------------
|
----> MLSAG{1} ------> MLSAG{*} ------> MLSAG{1} ---------------
--> mlsag_prepare mlsag_hash mlsag_sign -- |
| | |
--------------------------------------------------- |
|
----------------------------------------------------------------
|
----> CLOSE_TX
Note this state machine assume the multi-signature is not supported. For multi-signature the INS_MLSAG/mlsag_prepare and INS_MLSAG/mlsag_sign may be received several time.
Description
Open a new transaction. Once open the device impose a certain order in subsequent commands:
- OpenTX
- Stealth
- Get TX output keys
- Blind *
- Initialize MLSAG-prehash
- Update MLSAG-prehash *
- Finalize MLSAG-prehash
- MLSAG prepare
- MLSAG hash *
- MLSAG sign
- CloseTX
During this sequence low level API remains available, but no other transaction can be started until the current one is finished or aborted.
Initialize \mathcal{H}_\mathrm{outkeys}compute initial transaction key pair (\mathit{r, R})
return (\mathit{r, R})
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 70 | 01 | cnt | 05 |
Command data
| Length | Value |
|---|---|
| 01 | options |
| 04 | account identifier (ignored, RFU) |
Response data
| Length | Value |
|---|---|
| 20 | public transaction key \mathit{R} |
| 20 | encrypted private transaction key \widetilde{\mathit{r}} |
| 20 | ephemeral hmac |
| 20 | ephemeral hmac of view key |
| 20 | ephemeral hmac of spend key |
Description
Set the signature to 'fake' or 'real'. In fake mode a random key is used to signed the transaction and no user confirmation is requested.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 72 | 01 | 00 | 02 |
Command data
Response data
| Length | Value |
|---|---|
Description
Init prefix hash and ask user to validate time lock
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 7D | 01 | cnt | 05 |
Command data
| Length | Value |
|---|---|
| 01 | options |
| varint | TX version |
| varint | TX timelock |
Response data
| Length | Value |
|---|---|
Description
Update prefix hash with raw data. Options fields tells if there is more data to come or not.
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 7D | 02 | cnt | 05 |
Command data
| Length | Value |
|---|---|
| 01 | options |
| var | raw data to hash |
Response data
| Length | Value |
|---|---|
Description
Return \mathit{s}
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 77 | 00 | 00 | 21 |
Command data
| Length | Value |
|---|---|
| 01 | 00 |
| 20 | encryped blinding factor \mathcal{AK}_\mathrm{amount} |
| 20 | ephemeral hmac |
Response data
| Length | Value |
|---|---|
| 20 | commitment mask \mathit{s} |
Monero
Description
Blind amount and his mask.
First:
compute \mathcal{AK}_\mathrm{amount} = |dec|[|spk|](\widetilde{\mathcal{AK}_\mathrm{amount}})
If blind V1:
compute \mathit{s} = |Hs|(|AKout|)compute \widetilde{\mathcal{\mathrm{mask}}} = |k|+|s|compute \mathit{s} = |Hs|(|a|)compute \widetilde{\mathcal{\mathrm{amount}}} = |v|+|s|
If blind V2:
set \widetilde{\mathcal{\mathrm{mask}}} to 32 zero bytescompute \mathit{s} = |Hs|("amount" \| |AKout|)compute \widetilde{\mathcal{\mathrm{amount}}} = |v|[0:7]^|s|[0:7]
return \widetilde{\mathcal{\mathrm{mask}}},|ev|
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 78 | 00 | 00 | 81 |
specific options
|
Commitment scheme version Blind V2 Blind V1 |
Command data
| Length | Value |
|---|---|
| 01 | xx |
| 20 | encryped blinding factor \mathcal{AK}_\mathrm{amount} |
| 20 | ephemeral hmac |
| 20 | mask \mathcal{\mathrm{mask}} |
| 20 | amount \mathcal{\mathrm{amount}} |
Response data
| Length | Value |
|---|---|
| 20 | blinded mask \widetilde{\mathcal{\mathrm{mask}}} |
| 20 | blinded amount \widetilde{\mathcal{\mathrm{amount}}} |
Description
Compute additional key \mathit{P} if needed, amount key blinding and ephemeral destination key.
if \mathit{need\_additional\_key} :if \mathit{is\_subaddress} :compute \mathit{R}' = \mathit{additional\_key}.|Bout|elsecompute \mathit{R}' = \mathit{additional\_key}.|G|if \mathit{is\_change\_address} :compute \mathfrak{D}_\mathrm{in} = |keyDrv|(|a|,|R|)elseif \mathit{need\_additional\_key} and \mathit{is\_subaddress}:compute \mathfrak{D}_\mathrm{in} = |keyDrv|(|ak|,|Aout|)|else:compute \mathfrak{D}_\mathrm{in} = |keyDrv|(|r|,|Aout|)compute \mathcal{AK}_\mathrm{amount} = |Hps|(|Drv|,|idx|)compute \widetilde{\mathcal{AK}_\mathrm{amount}} = |enc|[|spk|](\mathcal{AK}_\mathrm{amount})compute \mathit{s} = |Hps|(|Drv|,|idx|)compute \mathit{P} = |Bout|+|s|.|G|update \mathcal{H}_\mathrm{outkeys} : |Hupd|(|Aout|,|Bout|,is_change,|AKout|)if option 'last' is set:finalize \mathcal{H}_\mathrm{outkeys}
The application returns
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 7B | 01 | cnt | EC |
Command data
Response data
Description
During the first step, the application updates the \mathcal{H} with the transaction header:
if cnt == 1
Finalize \mathcal{H}_\mathrm{outkeys}Initialize \mathcal{H}_\mathrm{outkeys}'Initialize \mathcal{H}_\mathrm{commitment}Initialize \mathcal{H}update \mathcal{H} : |Hupd|(txnFee)request user to validate txnFee
else
update \mathcal{H} : |Hupd|(pseudoOut)
Command
Command data
if cnt==1 :
| Length | Value |
|---|---|
| 01 | options |
| 01 | type |
| varint | txnFee |
if cnt>1 :
| Length | Value |
|---|---|
| 01 | options |
| 20 | pseudoOut |
Description
On the second step the application receives amount and destination and check values. It also re-compute the \mathcal{H}_\mathrm{outkeys} value to ensure consistency with steps 3 and 4. So for each command received, do:
compute \mathcal{AK}_\mathrm{amount} = |dec|[|spk|](\widetilde{\mathcal{AK}_\mathrm{amount}})update \mathcal{H}_\mathrm{outkeys}'' : |Hupd|(|Aout| | \mathit{B_{out}} | is_change | \mathcal{AK}_\mathrm{amount})if blind v1compute \mathcal{\mathrm{mask}} = \widetilde{\mathcal{\mathrm{mask}}} - |Hs|(|Akout|)compute \mathcal{\mathrm{amount}} = \widetilde{\mathcal{\mathrm{amount}}} - |Hs|(|Hs|(|Akout|))if blind v2compute \mathcal{\mathrm{mask}} = |Hs|("commitment_mask"||Akout|)) % \mathtt{\#n}compute \mathit{s} = |Hs|("amount"|||Akout|)check \mathit{C_v} == \mathcal{\mathrm{mask}}.|G| + \mathcal{\mathrm{amount}}.|H| |update \mathcal{H}_\mathrm{commitment} : |Hupd|(|Ct|)if last command:finalize \mathcal{H}_\mathrm{outkeys}'check \mathcal{H}_\mathrm{outkeys}' == \mathcal{H}_\mathrm{outkeys}finalize \mathcal{H}_\mathrm{commitment}update \mathcal{H} : |Hupd|(ecdhInfo)ask user validation of \mathit{A_{out}}, \mathit{B_{out}}, \mathcal{\mathrm{amount}}
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 7C | 02 | cnt | E3 |
Command data
| Length | Value |
|---|---|
| 01 | options |
| 01 | 1 if sub-address, 0 else |
| 01 | 1 if change-address, 0 else |
| 20 | Real destination public view key \mathit{A_{out}} |
| 20 | Real destination public spend key \mathit{B_{out}} |
| 20 | encrypted amount key blinding \widetilde{\mathcal{AK}_\mathrm{amount}} |
| 20 | ephemeral hmac |
| 20 | \mathit{C_v} of \mathcal{\mathrm{amount}},|k| |
| 40 | one serialized ecdhInfo : {
bytes[32] mask (\widetilde{\mathcal{\mathrm{mask}}})
bytes[32] amount (\widetilde{\mathcal{\mathrm{amount}}})
}
|
specific options
|
Mask scheme version Blind V2 Blind V1 |
Note: Whatever the mask scheme is, \mathcal{\mathrm{amount}} is always transmitted as 32 bytes.
Description
Finally the application receives the last part of data:
if cnt == 1Initialize \mathcal{H}_\mathrm{commitment}'if last command:finalize \mathcal{H}_\mathrm{commitment}'check \mathcal{H}_\mathrm{commitment} == \mathcal{H}_\mathrm{commitment}'\mathit{s} = finalize \mathcal{H}compute \mathcal{H} = \mathtt{HashToScalar} (message | \mathit{s} | proof)elseupdate \mathcal{H}_\mathrm{commitment}': |Hupd|(|Ct|)update \mathcal{H}: |Hupd|(|Ct|)
Keep \mathcal{H}
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 7C | 03 | cnt | 21 |
Command data
not last:
| Length | Value |
|---|---|
| 01 | options |
| 20 | one serialized commitment : {
bytes[32] mask (\mathit{C_v})
}
|
last:
| Length | Value |
|---|---|
| 01 | options |
| 20 | message (rctSig.message) |
| 20 | proof (proof range hash) |
Response data
| Length | Value |
|---|---|
Description
Generate the matrix ring parameters:
generate \mathit{\alpha_{in}} ,compute \mathit{\alpha_{in}.G}if real key:check the order of \mathit{H_i}compute \mathit{\alpha_{in}.H_i}compute \widetilde{\mathit{\alpha_{in}}} = |enc|[|spk|](\mathit{\alpha_{in}})if not option_clear_xin:compute \mathit{x_{in}} = |dec|[|spk|](\widetilde{\mathit{x_{in}}})compute \mathit{II_{in}} = \mathit{x_{in}}.|Hi|
return \widetilde{\mathit{\alpha_{in}}} , \mathit{\alpha_{in}.G} [\mathit{\alpha_{in}.H_i}, \mathit{II_{in}}]
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 7E | 01 | cnt | 61 |
specific options
|
Mask scheme version unencrypted \mathit{x_{in}} encryted \widetilde{\mathit{x_{in}}} |
Command data
for real key:
| Length | Value |
|---|---|
| 01 | options |
| 20 | point |
| 20 | secret spend key \widetilde{\mathit{x_{in}}} |
| 20 | ephemeral hmac |
for random ring key
| Length | Value |
|---|---|
| 01 | options |
Response data
for real key:
| Length | Value |
|---|---|
| 20 | encrypted \mathit{\alpha_{in}} : \widetilde{\mathit{\alpha_{in}}} |
| 20 | ephemeral hmac |
| 20 | \mathit{\alpha_{in}.G} |
| 20 | \mathit{II_{in}} |
| 20 | \mathit{\alpha_{in}.H_i} |
for random ring key
| Length | Value |
|---|---|
| 20 | encrypted \mathit{\alpha_{in}} : \widetilde{\mathit{\alpha_{in}}} |
| 20 | ephemeral hmac |
| 20 | \mathit{\alpha_{in}.G} |
Description
Compute the last matrix ring parameter:
if cnt == 1:replace the inputs by the previously computed MLSAG-prehashinitialize \mathcal{H}update \mathcal{H}: |Hs|(inputs)if last command:c = finalize \mathcal{H} % \mathtt{\#n}
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 7E | 02 | cnt | 21 |
Command data
| Length | Value |
|---|---|
| 01 | options |
| 20 | inputs |
Response data
if last command
| Length | Value |
|---|---|
| 20 | c |
else
| Length | Value |
|---|---|
Description
Finally compute all signatures:
compute \mathit{\alpha_{in}} = |dec|[|spk|](\widetilde{\mathit{\alpha_{in}}})compute \mathit{x_{in}} = |dec|[|spk|](\widetilde{\mathit{x_{in}}})compute \mathit{ss} = (\mathit{\alpha_{in}} - \mathit{c} * \mathit{x_{in}} ) % \mathit{l}
return \mathit{ss}
Command
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 03 | 7E | 03 | cnt | 81 |
Command data
| Length | Value |
|---|---|
| 01 | options |
| 20 | \widetilde{\mathit{x_{in}}} |
| 20 | ephemeral hmac |
| 20 | \widetilde{\mathit{\alpha_{in}}} |
| 20 | ephemeral hmac |
Response data
| Length | Value |
|---|---|
| 20 | signature \mathit{ss} |
Let's Go
|keyDrv| (\mathtt{KeyDerivation})
input : r , Poutput: \mathfrak{D}Monero: generate_key_derivation\mathfrak{D} = r.P\mathfrak{D} = 8.\mathfrak{D}
|Hs| (\mathtt{HashToScalar})
|Hps| (\mathtt{HashPointToScalar})
input: D, idxoutput: s
|Hp| (\mathtt{HashToPoint})
input: Poutput: Q
DeriveAES
input: R,a,boutput: spkseed = sha256(R|a|b|R)data = sha256(seed)spk = lower16(data)