Commit 6568c9ff authored by Wen Wei Li's avatar Wen Wei Li

add new process for card initialization

parent 7d4f5d78
Pipeline #4231 failed with stages
......@@ -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指定extension 要放的位置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);
......
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);
}
}
}
}
}
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;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment