From 4c0c58ac90e448e110e5b6f549f8d912a1038364 Mon Sep 17 00:00:00 2001 From: Michele Balistreri Date: Mon, 16 Oct 2017 17:34:37 +0300 Subject: [PATCH] add hint if public key derivation is optimized or not --- APPLICATION.MD | 1 + src/main/java/im/status/wallet/SEC256k1.java | 4 +++ .../java/im/status/wallet/WalletApplet.java | 4 +++ .../im/status/wallet/WalletAppletTest.java | 26 +++++++++---------- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/APPLICATION.MD b/APPLICATION.MD index d8c5b4c..1233c91 100644 --- a/APPLICATION.MD +++ b/APPLICATION.MD @@ -69,6 +69,7 @@ Response Data format: - Tag 0xC0 = PIN retry count (1 byte) - Tag 0xC1 = PUK retry count (1 byte) - Tag 0xC2 = 0 if key is not initialized, 1 otherwise + - Tag 0xC3 = 1 if public key derivation is hw optimized, 0 otherwise ### VERIFY PIN diff --git a/src/main/java/im/status/wallet/SEC256k1.java b/src/main/java/im/status/wallet/SEC256k1.java index 5a536b2..89a1196 100644 --- a/src/main/java/im/status/wallet/SEC256k1.java +++ b/src/main/java/im/status/wallet/SEC256k1.java @@ -90,4 +90,8 @@ public class SEC256k1 { return ecPoint.getW(out, outOff); } } + + static boolean hasFastECPointMultiplication() { + return ecPointMultiplier != null; + } } diff --git a/src/main/java/im/status/wallet/WalletApplet.java b/src/main/java/im/status/wallet/WalletApplet.java index aab6d4f..3f45d4d 100644 --- a/src/main/java/im/status/wallet/WalletApplet.java +++ b/src/main/java/im/status/wallet/WalletApplet.java @@ -47,6 +47,7 @@ public class WalletApplet extends Applet { static final byte TLV_PIN_RETRY_COUNT = (byte) 0xC0; static final byte TLV_PUK_RETRY_COUNT = (byte) 0xC1; static final byte TLV_KEY_INITIALIZATION_STATUS = (byte) 0xC2; + static final byte TLV_FAST_PUBLIC_KEY_DERIVATION = (byte) 0xC3; private OwnerPIN pin; private OwnerPIN puk; @@ -175,6 +176,9 @@ public class WalletApplet extends Applet { apduBuffer[off++] = TLV_KEY_INITIALIZATION_STATUS; apduBuffer[off++] = 1; apduBuffer[off++] = privateKey.isInitialized() ? (byte) 0x01 : (byte) 0x00; + apduBuffer[off++] = TLV_FAST_PUBLIC_KEY_DERIVATION; + apduBuffer[off++] = 1; + apduBuffer[off++] = SEC256k1.hasFastECPointMultiplication() ? (byte) 0x01 : (byte) 0x00; short len = secureChannel.encryptAPDU(apduBuffer, (short) (off - SecureChannel.SC_OUT_OFFSET)); apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, len); diff --git a/src/test/java/im/status/wallet/WalletAppletTest.java b/src/test/java/im/status/wallet/WalletAppletTest.java index 1752b56..70afe4e 100644 --- a/src/test/java/im/status/wallet/WalletAppletTest.java +++ b/src/test/java/im/status/wallet/WalletAppletTest.java @@ -117,24 +117,25 @@ public class WalletAppletTest { cmdSet.openSecureChannel(); // Good case. Since the order of test execution is undefined, the test cannot know if the keys are initialized or not. + // Additionally, support for fast public key derivation is hw dependent. response = cmdSet.getStatus(); assertEquals(0x9000, response.getSW()); byte[] data = secureChannel.decryptAPDU(response.getData()); - assertTrue(Hex.toHexString(data).matches("a309c00103c10105c2010[0-1]")); + assertTrue(Hex.toHexString(data).matches("a309c00103c10105c2010[0-1]c3010[0-1]")); response = cmdSet.verifyPIN("123456"); assertEquals(0x63C2, response.getSW()); response = cmdSet.getStatus(); assertEquals(0x9000, response.getSW()); data = secureChannel.decryptAPDU(response.getData()); - assertTrue(Hex.toHexString(data).matches("a309c00102c10105c2010[0-1]")); + assertTrue(Hex.toHexString(data).matches("a309c00102c10105c2010[0-1]c3010[0-1]")); response = cmdSet.verifyPIN("000000"); assertEquals(0x9000, response.getSW()); response = cmdSet.getStatus(); assertEquals(0x9000, response.getSW()); data = secureChannel.decryptAPDU(response.getData()); - assertTrue(Hex.toHexString(data).matches("a309c00103c10105c2010[0-1]")); + assertTrue(Hex.toHexString(data).matches("a309c00103c10105c2010[0-1]c3010[0-1]")); } @Test @@ -396,14 +397,21 @@ public class WalletAppletTest { response = cmdSet.sign(data, WalletApplet.SIGN_P1_DATA,false, true); assertEquals(0x6A86, response.getSW()); - // Correctly sign 1 block (P2: 0x81) - response = cmdSet.sign(smallData, WalletApplet.SIGN_P1_DATA,true, true); + // Correctly sign a precomputed hash + MessageDigest md = MessageDigest.getInstance("SHA-256", "BC"); + response = cmdSet.sign(md.digest(data), WalletApplet.SIGN_P1_PRECOMPUTED_HASH,true, true); assertEquals(0x9000, response.getSW()); byte[] sig = secureChannel.decryptAPDU(response.getData()); byte[] keyData = extractPublicKey(sig); sig = extractSignature(sig); assertEquals((SecureChannel.SC_KEY_LENGTH * 2 / 8) + 1, keyData.length); + signature.update(data); + assertTrue(signature.verify(sig)); + // Correctly sign 1 block (P2: 0x81) + response = cmdSet.sign(smallData, WalletApplet.SIGN_P1_DATA,true, true); + assertEquals(0x9000, response.getSW()); + sig = extractSignature(secureChannel.decryptAPDU(response.getData())); signature.update(smallData); assertTrue(signature.verify(sig)); @@ -469,14 +477,6 @@ public class WalletAppletTest { signature.update(data); signature.update(smallData); assertTrue(signature.verify(sig)); - - MessageDigest md = MessageDigest.getInstance("SHA-256", "BC"); - response = cmdSet.sign(md.digest(data), WalletApplet.SIGN_P1_PRECOMPUTED_HASH,true, true); - assertEquals(0x9000, response.getSW()); - sig = extractSignature(secureChannel.decryptAPDU(response.getData())); - signature.update(data); - assertTrue(signature.verify(sig)); - } @Test