\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)