Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
F
Fido2Applet
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Josh Ji
Fido2Applet
Commits
4fe40fff
Commit
4fe40fff
authored
Jun 13, 2022
by
Josh Ji
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
implement IDSecret.java, dumpIDSecret(), getPuKxRx(), getPuKxCx()
parent
0bd67b09
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
2415 additions
and
1505 deletions
+2415
-1505
build.gradle
build.gradle
+28
-0
build/javacard/applet.cap
build/javacard/applet.cap
+0
-0
build/javacard/applet.exp/vku2f.jar
build/javacard/applet.exp/vku2f.jar
+0
-0
build/javacard/applet.jca
build/javacard/applet.jca
+2204
-1480
src/main/java/com/josh/vku2f/AttestationKeyPair.java
src/main/java/com/josh/vku2f/AttestationKeyPair.java
+4
-5
src/main/java/com/josh/vku2f/CTAP2.java
src/main/java/com/josh/vku2f/CTAP2.java
+83
-15
src/main/java/com/josh/vku2f/IDSecret.java
src/main/java/com/josh/vku2f/IDSecret.java
+89
-0
src/main/java/com/josh/vku2f/Utf8Strings.java
src/main/java/com/josh/vku2f/Utf8Strings.java
+7
-5
No files found.
build.gradle
View file @
4fe40fff
...
...
@@ -65,6 +65,22 @@ javacard{
name
"getCertificate"
apdu
"80100000014A"
}
script
{
name
"getFreeSpace"
apdu
"80CAFF21"
}
script
{
name
"dumpIDSecret"
apdu
"80100000015F"
}
script
{
name
"getPuKxRx"
apdu
"80100000065050524C4142"
}
script
{
name
"getCx"
apdu
"801000000151"
}
task
{
name
"getFidoInfo"
scripts
"select applet"
,
"getFidoInfo"
...
...
@@ -85,5 +101,17 @@ javacard{
name
'reset'
scripts
'select applet'
,
'reset'
}
task
{
name
'getFreeSpace'
scripts
'getFreeSpace'
}
task
{
name
'dumpIDSecret'
scripts
'select applet'
,
'dumpIDSecret'
}
task
{
name
'getPuKxRx'
scripts
'select applet'
,
'getPuKxRx'
}
}
}
build/javacard/applet.cap
View file @
4fe40fff
No preview for this file type
build/javacard/applet.exp/vku2f.jar
View file @
4fe40fff
No preview for this file type
build/javacard/applet.jca
View file @
4fe40fff
This diff is collapsed.
Click to expand it.
src/main/java/com/josh/vku2f/AttestationKeyPair.java
View file @
4fe40fff
...
...
@@ -17,11 +17,7 @@
package
com.josh.vku2f
;
import
javacard.framework.Util
;
import
javacard.security.ECKey
;
import
javacard.security.ECPublicKey
;
import
javacard.security.KeyBuilder
;
import
javacard.security.KeyPair
;
import
javacard.security.Signature
;
import
javacard.security.*
;
/**
* Attestation keypair object.
...
...
@@ -88,4 +84,7 @@ public class AttestationKeyPair {
public
short
getPubkey
(
byte
[]
outBuf
,
short
outOff
)
{
return
((
ECPublicKey
)
kp
.
getPublic
()).
getW
(
outBuf
,
outOff
);
}
public
PrivateKey
getPrivate
(){
return
kp
.
getPrivate
();
}
}
src/main/java/com/josh/vku2f/CTAP2.java
View file @
4fe40fff
...
...
@@ -26,6 +26,7 @@ import javacard.framework.UserException;
import
javacard.framework.Util
;
import
javacard.security.*
;
import
javacardx.apdu.ExtendedLength
;
import
static
com
.
josh
.
vku2f
.
CTAP2ErrorCode
.*;
import
static
com
.
josh
.
vku2f
.
ClientPINSubCommand
.*;
...
...
@@ -40,7 +41,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
private
final
boolean
[]
isChaining
;
private
final
boolean
[]
isOutChaining
;
//transient memory, clear on reset
//transient memory, clear on reset
(power off)
private
final
short
[]
nextAssertion
;
private
byte
[]
fidoInfo
;
...
...
@@ -87,9 +88,12 @@ public class CTAP2 extends Applet implements ExtendedLength {
public
static
final
byte
FIDO2_VENDOR_GET_CREDENTIAL_COUNT
=
(
byte
)
0x45
;
public
static
final
byte
FIDO2_VENDOR_ATTEST_GETCERT
=
(
byte
)
0x4A
;
//PRLAB
public
static
final
byte
PRLAB_GET_PUKX_RX
=
(
byte
)
0x50
;
public
static
final
byte
PRLAB_GET_CX
=
(
byte
)
0x51
;
//IDSecret
public
final
IDSecret
idSecret
;
public
static
final
byte
ID_SECRET_GET_PUKX_RX
=
(
byte
)
0x50
;
public
static
final
byte
ID_SECRET_GET_CX
=
(
byte
)
0x51
;
public
static
final
byte
ID_SECRET_GET_PUKX_CX
=
(
byte
)
0x52
;
public
static
final
byte
ID_SECRET_DUMP_ALL
=
(
byte
)
0x5F
;
// AAGUID - Authenticator Attestation Global Unique Identifier
...
...
@@ -134,6 +138,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
clientPINCommand
=
new
ClientPINCommand
();
// ecDhKeyPair = new KeyPair(ecDhPub, ecDhPriv);
// ecDhSet = JCSystem.makeTransientBooleanArray((short) 1, JCSystem.CLEAR_ON_RESET);
idSecret
=
new
IDSecret
();
}
...
...
@@ -265,7 +270,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
}
p
ublic
void
handle
(
APDU
apdu
)
{
p
rivate
void
handle
(
APDU
apdu
)
{
byte
[]
buffer
=
apdu
.
getBuffer
();
tempCredential
=
null
;
authenticatorMakeCredential
=
null
;
...
...
@@ -312,11 +317,18 @@ public class CTAP2 extends Applet implements ExtendedLength {
case
FIDO2_VENDOR_GET_CREDENTIAL_COUNT:
//0x45
getCredentialCount
(
apdu
);
break
;
case
PRLAB_GET_PUKX_RX:
// 0x50
getPuKxRx
(
apdu
);
case
ID_SECRET_GET_PUKX_RX:
// 0x50
getPuKxRx
(
apdu
,
tempVars
[
3
]);
break
;
case
ID_SECRET_GET_CX:
// 0x51
getCx
(
apdu
,
tempVars
[
3
]);
break
;
case
ID_SECRET_GET_PUKX_CX:
getPuKxCx
(
apdu
,
tempVars
[
3
]);
break
;
case
PRLAB_GET_CX:
// 0x51
getCx
(
apdu
);
case
ID_SECRET_DUMP_ALL:
// 0x5F
// apdu.setOutgoingAndSend((short)0, (short)4); // testing, for break point setting
dumpIDSecret
(
apdu
);
break
;
case
FIDO2_VENDOR_ATTEST_GETCERT:
//0x4a
getCert
(
apdu
);
...
...
@@ -327,7 +339,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
}
p
ublic
void
personalizationComplete
(
APDU
apdu
)
{
p
rivate
void
personalizationComplete
(
APDU
apdu
)
{
if
(
attestationKeyPair
.
isCertSet
()
&&
!
personalizeComplete
)
{
personalizeComplete
=
true
;
returnError
(
apdu
,
CTAP1_ERR_SUCCESS
);
...
...
@@ -341,7 +353,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
*
* @param apdu apdu buffer
*/
p
ublic
void
getAttestPublic
(
APDU
apdu
)
{
p
rivate
void
getAttestPublic
(
APDU
apdu
)
{
if
(
personalizeComplete
)
{
returnError
(
apdu
,
CTAP1_ERR_INVALID_COMMAND
);
return
;
...
...
@@ -354,23 +366,79 @@ public class CTAP2 extends Applet implements ExtendedLength {
}
/** get counter's value */
p
ublic
void
getCredentialCount
(
APDU
apdu
){
p
rivate
void
getCredentialCount
(
APDU
apdu
){
Util
.
setShort
(
apdu
.
getBuffer
(),
(
short
)
0x00
,
credentialArray
.
getCount
());
apdu
.
setOutgoingAndSend
((
short
)
0x00
,
(
short
)
2
);
}
/**
* for original framework purpose
*
* input: IDx String
* return: PuKx and Rx in CBOR form
*/
private
void
getPuKxRx
(
APDU
apdu
){
private
void
getPuKxRx
(
APDU
apdu
,
short
dataLength
){
// TODO IDx have to get data from dataBuffer at index 1
idSecret
.
IDx
=
new
DomString
(
dataBuffer
,
dataLength
);
cborEncoder
.
init
(
dataBuffer
,
(
short
)
0
,
(
short
)
1200
);
cborEncoder
.
startArray
((
short
)
2
);
cborEncoder
.
encodeUInt32
(
idSecret
.
Rx
,
(
short
)
0
);
tempVars
[
0
]
=
attestationKeyPair
.
getPubkey
(
scratch
,
(
short
)
0
);
cborEncoder
.
encodeByteString
(
scratch
,
(
short
)
0
,
tempVars
[
0
]);
apdu
.
setOutgoing
();
apdu
.
setOutgoingLength
(
cborEncoder
.
getCurrentOffset
());
apdu
.
sendBytesLong
(
dataBuffer
,
(
short
)
0
,
cborEncoder
.
getCurrentOffset
());
}
/**
* pending
*/
private
void
getCx
(
APDU
apdu
,
short
dataLength
){
}
/**
* for alternative framework purpose
*
* input: IDx , PuKp in CBOR form
* return: PuKx, encryptedCx in CBOR form
*/
private
void
getCx
(
APDU
apdu
){
private
void
getPuKxCx
(
APDU
apdu
,
short
dataLength
){
cborDecoder
.
init
(
dataBuffer
,
(
short
)
1
,
dataLength
);
try
{
cborDecoder
.
readMajorType
(
CBORBase
.
TYPE_ARRAY
);
short
length
=
cborDecoder
.
readTextString
(
scratch
,
(
short
)
0
);
idSecret
.
IDx
=
new
DomString
(
scratch
,
length
);
cborDecoder
.
readByteString
(
scratch
,
(
short
)
0
);
Util
.
arrayCopy
(
scratch
,
(
short
)
8
,
idSecret
.
PuKp
,
(
short
)
1
,
(
short
)
64
);
}
catch
(
UserException
e
){
returnError
(
apdu
,
e
.
getReason
());
}
KeyAgreement
keyAgreement
=
KeyAgreement
.
getInstance
(
KeyAgreement
.
ALG_EC_SVDP_DH
,
false
);
keyAgreement
.
init
(
attestationKeyPair
.
getPrivate
());
keyAgreement
.
generateSecret
(
idSecret
.
PuKp
,
(
short
)
0
,
(
short
)
65
,
idSecret
.
sharedSecret
,
(
short
)
0
);
idSecret
.
initAesKey
();
idSecret
.
encryptCx
();
// this line will crash this applet
cborEncoder
.
init
(
dataBuffer
,
(
short
)
0
,
(
short
)
1200
);
cborEncoder
.
startArray
((
short
)
2
);
short
length
=
attestationKeyPair
.
getPubkey
(
scratch
,
(
short
)
0
);
cborEncoder
.
encodeByteString
(
scratch
,
(
short
)
0
,
length
);
cborEncoder
.
encodeByteString
(
idSecret
.
encryptedCx
,
(
short
)
0
,
(
short
)
idSecret
.
encryptedCx
.
length
);
apdu
.
setOutgoing
();
apdu
.
setOutgoingLength
(
cborEncoder
.
getCurrentOffset
());
apdu
.
sendBytesLong
(
dataBuffer
,
(
short
)
0
,
cborEncoder
.
getCurrentOffset
());
}
/**
*
*/
private
void
dumpIDSecret
(
APDU
apdu
){
tempVars
[
0
]
=
idSecret
.
dump
(
dataBuffer
,
cborEncoder
);
apdu
.
setOutgoing
();
apdu
.
setOutgoingLength
(
tempVars
[
0
]);
apdu
.
sendBytesLong
(
dataBuffer
,
(
short
)
0
,
tempVars
[
0
]);
}
/**
...
...
@@ -797,7 +865,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
*
* @param apdu apdu buffer
*/
p
ublic
void
authGetInfo
(
APDU
apdu
)
{
p
rivate
void
authGetInfo
(
APDU
apdu
)
{
// Create the authenticator info if not present.
if
(
fidoInfo
==
null
)
{
// Create the authGetInfo - 0x00 is success
...
...
src/main/java/com/josh/vku2f/IDSecret.java
0 → 100644
View file @
4fe40fff
package
com.josh.vku2f
;
import
javacard.framework.Util
;
import
javacard.security.AESKey
;
import
javacard.security.KeyBuilder
;
import
javacard.security.MessageDigest
;
import
javacardx.crypto.Cipher
;
/**
* Hold some params about the identification binding framework
*/
public
class
IDSecret
{
public
DomString
IDx
;
public
final
byte
[]
Rx
=
new
byte
[
4
];
public
final
byte
[]
Rp
=
new
byte
[
4
];
private
final
byte
[]
RxRp
=
new
byte
[
4
];
public
final
byte
[]
PuKp
=
new
byte
[
65
];
public
final
byte
[]
sharedSecret
=
new
byte
[
20
];
public
final
byte
[]
Cx
=
new
byte
[
16
];
public
final
byte
[]
encryptedCx
=
new
byte
[
16
];
public
final
byte
[]
aesRawKey
=
new
byte
[
32
];
private
AESKey
aesKey
;
private
Cipher
aesEncrypt
;
private
Cipher
aesDecrypt
;
private
final
byte
[]
IV_ZERO_AES
=
new
byte
[]{
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
};
private
MessageDigest
sha256
;
public
IDSecret
(){
IDx
=
new
DomString
(
Utf8Strings
.
UTF8_NULL
,
(
short
)
Utf8Strings
.
UTF8_NULL
.
length
);
Random
.
getInstance
().
nextBytes
(
Rx
,
(
short
)
0
,
(
short
)
Rx
.
length
);
Util
.
arrayFill
(
Rp
,
(
short
)
0
,
(
short
)
4
,
(
byte
)
Rp
.
length
);
Util
.
arrayFill
(
RxRp
,
(
short
)
0
,
(
short
)
4
,
(
byte
)
RxRp
.
length
);
PuKp
[(
byte
)
0
]
=
(
byte
)
0x04
;
Util
.
arrayFill
(
PuKp
,
(
short
)
1
,
(
byte
)(
PuKp
.
length
-
1
),
(
byte
)
0
);
Util
.
arrayFill
(
sharedSecret
,
(
short
)
0
,
(
byte
)
sharedSecret
.
length
,
(
byte
)
0
);
Random
.
getInstance
().
nextBytes
(
Cx
,
(
short
)
0
,
(
short
)
Cx
.
length
);
Util
.
arrayFill
(
encryptedCx
,
(
short
)
0
,
(
byte
)
encryptedCx
.
length
,
(
byte
)
0
);
Util
.
arrayFill
(
aesRawKey
,
(
short
)
0
,
(
byte
)
aesRawKey
.
length
,
(
byte
)
0
);
aesKey
=
(
AESKey
)
KeyBuilder
.
buildKey
(
KeyBuilder
.
TYPE_AES
,
KeyBuilder
.
LENGTH_AES_256
,
false
);
aesEncrypt
=
Cipher
.
getInstance
(
Cipher
.
ALG_AES_BLOCK_128_CBC_NOPAD
,
false
);
aesDecrypt
=
Cipher
.
getInstance
(
Cipher
.
ALG_AES_BLOCK_128_CBC_NOPAD
,
false
);
//
sha256
=
MessageDigest
.
getInstance
(
MessageDigest
.
ALG_SHA_256
,
false
);
}
private
byte
i
=
(
short
)
0
;
public
byte
[]
getRxRp
(){
for
(
i
=
(
short
)
0
;
i
<
(
short
)
4
;
i
++){
RxRp
[
i
]
=
(
byte
)
(
Rx
[
i
]
^
Rp
[
i
]);
}
return
RxRp
;
}
public
void
initAesKey
(){
sha256
.
doFinal
(
sharedSecret
,
(
short
)
0
,
(
short
)
20
,
aesRawKey
,
(
short
)
0
);
aesKey
.
setKey
(
aesRawKey
,
(
short
)
0
);
aesEncrypt
.
init
(
aesKey
,
Cipher
.
MODE_ENCRYPT
,
IV_ZERO_AES
,
(
short
)
0
,
(
short
)
IV_ZERO_AES
.
length
);
aesDecrypt
.
init
(
aesKey
,
Cipher
.
MODE_DECRYPT
,
IV_ZERO_AES
,
(
short
)
0
,
(
short
)
IV_ZERO_AES
.
length
);
}
public
void
encryptCx
(){
aesEncrypt
.
doFinal
(
Cx
,
(
short
)
0
,
(
short
)
Cx
.
length
,
encryptedCx
,
(
short
)
0
);
}
/**
* put IDSecret data with CBOR form in dataBuffer
* return data length
*/
public
short
dump
(
byte
[]
dataBuffer
,
CBOREncoder
encoder
){
encoder
.
init
(
dataBuffer
,
(
short
)
0
,
(
short
)
1200
);
encoder
.
startArray
((
short
)
9
);
encoder
.
encodeTextString
(
IDx
.
str
,
(
short
)
0
,
IDx
.
len
);
encoder
.
encodeByteString
(
Rx
,
(
short
)
0
,
(
short
)
Rx
.
length
);
encoder
.
encodeByteString
(
Rp
,
(
short
)
0
,
(
short
)
Rp
.
length
);
encoder
.
encodeByteString
(
getRxRp
(),
(
short
)
0
,
(
short
)
RxRp
.
length
);
encoder
.
encodeByteString
(
PuKp
,
(
short
)
0
,
(
short
)
PuKp
.
length
);
encoder
.
encodeByteString
(
sharedSecret
,
(
short
)
0
,
(
short
)
sharedSecret
.
length
);
encoder
.
encodeByteString
(
aesRawKey
,
(
short
)
0
,
(
short
)
aesRawKey
.
length
);
encoder
.
encodeByteString
(
Cx
,
(
short
)
0
,
(
short
)
Cx
.
length
);
encoder
.
encodeByteString
(
encryptedCx
,
(
short
)
0
,
(
short
)
encryptedCx
.
length
);
return
encoder
.
getCurrentOffset
();
}
}
src/main/java/com/josh/vku2f/Utf8Strings.java
View file @
4fe40fff
...
...
@@ -19,15 +19,15 @@ package com.josh.vku2f;
public
class
Utf8Strings
{
public
static
final
byte
[]
UTF8_UP
=
{
'u'
,
'p'
};
// Representation of "id" in UTF8
public
static
final
byte
[]
UTF8_ID
=
{
0x69
,
0x64
};
public
static
final
byte
[]
UTF8_ID
=
{
'i'
,
'd'
};
// Representation of "name" in UTF8
public
static
final
byte
[]
UTF8_NAME
=
{
0x6e
,
0x61
,
0x6d
,
0x65
};
public
static
final
byte
[]
UTF8_NAME
=
{
'n'
,
'a'
,
'm'
,
'e'
};
// Representation of "displayName" in UTF8
public
static
final
byte
[]
UTF8_DISPLAYNAME
=
{
0x64
,
0x69
,
0x73
,
0x70
,
0x6c
,
0x61
,
0x79
,
0x4e
,
0x61
,
0x6d
,
0x65
};
public
static
final
byte
[]
UTF8_DISPLAYNAME
=
{
'd'
,
'i'
,
's'
,
'p'
,
'l'
,
'a'
,
'y'
,
'n'
,
'a'
,
'm'
,
'e'
};
// Representation of "alg" in UTF8
public
static
final
byte
[]
UTF8_ALG
=
{
0x61
,
0x6c
,
0x67
};
public
static
final
byte
[]
UTF8_ALG
=
{
'a'
,
'l'
,
'g'
};
public
static
final
byte
[]
UTF8_UV
=
{
'u'
,
'v'
};
public
static
final
byte
[]
UTF8_RK
=
{
0x72
,
0x6b
};
public
static
final
byte
[]
UTF8_RK
=
{
'r'
,
'k'
};
public
static
final
byte
[]
UTF8_TYPE
=
{
't'
,
'y'
,
'p'
,
'e'
};
public
static
final
byte
[]
UTF8_PACKED
=
{
'p'
,
'a'
,
'c'
,
'k'
,
'e'
,
'd'
};
public
static
final
byte
[]
UTF8_SIG
=
{
's'
,
'i'
,
'g'
};
...
...
@@ -35,4 +35,6 @@ public class Utf8Strings {
public
static
final
byte
[]
UTF8_PUBLIC_KEY
=
{
'p'
,
'u'
,
'b'
,
'l'
,
'i'
,
'c'
,
'-'
,
'k'
,
'e'
,
'y'
};
public
static
final
byte
[]
UTF8_FIDO2
=
{
'F'
,
'I'
,
'D'
,
'O'
,
'_'
,
'2'
,
'_'
,
'0'
};
public
static
final
byte
[]
UTF8_ICON
=
{
'i'
,
'c'
,
'o'
,
'n'
};
public
static
final
byte
[]
UTF8_NULL
=
{
'n'
,
'u'
,
'l'
,
'l'
};
public
static
final
byte
[]
UTF8_EXTENSIONS
=
{
'e'
,
'x'
,
't'
,
'e'
,
'n'
,
's'
,
'i'
,
'o'
,
'n'
,
's'
};
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment