add support for derivation in sign

This commit is contained in:
Michele Balistreri 2019-03-29 14:55:59 +03:00
parent 12481c6f94
commit 5bc5e23272
1 changed files with 63 additions and 26 deletions

View File

@ -807,17 +807,18 @@ public class KeycardApplet extends Applet {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
} }
doDerive(apduBuffer, len, apduBuffer[ISO7816.OFFSET_P1], true); doDerive(apduBuffer, (short) 0, len, apduBuffer[ISO7816.OFFSET_P1], true);
} }
/** /**
* Internal derivation function, called by DERIVE KEY and EXPORT KEY * Internal derivation function, called by DERIVE KEY and EXPORT KEY
* @param apduBuffer the APDU buffer * @param apduBuffer the APDU buffer
* @param len the APDU len * @param off the offset in the APDU buffer relative to the data field
* @param len the len of the path
* @param source derivation source * @param source derivation source
* @param makeCurrent whether the results should be saved or not * @param makeCurrent whether the results should be saved or not
*/ */
private void doDerive(byte[] apduBuffer, short len, byte source, boolean makeCurrent) { private void doDerive(byte[] apduBuffer, short off, short len, byte source, boolean makeCurrent) {
if (!isExtended) { if (!isExtended) {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
} }
@ -876,7 +877,8 @@ public class KeycardApplet extends Applet {
ISOException.throwIt(ISO7816.SW_WRONG_DATA); ISOException.throwIt(ISO7816.SW_WRONG_DATA);
} }
short scratchOff = (short) (ISO7816.OFFSET_CDATA + len); short pathOff = (short) (ISO7816.OFFSET_CDATA + off);
short scratchOff = (short) (pathOff + len);
short dataOff = (short) (scratchOff + Crypto.KEY_DERIVATION_SCRATCH_SIZE); short dataOff = (short) (scratchOff + Crypto.KEY_DERIVATION_SCRATCH_SIZE);
short pubKeyOff = (short) (dataOff + sourcePriv.getS(apduBuffer, dataOff)); short pubKeyOff = (short) (dataOff + sourcePriv.getS(apduBuffer, dataOff));
@ -888,8 +890,8 @@ public class KeycardApplet extends Applet {
apduBuffer[pubKeyOff] = 0; apduBuffer[pubKeyOff] = 0;
} }
for (short i = ISO7816.OFFSET_CDATA; i < scratchOff; i += 4) { for (short i = pathOff; i < scratchOff; i += 4) {
if (i > ISO7816.OFFSET_CDATA) { if (i > pathOff) {
Util.arrayCopyNonAtomic(derivationOutput, (short) 0, apduBuffer, dataOff, (short) (Crypto.KEY_SECRET_SIZE + CHAIN_CODE_SIZE)); Util.arrayCopyNonAtomic(derivationOutput, (short) 0, apduBuffer, dataOff, (short) (Crypto.KEY_SECRET_SIZE + CHAIN_CODE_SIZE));
if (!crypto.bip32IsHardened(apduBuffer, i)) { if (!crypto.bip32IsHardened(apduBuffer, i)) {
@ -1198,7 +1200,41 @@ public class KeycardApplet extends Applet {
*/ */
private void sign(APDU apdu) { private void sign(APDU apdu) {
byte[] apduBuffer = apdu.getBuffer(); byte[] apduBuffer = apdu.getBuffer();
boolean usePinless = apduBuffer[ISO7816.OFFSET_P1] == SIGN_P1_PINLESS; boolean usePinless = false;
boolean derive = false;
boolean makeCurrent = false;
ECPrivateKey signingKey;
ECPublicKey outputKey;
switch((byte) (apduBuffer[ISO7816.OFFSET_P1] & ~DERIVE_P1_SOURCE_MASK)) {
case SIGN_P1_CURRENT_KEY:
signingKey = privateKey;
outputKey = publicKey;
break;
case SIGN_P1_DERIVE:
signingKey = secp256k1.tmpECPrivateKey;
outputKey = null;
derive = true;
break;
case SIGN_P1_DERIVE_AND_MAKE_CURRENT:
signingKey = privateKey;
outputKey = publicKey;
derive = true;
makeCurrent = true;
break;
case SIGN_P1_PINLESS:
if (pinlessPathLen == 0) {
ISOException.throwIt(SW_REFERENCED_DATA_NOT_FOUND);
}
usePinless = true;
signingKey = pinlessPrivateKey;
outputKey = pinlessPublicKey;
break;
default:
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
short len; short len;
@ -1212,35 +1248,36 @@ public class KeycardApplet extends Applet {
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
} }
if (len != MessageDigest.LENGTH_SHA_256) { if (derive) {
short pathLen = (short) (len - MessageDigest.LENGTH_SHA_256);
if (pathLen <= 0) {
ISOException.throwIt(ISO7816.SW_WRONG_DATA); ISOException.throwIt(ISO7816.SW_WRONG_DATA);
} }
ECPrivateKey signingKey; doDerive(apduBuffer, MessageDigest.LENGTH_SHA_256, pathLen, apduBuffer[ISO7816.OFFSET_P1], makeCurrent);
ECPublicKey outputKey;
if (usePinless) {
if (pinlessPathLen == 0) {
ISOException.throwIt(SW_REFERENCED_DATA_NOT_FOUND);
}
signingKey = pinlessPrivateKey;
outputKey = pinlessPublicKey;
} else { } else {
signingKey = privateKey; if (len != MessageDigest.LENGTH_SHA_256) {
outputKey = publicKey; ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
} }
signature.init(signingKey, Signature.MODE_SIGN);
apduBuffer[SecureChannel.SC_OUT_OFFSET] = TLV_SIGNATURE_TEMPLATE; apduBuffer[SecureChannel.SC_OUT_OFFSET] = TLV_SIGNATURE_TEMPLATE;
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 3)] = TLV_PUB_KEY; apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 3)] = TLV_PUB_KEY;
short outLen = apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 4)] = (byte) outputKey.getW(apduBuffer, (short) (SecureChannel.SC_OUT_OFFSET + 5)); short outLen = apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 4)] = Crypto.KEY_PUB_SIZE;
if (outputKey != null) {
outputKey.getW(apduBuffer, (short) (SecureChannel.SC_OUT_OFFSET + 5));
} else {
secp256k1.derivePublicKey(derivationOutput, (short) 0, apduBuffer, (short) (SecureChannel.SC_OUT_OFFSET + 5));
}
outLen += 5; outLen += 5;
short sigOff = (short) (SecureChannel.SC_OUT_OFFSET + outLen); short sigOff = (short) (SecureChannel.SC_OUT_OFFSET + outLen);
outLen += signature.signPreComputedHash(apduBuffer, ISO7816.OFFSET_CDATA, len, apduBuffer, sigOff); signature.init(signingKey, Signature.MODE_SIGN);
outLen += signature.signPreComputedHash(apduBuffer, ISO7816.OFFSET_CDATA, MessageDigest.LENGTH_SHA_256, apduBuffer, sigOff);
outLen += crypto.fixS(apduBuffer, sigOff); outLen += crypto.fixS(apduBuffer, sigOff);
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 1)] = (byte) 0x81; apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 1)] = (byte) 0x81;
@ -1274,7 +1311,7 @@ public class KeycardApplet extends Applet {
Util.arrayCopy(apduBuffer, ISO7816.OFFSET_CDATA, pinlessPath, (short) 0, len); Util.arrayCopy(apduBuffer, ISO7816.OFFSET_CDATA, pinlessPath, (short) 0, len);
if (pinlessPathLen > 0) { if (pinlessPathLen > 0) {
doDerive(apduBuffer, len, DERIVE_P1_SOURCE_MASTER, false); doDerive(apduBuffer, (short) 0, len, DERIVE_P1_SOURCE_MASTER, false);
pinlessPrivateKey.setS(derivationOutput, (short) 0, Crypto.KEY_SECRET_SIZE); pinlessPrivateKey.setS(derivationOutput, (short) 0, Crypto.KEY_SECRET_SIZE);
secp256k1.derivePublicKey(pinlessPrivateKey, apduBuffer, (short) 0); secp256k1.derivePublicKey(pinlessPrivateKey, apduBuffer, (short) 0);
pinlessPublicKey.setW(apduBuffer, (short) 0, Crypto.KEY_PUB_SIZE); pinlessPublicKey.setW(apduBuffer, (short) 0, Crypto.KEY_PUB_SIZE);
@ -1341,7 +1378,7 @@ public class KeycardApplet extends Applet {
} }
if (derive) { if (derive) {
doDerive(apduBuffer, dataLen, derivationSource, makeCurrent); doDerive(apduBuffer, (short) 0, dataLen, derivationSource, makeCurrent);
} }
short off = SecureChannel.SC_OUT_OFFSET; short off = SecureChannel.SC_OUT_OFFSET;