Commit 97229b46 authored by Josh Ji's avatar Josh Ji

hkdf not finished

parent a1fe309b
......@@ -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_PIV_INFO_INIT = (byte) 0x53;
public static final byte ID_SECRET_DUMP_ALL = (byte) 0x5F;
......@@ -324,13 +325,17 @@ public class CTAP2 extends Applet implements ExtendedLength {
getCredentialCount(apdu);
break;
case ID_SECRET_GET_PUKX_RX: // 0x50
getPuKxRx(apdu, tempVars[3]);
// deprecated
break;
case ID_SECRET_GET_CX: // 0x51
getCx(apdu, tempVars[3]);
// deprecated
break;
case ID_SECRET_GET_PUKX_CX: // 0x52
getPuKxCx(apdu, tempVars[3]);
// deprecated
// getPuKxCx(apdu, tempVars[3]);
break;
case ID_SECRET_PIV_INFO_INIT: // 0x53
pivInfoInit(apdu, tempVars[3]);
break;
case ID_SECRET_DUMP_ALL: // 0x5F
dumpIDSecret(apdu);
......@@ -386,16 +391,16 @@ public class CTAP2 extends Applet implements ExtendedLength {
*/
private void getPuKxRx(APDU apdu, short dataLength) {
// Done IDx have to get data from dataBuffer at index 1
Util.arrayCopy(dataBuffer, (short) 1, scratch, (short) 0, (short) (dataLength - 1));
idSecret.IDx = new DomString(scratch, (short) (dataLength - 1));
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());
// Util.arrayCopy(dataBuffer, (short) 1, scratch, (short) 0, (short) (dataLength - 1));
// idSecret.IDx = new DomString(scratch, (short) (dataLength - 1));
// 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());
}
/**
......@@ -412,23 +417,24 @@ public class CTAP2 extends Applet implements ExtendedLength {
* return: PuKx, encryptedCx in CBOR form
*/
private void getPuKxCx(APDU apdu, short dataLength) {
/*
cborDecoder.init(dataBuffer, (short) 1, dataLength);
try {
cborDecoder.readMajorType(CBORBase.TYPE_ARRAY);
// read IDx
short length = cborDecoder.readTextString(scratch, (short) 0);
idSecret.IDx = new DomString(scratch, length);
idSecret.id = new DomString(scratch, length);
// read PuKp
cborDecoder.readByteString(scratch, (short) 0);
Util.arrayCopy(scratch, (short) 8, idSecret.PuKp, (short) 1, (short) 64);
Util.arrayCopy(scratch, (short) 8, idSecret.puK_idp, (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);
keyAgreement.generateSecret(idSecret.puK_idp, (short) 0, (short) 65, idSecret.sharedSecret, (short) 0);
idSecret.initAesKey();
idSecret.encryptCx();
......@@ -447,6 +453,44 @@ public class CTAP2 extends Applet implements ExtendedLength {
// generate HMAC
idSecret.generateHMAC();
apdu.setOutgoing();
apdu.setOutgoingLength(cborEncoder.getCurrentOffset());
apdu.sendBytesLong(dataBuffer, (short) 0, cborEncoder.getCurrentOffset());
*/
}
private void pivInfoInit(APDU apdu, short dataLength){
cborDecoder.init(dataBuffer, (short) 1, dataLength);
try {
cborDecoder.readMajorType(CBORBase.TYPE_ARRAY);
// read IDx
short length = cborDecoder.readTextString(scratch, (short) 0);
idSecret.id = new DomString(scratch, length);
// read PuKp
cborDecoder.readByteString(scratch, (short) 0);
Util.arrayCopy(scratch, (short) 8, idSecret.puK_idp, (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.puK_idp, (short) 0, (short) 65, idSecret.sharedSecret, (short) 0);
idSecret.initHKDFChain();
// make output cbor
cborEncoder.init(dataBuffer, (short) 0, (short) 1200);
cborEncoder.startArray((short) 2);
// put PuK_card
short length = attestationKeyPair.getPubkey(scratch, (short) 0);
cborEncoder.encodeByteString(scratch, (short) 0, length);
// put HKDF_CHAIN_IV
cborEncoder.encodeByteString(idSecret.HKDF_CHAIN_IV, (short) 0, (short) idSecret.HKDF_CHAIN_IV.length);
apdu.setOutgoing();
apdu.setOutgoingLength(cborEncoder.getCurrentOffset());
apdu.sendBytesLong(dataBuffer, (short) 0, cborEncoder.getCurrentOffset());
......@@ -623,13 +667,15 @@ 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){
idSecret.PIVInfoNext();
// add extension label 這邊是暫時找個地方放
cborEncoder.encodeTextString(Utf8Strings.UTF8_EXTENSIONS, (short) 0, (short) Utf8Strings.UTF8_EXTENSIONS.length);
// add extension element
cborEncoder.startArray((short) 2);
idSecret.next();
cborEncoder.encodeByteString(idSecret.hmac, (short) 0, (short) idSecret.hmac.length);
cborEncoder.encodeByteString(idSecret.encryptedCx, (short) 0, (short) idSecret.encryptedCx.length);
cborEncoder.startArray((short) 4);
cborEncoder.encodeByteString(idSecret.nonce, (short) 0, (short) idSecret.nonce.length);
cborEncoder.encodeByteString(idSecret.idHash, (short) 0, (short) idSecret.idHash.length);
cborEncoder.encodeByteString(idSecret.hmacValue, (short) 0, (short) idSecret.hmacValue.length);
cborEncoder.encodeByteString(idSecret.counter, (short) 0, (short) idSecret.counter.length);
}
// We're actually done, send this out
......
......@@ -6,9 +6,10 @@ public class HKDF
{
private HMAC hmac;
private byte[] counter = new byte[1]; // for expand()
private byte[] temp = JCSystem.makeTransientByteArray((short)32, JCSystem.CLEAR_ON_RESET);
private byte[] temp;
public HKDF(){
hmac = new HMAC();
temp = JCSystem.makeTransientByteArray((short)32, JCSystem.CLEAR_ON_RESET);
}
public void extract(byte[] salt, byte[] ikm, byte[] output, short offset){
hmac.setKey(salt);
......@@ -17,7 +18,7 @@ public class HKDF
}
public void expand(byte[] prk, byte[] info, short length, byte[] output, short offset){
if(length/32>=255 && length%32>0){
if((short)(length/32)>=255 && (short)(length%32)>0){
// up to 255 rounds for hmac operation
// which means the max output length is 255*32=8160
return;
......@@ -33,7 +34,7 @@ public class HKDF
hmac.update(info);
counter[0] = (byte)(i+1);
hmac.update(counter);
if(i<N-1)
if(i<(short)(N-1))
hmac.doFinal(output, (short)(i*32+offset));
else{
if(remainder==0)
......
......@@ -10,33 +10,19 @@ public class HKDFChain
private byte[] info;
private short okmLength;
private HKDF hkdf;
private byte[] tempPrk;
private byte[] previousPrk;
public HKDFChain(byte[] key, byte[] iv, byte[] info, short okmLength){
this.key = new byte[key.length];
this.iv = new byte[iv.length];
this.info = new byte[info.length];
this.previousPrk = new byte[32];
this.previousPrk = new byte[iv.length];
Util.arrayCopy(key, (short)0, this.key, (short)0, (short)key.length);
Util.arrayCopy(iv, (short)0, this.iv, (short)0, (short)iv.length);
Util.arrayCopy(iv, (short)0, this.previousPrk, (short)0, (short)iv.length);
Util.arrayCopy(info, (short)0, this.info, (short)0, (short)info.length);
this.okmLength = okmLength;
hkdf = new HKDF();
tempPrk = JCSystem.makeTransientByteArray((short)iv.length, JCSystem.CLEAR_ON_RESET);
}
public void getOutputKey(short iteration, byte[] info, short length, byte[] output, short offset){
getPRK(iteration, tempPrk, (short)0);
hkdf.expand(tempPrk, info, length, output, offset);
}
public void getPRK(short iteration, byte[] output, short offset){
Util.arrayCopy(iv, (short)0, tempPrk, (short)0, (short)iv.length);
for(short i=0; i<iteration; i++){
hkdf.extract(tempPrk, key, tempPrk, (short)0);
}
Util.arrayCopy(tempPrk, (short)0, output, (short)offset, (short)tempPrk.length);
Util.arrayCopy(tempPrk, (short)0, previousPrk, (short)0, (short)tempPrk.length);
}
public void next(byte[] output, short offset){
hkdf.extract(previousPrk, key, previousPrk, (short)0);
......
......@@ -6,32 +6,38 @@ 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[] key;
private short keyLength = (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);
private byte[] ikeypad;
private byte[] okeypad;
public HMAC(){
key = new byte[BLOCK_SIZE];
ikeypad = new byte[BLOCK_SIZE];
okeypad = new byte[BLOCK_SIZE];
}
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;
Util.arrayCopy(key, (short)0, this.key, (short)0, (short)key.length);
keyLength = (short)key.length;
}else{
sha256.doFinal(key, (short)0, (short)key.length, prk, (short)0);
prkLength = BLOCK_SIZE;
sha256.doFinal(key, (short)0, (short)key.length, this.key, (short)0);
keyLength = BLOCK_SIZE;
}
for(short i = 0; i<prkLength; i++){
ikeypad[i] = (byte)(prk[i]^0x36);
okeypad[i] = (byte)(prk[i]^0x5c);
for(short i = 0; i< keyLength; i++){
ikeypad[i] = (byte)(this.key[i]^0x36);
okeypad[i] = (byte)(this.key[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);
Util.arrayFillNonAtomic(ikeypad, keyLength, (short)(ikeypad.length- keyLength), (byte)0x36);
Util.arrayFillNonAtomic(okeypad, keyLength, (short)(okeypad.length- keyLength), (byte)0x5c);
}
public void update(byte[] newMsg){
if(newMsg.length + msgCursor > msg.length)
if((short)(newMsg.length + msgCursor) > msg.length)
return;
Util.arrayCopy(newMsg, (short)0, msg, msgCursor, (short)newMsg.length);
msgCursor+=(short)newMsg.length;
......
package com.josh.vku2f;
import javacard.framework.*;
import javacard.security.AESKey;
import javacard.security.KeyBuilder;
import javacard.security.MessageDigest;
import javacardx.crypto.Cipher;
import josh.passport.ShareName;
import josh.passport.SharePIN;
......@@ -15,131 +12,44 @@ import josh.passport.SharePIN;
*/
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 DomString id;
public final byte[] puK_idp = new byte[65];
public final byte[] sharedSecret = new byte[20];
public final byte[] hashedSharedSecret = new byte[32];
public final byte[] Cx = new byte[4];
public final byte[] paddedCx = new byte[16]; // for pkcs#7 padding
public final byte[] encryptedCx = new byte[16];
public final byte[] hmac = new byte[48];
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;
private byte[] scratch;
private final short SCRATCH_LENGTH = (short)128 ;
private CBOREncoder encoder = new CBOREncoder();
public static byte[] tempBuffer = new byte[256];
public short tempBufferLength = (short)0;
private static short tempCursor = (short)0;
public final byte[] HKDF_CHAIN_IV = new byte[32];
public final byte[] nonce = new byte[32];
public final byte[] idHash = new byte[32];
public final byte[] counter = new byte[4]; // for hkdfChain
public final byte[] hmacValue = new byte[32];
public final byte[] hmacKey = new byte[32];
public final short OKM_LENGTH = (short)32;
public final MessageDigest sha256;
public HKDFChain hkdfChain;
public final HMAC hmac = new HMAC();
public final CBOREncoder encoder = new CBOREncoder();
public final byte[] temp = JCSystem.makeTransientByteArray((short)32, JCSystem.CLEAR_ON_DESELECT);
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);
Util.arrayFill(hashedSharedSecret, (short)0, (byte) hashedSharedSecret.length, (byte)0);
Random.getInstance().nextBytes(Cx, (short)0, (short)Cx.length);
Util.arrayFill(encryptedCx, (short)0, (byte)encryptedCx.length, (byte)0);
Util.arrayFill(hmac, (short)0, (byte)hmac.length, (byte)0);
Util.arrayFill(tempBuffer, (short)0, (byte)tempBuffer.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);
//
id = new DomString(Utf8Strings.UTF8_NULL, (short)Utf8Strings.UTF8_NULL.length);
puK_idp[(byte)0] = (byte)0x04;
Random.getInstance().nextBytes(HKDF_CHAIN_IV, (short)0, (short)HKDF_CHAIN_IV.length);
sha256 = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
scratch = JCSystem.makeTransientByteArray(SCRATCH_LENGTH, JCSystem.CLEAR_ON_DESELECT);
}
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, hashedSharedSecret, (short)0);
aesKey.setKey(hashedSharedSecret, (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(){
// pkcs#7 padding
Util.arrayFill(paddedCx, (short)4, (short)12, (byte)0x0c );
Util.arrayCopy(Cx, (short)0, paddedCx, (short)0, (short)Cx.length);
aesEncrypt.doFinal(paddedCx, (short)0, (short) paddedCx.length, encryptedCx, (short)0);
}
/**
* AES(aesRawKey, SHA256(IDx||Cx))
*/
public void generateHMAC(){
Util.arrayCopy(IDx.str, (short)0, scratch, (short)0, (short)IDx.str.length);
Util.arrayCopy(Cx, (short)0, scratch, (short)IDx.str.length, (short)Cx.length);
short scratchLength = (short)(IDx.str.length + Cx.length);
short updateOffset = (short)0;
while(scratchLength > (byte)32){
sha256.update(scratch, updateOffset, (byte)32);
scratchLength -= (byte)32;
updateOffset += (byte)32;
}
sha256.doFinal(scratch, updateOffset, scratchLength, scratch, (short)0);
// pkcs#7 padding
Util.arrayFill(scratch, (short)32, (short)16, (byte)0x10);
aesEncrypt.update(scratch, (short)0, (short)16, hmac, (short)0);
aesEncrypt.update(scratch, (short)16, (short)16 , hmac, (short)16 );
aesEncrypt.doFinal(scratch, (short)32, (short)16, hmac, (short)32 );
}
public void next(){
plusOne(Cx);
encryptCx();
generateHMAC();
public void initHKDFChain(){
hkdfChain = new HKDFChain(sharedSecret, HKDF_CHAIN_IV, id.str, OKM_LENGTH);
}
/**
*
* @param extensionBuffer outputBuffer
* @return extension byte string length
*/
public short generateExtensions(byte[] extensionBuffer){
encoder.init(tempBuffer, (short)0, (short) tempBuffer.length);
encoder.startMap((short)1);
encoder.encodeTextString(Utf8Strings.UTF8_PRLab, (short)0, (short)Utf8Strings.UTF8_PRLab.length);
encoder.startMap((short)2);
encoder.encodeTextString(Utf8Strings.UTF8_HMAC, (short)0, (short)Utf8Strings.UTF8_HMAC.length);
encoder.encodeByteString(hmac, (short)0, (short)hmac.length );
encoder.encodeTextString(Utf8Strings.UTF8_Cx, (short)0, (short)Utf8Strings.UTF8_Cx.length);
encoder.encodeByteString(encryptedCx, (short)0, (short)encryptedCx.length);
extensionBuffer = new byte[encoder.getCurrentOffset()];
Util.arrayCopy(tempBuffer, (short)0, extensionBuffer, (short)0, encoder.getCurrentOffset());
return encoder.getCurrentOffset();
}
public static void pushTempBuffer(byte[] inputBuffer, short inputOffset, short inputLength){
if((short)(inputLength + tempCursor) > (short)tempBuffer.length){
tempBuffer[(short)(tempBuffer.length-2)] = 'T'; // too
tempBuffer[(short)(tempBuffer.length-1)] = 'L'; // long
Util.setShort(tempBuffer, (short)2, (short)inputBuffer.length); // input length
return;
}
Util.arrayCopy(inputBuffer, inputOffset, tempBuffer, tempCursor, inputLength);
tempCursor += inputLength;
public void PIVInfoNext(){
Random.getInstance().nextBytes(nonce, (short)0, (short)nonce.length);
sha256.doFinal(id.str, (short)0, id.len, temp, (short)0);
sha256.update(temp, (short)0, (short)temp.length);
sha256.doFinal(nonce, (short)0, (short)nonce.length, idHash, (short)0);
hkdfChain.next(hmacKey, (short)0);
plusOne(counter);
hmac.setKey(hmacKey);
hmac.update(idHash);
hmac.doFinal(hmacValue, (short)0);
}
/**
......@@ -147,12 +57,18 @@ public class IDSecret {
* return data length
*/
public short dump(byte[] apduBuffer, byte[] dataBuffer, CBOREncoder encoder){
PIVInfoNext();
encoder.init(dataBuffer, (short)0, (short)1200);
encoder.startMap((short)1);
encoder.encodeTextString(Utf8Strings.UTF8_TEMP, (short)0, (short)Utf8Strings.UTF8_TEMP.length);
encoder.encodeByteString(tempBuffer, (short)0, (short)tempBuffer.length);
encoder.startArray((short)9);
encoder.encodeTextString(id.str ,(short)0, id.len);
encoder.encodeByteString(nonce ,(short)0, (short)nonce.length);
encoder.encodeByteString(idHash ,(short)0, (short)idHash.length);
encoder.encodeByteString(HKDF_CHAIN_IV ,(short)0, (short)HKDF_CHAIN_IV.length);
encoder.encodeByteString(puK_idp ,(short)0, (short)puK_idp.length);
encoder.encodeByteString(sharedSecret ,(short)0, (short)sharedSecret.length);
encoder.encodeByteString(hashedSharedSecret ,(short)0, (short)hashedSharedSecret.length);
encoder.encodeByteString(counter ,(short)0, (short)counter.length);
encoder.encodeByteString(hmacValue ,(short)0, (short)hmacValue.length);
return encoder.getCurrentOffset();
}
......@@ -178,7 +94,7 @@ public class IDSecret {
}
private void testSharedObjects(byte[] apduBuffer){
/*
byte[] aid = new byte[]{(byte)0xa0,(byte)0x00,(byte)0x00,(byte)0x02,(byte)0x47,(byte)0x10,(byte)0x01};
AID passportAID = JCSystem.lookupAID(aid, (short)0,(byte)aid.length);
SharePIN sharePIN = (SharePIN) JCSystem.getAppletShareableInterfaceObject(passportAID,(byte)0x00);
......@@ -214,5 +130,6 @@ public class IDSecret {
}catch (Exception e){
tempBuffer[0] = 'a';
}
*/
}
}
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