Skip to content

Commit 9ef767d

Browse files
applets
1 parent 7054616 commit 9ef767d

38 files changed

+5259
-2
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
build/
2+
__pycache__
3+
.venv
4+
.vscode
5+
.DS_Store

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "sdks"]
2+
path = sdks
3+
url = https://github.com/martinpaljak/oracle_javacard_sdks.git

README.md

Lines changed: 142 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,142 @@
1-
# specter-javacard
2-
JavaCard applets for Specter-DIY secrets storage
1+
# Specter-Javacard
2+
3+
This is a collection of JavaCardOS applets for [Specter-DIY](https://github.com/cryptoadvance/specter-diy) secrets storage.
4+
5+
Documentation for classes and applets is in the [`docs/`](./docs) folder.
6+
7+
Currently all the applets are tested on [NXP JCOP3 J3H145 card](https://www.smartcardfocus.com/shop/ilp/id~879/nxp-j3h145-dual-interface-java-card-144k/p/index.shtml), but we plan to add support of `Infineon SLE78` and `G&D SmartCafe 7.0` soon.
8+
9+
## Applets
10+
11+
- [`Teapot`](./docs/Teapot.md) — a very simple "Hello world" class that doesn't use any PIN protection or secure communication. It can only store up to `255` bytes of data and give it back on request. Perfect for testing communication with the card.
12+
- [`SecureApplet`](./docs/SecureApplet.md) — base class with PIN protection and secure communication.
13+
- [`MemoryCard`](./docs/MemoryCard.md) — extends `SecureApplet`, allows arbitrary data storage.
14+
- [`BlindOracle`](./docs/BlindOracle.md) — extends `SecureApplet`, stores root xprv and supports bip32 key derivation and signing.
15+
- [`SingleUseKey`](./docs/SingleUseKey.md) — extends `SecureApplet`, generates a temporary key on the card that can be used only once to sign a single hash. After that the key is deleted. Can be used for proposals like Bob's and Bryan's presigned transactions stuff.
16+
17+
# Toolchain installation
18+
19+
Instructions are for MacOS only at the moment.
20+
21+
JDK8 works. The most recent one doesn't.
22+
23+
Big thanks to https://adoptopenjdk.net/ for all old versions of jdk!
24+
25+
Install deps:
26+
27+
```sh
28+
brew tap adoptopenjdk/openjdk
29+
brew cask install adoptopenjdk/openjdk/adoptopenjdk8
30+
brew install [email protected]
31+
```
32+
33+
Add to your path (maybe put into `.bash_profile`):
34+
35+
```sh
36+
export PATH="/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/bin/:$PATH"
37+
export PATH="/usr/local/opt/[email protected]/bin:$PATH"
38+
export JAVA_HOME="/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home"
39+
```
40+
41+
# Tools
42+
43+
- `gp.jar` - a working and easy to use tool for applets management, by [martinpaljak](https://github.com/martinpaljak/GlobalPlatformPro) (LGPL3)
44+
- `ant-javacard.jar` - ant task to build javacard applet, by [martinpaljak](https://github.com/martinpaljak/ant-javacard) (MIT)
45+
- `sdks` folder - submodule with JavaCard SDKs of different versions (Oracle-owns-you-and-your-grandma license)
46+
47+
It's convenient to make an alias for `gp.jar`:
48+
49+
```sh
50+
alias gp="java -jar $PWD/gp.jar"
51+
```
52+
53+
# How to build
54+
55+
Run to compile all applets:
56+
57+
```sh
58+
ant all
59+
```
60+
61+
You should get `.cap` files for all the applets in the `build/cap` folder.
62+
63+
Run to compile a specific applet:
64+
65+
```sh
66+
ant Teapot
67+
```
68+
69+
To see the build targets:
70+
71+
```sh
72+
ant -projecthelp
73+
```
74+
75+
Now upload applet to the card:
76+
77+
```sh
78+
gp --install build/cap/TeapotApplet.cap
79+
```
80+
81+
Check that it appeared in the list of applets (should appear with aid `B00B5111CA01`):
82+
83+
```sh
84+
gp -l
85+
```
86+
87+
Now you can communicate with the applet.
88+
89+
Jupyter notebook with some examples for applets are in [`jupyter/`](jupyter/) folder.
90+
91+
# Simulator
92+
93+
Example how to run `BlindOracle` on port `21111` with AID `B00B5111CE01`:
94+
95+
```sh
96+
java -jar "simulator.jar" -p 21111 -a "B00B5111CE01" -c "toys.BlindOracleApplet" -u "file://$PWD/build/classes/BlindOracle/"
97+
```
98+
99+
# Useful links
100+
101+
- https://github.com/OpenCryptoProject/JCMathLib - library for arbitrary elliptic curve operations on javacard
102+
- https://opencryptojc.org/ - making JavaCards open
103+
- https://pyscard.sourceforge.io/ - python tool to talk to smartcards
104+
- https://smartcard-atr.apdu.fr/ - ATR (Answer To Reset) parser
105+
- [keycard.tech](https://keycard.tech/) - JavaCard applet with BIP-32 support
106+
- https://www.youtube.com/watch?v=vd0-Uhx2OoQ - nice talk about JavaCards and open-source ecosystem
107+
108+
# Cards that make sense
109+
110+
Compatibility table: https://www.fi.muni.cz/~xsvenda/jcalgtest/table.html
111+
112+
## Algorithms
113+
114+
`ALG_EC_SVDP_DH_PLAIN` should be there. Many cards support it. Not necessarily `ALG_EC_SVDP_DH_PLAIN_XY`. Required for point multiplication (other than G, i.e. for Schnorr)
115+
116+
`ALG_EC_PACE_GM` is a nice one - allows point addition. AFAIK available only on NXP JCOP3 J3H145 and NXP JCOP4 series.
117+
118+
`TYPE_EC_FP_PRIVATE_TRANSIENT` - useful for bip32 derivation
119+
Infineon SLE78 JCard, G&D Smartcafe 7.0, NXP JCOP4 P71D321, NXP JCOP4 J3R200
120+
Taisys SIMoME Vault
121+
122+
`ALG_HMAC_SHA512` - useful for fast PBKDF2 in BIP-39
123+
Taisys SIMoME Vault
124+
125+
# Don't write your own crypto
126+
127+
But sometimes we have to...
128+
Here we have modulo addition for bip32 key derivation, this one is critical.
129+
For public key uncompression we can use fast functions as no secrets are involved there.
130+
131+
For finite field ariphmetics we are abusing `RSA` encryption coprocessor where we set modulo to `FP` or `N` of `secp256k1` curve and public key to the exponent we need.
132+
133+
Point addition is implemented using `ALG_EC_PACE_GM`, but can be also done manually with a few simple equations over `FP`.
134+
135+
## Rules for crypto
136+
137+
- No branching - `if/switch` statements can leak information through side channels
138+
- Don't do case-via-offset - access time to elements with different indexes can be different
139+
- Use transient arrays when possible - it's orders of magnitude faster than EEPROM
140+
- Use `Key` class when possible, JC platforms secures them better than simple arrays
141+
- Encrypt-then-hmac is the right way to build the secure communiaction channel
142+
- Use ephimerial keys or random nonces when possible, they help against replay attacks

ant-javacard.jar

50.6 KB
Binary file not shown.

build.xml

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project basedir="." default="all" name="JavaCardPlayground build">
3+
<!-- Applet building dependencies -->
4+
<property name="JC211" value="sdks/jc211_kit"/>
5+
<property name="JC212" value="sdks/jc212_kit"/>
6+
<property name="JC221" value="sdks/jc221_kit"/>
7+
<property name="JC222" value="sdks/jc222_kit"/>
8+
<property name="JC303" value="sdks/jc303_kit"/>
9+
<property name="JC304" value="sdks/jc304_kit"/>
10+
<property name="JC305" value="sdks/jc305u3_kit"/>
11+
<property name="JCKIT" value="${JC304}"/>
12+
13+
<property name="src.dir" value="src/main/java"/>
14+
<property name="build.dir" value="build"/>
15+
<property name="cap.dir" value="${build.dir}/cap"/>
16+
<property name="class.dir" value="${build.dir}/classes"/>
17+
<property name="exp.dir" value="${build.dir}/exp"/>
18+
<property name="deps.crypto" value="Crypto*,HMAC*,Secp*,Transient*,Finite*"/>
19+
<property name="deps.secureapplet" value="Secure*,${deps.crypto},PinCode.java"/>
20+
21+
<!-- ant-javacard task from javacard.pro -->
22+
<taskdef name="javacard" classname="pro.javacard.ant.JavaCard" classpath="ant-javacard.jar"/>
23+
<!-- All included applets -->
24+
<target name="Teapot" depends="init">
25+
<javacard jckit="${JCKIT}">
26+
<cap output="${cap.dir}/TeapotApplet.cap"
27+
sources="${src.dir}/toys"
28+
classes="${class.dir}/Teapot"
29+
includes="TeapotApplet.java,DataEntry.java"
30+
>
31+
<applet class="toys.TeapotApplet" aid="B00B5111CA01"/>
32+
</cap>
33+
</javacard>
34+
</target>
35+
<target name="SecureApplet" depends="init">
36+
<javacard jckit="${JCKIT}">
37+
<cap output="${cap.dir}/SecureApplet.cap"
38+
sources="${src.dir}/toys"
39+
classes="${class.dir}/SecureApplet"
40+
includes="${deps.secureapplet}"
41+
>
42+
<applet class="toys.SecureApplet" aid="B00B5111FF01"/>
43+
</cap>
44+
</javacard>
45+
</target>
46+
<target name="MemoryCard" depends="init">
47+
<javacard jckit="${JCKIT}">
48+
<cap output="${cap.dir}/MemoryCardApplet.cap"
49+
sources="${src.dir}/toys"
50+
classes="${class.dir}/MemoryCard"
51+
includes="${deps.secureapplet},DataEntry.java,MemoryCardApplet.java"
52+
>
53+
<applet class="toys.MemoryCardApplet" aid="B00B5111CB01"/>
54+
</cap>
55+
</javacard>
56+
</target>
57+
<target name="SingleUseKey" depends="init">
58+
<javacard jckit="${JCKIT}">
59+
<cap output="${cap.dir}/SingleUseKeyApplet.cap"
60+
sources="${src.dir}/toys"
61+
classes="${class.dir}/SingleUseKey"
62+
includes="${deps.secureapplet},SingleUseKeyApplet.java"
63+
>
64+
<applet class="toys.SingleUseKeyApplet" aid="B00B5111CD01"/>
65+
</cap>
66+
</javacard>
67+
</target>
68+
<target name="BlindOracle" depends="init">
69+
<javacard jckit="${JCKIT}">
70+
<cap output="${cap.dir}/BlindOracleApplet.cap"
71+
sources="${src.dir}/toys"
72+
classes="${class.dir}/BlindOracle"
73+
includes="${deps.secureapplet},BlindOracleApplet.java"
74+
>
75+
<applet class="toys.BlindOracleApplet" aid="B00B5111CE01"/>
76+
</cap>
77+
</javacard>
78+
</target>
79+
<target name="Bundle" depends="init">
80+
<javacard jckit="${JCKIT}">
81+
<cap output="${cap.dir}/Bundle.cap" sources="${src.dir}/toys" classes="${class.dir}/Bundle">
82+
<applet class="toys.TeapotApplet" aid="B00B5111CF01"/>
83+
<applet class="toys.SecureApplet" aid="B00B5111CF02"/>
84+
<applet class="toys.MemoryCardApplet" aid="B00B5111CF03"/>
85+
<applet class="toys.BlindOracleApplet" aid="B00B5111CF04"/>
86+
<applet class="toys.SingleUseKeyApplet" aid="B00B5111CF05"/>
87+
</cap>
88+
</javacard>
89+
</target>
90+
91+
<target name="all" depends="Teapot,SecureApplet,MemoryCard,SingleUseKey,BlindOracle,Bundle"/>
92+
93+
<target name="init">
94+
<mkdir dir="${build.dir}"/>
95+
<mkdir dir="${cap.dir}"/>
96+
<mkdir dir="${class.dir}"/>
97+
</target>
98+
99+
<target name="clean">
100+
<delete dir="${build.dir}"/>
101+
</target>
102+
103+
<target name="clean-build" depends="clean,all"/>
104+
105+
</project>

docs/BlindOracle.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# `BlindOracle`
2+
3+
Allows storage of bitcoin secrets on the card, deriving keys according to bip32 and signing arbitrary messages with these keys.
4+
5+
Applet can:
6+
- import bip32 seed or master private key
7+
- generate a unique xprv internally (careful with this mode, no backup possible)
8+
- derive child keys from the root key and return corresponding xpubs
9+
- sign arbitrary messages with a derived key
10+
11+
Instead of the full hd key serialization we use a stripped-down version where only `chain code` and the `public key` is transmitted. Because for the full hd key you need ripemd160 hash function to calculate parent fingerprint. This hash function is not available on javacards and software implementation requires plenty of storage space. But the host can calculate the parent fingerprint by asking for the parent xpub first.
12+
13+
## APDUs
14+
15+
Applet ID: `B00B5111CE01`
16+
17+
To select applet use `SELECT` APDU: `00A4040006B00B5111CE0100`
18+
19+
## SC commands
20+
21+
All commands defined in the [`SecureApplet`](./SecureApplet.md) are available. On top of that we have two more commands:
22+
23+
All commands are **PIN protected** - the card should be unlocked first.
24+
25+
### Key management
26+
27+
Loads or generates the root key on the card.
28+
29+
#### Set seed
30+
31+
Calculates root key from bip32 seed (64 bytes)
32+
33+
| Field | Value |
34+
| ------ | ---------------------------------------- |
35+
| CMD | `0x10` |
36+
| SUBCMD | `0x00` |
37+
| DATA | 64-byte seed |
38+
| RETURN | Responce code: `0x9000`, `DATA`: root xpub: `<chain_code><pubkey>` |
39+
40+
#### Set root key
41+
42+
Sets root key directly, format: `<chain_code><00><private_key>`
43+
44+
| Field | Value |
45+
| ------ | ---------------------------------------- |
46+
| CMD | `0x10` |
47+
| SUBCMD | `0x01` |
48+
| DATA | 65-byte root key: `<chain_code><00><private_key>` |
49+
| RETURN | Responce code: `0x9000`, `DATA`: root xpub: `<chain_code><pubkey>` |
50+
51+
#### Generate random key
52+
53+
Generates a random key, this key never leaves the device so it can't be backed up. Be careful with it - add some kind of backup mechanism to your script, otherwise if the card dies you will lose your funds.
54+
55+
| Field | Value |
56+
| ------ | ---------------------------------------- |
57+
| CMD | `0x10` |
58+
| SUBCMD | `0x7D` |
59+
| DATA | ignored |
60+
| RETURN | Responce code: `0x9000`, `DATA`: root xpub: `<chain_code><pubkey>` |
61+
62+
### Key derivation and signing
63+
64+
#### Get root xpub
65+
66+
Returns xpub of the root key stored on the card.
67+
68+
| Field | Value |
69+
| ------ | ---------------------------------------- |
70+
| CMD | `0x11` |
71+
| SUBCMD | `0x00` |
72+
| DATA | ignored |
73+
| RETURN | Responce code: `0x9000`, `DATA`: root xpub: `<chain_code><pubkey>` |
74+
75+
#### Derive child
76+
77+
Derives a child from root and temporary stores it in RAM. This cached child is available until reset or next call of this method.
78+
79+
You can derive a child from the root (`keyid = 0x00`) or from currently derived child (`keyid = 0x01`). Currently derived child is replaced by the result of the function for all subsequent commands.
80+
81+
| Field | Value |
82+
| ------ | ---------------------------------------- |
83+
| CMD | `0x11` |
84+
| SUBCMD | `0x01` |
85+
| DATA | 1-byte keyid, derivation path: sequence of 4-byte big endian encoded indexes. `<keyid><index><index><index>` |
86+
| RETURN | Responce code: `0x9000`, `DATA`: xpub of the derived child: `<chain_code><pubkey>` |
87+
88+
#### Get current child
89+
90+
Returns currently derived xpub.
91+
92+
| Field | Value |
93+
| ------ | ---------------------------------------- |
94+
| CMD | `0x11` |
95+
| SUBCMD | `0x02` |
96+
| DATA | ignored |
97+
| RETURN | Responce code: `0x9000`, `DATA`: current child xpub: `<chain_code><pubkey>` |
98+
99+
#### Sign
100+
101+
Signs a 32-byte message hash with one of current keys - root or derived.
102+
103+
Use `keyid = 0x00` to sign with root and `keyid = 0x01` to sign with current child.
104+
105+
| Field | Value |
106+
| ------ | ---------------------------------------- |
107+
| CMD | `0x11` |
108+
| SUBCMD | `0x03` |
109+
| DATA | 32-byte message, keyid |
110+
| RETURN | Responce code: `0x9000`, `DATA`: der-encoded ecdsa signature |
111+
112+
113+
#### Derive and sign
114+
115+
Derives a key, signs a message hash with this key. Doesn't store the key in the `01` slot, so the card state is unchanged.
116+
117+
Use `keyid = 0x00` to derive from root and `keyid = 0x01` to derive from current child.
118+
119+
Derivation path is a sequence of 4-byte big-endian indexes of the derivation paths.
120+
121+
| Field | Value |
122+
| ------ | ---------------------------------------- |
123+
| CMD | `0x11` |
124+
| SUBCMD | `0x04` |
125+
| DATA | 32-byte message, keyid, derivation path |
126+
| RETURN | Responce code: `0x9000`, `DATA`: der-encoded ecdsa signature |

0 commit comments

Comments
 (0)