diff --git a/src/fr/anssi/smartpgp/Common.java b/src/fr/anssi/smartpgp/Common.java index 5a822d5..c3fd5ce 100644 --- a/src/fr/anssi/smartpgp/Common.java +++ b/src/fr/anssi/smartpgp/Common.java @@ -27,6 +27,62 @@ import javacardx.crypto.*; public final class Common { + protected static final void checkPinFormat2(final byte[] buf, + short off, + short lc, + final short minlen, + final short maxlen) { + if(lc != (short)8) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return; + } + + if((buf[off] & (byte)0xf0) != (byte)0x20) { + ISOException.throwIt(ISO7816.SW_WRONG_DATA); + return; + } + + lc = (byte)(buf[off++] & (byte)0xf); + if((lc < minlen) || (lc > maxlen)) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return; + } + + for(short i = 0; i < lc; ++i) { + byte value; + + if((byte)(i & (byte)0x1) == (byte)0) { + value = (byte)(buf[off] & (byte)0xf0); + value >>= 4; + } else { + value = (byte)(buf[off] & (byte)0x0f); + ++off; + } + + if((value < (byte)0) || (value > (byte)9)) { + ISOException.throwIt(ISO7816.SW_WRONG_DATA); + return; + } + } + + for(short i = lc; i < (byte)14; ++i) { + byte value; + + if((byte)(i & (byte)0x1) == (byte)0) { + value = (byte)(buf[off] & (byte)0xf0); + value >>= 4; + } else { + value = (byte)(buf[off] & (byte)0x0f); + ++off; + } + + if(value != (byte)0x0f) { + ISOException.throwIt(ISO7816.SW_WRONG_DATA); + return; + } + } + } + protected static final short aesKeyLength(final ECParams params) { if(params.nb_bits < (short)512) { return (short)16; diff --git a/src/fr/anssi/smartpgp/Constants.java b/src/fr/anssi/smartpgp/Constants.java index 2c13ea5..e8bd7df 100644 --- a/src/fr/anssi/smartpgp/Constants.java +++ b/src/fr/anssi/smartpgp/Constants.java @@ -39,22 +39,47 @@ public final class Constants { protected static final byte USER_PIN_RETRY_COUNT = 3; protected static final byte USER_PIN_MIN_SIZE = 0x06; protected static final byte USER_PIN_MAX_SIZE = 0x7f; /* max is 0x7f because PIN format 2 */ + protected static final byte USER_PIN_MIN_SIZE_FORMAT_2 = 6; + protected static final byte USER_PIN_MAX_SIZE_FORMAT_2 = 12; protected static final byte[] USER_PIN_DEFAULT = { 0x31, 0x32, 0x33,0x34, 0x35, 0x36 }; + protected static final boolean USER_PIN_DEFAULT_IS_FORMAT_2 = false; + /* + protected static final byte[] USER_PIN_DEFAULT = { + (byte)0x26, + (byte)0x12, (byte)0x34, (byte)0x56, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff + }; + protected static final boolean USER_PIN_DEFAULT_IS_FORMAT_2 = true; + */ protected static final boolean USER_PIN_DEFAULT_FORCE_VERIFY_SIGNATURE = true; protected static final byte USER_PUK_RETRY_COUNT = 3; protected static final byte USER_PUK_MIN_SIZE = 0x08; protected static final byte USER_PUK_MAX_SIZE = 0x7f; /* max is 0x7f because PIN format 2 */ + protected static final byte USER_PUK_MIN_SIZE_FORMAT_2 = 8; + protected static final byte USER_PUK_MAX_SIZE_FORMAT_2 = 12; protected static final byte ADMIN_PIN_RETRY_COUNT = 3; protected static final byte ADMIN_PIN_MIN_SIZE = 0x08; protected static final byte ADMIN_PIN_MAX_SIZE = 0x7f; /* max is 0x7f because PIN format 2 */ + protected static final byte ADMIN_PIN_MIN_SIZE_FORMAT_2 = 8; + protected static final byte ADMIN_PIN_MAX_SIZE_FORMAT_2 = 12; protected static final byte[] ADMIN_PIN_DEFAULT = { 0x31, 0x32, 0x33,0x34, 0x35, 0x36, 0x37, 0x38 }; + protected static final boolean ADMIN_PIN_DEFAULT_IS_FORMAT_2 = false; + /* + protected static final byte[] ADMIN_PIN_DEFAULT = { + (byte)0x28, + (byte)0x12, (byte)0x34, (byte)0x56, (byte)0x78, (byte)0xff, + (byte)0xff, (byte)0xff + }; + protected static final boolean ADMIN_PIN_DEFAULT_IS_FORMAT_2 = true; + */ + protected static final byte FINGERPRINT_SIZE = 20; protected static final byte GENERATION_DATE_SIZE = 4; @@ -167,7 +192,7 @@ public final class Constants { (byte)0x00, (byte)0x20, /* max length get challenge */ (byte)0x04, (byte)0x80, /* max length of carholder certificate */ (byte)0x00, (byte)0xff, /* max length of special DOs (private, login, url, KDF-DO) */ - (byte)0x00, /* PIN format 2 not supported */ + (byte)0x01, /* PIN format 2 is supported */ (byte)0x00 /* MSE not supported */ }; diff --git a/src/fr/anssi/smartpgp/Persistent.java b/src/fr/anssi/smartpgp/Persistent.java index 3e7c2ea..0f2ec48 100644 --- a/src/fr/anssi/smartpgp/Persistent.java +++ b/src/fr/anssi/smartpgp/Persistent.java @@ -80,13 +80,16 @@ public final class Persistent { protected final OwnerPIN user_pin; /* PW1 */ protected byte user_pin_length; + protected boolean user_pin_is_format_2; protected boolean user_pin_force_verify_signature; protected final OwnerPIN user_puk; /* resetting code */ protected byte user_puk_length; + protected boolean user_puk_is_format_2; protected final OwnerPIN admin_pin; /* PW3 */ protected byte admin_pin_length; + protected boolean admin_pin_is_format_2; @@ -237,14 +240,17 @@ public final class Persistent { JCSystem.beginTransaction(); user_pin_length = (byte)Constants.USER_PIN_DEFAULT.length; + user_pin_is_format_2 = Constants.USER_PIN_DEFAULT_IS_FORMAT_2; user_pin.update(Constants.USER_PIN_DEFAULT, (short)0, user_pin_length); user_pin.resetAndUnblock(); JCSystem.commitTransaction(); user_puk_length = 0; + user_puk_is_format_2 = Constants.USER_PIN_DEFAULT_IS_FORMAT_2; JCSystem.beginTransaction(); admin_pin_length = (byte)Constants.ADMIN_PIN_DEFAULT.length; + admin_pin_is_format_2 = Constants.ADMIN_PIN_DEFAULT_IS_FORMAT_2; admin_pin.update(Constants.ADMIN_PIN_DEFAULT, (short)0, admin_pin_length); admin_pin.resetAndUnblock(); JCSystem.commitTransaction(); diff --git a/src/fr/anssi/smartpgp/SmartPGPApplet.java b/src/fr/anssi/smartpgp/SmartPGPApplet.java index b65afac..226f37f 100644 --- a/src/fr/anssi/smartpgp/SmartPGPApplet.java +++ b/src/fr/anssi/smartpgp/SmartPGPApplet.java @@ -158,14 +158,24 @@ public final class SmartPGPApplet extends Applet { } private final short writePwStatus(final byte[] buf, short off) { - if(data.user_pin_force_verify_signature) { - buf[off++] = (byte)0x00; + buf[off++] = (byte)(data.user_pin_force_verify_signature ? 0x00 : 0x01); + + if(data.user_pin_is_format_2) { + buf[off++] = (byte)0x80 | Constants.USER_PIN_MAX_SIZE_FORMAT_2; } else { - buf[off++] = (byte)0x01; + buf[off++] = Constants.USER_PIN_MAX_SIZE; } - buf[off++] = Constants.USER_PIN_MAX_SIZE; - buf[off++] = Constants.USER_PUK_MAX_SIZE; - buf[off++] = Constants.ADMIN_PIN_MAX_SIZE; + if(data.user_puk_is_format_2) { + buf[off++] = (byte)0x80 | Constants.USER_PUK_MAX_SIZE_FORMAT_2; + } else { + buf[off++] = Constants.USER_PUK_MAX_SIZE; + } + if(data.admin_pin_is_format_2) { + buf[off++] = (byte)0x80 | Constants.ADMIN_PIN_MAX_SIZE_FORMAT_2; + } else { + buf[off++] = Constants.ADMIN_PIN_MAX_SIZE; + } + buf[off++] = data.user_pin.getTriesRemaining(); if(data.user_puk_length > 0) { buf[off++] = data.user_puk.getTriesRemaining(); @@ -173,6 +183,7 @@ public final class SmartPGPApplet extends Applet { buf[off++] = (byte)0x00; } buf[off++] = data.admin_pin.getTriesRemaining(); + return off; } @@ -477,8 +488,7 @@ public final class SmartPGPApplet extends Applet { k.certificate_length); } - private final void processVerify(final short lc, - final byte p1, final byte p2) { + private final void processVerify(short lc, final byte p1, final byte p2) { sensitiveData(); @@ -523,20 +533,30 @@ public final class SmartPGPApplet extends Applet { switch(p2) { case (byte)0x81: case (byte)0x82: - if((lc < Constants.USER_PIN_MIN_SIZE) || - (lc > Constants.USER_PIN_MAX_SIZE)) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - return; + if(data.user_pin_is_format_2) { + Common.checkPinFormat2(transients.buffer, + (short)0, lc, + Constants.USER_PIN_MIN_SIZE_FORMAT_2, + Constants.USER_PIN_MAX_SIZE_FORMAT_2); + } else { + if((lc < Constants.USER_PIN_MIN_SIZE) || + (lc > Constants.USER_PIN_MAX_SIZE)) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return; + } } + if(p2 == (byte)0x81) { transients.setUserPinMode81(false); } else { transients.setUserPinMode82(false); } + if(!data.user_pin.check(transients.buffer, (short)0, (byte)lc)) { ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED); return; } + if(p2 == (byte)0x81) { transients.setUserPinMode81(true); } else { @@ -545,11 +565,19 @@ public final class SmartPGPApplet extends Applet { return; case (byte)0x83: - if((lc < Constants.ADMIN_PIN_MIN_SIZE) || - (lc > Constants.ADMIN_PIN_MAX_SIZE)) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - return; + if(data.admin_pin_is_format_2) { + Common.checkPinFormat2(transients.buffer, + (short)0, lc, + Constants.ADMIN_PIN_MIN_SIZE_FORMAT_2, + Constants.ADMIN_PIN_MAX_SIZE_FORMAT_2); + } else { + if((lc < Constants.ADMIN_PIN_MIN_SIZE) || + (lc > Constants.ADMIN_PIN_MAX_SIZE)) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return; + } } + if(!data.admin_pin.check(transients.buffer, (short)0, (byte)lc)) { ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED); return; @@ -606,10 +634,25 @@ public final class SmartPGPApplet extends Applet { switch(p2) { case (byte)0x81: - if((lc < (data.user_pin_length + Constants.USER_PIN_MIN_SIZE)) || - (lc > (data.user_pin_length + Constants.USER_PIN_MAX_SIZE))) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - return; + if(data.user_pin_is_format_2) { + if(lc != (short)(2 * data.user_pin_length)) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return; + } + Common.checkPinFormat2(transients.buffer, + (short)0, data.user_pin_length, + Constants.USER_PIN_MIN_SIZE_FORMAT_2, + Constants.USER_PIN_MAX_SIZE_FORMAT_2); + Common.checkPinFormat2(transients.buffer, + data.user_pin_length, data.user_pin_length, + Constants.USER_PIN_MIN_SIZE_FORMAT_2, + Constants.USER_PIN_MAX_SIZE_FORMAT_2); + } else { + if((lc < (data.user_pin_length + Constants.USER_PIN_MIN_SIZE)) || + (lc > (data.user_pin_length + Constants.USER_PIN_MAX_SIZE))) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return; + } } off = data.user_pin_length; if(!data.user_pin.check(transients.buffer, (short)0, off)) { @@ -619,17 +662,32 @@ public final class SmartPGPApplet extends Applet { transients.setUserPinMode81(false); transients.setUserPinMode82(false); JCSystem.beginTransaction(); - data.user_pin.update(transients.buffer, off, (byte)(lc - off)); - data.user_pin_length = off; + data.user_pin_length = (byte)(lc - off); + data.user_pin.update(transients.buffer, off, data.user_pin_length); JCSystem.commitTransaction(); data.user_pin.resetAndUnblock(); break; case (byte)0x83: - if((lc < (data.admin_pin_length + Constants.ADMIN_PIN_MIN_SIZE)) || - (lc > (data.admin_pin_length + Constants.ADMIN_PIN_MAX_SIZE))) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - return; + if(data.admin_pin_is_format_2) { + if(lc != (short)(2 * data.admin_pin_length)) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return; + } + Common.checkPinFormat2(transients.buffer, + (short)0, data.admin_pin_length, + Constants.ADMIN_PIN_MIN_SIZE_FORMAT_2, + Constants.ADMIN_PIN_MAX_SIZE_FORMAT_2); + Common.checkPinFormat2(transients.buffer, + data.admin_pin_length, data.admin_pin_length, + Constants.ADMIN_PIN_MIN_SIZE_FORMAT_2, + Constants.ADMIN_PIN_MAX_SIZE_FORMAT_2); + } else { + if((lc < (data.admin_pin_length + Constants.ADMIN_PIN_MIN_SIZE)) || + (lc > (data.admin_pin_length + Constants.ADMIN_PIN_MAX_SIZE))) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return; + } } off = data.admin_pin_length; if(!data.admin_pin.check(transients.buffer, (short)0, off)) { @@ -637,8 +695,8 @@ public final class SmartPGPApplet extends Applet { return; } JCSystem.beginTransaction(); - data.admin_pin.update(transients.buffer, off, (byte)(lc - off)); - data.admin_pin_length = off; + data.admin_pin_length = (byte)(lc - off); + data.admin_pin.update(transients.buffer, off, data.admin_pin_length); JCSystem.commitTransaction(); data.admin_pin.resetAndUnblock();; break; @@ -663,10 +721,27 @@ public final class SmartPGPApplet extends Applet { switch(p1) { case (byte)0x00: - if((lc < (data.user_puk_length + Constants.USER_PIN_MIN_SIZE)) || - (lc > (data.user_puk_length + Constants.USER_PIN_MAX_SIZE))) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - return; + if(data.user_pin_is_format_2) { + if(lc != (short)(data.user_puk_length + data.user_pin_length)) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return; + } + Common.checkPinFormat2(transients.buffer, + data.user_puk_length, data.user_pin_length, + Constants.USER_PIN_MIN_SIZE_FORMAT_2, + Constants.USER_PIN_MAX_SIZE_FORMAT_2); + } else { + if((lc < (data.user_puk_length + Constants.USER_PIN_MIN_SIZE)) || + (lc > (data.user_puk_length + Constants.USER_PIN_MAX_SIZE))) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return; + } + } + if(data.user_puk_is_format_2) { + Common.checkPinFormat2(transients.buffer, + (short)0, data.user_puk_length, + Constants.USER_PUK_MIN_SIZE_FORMAT_2, + Constants.USER_PUK_MAX_SIZE_FORMAT_2); } off = data.user_puk_length; if(!data.user_puk.check(transients.buffer, (short)0, off)) { @@ -676,18 +751,25 @@ public final class SmartPGPApplet extends Applet { transients.setUserPinMode81(false); transients.setUserPinMode82(false); JCSystem.beginTransaction(); - data.user_pin.update(transients.buffer, off, (byte)(lc - off)); - data.user_pin_length = off; + data.user_pin_length = (byte)(lc - off); + data.user_pin.update(transients.buffer, off, data.user_pin_length); JCSystem.commitTransaction(); data.user_pin.resetAndUnblock(); break; case (byte)0x02: assertAdmin(); - if((lc < Constants.USER_PIN_MIN_SIZE) || - (lc > Constants.USER_PIN_MAX_SIZE)) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - return; + if(data.user_pin_is_format_2) { + Common.checkPinFormat2(transients.buffer, + (short)0, lc, + Constants.USER_PIN_MIN_SIZE_FORMAT_2, + Constants.USER_PIN_MAX_SIZE_FORMAT_2); + } else { + if((lc < Constants.USER_PIN_MIN_SIZE) || + (lc > Constants.USER_PIN_MAX_SIZE)) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return; + } } transients.setUserPinMode81(false); transients.setUserPinMode82(false); @@ -984,7 +1066,7 @@ public final class SmartPGPApplet extends Applet { ISOException.throwIt(ISO7816.SW_WRONG_DATA); return; } - data.user_pin_force_verify_signature = (buf[0] == 0x00); + data.user_pin_force_verify_signature = (buf[0] == 0); break; case Constants.TAG_FINGERPRINT_SIG: @@ -1034,10 +1116,17 @@ public final class SmartPGPApplet extends Applet { case Constants.TAG_RESETTING_CODE: assertAdmin(); - if((lc < Constants.USER_PUK_MIN_SIZE) || - (lc > Constants.USER_PUK_MAX_SIZE)) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - return; + if(data.user_puk_is_format_2) { + Common.checkPinFormat2(transients.buffer, + (short)0, lc, + Constants.USER_PUK_MIN_SIZE_FORMAT_2, + Constants.USER_PUK_MAX_SIZE_FORMAT_2); + } else { + if((lc < Constants.USER_PUK_MIN_SIZE) || + (lc > Constants.USER_PUK_MAX_SIZE)) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return; + } } JCSystem.beginTransaction(); data.user_puk_length = (byte)lc;