Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 6 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,13 @@
# What is Keycard?
# Keyring Applets

Keycard is an implementation of a BIP-32 HD wallet running on Javacard 3.0.4+ (see implementation notes)
Hardware wallet running on Javacard 3.0.4+ (see implementation notes)

It supports among others
- key generation, derivation and signing
- exporting keys defined in the context of EIP-1581 https://eips.ethereum.org/EIPS/eip-1581
- setting up a NFC NDEF tag
- BIP32, BIP39, BIP44, SLIP-10 for ED25519

Communication with the Keycard happens through a simple APDU interface, together with a Secure Channel guaranteeing confidentiality, authentication and integrity of all commands. It supports both NFC and ISO7816 physical interfaces, meaning that it is compatible with any Android phone equipped with NFC, and all USB Smartcard readers.
Communication with the card happens through a simple APDU interface, together with a Secure Channel guaranteeing confidentiality, authentication and integrity of all commands. It supports both NFC and ISO7816 physical interfaces, meaning that it is compatible with any Android phone equipped with NFC, and all USB Smartcard readers.

The most obvious case for integration of Keycard is crypto wallets (ETH, BTC, etc), however it can be used in other systems where a BIP-32 key tree is used and/or you perform authentication/identification.

# Where to start?

A good place to start is our documentation site https://keycard.tech/docs/

You can also join the dicussion about this project on Status channel: https://get.status.im/chat/public/status-keycard

If you just want to use the Keycard as your hardware wallet there are currently three apps supporting it

1. Status [[Android](https://play.google.com/store/apps/details?id=im.status.ethereum)][[iOS](https://apps.apple.com/us/app/status-private-communication/id1178893006)]
2. WallETH [[Android](https://play.google.com/store/apps/details?id=org.walleth)]
3. Enno Walet https://ennowallet.com/

# How to contribute?

Anyone is welcome to contribute to Keycard!

Most of our communication about the project is going on here: https://get.status.im/chat/public/status-keycard

Should you wish to work on an issue, please claim it first by commenting on the GitHub issue that you want to work on it. This is to prevent duplicated efforts from contributors on the same issue.

# How to build the project?

Expand All @@ -43,11 +21,11 @@ log and there is no way to set breakpoints in the applet.

In order to test with the simulator with an IDE, you need to pass these additional parameters to the JVM

```-noverify -Dim.status.keycard.test.target=simulator```
```-noverify -Dso.keyring.card.test.target=simulator```

## Compilation
1. Download and install the JavaCard 3.0.4 SDK from [Oracle](http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-javame-419430.html#java_card_kit-classic-3_0_4-rr-bin-do)
2. Clone the Github repo for our fork of [jCardSim](https://github.com/status-im/jcardsim)
2. Clone the Github repo of [jCardSim](https://github.com/status-im/jcardsim)
3. Create a gradle.properties (see below for an example)
4. Run `./gradlew convertJavacard`

Expand Down Expand Up @@ -83,11 +61,3 @@ The algorithms the card must support are at least:

Best performance is achieved if the card supports:
* Signature.ALG_HMAC_SHA_512

# Other related repositories

Java SDK for Android and Desktop https://github.com/status-im/status-keycard-java

Swift SDK for iOS13 and above https://github.com/status-im/Keycard.swift

Keycard CLI for Desktop https://github.com/status-im/keycard-cli
10 changes: 5 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ javacard {

cap {
aid = '0xA0:0x00:0x00:0x08:0x04:0x00:0x01'
packageName = 'im.status.keycard'
packageName = 'so.keyring.card'
applet {
aid = '0xA0:0x00:0x00:0x08:0x04:0x00:0x01:0x01'
className = 'KeycardApplet'
Expand All @@ -39,12 +39,12 @@ javacard {
}
}

def testTarget = project.properties['im.status.keycard.test.target']
def testTarget = project.properties['so.keyring.card.test.target']
if (!testTarget) {
testTarget = 'card'
}

def pairingPass = project.properties['im.status.keycard.test.pairing']
def pairingPass = project.properties['so.keyring.card.test.pairing']
if (!pairingPass) {
pairingPass = 'KeycardDefaultPairing'
}
Expand Down Expand Up @@ -72,7 +72,7 @@ test {
}
}

task install (type: im.status.keycard.build.InstallTask)
task install (type: so.keyring.card.build.InstallTask)

if (testTarget == 'card') {
tasks.install.dependsOn(convertJavacard)
Expand All @@ -90,7 +90,7 @@ compileTestJava {
}

afterEvaluate {
def jvmArgs = ["-Dim.status.keycard.test.target=${testTarget}", "-Dim.status.keycard.test.pairing=${pairingPass}"]
def jvmArgs = ["-Dso.keyring.card.test.target=${testTarget}", "-Dso.keyring.so.test.pairing=${pairingPass}"]

if (testTarget == 'simulator') {
jvmArgs.push('-noverify')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package im.status.keycard.build;
package so.keyring.card.build;

import im.status.keycard.applet.Identifiers;
import im.status.keycard.desktop.PCSCCardChannel;
Expand Down Expand Up @@ -59,7 +59,7 @@ public void install() {
logger.info("Deleting the old instances and package (if present)");
cmdSet.deleteKeycardInstancesAndPackage();
logger.info("Loading the new package");
cmdSet.loadKeycardPackage(new FileInputStream(this.getProject().file("build/javacard/im/status/keycard/javacard/keycard.cap")), new LoadCallback() {
cmdSet.loadKeycardPackage(new FileInputStream(this.getProject().file("build/javacard/so/keyring/card/javacard/keycard.cap")), new LoadCallback() {
@Override
public void blockLoaded(int loadedBlock, int blockCount) {
logger.info("Loaded block " + loadedBlock + "/" + blockCount);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package im.status.keycard;
package so.keyring.card;

import javacard.framework.*;
import javacard.security.*;
Expand Down
50 changes: 50 additions & 0 deletions src/main/java/so/keyring/card/Consts.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package so.keyring.card;

public class Consts {
public static final byte CLA_ED25519 = (byte) 0x00;
public static final byte INS_KEYGEN = (byte) 0xD0;
public static final byte INS_GET_PRIV = (byte) 0xD2;
public static final byte INS_SET_PUB = (byte) 0xD3;
public static final byte INS_SIGN_INIT = (byte) 0xD4;
public static final byte INS_SIGN_NONCE = (byte) 0xD5;
public static final byte INS_SIGN_FINALIZE = (byte) 0xD6;
public static final byte INS_SIGN_UPDATE = (byte) 0xD7;
public static final byte INS_GET_PRIV_NONCE = (byte) 0xD8;

public final static short E_ALREADY_INITIALIZED = (short) 0xee00;
public final static short E_UNINITIALIZED = (short) 0xee01;
public final static short E_DEBUG_DISABLED = (short) 0xee02;

public final static short SW_Exception = (short) 0xff01;
public final static short SW_ArrayIndexOutOfBoundsException = (short) 0xff02;
public final static short SW_ArithmeticException = (short) 0xff03;
public final static short SW_ArrayStoreException = (short) 0xff04;
public final static short SW_NullPointerException = (short) 0xff05;
public final static short SW_NegativeArraySizeException = (short) 0xff06;
public final static short SW_CryptoException_prefix = (short) 0xf100;
public final static short SW_SystemException_prefix = (short) 0xf200;
public final static short SW_PINException_prefix = (short) 0xf300;
public final static short SW_TransactionException_prefix = (short) 0xf400;
public final static short SW_CardRuntimeException_prefix = (short) 0xf500;

public final static byte[] TRANSFORM_C = {
(byte) 0x70, (byte) 0xd9, (byte) 0x12, (byte) 0x0b,
(byte) 0x9f, (byte) 0x5f, (byte) 0xf9, (byte) 0x44,
(byte) 0x2d, (byte) 0x84, (byte) 0xf7, (byte) 0x23,
(byte) 0xfc, (byte) 0x03, (byte) 0xb0, (byte) 0x81,
(byte) 0x3a, (byte) 0x5e, (byte) 0x2c, (byte) 0x2e,
(byte) 0xb4, (byte) 0x82, (byte) 0xe5, (byte) 0x7d,
(byte) 0x33, (byte) 0x91, (byte) 0xfb, (byte) 0x55,
(byte) 0x00, (byte) 0xba, (byte) 0x81, (byte) 0xe7
};
public final static byte[] TRANSFORM_A3 = {
(byte) 0x2a, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
(byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
(byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
(byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
(byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
(byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
(byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa,
(byte) 0xaa, (byte) 0xad, (byte) 0x24, (byte) 0x51
};
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package im.status.keycard;
package so.keyring.card;

import javacard.framework.JCSystem;
import javacard.framework.Util;
Expand All @@ -25,6 +25,7 @@ public class Crypto {
final static private short HMAC_BLOCK_SIZE = (short) 128;

final static private byte[] KEY_BITCOIN_SEED = {'B', 'i', 't', 'c', 'o', 'i', 'n', ' ', 's', 'e', 'e', 'd'};
final static public byte[] KEY_ED25519_SEED = {'e', 'd', '2', '5', '5', '1', '9', ' ', 's', 'e', 'e', 'd'};

// The below 5 objects can be accessed anywhere from the entire applet
RandomData random;
Expand Down Expand Up @@ -105,6 +106,20 @@ void bip32MasterFromSeed(byte[] seed, short seedOff, short seedSize, byte[] mast
hmacSHA512(KEY_BITCOIN_SEED, (short) 0, (short) KEY_BITCOIN_SEED.length, seed, seedOff, seedSize, masterKey, keyOff);
}

/**
* Applies the algorithm for master key derivation defined by SLIP-10 to the binary seed provided as input.
*
* @param curveKey the key used for hmac512
* @param seed the binary seed
* @param seedOff the offset of the binary seed
* @param seedSize the size of the binary seed
* @param masterKey the output buffer
* @param keyOff the offset in the output buffer
*/
void slip10MasterFromSeed(byte[] curveKey, byte[] seed, short seedOff, short seedSize, byte[] masterKey, short keyOff) {
hmacSHA512(curveKey, (short) 0, (short) curveKey.length, seed, seedOff, seedSize, masterKey, keyOff);
}

/**
* Fixes the S value of the signature as described in BIP-62 to avoid malleable signatures. It also fixes the all
* internal TLV length fields. Returns the number of bytes by which the overall signature length changed (0 or -1).
Expand Down
Loading