SmartPGP/test_applet/src/fr/anssi/smartpgp/TestApplet.java

396 lines
12 KiB
Java

package dev.c0de.smartpgp;
import javacard.framework.*;
import javacard.security.*;
import javacardx.apdu.*;
import javacardx.crypto.*;
public final class TestApplet extends Applet {
private final byte[] buffer_red;
private final byte[] buffer_black;
private final OwnerPIN pin;
public TestApplet() {
buffer_red = JCSystem.makeTransientByteArray(Data.BUFFER_RED_LENGTH,
JCSystem.CLEAR_ON_DESELECT);
buffer_black = JCSystem.makeTransientByteArray(Data.BUFFER_BLACK_LENGTH,
JCSystem.CLEAR_ON_DESELECT);
pin = new OwnerPIN(Data.PIN_RETRY_COUNT, (byte)Data.PIN_DEFAULT.length);
pin.update(Data.PIN_DEFAULT, (short)0, (byte)Data.PIN_DEFAULT.length);
}
public static final void install(byte[] buf, short off, byte len) {
new TestApplet().register();
}
private final void processTestRandom(final byte p1, final byte p2) {
final RandomData rand = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
rand.generateData(buffer_red, (short)0, (short)buffer_red.length);
}
private final void processTestRsa(final boolean crt, final byte p1, final byte p2) {
boolean generate;
short size;
PrivateKey priv;
RSAPublicKey pub;
byte[] p, q, pq, dp1, dq1, n, e, d;
switch(p1) {
case (byte)0x00:
generate = false;
break;
case (byte)0x01:
generate = true;
break;
default:
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
switch(p2) {
case (byte)0x00:
size = (short)1024;
p = Data.RSA_1024_PRIV_PRIME_P;
q = Data.RSA_1024_PRIV_PRIME_Q;
pq = Data.RSA_1024_PRIV_PQ;
dp1 = Data.RSA_1024_PRIV_EXPONENT_DP1;
dq1 = Data.RSA_1024_PRIV_EXPONENT_DQ1;
n = Data.RSA_1024_PUB_MODULUS_N;
e = Data.RSA_1024_PUB_EXPONENT_E;
d = Data.RSA_1024_PRIV_EXPONENT_D;
break;
case (byte)0x01:
size = (short)2048;
p = Data.RSA_2048_PRIV_PRIME_P;
q = Data.RSA_2048_PRIV_PRIME_Q;
pq = Data.RSA_2048_PRIV_PQ;
dp1 = Data.RSA_2048_PRIV_EXPONENT_DP1;
dq1 = Data.RSA_2048_PRIV_EXPONENT_DQ1;
n = Data.RSA_2048_PUB_MODULUS_N;
e = Data.RSA_2048_PUB_EXPONENT_E;
d = Data.RSA_2048_PRIV_EXPONENT_D;
break;
case (byte)0x02:
size = (short)3072;
p = Data.RSA_3072_PRIV_PRIME_P;
q = Data.RSA_3072_PRIV_PRIME_Q;
pq = Data.RSA_3072_PRIV_PQ;
dp1 = Data.RSA_3072_PRIV_EXPONENT_DP1;
dq1 = Data.RSA_3072_PRIV_EXPONENT_DQ1;
n = Data.RSA_3072_PUB_MODULUS_N;
e = Data.RSA_3072_PUB_EXPONENT_E;
d = Data.RSA_3072_PRIV_EXPONENT_D;
break;
case (byte)0x03:
size = (short)4096;
p = Data.RSA_4096_PRIV_PRIME_P;
q = Data.RSA_4096_PRIV_PRIME_Q;
pq = Data.RSA_4096_PRIV_PQ;
dp1 = Data.RSA_4096_PRIV_EXPONENT_DP1;
dq1 = Data.RSA_4096_PRIV_EXPONENT_DQ1;
n = Data.RSA_4096_PUB_MODULUS_N;
e = Data.RSA_4096_PUB_EXPONENT_E;
d = Data.RSA_4096_PRIV_EXPONENT_D;
break;
default:
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
pub = (RSAPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, size, false);
if(pub == null) {
ISOException.throwIt(Data.SW_FAILED_TO_BUILD_PUB_KEY);
return;
}
if(crt) {
priv = (RSAPrivateCrtKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_CRT_PRIVATE, size, false);
if(!generate) {
((RSAPrivateCrtKey)priv).setP(p, (short)0, (short)p.length);
((RSAPrivateCrtKey)priv).setQ(q, (short)0, (short)q.length);
((RSAPrivateCrtKey)priv).setPQ(pq, (short)0, (short)pq.length);
((RSAPrivateCrtKey)priv).setDP1(dp1, (short)0, (short)dp1.length);
((RSAPrivateCrtKey)priv).setDQ1(dq1, (short)0, (short)dq1.length);
pub.setModulus(n, (short)0, (short)n.length);
pub.setExponent(e, (short)0, (short)e.length);
}
} else {
priv = (RSAPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, size, false);
if(!generate) {
((RSAPrivateKey)priv).setModulus(n, (short)0, (short)n.length);
((RSAPrivateKey)priv).setExponent(d, (short)0, (short)d.length);
pub.setModulus(n, (short)0, (short)n.length);
pub.setExponent(e, (short)0, (short)e.length);
}
}
if(priv == null) {
ISOException.throwIt(Data.SW_FAILED_TO_BUILD_PRIV_KEY);
return;
}
if(generate) {
final KeyPair kp = new KeyPair(pub, priv);
kp.genKeyPair();
}
if(!pub.isInitialized()) {
ISOException.throwIt(Data.SW_PUB_KEY_NOT_INITIALIZED);
return;
}
if(!priv.isInitialized()) {
ISOException.throwIt(Data.SW_PRIV_KEY_NOT_INITIALIZED);
return;
}
final Cipher cipher_rsa_pkcs1 = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
cipher_rsa_pkcs1.init(pub, Cipher.MODE_ENCRYPT);
cipher_rsa_pkcs1.doFinal(buffer_red, (short)0, (short)64,
buffer_black, (short)0);
cipher_rsa_pkcs1.init(priv, Cipher.MODE_DECRYPT);
cipher_rsa_pkcs1.doFinal(buffer_black, (short)0, (short)(size / 8),
buffer_red, (short)0);
}
private final void processTestEc(final byte p1, final byte p2) {
boolean generate, provide_w;
short size;
ECPrivateKey priv;
ECPublicKey pub;
byte[] field, a, b, g, r, s, w;
short k;
switch(p1 & (byte)0x01) {
case (byte)0x00:
generate = false;
break;
case (byte)0x01:
generate = true;
break;
default:
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
switch(p1 & (byte)0x10) {
case (byte)0x00:
provide_w = true;
break;
case (byte)0x10:
provide_w = false;
break;
default:
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
switch(p2) {
case (byte)0x00:
size = 256;
field = ECConstants.ansix9p256r1_field;
a = ECConstants.ansix9p256r1_a;
b = ECConstants.ansix9p256r1_b;
g = ECConstants.ansix9p256r1_g;
r = ECConstants.ansix9p256r1_r;
k = (short)1;
s = Data.EC_ANSIX9P256R1_S;
w = Data.EC_ANSIX9P256R1_W;
break;
case (byte)0x01:
size = 521;
field = ECConstants.ansix9p521r1_field;
a = ECConstants.ansix9p521r1_a;
b = ECConstants.ansix9p521r1_b;
g = ECConstants.ansix9p521r1_g;
r = ECConstants.ansix9p521r1_r;
k = (short)1;
s = Data.EC_ANSIX9P521R1_S;
w = Data.EC_ANSIX9P521R1_W;
break;
case (byte)0x11:
size = 528;
field = ECConstants.ansix9p521r1_field;
a = ECConstants.ansix9p521r1_a;
b = ECConstants.ansix9p521r1_b;
g = ECConstants.ansix9p521r1_g;
r = ECConstants.ansix9p521r1_r;
k = (short)1;
s = Data.EC_ANSIX9P521R1_S;
w = Data.EC_ANSIX9P521R1_W;
break;
default:
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
return;
}
pub = (ECPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, size, false);
if(pub == null) {
ISOException.throwIt(Data.SW_FAILED_TO_BUILD_PUB_KEY);
return;
}
pub.setFieldFP(field, (short)0, (short)field.length);
pub.setA(a, (short)0, (short)a.length);
pub.setB(b, (short)0, (short)b.length);
pub.setG(g, (short)0, (short)g.length);
pub.setR(r, (short)0, (short)r.length);
pub.setK(k);
priv = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, size, false);
if(priv == null) {
ISOException.throwIt(Data.SW_FAILED_TO_BUILD_PRIV_KEY);
return;
}
priv.setFieldFP(field, (short)0, (short)field.length);
priv.setA(a, (short)0, (short)a.length);
priv.setB(b, (short)0, (short)b.length);
priv.setG(g, (short)0, (short)g.length);
priv.setR(r, (short)0, (short)r.length);
priv.setK(k);
if(generate) {
final KeyPair kp = new KeyPair(pub, priv);
kp.genKeyPair();
} else {
priv.setS(s, (short)0, (short)s.length);
if(provide_w) {
pub.setW(w, (short)0, (short)w.length);
}
}
if(!pub.isInitialized()) {
ISOException.throwIt(Data.SW_PUB_KEY_NOT_INITIALIZED);
return;
}
if(!priv.isInitialized()) {
ISOException.throwIt(Data.SW_PRIV_KEY_NOT_INITIALIZED);
return;
}
final Signature sig = Signature.getInstance(Signature.ALG_ECDSA_SHA_512, false);
sig.init(priv, Signature.MODE_SIGN);
sig.signPreComputedHash(buffer_red, (short)0, MessageDigest.LENGTH_SHA_512,
buffer_black, (short)0);
}
private final void processPin(final APDU apdu) {
if(0 < apdu.setIncomingAndReceive()) {
final boolean ok = pin.check(apdu.getBuffer(),
apdu.getOffsetCdata(),
(byte)apdu.getIncomingLength());
pin.resetAndUnblock();
if(!ok) {
ISOException.throwIt(Data.SW_PIN_INVALID);
}
} else {
ISOException.throwIt(Data.SW_PIN_EMPTY);
}
}
private final void processPinT0(final APDU apdu) {
short blen = (short) (apdu.getBuffer()[ISO7816.OFFSET_LC] & 0x00FF);
if(0 < blen) {
short off = ISO7816.OFFSET_CDATA;
short read = apdu.setIncomingAndReceive();
while(0 < blen) {
blen -= read;
read = apdu.receiveBytes(off);
off += read;
}
pin.resetAndUnblock();
final boolean ok = pin.check(apdu.getBuffer(),
apdu.getOffsetCdata(),
(byte)apdu.getIncomingLength());
if(!ok) {
ISOException.throwIt(Data.SW_PIN_INVALID);
}
} else {
ISOException.throwIt(Data.SW_PIN_EMPTY);
}
}
private final void processDeletion() {
if(!JCSystem.isObjectDeletionSupported()) {
ISOException.throwIt(Data.SW_DELETION_NOT_SUPPORTED);
}
JCSystem.requestObjectDeletion();
}
public final void process(final APDU apdu) {
final byte[] apdubuf = apdu.getBuffer();
if(apdu.isISOInterindustryCLA() && selectingApplet()) {
return;
}
final byte p1 = apdubuf[ISO7816.OFFSET_P1];
final byte p2 = apdubuf[ISO7816.OFFSET_P2];
switch(apdubuf[ISO7816.OFFSET_INS]) {
case Data.INS_TEST_RANDOM:
processTestRandom(p1, p2);
break;
case Data.INS_TEST_RSA:
processTestRsa(false, p1, p2);
break;
case Data.INS_TEST_RSA_CRT:
processTestRsa(true, p1, p2);
break;
case Data.INS_TEST_EC:
processTestEc(p1, p2);
break;
case Data.INS_TEST_PIN:
if(APDU.getProtocol() == APDU.PROTOCOL_T0) {
processPinT0(apdu);
} else {
processPin(apdu);
}
break;
case Data.INS_TEST_DELETION:
processDeletion();
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
return;
}
}
}