Merge pull request #75 from NicolasDorier/qfointq
Remove the need to generate random CardKey
This commit is contained in:
commit
e60705ba49
1 changed files with 58 additions and 43 deletions
|
|
@ -1,17 +1,17 @@
|
|||
## Abstract
|
||||
|
||||
The NXP NTAG424DNA allows applications to configure five application keys, named `K0`, `K1`, `K2`, `K3`, and `K4`. In the Bolt card configuration:
|
||||
The NXP NTAG424DNA allows applications to configure five application keys, named `K0`, `K1`, `K2`, `K3`, and `K4`. In the BoltCard configuration:
|
||||
|
||||
* `K0` is the `App Master Key`, it is the only key permitted to change the application keys.
|
||||
* `K1` serves as the `encryption key` for the `PICCData`, represented by the `p=` parameter.
|
||||
* `K2` is the `authentication key` used for calculating the SUN MAC of the `PICCData`, represented by the `c=` parameter.
|
||||
* `K3` and `K4` are not used but should be configured as recommended in the [NTag424 application notes](https://www.nxp.com/docs/en/application-note/AN12196.pdf).
|
||||
|
||||
A simplistic approach to issuing Bolt cards would involve randomly generating the five different keys and storing them in a database.
|
||||
A simple approach to issuing BoltCards would involve randomly generating the five different keys and storing them in a database.
|
||||
|
||||
When a validation request is made, the verifier would attempt to decrypt the `p=` parameter using all existing encryption keys until finding a match. Once decrypted, the `p=` parameter would reveal the card's uid, which can then be used to retrieve the remaining keys.
|
||||
|
||||
The primary drawback of this method is its lack of scalability. If many cards have been issued, identifying the correct encryption key could become computationally expensive.
|
||||
The primary drawback of this method is its lack of scalability. If many cards have been issued, identifying the correct encryption key could become a computationally intensive task.
|
||||
|
||||
In this document, we propose a solution to this issue.
|
||||
|
||||
|
|
@ -19,43 +19,55 @@ In this document, we propose a solution to this issue.
|
|||
|
||||
First, the `LNUrl Withdraw Service` generates a `IssuerKey` that it will use to generate the keys for every NTag424.
|
||||
|
||||
Then configure a Boltcard the following way:
|
||||
Then, configure a BoltCard as follows:
|
||||
|
||||
* `CardKey = GetRandomBytes(16)`
|
||||
* `K0 = PRF(CardKey, '2d003f76' || UID)`
|
||||
* `CardKey = PRF(IssuerKey, '2d003f75' || UID || Version)`
|
||||
* `K0 = PRF(CardKey, '2d003f76')`
|
||||
* `K1 = PRF(IssuerKey, '2d003f77')`
|
||||
* `K2 = PRF(CardKey, '2d003f78' || UID)`
|
||||
* `K3 = PRF(CardKey, '2d003f79' || UID)`
|
||||
* `K4 = PRF(CardKey, '2d003f7a' || UID)`
|
||||
* `K2 = PRF(CardKey, '2d003f78')`
|
||||
* `K3 = PRF(CardKey, '2d003f79')`
|
||||
* `K4 = PRF(CardKey, '2d003f7a')`
|
||||
* `ID = PRF(IssuerKey, '2d003f7b' || UID)`
|
||||
|
||||
With the following parameters:
|
||||
* `IssuerKey`: This 16-bytes key is used by an `LNUrl Withdraw Service` to setup all its BoltCards.
|
||||
* `UID`: This is the 7-byte ID of the card. You can retrieve it from the NTag424 using the `GetCardUID` function after identification with K1, or by decrypting the `p=` parameter, also known as `PICCData`.
|
||||
* `Version`: A 4-bytes little endian version number. This must be incremented every time the user re-programs (reset/setup) the same BoltCard on the same `LNUrl Withdraw Service`.
|
||||
|
||||
The Pseudo Random Function `PRF(key, message)` applied during the key generation is the CMAC algorithm described in NIST Special Publication 800-38B. [See implementation notes](#notes)
|
||||
|
||||
## How to setup a new boltcard
|
||||
## How to setup a new BoltCard
|
||||
|
||||
1. Generate a random `CardKey` of 16 bytes.
|
||||
2. `ReadData` or `ISOReaDBinary` on the boltcard, to make sure the card is blank.
|
||||
3. Execute `AuthenticateEV2First` with `00000000000000000000000000000000`
|
||||
4. Fetch the `UID` with `GetCardUID`.
|
||||
2. Calculate `K0`, `K1`, `K2`, `K3`, `K4`.
|
||||
4. [Setup the boltcard](./CARD_MANUAL.md).
|
||||
1. Execute `ReadData` or `ISOReaDBinary` on the BoltCard to ensure the card is blank.
|
||||
2. Execute `AuthenticateEV2First` with the application key `00000000000000000000000000000000`
|
||||
3. Fetch the `UID` with `GetCardUID`.
|
||||
4. Calculate `ID`
|
||||
5. Fetch the `State` and `Version` of the BoltCard with the specified `ID` from the database.
|
||||
6. Ensure either:
|
||||
* If no BoltCard is found, insert an entry in the database with `Version=0` and its state set to `Configured`.
|
||||
* If a BoltCard is found and its state is `Reset` then increment `Version` by `1`, and change its state to `Configured`.
|
||||
7. Generate `CardKey` with `UID` and `Version`.
|
||||
8. Calculate `K0`, `K1`, `K2`, `K3`, `K4`.
|
||||
9. [Setup the BoltCard](./CARD_MANUAL.md).
|
||||
|
||||
## How to implement a Reset feature
|
||||
|
||||
If a `LNUrl Withdraw Service` offers a factory reset feature for a user's bolt card, here is the recommended procedure:
|
||||
If a `LNUrl Withdraw Service` offers a factory reset feature for a user's BoltCard, here is the recommended procedure:
|
||||
|
||||
1. Read the NDEF lnurlw URL, extract `p=` and `c=`.
|
||||
2. Derive `Encryption Key (K1)`, decrypts `p=` to get the `PICCData`.
|
||||
2. Derive `Encryption Key (K1)`, decrypt `p=` to obtain the `PICCData`.
|
||||
3. Check `PICCData[0] == 0xc7`.
|
||||
4. Calculate `ID=PRF(IssuerKey, '2d003f7b' || UID)` with the `UID` from the `PICCData`.
|
||||
5. Fetch `CardKey` from database with `ID`.
|
||||
6. Derive `K0`, `K2`, `K3`, `K4` with `CardKey` and the `UID`.
|
||||
7. Verify that the SUN MAC in `c=` matches the one calculated using `Authentication Key (K2)`.
|
||||
8. Execute `AuthenticateEV2First` with `K0`
|
||||
9. Erase the NDEF data file using `WriteData` or `ISOUpdateBinary`
|
||||
10. Restore the NDEF file settings to default values with `ChangeFileSettings`.
|
||||
11. Use `ChangeKey` with the recovered application keys to reset `K4` through `K0` to `00000000000000000000000000000000`.
|
||||
4. Calculate `ID` with the `UID` from the `PICCData`.
|
||||
5. Fetch the BoltCard's `Version` with `ID` from the database.
|
||||
6. Ensure the BoltCard's state is `Configured`.
|
||||
7. Generate `CardKey` with `UID` and `Version`.
|
||||
8. Derive `K0`, `K2`, `K3`, `K4` with `CardKey` and the `UID`.
|
||||
9. Verify that the SUN MAC in `c=` matches the one calculated using `Authentication Key (K2)`.
|
||||
10. Execute `AuthenticateEV2First` with `K0`
|
||||
11. Erase the NDEF data file using `WriteData` or `ISOUpdateBinary`
|
||||
12. Restore the NDEF file settings to default values with `ChangeFileSettings`.
|
||||
13. Use `ChangeKey` with the recovered application keys to reset `K4` through `K0` to `00000000000000000000000000000000`.
|
||||
14. Update the BoltCard's state to `Reset` in the database.
|
||||
|
||||
Rational: Attempting to call `AuthenticateEV2First` without validating the `p=` and `c=` parameters could render the NTag inoperable after a few attempts.
|
||||
|
||||
|
|
@ -66,12 +78,14 @@ If a `LNUrl Withdraw Service` needs to verify a payment request, follow these st
|
|||
1. Read the NDEF lnurlw URL, extract `p=` and `c=`.
|
||||
2. Derive `Encryption Key (K1)`, decrypts `p=` to get the `PICCData`.
|
||||
3. Check `PICCData[0] == 0xc7`.
|
||||
4. Calculate `ID=PRF(IssuerKey, '2d003f7b' || UID)` with the `UID` from the `PICCData`.
|
||||
5. Fetch `CardKey` from database with `ID`.
|
||||
6. Derive `Authentication Key (K2)` with `CardKey` and the `UID`.
|
||||
7. Verify that the SUN MAC in `c=` matches the one calculated using `Authentication Key (K2)`.
|
||||
8. Confirm that the last-seen counter for `ID` is lower than what is stored in `counter=PICCData[8..11]`. (Little Endian)
|
||||
9. Update the last-seen counter.
|
||||
4. Calculate `ID` with the `UID` from the `PICCData`.
|
||||
5. Fetch the BoltCard's `Version` with `ID` from the database.
|
||||
6. Ensure the BoltCard's state in the database is not `Reset`.
|
||||
7. Generate `CardKey` with `UID` and `Version`.
|
||||
8. Derive `Authentication Key (K2)` with `CardKey` and the `UID`.
|
||||
9. Verify that the SUN MAC in `c=` matches the one calculated using `Authentication Key (K2)`.
|
||||
10. Confirm that the last-seen counter for `ID` is lower than what is stored in `counter=PICCData[8..11]`. (Little Endian)
|
||||
11. Update the last-seen counter.
|
||||
|
||||
Rationale: The `ID` is calculated to prevent the exposure of the `UID` in the `LNUrl Withdraw Service` database. This approach provides both privacy and security. Specifically, because the `UID` is used to derive keys, it is preferable not to store it outside the NTag.
|
||||
|
||||
|
|
@ -79,20 +93,20 @@ Rationale: The `ID` is calculated to prevent the exposure of the `UID` in the `L
|
|||
|
||||
A single `LNUrl Withdraw Service` can own multiple `IssuerKeys`. In such cases, it will need to attempt them all to decrypt `p=`, and pick the first one which satisfies `PICCData[0] == 0xc7` and verifies the `c=` checksum.
|
||||
|
||||
Using multiple `IssuerKeys`, can decrease the impact of a compromised `Encryption Key (K1)` at the cost of performance.
|
||||
Using multiple `IssuerKeys` can decrease the impact of a compromised `Encryption Key (K1)` at the cost of performance.
|
||||
|
||||
## Security consideration
|
||||
|
||||
### K1 security
|
||||
|
||||
Since `K1` is shared among multiple Bolt Cards, the security of this scheme is based on the following assumptions:
|
||||
Since `K1` is shared among multiple BoltCards, the security of this scheme is based on the following assumptions:
|
||||
|
||||
* `K1` cannot be extracted from a legitimate NTag424.
|
||||
* Bolt Card setup occurs in a trusted environment.
|
||||
* BoltCard setup occurs in a trusted environment.
|
||||
|
||||
While NXP gives assurance keys can't be extracted, a non genuine NTag424 could potentially expose these keys.
|
||||
|
||||
Furthermore, because blank NTag424 uses the well-known initial application keys `00000000000000000000000000000000`, communication between the PCD and the PICC could be intercepted. If the Bolt Card setup doesn't occurs in a trusted environment, `K1` could be exposed during the calls to `ChangeKey`.
|
||||
Furthermore, because blank NTag424 uses the well-known initial application keys `00000000000000000000000000000000`, communication between the PCD and the PICC could be intercepted. If the BoltCard setup does not occur in a trusted environment, `K1` could be exposed during the calls to `ChangeKey`.
|
||||
|
||||
However, if `K1` is compromised, the attacker still cannot produce a valid checksum and can only recover the `UID` for tracking purposes.
|
||||
|
||||
|
|
@ -102,7 +116,7 @@ Note that verifying the signature returned by `Read_Sig` can only prove NXP issu
|
|||
|
||||
If the issuer's database is compromised, revealing both the IssuerKey and CardKeys, it would still be infeasible for an attacker to derive `K2` and thus to forge signatures for an arbitrary card.
|
||||
|
||||
This is because the database only stores `ID=PRF(IssuerKey, '2d003f7b' || UID)` and not the `UID` itself.
|
||||
This is because the database only stores `ID` and not the `UID` itself.
|
||||
|
||||
## Implementation notes {#notes}
|
||||
|
||||
|
|
@ -179,7 +193,7 @@ static byte[] RotateLeft(byte[] b)
|
|||
|
||||
## Implementation
|
||||
|
||||
* [BTCPayServer.BoltCardTools](https://github.com/btcpayserver/BTCPayServer.BoltCardTools), a Boltcard/NTag424 library in C#.
|
||||
* [BTCPayServer.BoltCardTools](https://github.com/btcpayserver/BTCPayServer.BoltCardTools), a BoltCard/NTag424 library in C#.
|
||||
|
||||
## Test vectors
|
||||
|
||||
|
|
@ -187,15 +201,16 @@ Input:
|
|||
```
|
||||
UID: 04a39493cc8680
|
||||
Issuer Key: 00000000000000000000000000000001
|
||||
Card Key: 00000000000000000000000000000002
|
||||
Version: 1
|
||||
```
|
||||
|
||||
Expected:
|
||||
```
|
||||
K0: 21940feffa2437910d8eb62b3b0a0648
|
||||
K0: a29119fcb48e737d1591d3489557e49b
|
||||
K1: 55da174c9608993dc27bb3f30a4a7314
|
||||
K2: 2934c4ab339979142dfd50ae0ca55dc2
|
||||
K3: b696f18e5a79e5a0defb25c38109b8e3
|
||||
K4: c9d493b9d3e62ce963586aafcd7c6cfe
|
||||
K2: f4b404be700ab285e333e32348fa3d3b
|
||||
K3: 73610ba4afe45b55319691cb9489142f
|
||||
K4: addd03e52964369be7f2967736b7bdb5
|
||||
ID: e07ce1279d980ecb892a81924b67bf18
|
||||
CardKey: ebff5a4e6da5ee14cbfe720ae06fbed9
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue