Skip to content

Commit df9ba31

Browse files
committed
Finish Javadoc
1 parent fa7497e commit df9ba31

File tree

3 files changed

+88
-15
lines changed

3 files changed

+88
-15
lines changed

docs/FAQ.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,8 @@ stacks.
126126

127127
You haven't set a PIN. You can turn off this feature in the code, or you can
128128
set a PIN. If I were you, I would use a PIN with resident keys.
129+
130+
## I'm getting some strange CBOR error when I try to use this
131+
132+
Run the app in JCardSim with VSmartCard and hook up your Java debugger.
133+
See what's going on. Raise a pull request to fix it.

docs/security.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ device is entirely compromised. Despite the name, there is no
5757
requirement that PINs be numeric. You can use any sequence of
5858
characters up to 64 bytes long.
5959

60+
Your PIN is presented to the authenticator at least once per power-up,
61+
and it's done encrypted over an ECDH channel. The authenticator returns a
62+
32-byte "pinToken", also encrypted. From then on proof of possession of the
63+
PIN is via challenge-response using 16 bytes of the hash of whatever content
64+
with pinToken as the key.
65+
66+
In other words, it's pretty secure. The PIN token is rerandomized each time a
67+
guess is unsuccessful and each authenticator reset.
68+
6069
### hmac-secret keys
6170

6271
The hmac-secret extension keys are made by performing an HMAC-SHA256

src/main/java/us/q3q/fido2/FIDO2Applet.java

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,17 @@ private short truncateRPId(byte[] rpIdBuf, short rpIdIdx, short rpIdLen, byte[]
992992
return rpIdLen;
993993
}
994994

995+
/**
996+
* Handspun implementation of HMAC-SHA256, to work around lack of hardware support
997+
*
998+
* @param keyBuff Buffer containing 32-byte-long private key
999+
* @param keyOff Offset of private key in key buffer
1000+
* @param content Buffer containing arbitrary-length content to be HMACed
1001+
* @param contentOff Offset of content in buffer
1002+
* @param contentLen Length of content
1003+
* @param outputBuff Buffer into which output should be written - must have 32 bytes available
1004+
* @param outputOff Write index into output buffer
1005+
*/
9951006
private void hmacSha256(byte[] keyBuff, short keyOff,
9961007
byte[] content, short contentOff, short contentLen,
9971008
byte[] outputBuff, short outputOff) {
@@ -1023,6 +1034,16 @@ private void hmacSha256(byte[] keyBuff, short keyOff,
10231034
scratchRelease(scratchAmt);
10241035
}
10251036

1037+
/**
1038+
* Uses the currently-set pinToken to hash some data and compare against a verification value
1039+
*
1040+
* @param apdu Request/response object
1041+
* @param content Buffer containing content to HMAC using the pinToken
1042+
* @param contentIdx Index of content in given buffer
1043+
* @param contentLen Length of content
1044+
* @param checkAgainst Buffer containing "correct" hash we're looking for
1045+
* @param checkIdx Index of correct hash in corresponding buffer
1046+
*/
10261047
private void checkPinTokenProtocolOne(APDU apdu, byte[] content, short contentIdx, short contentLen,
10271048
byte[] checkAgainst, short checkIdx) {
10281049
short scratchAmt = (short) 32;
@@ -1049,11 +1070,19 @@ private void checkPinTokenProtocolOne(APDU apdu, byte[] content, short contentId
10491070
scratchRelease(scratchAmt);
10501071
}
10511072

1073+
/**
1074+
* Consumes an incoming pinAuth block and checks it matches our set pinToken.
1075+
*
1076+
* @param apdu Request/response object
1077+
* @param readIdx Read index into bufferMem pointing to a 16-byte array (pinAuth)
1078+
* @param clientDataHashIdx Index in bufferMem of the hash of the clientData object, as given by the platform
1079+
*
1080+
* @return New read index into bufferMem after consuming the pinAuth options block
1081+
*/
10521082
private short verifyPinAuth(APDU apdu, short readIdx, short clientDataHashIdx) {
1053-
if (bufferMem[readIdx] != 0x50) { // byte array, 16 bytes long
1083+
if (bufferMem[readIdx++] != 0x50) { // byte array, 16 bytes long
10541084
sendErrorByte(apdu, FIDOConstants.CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
10551085
}
1056-
readIdx++;
10571086

10581087
checkPinTokenProtocolOne(apdu, bufferMem, clientDataHashIdx, CLIENT_DATA_HASH_LEN,
10591088
bufferMem, readIdx);
@@ -1063,6 +1092,17 @@ private short verifyPinAuth(APDU apdu, short readIdx, short clientDataHashIdx) {
10631092
return readIdx;
10641093
}
10651094

1095+
/**
1096+
* Consumes a CBOR block of public key data, and checks if it represents a supported algorithm.
1097+
* After call, tempShorts[IDX_RESET_FOUND_KEY_MATCH] will be true if the key is compatible. It should be set
1098+
* to false prior to call
1099+
*
1100+
* @param apdu Request/response object
1101+
* @param readIdx Read index into bufferMem
1102+
* @param lc Length of incoming request, as sent by the platform
1103+
*
1104+
* @return New read index into bufferMem after consuming public key block
1105+
*/
10661106
private short checkIfPubKeyBlockSupported(APDU apdu, short readIdx, short lc) {
10671107
if (bufferMem[readIdx++] != (byte) 0xA2) {
10681108
sendErrorByte(apdu, FIDOConstants.CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
@@ -1114,29 +1154,40 @@ private short checkIfPubKeyBlockSupported(APDU apdu, short readIdx, short lc) {
11141154
return readIdx;
11151155
}
11161156

1117-
private short writeADBasic(short adLen, short outputLen, byte flags, byte[] rpIdBuffer, short rpIdOffset) {
1157+
/**
1158+
* Write the portions of an authData block that are used for both makeCredential and getAssertion
1159+
*
1160+
* @param adLen Length of the overall AD block
1161+
* @param writeIdx Write index into bufferMem
1162+
* @param flags CTAP2 "flags" byte value
1163+
* @param rpIdBuffer Buffer containing a hash of the RP ID
1164+
* @param rpIdOffset Offset of the RP ID hash in the given buffer
1165+
*
1166+
* @return New write index into bufferMem after serializing AD block
1167+
*/
1168+
private short writeADBasic(short adLen, short writeIdx, byte flags, byte[] rpIdBuffer, short rpIdOffset) {
11181169
short adAddlBytes;
11191170

11201171
if (adLen < 24) {
1121-
bufferMem[outputLen++] = (byte)(0x50 + adLen);
1172+
bufferMem[writeIdx++] = (byte)(0x50 + adLen);
11221173
adAddlBytes = 1;
11231174
} else if (adLen < 256) {
1124-
bufferMem[outputLen++] = 0x58; // byte string, with one-byte length
1125-
bufferMem[outputLen++] = (byte) adLen;
1175+
bufferMem[writeIdx++] = 0x58; // byte string, with one-byte length
1176+
bufferMem[writeIdx++] = (byte) adLen;
11261177
adAddlBytes = 2;
11271178
} else {
1128-
bufferMem[outputLen++] = 0x59; // byte string, with two-byte length
1129-
outputLen = Util.setShort(bufferMem, outputLen, adLen);
1179+
bufferMem[writeIdx++] = 0x59; // byte string, with two-byte length
1180+
writeIdx = Util.setShort(bufferMem, writeIdx, adLen);
11301181
adAddlBytes = 3;
11311182
}
11321183

11331184
// RPID hash
1134-
outputLen = Util.arrayCopyNonAtomic(rpIdBuffer, rpIdOffset, bufferMem, outputLen, RP_HASH_LEN);
1185+
writeIdx = Util.arrayCopyNonAtomic(rpIdBuffer, rpIdOffset, bufferMem, writeIdx, RP_HASH_LEN);
11351186

1136-
bufferMem[outputLen++] = flags; // flags
1187+
bufferMem[writeIdx++] = flags; // flags
11371188

11381189
// counter
1139-
outputLen = encodeCounter(bufferMem, outputLen);
1190+
writeIdx = encodeCounter(bufferMem, writeIdx);
11401191
incrementCounter();
11411192

11421193
return adAddlBytes;
@@ -2424,6 +2475,13 @@ private void credManagementSubcommand(APDU apdu, short lc, short readIdx) {
24242475
}
24252476
}
24262477

2478+
/**
2479+
* Deletes a resident key by its credential ID blob, and updates bookkeeping state to match
2480+
*
2481+
* @param apdu Request/response object
2482+
* @param readOffset Read index into bufferMem
2483+
* @param lc Length of incoming request, as sent by the platform
2484+
*/
24272485
private void handleDeleteCred(APDU apdu, short readOffset, short lc) {
24282486
tempShorts[IDX_CRED_ITERATION_POINTER] = 0;
24292487
tempShorts[IDX_RP_ITERATION_POINTER] = 0;
@@ -2518,10 +2576,11 @@ private void handleDeleteCred(APDU apdu, short readOffset, short lc) {
25182576
/**
25192577
* Enumerates creds
25202578
*
2521-
* @param apdu
2522-
* @param bufferIdx
2523-
* @param startCredIdx
2524-
* @param lc
2579+
* @param apdu Request/response object
2580+
* @param bufferIdx Read index into bufferMem
2581+
* @param startCredIdx Offset of the first credential to consider, in the resident key slots.
2582+
* If zero, we're starting a new iteration
2583+
* @param lc Length of the incoming request, as sent by the platform
25252584
*/
25262585
private void handleEnumerateCreds(APDU apdu, short bufferIdx, short startCredIdx, short lc) {
25272586
tempShorts[IDX_CRED_ITERATION_POINTER] = 0;

0 commit comments

Comments
 (0)