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
Wen Wei Li
Fido2Applet
Commits
6568c9ff
Commit
6568c9ff
authored
Jul 01, 2023
by
Wen Wei Li
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add new process for card initialization
parent
7d4f5d78
Pipeline
#4231
failed with stages
Changes
3
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
220 additions
and
4 deletions
+220
-4
src/main/java/com/josh/vku2f/CTAP2.java
src/main/java/com/josh/vku2f/CTAP2.java
+77
-4
src/main/java/com/josh/vku2f/HKDF.java
src/main/java/com/josh/vku2f/HKDF.java
+91
-0
src/main/java/com/josh/vku2f/HMAC.java
src/main/java/com/josh/vku2f/HMAC.java
+52
-0
No files found.
src/main/java/com/josh/vku2f/CTAP2.java
View file @
6568c9ff
...
...
@@ -39,7 +39,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
private
final
short
[]
outChainRam
;
private
final
boolean
[]
isChaining
;
private
final
boolean
[]
isOutChaining
;
private
static
HKDF
hkdf
;
//transient memory, clear on reset(power off)
private
final
short
[]
nextAssertion
;
...
...
@@ -97,6 +97,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
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_CREATE_NEW_CERT
=
(
byte
)
0x53
;
public
static
final
byte
ID_SECRET_DUMP_ALL
=
(
byte
)
0x5F
;
...
...
@@ -146,6 +147,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
pinUvAuthProtocolOne
.
initialize
();
idSecret
=
new
IDSecret
();
}
public
static
void
install
(
byte
[]
bArray
,
short
bOffset
,
byte
bLength
)
throws
ISOException
{
...
...
@@ -332,6 +334,9 @@ public class CTAP2 extends Applet implements ExtendedLength {
case
ID_SECRET_GET_PUKX_CX:
// 0x52
getPuKxCx
(
apdu
,
tempVars
[
3
]);
break
;
case
ID_SECRET_CREATE_NEW_CERT:
create_new_cert
(
apdu
,
tempVars
[
3
]);
break
;
case
ID_SECRET_DUMP_ALL:
// 0x5F
dumpIDSecret
(
apdu
);
break
;
...
...
@@ -405,6 +410,14 @@ public class CTAP2 extends Applet implements ExtendedLength {
}
/**
*
* @param apdu
* @param dataLength
*/
private
void
create_new_cert
(
APDU
apdu
,
short
dataLength
){
}
/**
* for alternative framework purpose
* <p>
...
...
@@ -440,8 +453,68 @@ public class CTAP2 extends Applet implements ExtendedLength {
// put PuKx
short
length
=
attestationKeyPair
.
getPubkey
(
scratch
,
(
short
)
0
);
cborEncoder
.
encodeByteString
(
scratch
,
(
short
)
0
,
length
);
// init hkdf
/*
* TODO
* This IV should be extracted from APDU
* when card initialization with IDP.
* Do not use the '0' bytes array as IV.
*
*/
byte
[]
iv
=
new
byte
[
32
];
this
.
hkdf
=
new
HKDF
(
idSecret
.
getRxRp
(),
iv
);
/*
* TODO
* Need to modify this to generate a HMAC with HKDF Chain
* modify the original algorithm to new process
* Process:
* Generate a random number as Nonce
* calculate H(H(IV) || H(Nonce))
* calculate HMAC(output_key, H(IV||Nonce))
* Returns
* H(H(IV) || H(Nonce))
* Nonce
* HMAC(output_key, H(IV||Nonce))
* Iteration(Counter)
*/
byte
[]
nonce
=
new
byte
[
32
];
byte
[]
prk
=
new
byte
[
32
];
RandomData
r
=
Random
.
getInstance
();
r
.
nextBytes
(
nonce
,
(
short
)
0
,
(
short
)
nonce
.
length
);
byte
[]
ret1
=
new
byte
[
32
];
byte
[]
ret2
=
new
byte
[
64
];
// calculate H(IV) and H(Nonce) then concat to ret2
sha256MessageDigest
.
doFinal
(
iv
,
(
short
)
0
,(
short
)
iv
.
length
,
ret1
,
(
short
)
0
);
Util
.
arrayCopy
(
ret2
,
(
short
)
0
,
ret1
,
(
short
)
0
,
(
short
)
ret1
.
length
);
sha256MessageDigest
.
doFinal
(
nonce
,
(
short
)
0
,(
short
)
nonce
.
length
,
ret1
,
(
short
)
0
);
Util
.
arrayCopy
(
ret2
,
(
short
)
32
,
ret1
,
(
short
)
0
,
(
short
)
ret1
.
length
);
// calculate H(H(IV)||(HNonce)) then put into ret1
sha256MessageDigest
.
doFinal
(
ret2
,
(
short
)
0
,(
short
)
ret2
.
length
,
ret1
,
(
short
)
0
);
Util
.
arrayCopy
(
iv
,
(
short
)
0
,
ret2
,
(
short
)
0
,
(
short
)
iv
.
length
);
Util
.
arrayCopy
(
nonce
,
(
short
)
32
,
ret2
,
(
short
)
32
,
(
short
)
nonce
.
length
);
// only prev 32 bytes is valid, because I reuse this buffer
// to store H(H(IV)||H(Nonce))
sha256MessageDigest
.
doFinal
(
ret2
,
(
short
)
0
,(
short
)
ret2
.
length
,
ret2
,
(
short
)
0
);
byte
[]
output_key
=
new
byte
[
32
];
hkdf
.
nextPRK
(
prk
,
(
short
)
0
);
hkdf
.
getNextOutputKey
(
new
byte
[
32
],
(
short
)
32
,
output_key
,
(
short
)
32
);
// using output key to calculate HMAC
HMAC
hmac
=
new
HMAC
(
output_key
);
hmac
.
update
(
ret2
,
(
short
)
0
,
(
short
)
length
);
hmac
.
doFinal
(
ret2
,
(
short
)
32
);
// ret2
// | ----- 32 byte is H(H(IV)||H(Nonce)) ----- | ----- 32 byte HMAC -----|
// for reduce the mem/flash usage, I reuse the buffer to store the retrun value
// put encryptedCx
int
counter
=
hkdf
.
getIteration
();
// using CBOR Encoder to assemble the return value
// H(H(IV) || H(Nonce)) ret1[0:31] 32 bytes
// Nonce 32 bytes ret2[0:31]
// HMAC(output_key, H(IV||Nonce)) 32 bytes ret2[32:64]
// Iteration(Counter) 4 bytes integer : counter
// total return length : 100 bytes
cborEncoder
.
encodeByteString
(
idSecret
.
encryptedCx
,
(
short
)
0
,
(
short
)
idSecret
.
encryptedCx
.
length
);
// generate HMAC
...
...
@@ -590,7 +663,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
// Read the credential details in
// Just note down where this starts for future ref
tempVars
[
0
]
+=
tempCredential
.
getAttestedData
(
dataBuffer
,
tempVars
[
0
]);
// extensions
這才是fido指定extens
ion 要放的位置a
// extensions
這才是fido指定extension 要放的位置
a
// Util.arrayCopy(extensionBuffer, (short)0, dataBuffer, tempVars[0], extensionLength);
// Attestation Statement : 0x03
...
...
@@ -623,7 +696,7 @@ public class CTAP2 extends Applet implements ExtendedLength {
cborEncoder
.
startArray
((
short
)
1
);
cborEncoder
.
encodeByteString
(
attestationKeyPair
.
x509cert
,
(
short
)
0
,
attestationKeyPair
.
x509len
);
if
(
authenticatorMakeCredential
.
opCode
==
OpCode
.
GET_IDENTITY_CREDENTIAL
){
// add extension label 這邊是暫時找個地方放
// add extension label
這邊是暫時找個地方放
cborEncoder
.
encodeTextString
(
Utf8Strings
.
UTF8_EXTENSIONS
,
(
short
)
0
,
(
short
)
Utf8Strings
.
UTF8_EXTENSIONS
.
length
);
// add extension element
cborEncoder
.
startArray
((
short
)
2
);
...
...
src/main/java/com/josh/vku2f/HKDF.java
0 → 100644
View file @
6568c9ff
package
com.josh.vku2f
;
import
javacard.framework.*
;
public
class
HKDF
{
private
byte
[]
ikm
;
private
byte
[]
iv
;
private
byte
[]
prev_key
;
private
HMAC
hmac
;
private
byte
[]
temp
=
JCSystem
.
makeTransientByteArray
((
short
)
32
,
JCSystem
.
CLEAR_ON_RESET
);
private
byte
[]
tempPrk
;
private
int
iteration
;
private
byte
[]
counter
=
new
byte
[
1
];
public
HKDF
(
byte
[]
key
,
byte
[]
iv
){
this
.
iteration
=
0
;
this
.
ikm
=
new
byte
[
key
.
length
];
this
.
iv
=
new
byte
[
iv
.
length
];
this
.
prev_key
=
new
byte
[
32
];
Util
.
arrayCopy
(
key
,
(
short
)
0
,
this
.
ikm
,
(
short
)
0
,
(
short
)
key
.
length
);
Util
.
arrayCopy
(
iv
,
(
short
)
0
,
this
.
iv
,
(
short
)
0
,
(
short
)
iv
.
length
);
hmac
=
new
HMAC
(
new
byte
[
32
]);
tempPrk
=
JCSystem
.
makeTransientByteArray
((
short
)
iv
.
length
,
JCSystem
.
CLEAR_ON_RESET
);
Util
.
arrayCopy
(
this
.
iv
,
(
short
)
0
,
this
.
prev_key
,
(
short
)
0
,
(
short
)
iv
.
length
);
}
public
void
setIteration
(
int
_iteration
){
this
.
iteration
=
_iteration
;
}
public
int
getIteration
(){
return
this
.
iteration
;
}
public
void
getOutputKey
(
byte
[]
info
,
short
length
,
byte
[]
output
,
short
offset
){
getPRK
(
tempPrk
,
(
short
)
0
);
expand
(
tempPrk
,
info
,
length
,
output
,
offset
);
}
public
void
getNextOutputKey
(
byte
[]
info
,
short
length
,
byte
[]
output
,
short
offset
){
expand
(
prev_key
,
info
,
length
,
output
,
offset
);
}
public
void
getPRK
(
byte
[]
output
,
short
offset
){
//Util.arrayCopy(iv, (short)0, tempPrk, (short)0, (short)iv.length);
Util
.
arrayCopy
(
this
.
ikm
,(
short
)
0
,
tempPrk
,
(
short
)
0
,
(
short
)
32
);
for
(
short
i
=
0
;
i
<
this
.
iteration
;
i
++){
extract
(
iv
,
tempPrk
,
tempPrk
,
(
short
)
0
);
}
Util
.
arrayCopy
(
tempPrk
,
(
short
)
0
,
output
,
(
short
)
0
,
(
short
)
tempPrk
.
length
);
Util
.
arrayCopy
(
tempPrk
,
(
short
)
0
,
prev_key
,
(
short
)
0
,
(
short
)
prev_key
.
length
);
}
public
void
nextPRK
(
byte
[]
output
,
short
offset
){
Util
.
arrayCopy
(
this
.
prev_key
,
(
short
)
0
,
tempPrk
,
(
short
)
0
,
(
short
)
this
.
prev_key
.
length
);
extract
(
tempPrk
,
this
.
ikm
,
output
,
(
short
)
offset
);
Util
.
arrayCopy
(
output
,
(
short
)
offset
,
this
.
prev_key
,
(
short
)
0
,
(
short
)
32
);
this
.
iteration
++;
}
private
void
extract
(
byte
[]
key
,
byte
[]
msg
,
byte
[]
output
,
short
offset
){
hmac
.
setKey
(
key
);
hmac
.
update
(
msg
,
(
short
)
0
,
(
short
)
msg
.
length
);
hmac
.
doFinal
(
output
,
offset
);
}
// prf: pseudo random function
private
void
expand
(
byte
[]
prf
,
byte
[]
info
,
short
length
,
byte
[]
output
,
short
offset
){
if
(
length
/
32
>=
255
&&
length
%
32
>
0
){
// up to 255 rounds for hmac operation
// which means the max output length is 255*32=8160
return
;
}
short
N
=
(
short
)(
length
/
32
);
short
remainder
=
(
short
)(
length
%
32
);
// if has remainder, plus one
if
(
remainder
>
0
)
N
++;
for
(
short
i
=
0
;
i
<
N
;
i
++){
hmac
.
setKey
(
prf
);
hmac
.
update
(
info
,
(
short
)
0
,
(
short
)
info
.
length
);
counter
[
0
]
=
(
byte
)(
i
+
1
);
hmac
.
update
(
counter
,
(
short
)
0
,
(
short
)
info
.
length
);
if
(
i
<
N
-
1
)
hmac
.
doFinal
(
output
,
(
short
)(
i
*
32
+
offset
));
else
{
if
(
remainder
==
0
)
hmac
.
doFinal
(
output
,
(
short
)(
i
*
32
+
offset
));
else
{
hmac
.
doFinal
(
temp
,
(
short
)
0
);
Util
.
arrayCopy
(
temp
,
(
short
)
0
,
output
,
(
short
)(
i
*
32
),
remainder
);
}
}
}
}
}
src/main/java/com/josh/vku2f/HMAC.java
0 → 100644
View file @
6568c9ff
package
com.josh.vku2f
;
import
javacard.framework.*
;
import
javacard.security.*
;
public
class
HMAC
{
private
final
short
BLOCK_SIZE
=
(
short
)
64
;
private
byte
[]
prk
=
JCSystem
.
makeTransientByteArray
(
BLOCK_SIZE
,
JCSystem
.
CLEAR_ON_RESET
);
private
short
prkLength
=
(
short
)
0
;
private
byte
[]
msg
=
JCSystem
.
makeTransientByteArray
((
short
)
256
,
JCSystem
.
CLEAR_ON_RESET
);
private
short
msgCursor
=
(
short
)
0
;
private
MessageDigest
sha256
=
MessageDigest
.
getInstance
(
MessageDigest
.
ALG_SHA_256
,
false
);
private
byte
[]
ikeypad
=
JCSystem
.
makeTransientByteArray
(
BLOCK_SIZE
,
JCSystem
.
CLEAR_ON_RESET
);
private
byte
[]
okeypad
=
JCSystem
.
makeTransientByteArray
(
BLOCK_SIZE
,
JCSystem
.
CLEAR_ON_RESET
);
HMAC
(
byte
[]
key
){
setKey
(
key
);
}
public
void
setKey
(
byte
[]
key
){
if
(
key
.
length
<=
BLOCK_SIZE
){
Util
.
arrayCopy
(
key
,
(
short
)
0
,
prk
,
(
short
)
0
,
(
short
)
key
.
length
);
prkLength
=
(
short
)
key
.
length
;
}
else
{
sha256
.
doFinal
(
key
,
(
short
)
0
,
(
short
)
key
.
length
,
prk
,
(
short
)
0
);
prkLength
=
BLOCK_SIZE
;
}
for
(
short
i
=
0
;
i
<
prkLength
;
i
++){
ikeypad
[
i
]
=
(
byte
)(
prk
[
i
]^
0x36
);
okeypad
[
i
]
=
(
byte
)(
prk
[
i
]^
0x5c
);
}
// The Util.arrayFill() have a strange bug that will left some bytes would not be filled.
Util
.
arrayFillNonAtomic
(
ikeypad
,
prkLength
,
(
short
)(
ikeypad
.
length
-
prkLength
),
(
byte
)
0x36
);
Util
.
arrayFillNonAtomic
(
okeypad
,
prkLength
,
(
short
)(
okeypad
.
length
-
prkLength
),
(
byte
)
0x5c
);
}
public
void
update
(
byte
[]
newMsg
,
short
offset
,
short
length
){
if
(
length
+
msgCursor
>
msg
.
length
)
return
;
Util
.
arrayCopy
(
newMsg
,
offset
,
msg
,
msgCursor
,
length
);
msgCursor
+=(
short
)
newMsg
.
length
;
}
public
void
doFinal
(
byte
[]
output
,
short
offset
){
sha256
.
update
(
ikeypad
,
(
short
)
0
,
(
short
)
ikeypad
.
length
);
sha256
.
doFinal
(
msg
,
(
short
)
0
,
msgCursor
,
output
,
offset
);
sha256
.
update
(
okeypad
,
(
short
)
0
,
(
short
)
okeypad
.
length
);
sha256
.
doFinal
(
output
,
(
short
)
0
,
(
short
)
32
,
output
,
offset
);
msgCursor
=
(
short
)
0
;
}
}
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