Skip to content

Commit 46ae9e1

Browse files
committed
Initial commit
1 parent 680f05a commit 46ae9e1

File tree

17 files changed

+4747
-1
lines changed

17 files changed

+4747
-1
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/.gradle
2+
/.idea
3+
/build

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2022 BryanJacobs
3+
Copyright (c) 2022 Bryan Jacobs
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# FIDO2 CTAP2 Javacard Applet
2+
3+
## Overview
4+
5+
This repository contains sources for a FIDO2 CTAP2 compatible(-ish)
6+
applet targeting the Javacard Classic system, version 3.0.4. In a
7+
nutshell, this lets you take a smartcard, install an app onto it,
8+
and have it work as a FIDO2 authenticator device with a variety of
9+
features. You can generate and use OpenSSH `ecdsa-sk` type keys. You
10+
can securely unlock a LUKS encrypted disk with `systemd-cryptenroll`.
11+
12+
This applet does **not** presently implement U2F support, for
13+
[valid reasons](docs/FAQ.md).
14+
15+
In order to run this, you will need
16+
[a compatible smartcard](docs/requirements.md).
17+
18+
You might be interested in [reading about the security model](docs/security.md).
19+
20+
## Building the application
21+
22+
You'll need to get a copy of:
23+
- com.licel.jcardsim-3.0.5
24+
- JavacardKit, version 3.0.4 (`jckit_304`)
25+
26+
Drop the jcardsim jar into the root of the repository. Set the
27+
environment variable `JC_HOME` to point to your jckit folder.
28+
29+
Run `./gradlew buildJavaCard`, which will produce a `.cap` file
30+
for installation.
31+
32+
## Testing the application
33+
34+
While you can test on an actual smartcard, I prefer to use VSmartCard
35+
and run JCardSim connected to that. There are a few example JCardSim
36+
unit tests in the repository, but you'll get much better analysis
37+
of the behaviour by using real applications or other testing suites
38+
like SoloKey's `fido2-tests`, which you can run against the simulated
39+
application.
40+
41+
The `VSim` class might get you started.
42+
43+
## Contributing
44+
45+
If you want to, feel free!
46+
47+
## Where to go Next
48+
49+
I suggest [reading the FAQ](docs/FAQ.md) and perhaps [the security model](docs/security.md).
50+
51+
## Implementation Status
52+
53+
| Feature | Status |
54+
|--------------------------------|---------------------------------------------------------|
55+
| CTAP1/U2F | Not implemented |
56+
| CTAP2.0 core | Implemented, many caveats |
57+
| CTAP2.0 hmac-secret extension | Implemented |
58+
| Resident keys | Implemented |
59+
| CTAP2.1 alwaysUv | Implemented |
60+
| CTAP2.1 PIN Protocol 1 | Implemented |
61+
| User Presence | User always considered present: not standards compliant |
62+
| CTAP2.1 credProtect extension | Implemented, one caveat |
63+
| CTAP2.1 credential management | Mostly implemented - no user update |
64+
| CTAP2.1 PIN Protocol 2 | Not implemented |
65+
| Attestation certificates | Not implemented |
66+
| CTAP2.1 Enterprise attestation | Not implemented |
67+
| Self attestation | Implemented |
68+
| ECDSA (SecP256r1) | Implemented |
69+
| CTAP2.1 authenticator config | Not implemented |
70+
| CTAP2.1 blob storage | Not implemented |
71+
| Other crypto like ed25519 | Not implemented |
72+
| Extended APDUs | Supported |
73+
| APDU chaining | Supported |
74+
| Performance | Adequate |
75+
| Resource consumption | Constant, but unoptimized |
76+
| Bugs | Yes |
77+
| Code quality | No |

build.gradle

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
buildscript {
2+
repositories {
3+
mavenCentral()
4+
maven { url "https://javacard.pro/maven" }
5+
maven { url "https://deadcode.me/mvn" }
6+
}
7+
dependencies {
8+
classpath("com.klinec:gradle-javacard:1.8.0")
9+
}
10+
}
11+
12+
plugins {
13+
id("java")
14+
id("com.klinec.gradle.javacard") version "1.8.0"
15+
}
16+
17+
group = "us.q3q"
18+
version = "1.0-SNAPSHOT"
19+
20+
repositories {
21+
mavenCentral()
22+
}
23+
24+
dependencies {
25+
testImplementation(files("./jcardsim-3.0.5-SNAPSHOT.jar"))
26+
27+
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
28+
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
29+
}
30+
31+
test {
32+
useJUnitPlatform()
33+
}
34+
35+
javacard {
36+
config {
37+
jckit System.getenv("JC_HOME")
38+
cap {
39+
packageName 'us.q3q.fido2'
40+
version '0.1'
41+
aid 'A0:00:00:06:47'
42+
output 'FIDO2.cap'
43+
applet {
44+
className 'us.q3q.fido2.FIDO2Applet'
45+
aid 'A0:00:00:06:47:2F:00:01'
46+
}
47+
}
48+
}
49+
}

docs/FAQ.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Frequently Asked Questions
2+
3+
## How can you have an FAQ on a newly-created repository? Surely the questions aren't frequent yet.
4+
5+
You caught me, I'm a fraud and these are anticipatory questions.
6+
7+
## What's FIDO2?
8+
9+
If you don't know what that is, you don't need this.
10+
11+
## What's a JavaCard?
12+
13+
If you don't know what that is, you DEFINITELY don't need this.
14+
15+
## Don't you need a CBOR parser to write a CTAP2 authenticator?
16+
17+
Apparently not. Instead of implementing a real CBOR parser I just
18+
poured more sweat into the implemnentation, and added a topping of
19+
non-standards-compliance.
20+
21+
As a result of not having a proper CBOR parser, the app will often
22+
return undesirable error codes on invalid input, but it should
23+
handle most valid input acceptably.
24+
25+
## Why did you write this, when someone else said they were almost done writing a better version?
26+
27+
Well, they said that, but they hadn't published the source code and I got impatient.
28+
29+
Two is better than zero, right?
30+
31+
## Why did you write this at all?
32+
33+
I was pretty unhappy with the idea of trusting my "two factor" SSH
34+
keys' security to a closed-source hardware device, and even the
35+
existing open hardware devices didn't work the way I wanted.
36+
37+
I wanted my password to be used in such a way that without it, the
38+
authenticator was useless - in other words, a true second factor.
39+
40+
So I wrote a CTAP2 implementation that [had that property](security.md).

docs/requirements.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Runtime requirements
2+
3+
This app requires Javacard 3.0.4. I really, really, really wanted to
4+
support Javacard 3.0.1, which runs on cool products like the
5+
Mclear/NFCRings.com OMNI Ring (seriously nice tech!).
6+
7+
Unfortunately, CTAP2.0 requires an EC diffie-helmann key exchange
8+
in order to support PINs or the hmac-secret extension, and it uses
9+
DH with a SHA256 hash. Javacard 3.0.1 supports only ECDH-SHA1.
10+
Javacard 3.0.4 doesn't support ECDH-SHA256 either... but it provides
11+
a "plain" variant that returns the raw DH output which you then
12+
hash yourself - good enough for me.
13+
14+
So it's not possible to make this app work in a meaningful way on
15+
Javacard 3.0.1 or earlier.
16+
17+
So let's discuss the full requirements:
18+
19+
- Javacard Classic 3.0.4
20+
- Approximately 2kB of total RAM, of which around 300 bytes will be reserved
21+
- Support for AES256-CBC
22+
- Support for ECDH-plain
23+
- Support for SHA-256 hashing
24+
- Support for EC with 256-bit keys
25+
- Approximately 15k of storage by default (very tunable)
26+
- Ideally, support for EC TRANSIENT_DESELECT keys, as otherwise you'll get flash usage every app selection
27+
28+
An example of a card I've tested working is the NXP J3H145, but many
29+
others should work fine too.

docs/security.md

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# Security Model
2+
3+
First off, this application is likely not secure. It hasn't been
4+
audit, fuzz tested, or even really tested at all. That means
5+
there's very likely a critical bug that breaks its model.
6+
7+
This page aims to describe the **theoretical** security of the
8+
app, with that in mind. It focuses on cases where this authenticator
9+
deviates from what you might expect from the CTAP2 standard.
10+
11+
## Principle: PINs Matter
12+
13+
The core principle of a FIDO2 device is to be one of two factors.
14+
The authenticator represents "something you have". The PIN represents
15+
"something you know".
16+
17+
Gaining complete access to the authenticator should not be useful for
18+
an attacker unless they know your PIN.
19+
20+
Traditional implementations of FIDO2 devices do this by storing the
21+
PIN on the device "securely", and then relying on the correctness
22+
of their software and the tamper-proofness of their hardware to
23+
protect private keying material.
24+
25+
This app is different. In this app, once you set a PIN, the "wrapping
26+
key" - without which the authenticator is useless - is encrypted using
27+
a key derived from the PIN. This means no credentials can be created
28+
or used unless you provide your PIN...
29+
30+
## Details: Security Levels
31+
32+
On boot, the device generates an AES256 key, the "wrapping key".
33+
34+
Each individual credential issued by the app is a SecP256r1 keypoint,
35+
generated randomly on-device for that credential. The private key
36+
is then AES256-CBC encrypted along with the RP ID, using a random
37+
IV, and the result is used as the "credential ID".
38+
39+
This means relying parties are holding their own encrypted private
40+
keys. The keypair itself provides approximately 128 bits of
41+
brute-force resistance. The wrapping key provides a strong 256 bits.
42+
43+
The wrapping key is - when a PIN is set - encrypted using a "PIN key"
44+
produced by running five (by default) rounds of PBKDF2 over the first
45+
sixteen bytes of a SHA-256 of the user's PIN, with a 28-byte random
46+
salt. This means that "unwrapping" the wrapping key, were an attacker
47+
to gain access to it, would require a targeted attack whose brute-force
48+
difficulty is at most 128 bits, and is likely set by the entropy of
49+
the user's PIN.
50+
51+
**Use a strong PIN** if you care about security in the event your
52+
device is entirely compromised. Despite the name, there is no
53+
requirement that PINs be numeric. You can use any sequence of
54+
characters up to 64 bytes long.
55+
56+
### hmac-secret keys
57+
58+
The hmac-secret extension keys are made by performing an HMAC-SHA256
59+
of a particular credential's ECDSA private key, using a unique
60+
32-byte key. This makes the brute-force resistance of these keys
61+
dependent on the entropy in a raw ECDSA private key, which is
62+
somewhat less than 256 bits and likely considerably stronger
63+
than the ECDSA keypair itself.
64+
65+
## Threat Modeling
66+
67+
### An attacker has my device and knows my PIN
68+
69+
The attacker is you. Do better next time.
70+
71+
### An attacker can intercept traffic to and from my authenticator
72+
73+
Yeah, that's how NFC works.
74+
75+
The important parts of the traffic are encrypted and authenticated
76+
with ECDH. The attacker cannot reasonably "see" your PIN. They can
77+
see incidental data like credential IDs being newly created, but
78+
the security impact is minimal.
79+
80+
If they can forge traffic to the authenticator, they could hard-reset it,
81+
wiping your keys.
82+
83+
### An attacker has malware on the machine I'm using with my authenticator
84+
85+
- The attacker could reset the authenticator, deleting your keys
86+
- They could keylog your PIN, removing its additional security, but
87+
only when you provide it on the compromised machine
88+
- They could attempt to guess your PIN, but they only get eight tries
89+
- If they DO get your PIN, they can use the authenticator as you
90+
for the duration it's connected to the compromised machine
91+
- If you haven't set a PIN, that's the same as them knowing your PIN
92+
for this threat model
93+
94+
### An attacker has physical acccess to my smartcard, and I didn't set a PIN
95+
96+
The attacker has fully compromised your security and can use the
97+
authenticator to pretend to be you in any way it is able.
98+
99+
Resident keys stored on the device will let the attacker see your
100+
user IDs on different web sites, etc. They can use those to log
101+
in to those sites.
102+
103+
Non-resident keys are slightly better: the attacker has to guess
104+
which service you'd registered with, and your username.
105+
106+
### An attacker has physical access to my smartcard, but I set a PIN
107+
108+
If the smartcard itself is physically secure, this is the same as
109+
the "malware" case above. **If not**, then we are in an interesting
110+
situation.
111+
112+
The attacker needs to decrypt the on-device wrapping key. Without
113+
doing that, they can see incidentals like:
114+
- how many different resident keys you've stored on the device
115+
- how long each key's RP ID is, if less than 32 characters
116+
- how long each key's user ID is
117+
- how many different RPs in total have resident keys on the device
118+
119+
What they can't do without decrypting the wrapping key is get at
120+
your actual credentials for sites - the private keys, the RP IDs,
121+
etc. They might - if your device doesn't support transient memory
122+
for EC private keys AND was inopportunely powered off the last time
123+
you used it - be able to use the most recently used keypair you did.
124+
125+
#### Decrypting the wrapping key
126+
127+
As described above, the wrapping key is itself encrypted using
128+
PBKDF2 with a very low iteration count, performed on the first
129+
sixteen bytes of your PIN's SHA256 hash. This means the attacker is
130+
unlikely to already HAVE a rainbow table, but they can start
131+
brute-forcing your PIN.
132+
133+
If you used a strong PIN, you're likely okay for quite a while.
134+
135+
### I left my authenticator plugged into my computer, after entering my PIN
136+
137+
Anyone can use your authenticator as you. Despite what the CTAP2 standard
138+
says about user presence, it's not readily possible to implement
139+
a "timeout" on a Javacard 3.0.4 device. Javacard 3.1 introduces an
140+
(unreliable) uptime counter....
141+
142+
But the implementation currently always assumes user presence.
143+
144+
This Could Be Better.

gradle/wrapper/gradle-wrapper.jar

58.4 KB
Binary file not shown.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
distributionBase=GRADLE_USER_HOME
2+
distributionPath=wrapper/dists
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
4+
zipStoreBase=GRADLE_USER_HOME
5+
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)