849 lines
27 KiB
Java
849 lines
27 KiB
Java
![]() |
/*
|
||
|
SmartPGP : JavaCard implementation of OpenPGP card v3 specification
|
||
|
https://github.com/ANSSI-FR/smartpgp
|
||
|
Copyright (C) 2016 ANSSI
|
||
|
|
||
|
This program is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU General Public License
|
||
|
as published by the Free Software Foundation; either version 2
|
||
|
of the License, or (at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; if not, write to the Free Software
|
||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
package fr.anssi.smartpgp;
|
||
|
|
||
|
import javacard.framework.*;
|
||
|
import javacard.security.*;
|
||
|
import javacardx.apdu.*;
|
||
|
import javacardx.crypto.*;
|
||
|
|
||
|
public final class PGPKey {
|
||
|
|
||
|
protected final Fingerprint fingerprint;
|
||
|
|
||
|
protected final byte[] generation_date;
|
||
|
|
||
|
protected final byte[] certificate;
|
||
|
protected short certificate_length;
|
||
|
|
||
|
protected final byte[] attributes;
|
||
|
protected byte attributes_length;
|
||
|
|
||
|
protected final boolean is_secure_messaging_key;
|
||
|
|
||
|
private KeyPair keys;
|
||
|
|
||
|
|
||
|
protected PGPKey(final boolean for_secure_messaging) {
|
||
|
|
||
|
is_secure_messaging_key = for_secure_messaging;
|
||
|
|
||
|
if(is_secure_messaging_key) {
|
||
|
fingerprint = null;
|
||
|
generation_date = null;
|
||
|
} else {
|
||
|
fingerprint = new Fingerprint();
|
||
|
generation_date = new byte[Constants.GENERATION_DATE_SIZE];
|
||
|
}
|
||
|
|
||
|
certificate = new byte[Constants.cardholderCertificateMaxLength()];
|
||
|
certificate_length = 0;
|
||
|
|
||
|
attributes = new byte[Constants.ALGORITHM_ATTRIBUTES_MAX_LENGTH];
|
||
|
attributes_length = 0;
|
||
|
|
||
|
reset();
|
||
|
}
|
||
|
|
||
|
private final void resetKeys() {
|
||
|
if(keys != null) {
|
||
|
keys.getPrivate().clearKey();
|
||
|
keys.getPublic().clearKey();
|
||
|
keys = null;
|
||
|
}
|
||
|
|
||
|
if(certificate_length > 0) {
|
||
|
certificate_length = (short)0;
|
||
|
Util.arrayFillNonAtomic(certificate, (short)0, certificate_length, (byte)0);
|
||
|
}
|
||
|
|
||
|
if(!is_secure_messaging_key) {
|
||
|
fingerprint.reset();
|
||
|
|
||
|
Util.arrayFillNonAtomic(generation_date, (short)0, Constants.GENERATION_DATE_SIZE, (byte)0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected final void reset() {
|
||
|
resetKeys();
|
||
|
|
||
|
JCSystem.beginTransaction();
|
||
|
if(attributes_length > 0) {
|
||
|
Util.arrayFillNonAtomic(attributes, (short)0, attributes_length, (byte)0);
|
||
|
attributes_length = (byte)0;
|
||
|
}
|
||
|
|
||
|
if(is_secure_messaging_key) {
|
||
|
Util.arrayCopyNonAtomic(Constants.ALGORITHM_ATTRIBUTES_DEFAULT_SECURE_MESSAGING, (short)0,
|
||
|
attributes, (short)0,
|
||
|
(short)Constants.ALGORITHM_ATTRIBUTES_DEFAULT_SECURE_MESSAGING.length);
|
||
|
attributes_length = (byte)Constants.ALGORITHM_ATTRIBUTES_DEFAULT_SECURE_MESSAGING.length;
|
||
|
} else {
|
||
|
Util.arrayCopyNonAtomic(Constants.ALGORITHM_ATTRIBUTES_DEFAULT, (short)0,
|
||
|
attributes, (short)0,
|
||
|
(short)Constants.ALGORITHM_ATTRIBUTES_DEFAULT.length);
|
||
|
attributes_length = (byte)Constants.ALGORITHM_ATTRIBUTES_DEFAULT.length;
|
||
|
}
|
||
|
JCSystem.commitTransaction();
|
||
|
}
|
||
|
|
||
|
protected final boolean isInitialized() {
|
||
|
return (keys != null) && keys.getPrivate().isInitialized() && keys.getPublic().isInitialized();
|
||
|
}
|
||
|
|
||
|
protected final void setCertificate(final byte[] buf, final short off, final short len) {
|
||
|
if((len < 0) ||
|
||
|
(len > Constants.cardholderCertificateMaxLength())) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
JCSystem.beginTransaction();
|
||
|
if(certificate_length > 0) {
|
||
|
Util.arrayFillNonAtomic(certificate, (short)0, certificate_length, (byte)0);
|
||
|
}
|
||
|
Util.arrayCopyNonAtomic(buf, off, certificate, (short)0, len);
|
||
|
certificate_length = len;
|
||
|
JCSystem.commitTransaction();
|
||
|
}
|
||
|
|
||
|
protected final void setGenerationDate(final byte[] buf, final short off, final short len) {
|
||
|
if(len != Constants.GENERATION_DATE_SIZE) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||
|
return;
|
||
|
}
|
||
|
Util.arrayCopy(buf, off, generation_date, (short)0, len);
|
||
|
}
|
||
|
|
||
|
protected final void setAttributes(final ECCurves ec,
|
||
|
final byte[] buf, final short off, final short len) {
|
||
|
if((len < Constants.ALGORITHM_ATTRIBUTES_MIN_LENGTH) ||
|
||
|
(len > Constants.ALGORITHM_ATTRIBUTES_MAX_LENGTH)) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch(buf[0]) {
|
||
|
case 0x01:
|
||
|
if((len != 6) || is_secure_messaging_key) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return;
|
||
|
}
|
||
|
if((Util.getShort(buf, (short)1) < 2048) ||
|
||
|
(Util.getShort(buf, (short)3) != 0x11) ||
|
||
|
(buf[5] < 0) || (buf[5] > 3)) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 0x12:
|
||
|
case 0x13:
|
||
|
if(len < 2) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return;
|
||
|
}
|
||
|
final byte delta = (buf[(short)(len - 1)] == (byte)0xff) ? (byte)1 : (byte)0;
|
||
|
final ECParams params = ec.findByOid(buf, (short)(off + 1), (byte)(len - 1 - delta));
|
||
|
if(params == null) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return;
|
||
|
}
|
||
|
if(is_secure_messaging_key) {
|
||
|
if((buf[0] != 0x12) ||
|
||
|
(((short)(Constants.aesKeyLength()* 8) > (short)128) &&
|
||
|
(params.nb_bits < 512))) {
|
||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
resetKeys();
|
||
|
|
||
|
JCSystem.beginTransaction();
|
||
|
if(attributes_length > 0) {
|
||
|
Util.arrayFillNonAtomic(attributes, (short)0, attributes_length, (byte)0);
|
||
|
}
|
||
|
Util.arrayCopyNonAtomic(buf, off, attributes, (short)0, len);
|
||
|
attributes_length = (byte)len;
|
||
|
JCSystem.commitTransaction();
|
||
|
}
|
||
|
|
||
|
|
||
|
protected final boolean isRsa() {
|
||
|
return (attributes[0] == 1);
|
||
|
}
|
||
|
|
||
|
protected final short rsaModulusBitSize() {
|
||
|
return Util.getShort(attributes, (short)1);
|
||
|
}
|
||
|
|
||
|
protected final short rsaExponentBitSize() {
|
||
|
return Util.getShort(attributes, (short)3);
|
||
|
}
|
||
|
|
||
|
protected final boolean isEc() {
|
||
|
return ((attributes[0] == (byte)0x12) ||
|
||
|
(attributes[0] == (byte)0x13));
|
||
|
}
|
||
|
|
||
|
protected final ECParams ecParams(final ECCurves ec) {
|
||
|
final byte delta = (attributes[(short)(attributes_length - 1)] == (byte)0xff) ? (byte)1 : (byte)0;
|
||
|
return ec.findByOid(attributes, (short)1, (byte)(attributes_length - 1 - delta));
|
||
|
}
|
||
|
|
||
|
|
||
|
private final KeyPair generateRSA() {
|
||
|
final PrivateKey priv = (PrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_CRT_PRIVATE, rsaModulusBitSize(), false);
|
||
|
final RSAPublicKey pub = (RSAPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, rsaModulusBitSize(), false);
|
||
|
|
||
|
if((priv == null) || (pub == null)) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
pub.setExponent(Constants.RSA_EXPONENT, (short)0, (byte)Constants.RSA_EXPONENT.length);
|
||
|
|
||
|
return new KeyPair(pub, priv);
|
||
|
}
|
||
|
|
||
|
|
||
|
private final KeyPair generateEC(final ECCurves ec) {
|
||
|
|
||
|
final ECParams params = ecParams(ec);
|
||
|
|
||
|
final ECPrivateKey priv = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, params.nb_bits, false);
|
||
|
final ECPublicKey pub = (ECPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, params.nb_bits, false);
|
||
|
|
||
|
if((priv == null) || (pub == null)) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
params.setParams(priv);
|
||
|
params.setParams(pub);
|
||
|
|
||
|
return new KeyPair(pub, priv);
|
||
|
}
|
||
|
|
||
|
|
||
|
protected final void generate(final ECCurves ec) {
|
||
|
|
||
|
KeyPair nkeys = null;
|
||
|
|
||
|
if(isRsa()) {
|
||
|
nkeys = generateRSA();
|
||
|
} else if(isEc()) {
|
||
|
nkeys = generateEC(ec);
|
||
|
}
|
||
|
|
||
|
if(nkeys == null) {
|
||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
nkeys.genKeyPair();
|
||
|
|
||
|
if(!nkeys.getPublic().isInitialized() || !nkeys.getPrivate().isInitialized()) {
|
||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
resetKeys();
|
||
|
|
||
|
keys = nkeys;
|
||
|
}
|
||
|
|
||
|
|
||
|
private final KeyPair importRSAKey(final byte[] buf,
|
||
|
final short boff, final short len,
|
||
|
final byte tag_count, final byte[] tag_val, final short[] tag_len) {
|
||
|
|
||
|
final short attr_modulus_bit_size = rsaModulusBitSize();
|
||
|
final short attr_modulus_byte_size = Common.bitsToBytes(attr_modulus_bit_size);
|
||
|
|
||
|
final RSAPrivateCrtKey priv = (RSAPrivateCrtKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_CRT_PRIVATE, attr_modulus_bit_size, false);
|
||
|
final RSAPublicKey pub = (RSAPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, attr_modulus_bit_size, false);
|
||
|
|
||
|
if((priv == null) || (pub == null)) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
short off = boff;
|
||
|
byte i = 0;
|
||
|
while(i < tag_count) {
|
||
|
|
||
|
if((short)((short)(off - boff) + tag_len[i]) > len) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
switch(tag_val[i]) {
|
||
|
case (byte)0x91:
|
||
|
if(tag_len[i] != Common.bitsToBytes(rsaExponentBitSize())) {
|
||
|
return null;
|
||
|
}
|
||
|
pub.setExponent(buf, off, tag_len[i]);
|
||
|
break;
|
||
|
|
||
|
case (byte)0x92:
|
||
|
if(tag_len[i] != (short)(attr_modulus_byte_size / 2)) {
|
||
|
return null;
|
||
|
}
|
||
|
priv.setP(buf, off, tag_len[i]);
|
||
|
break;
|
||
|
|
||
|
case (byte)0x93:
|
||
|
if(tag_len[i] != (short)(attr_modulus_byte_size / 2)) {
|
||
|
return null;
|
||
|
}
|
||
|
priv.setQ(buf, off, tag_len[i]);
|
||
|
break;
|
||
|
|
||
|
case (byte)0x94:
|
||
|
if(tag_len[i] != (short)(attr_modulus_byte_size / 2)) {
|
||
|
return null;
|
||
|
}
|
||
|
priv.setPQ(buf, off, tag_len[i]);
|
||
|
break;
|
||
|
|
||
|
case (byte)0x95:
|
||
|
if(tag_len[i] != (short)(attr_modulus_byte_size / 2)) {
|
||
|
return null;
|
||
|
}
|
||
|
priv.setDP1(buf, off, tag_len[i]);
|
||
|
break;
|
||
|
|
||
|
case (byte)0x96:
|
||
|
if(tag_len[i] != (short)(attr_modulus_byte_size / 2)) {
|
||
|
return null;
|
||
|
}
|
||
|
priv.setDQ1(buf, off, tag_len[i]);
|
||
|
break;
|
||
|
|
||
|
case (byte)0x97:
|
||
|
if(tag_len[i] != attr_modulus_byte_size) {
|
||
|
return null;
|
||
|
}
|
||
|
pub.setModulus(buf, off, tag_len[i]);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
off += tag_len[i];
|
||
|
++i;
|
||
|
}
|
||
|
|
||
|
if(!priv.isInitialized() || !pub.isInitialized()) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return new KeyPair(pub, priv);
|
||
|
}
|
||
|
|
||
|
private final KeyPair importECKey(final ECCurves ec,
|
||
|
final byte[] buf,
|
||
|
final short boff, final short len,
|
||
|
final byte tag_count, final byte[] tag_val, final short[] tag_len) {
|
||
|
final ECParams params = ecParams(ec);
|
||
|
|
||
|
final ECPrivateKey priv = (ECPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE,
|
||
|
params.nb_bits,
|
||
|
false);
|
||
|
final ECPublicKey pub = (ECPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC,
|
||
|
params.nb_bits,
|
||
|
false);
|
||
|
|
||
|
if((priv == null) || (pub == null)) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
params.setParams(priv);
|
||
|
params.setParams(pub);
|
||
|
|
||
|
short off = boff;
|
||
|
byte i = 0;
|
||
|
while(i < tag_count) {
|
||
|
|
||
|
if((short)((short)(off - boff) + tag_len[i]) > len) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
switch(tag_val[i]) {
|
||
|
case (byte)0x92:
|
||
|
if(tag_len[i] > Common.bitsToBytes(params.nb_bits)) {
|
||
|
return null;
|
||
|
}
|
||
|
priv.setS(buf, off, tag_len[i]);
|
||
|
break;
|
||
|
|
||
|
case (byte)0x99:
|
||
|
if(tag_len[i] > (short)(2 * Common.bitsToBytes(params.nb_bits) + 1)) {
|
||
|
return null;
|
||
|
}
|
||
|
if(((byte)(tag_len[i] - 1) & (byte)0x1) != 0) {
|
||
|
return null;
|
||
|
}
|
||
|
pub.setW(buf, off, tag_len[i]);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
off += tag_len[i];
|
||
|
++i;
|
||
|
}
|
||
|
|
||
|
if(!priv.isInitialized() || !pub.isInitialized()) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return new KeyPair(pub, priv);
|
||
|
}
|
||
|
|
||
|
protected final void importKey(final ECCurves ec,
|
||
|
final byte[] buf, final short boff, final short len) {
|
||
|
|
||
|
short off = boff;
|
||
|
|
||
|
short template_len = 0;
|
||
|
short template_off = 0;
|
||
|
|
||
|
short data_len = 0;
|
||
|
short data_off = 0;
|
||
|
|
||
|
byte data_tag_count = 0;
|
||
|
byte[] data_tag_val = new byte[7];
|
||
|
short[] data_tag_len = new short[7];
|
||
|
|
||
|
while((short)(len - (short)(off - boff)) > 2) {
|
||
|
switch(Util.getShort(buf, off)) {
|
||
|
|
||
|
case (short)0x7f48:
|
||
|
off += 2;
|
||
|
template_len = Common.readLength(buf, off, (short)(len - (short)(off - boff)));
|
||
|
off = Common.skipLength(buf, off, (short)(len - (short)(off - boff)));
|
||
|
template_off = off;
|
||
|
|
||
|
if(template_len > (short)(len - ((short)off - boff))) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
while((short)(template_len - (short)(off - template_off)) > 1) {
|
||
|
if((buf[off] < (byte)0x91) ||
|
||
|
(buf[off] > (byte)0x99)) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(data_tag_count >= data_tag_val.length) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
data_tag_val[data_tag_count] = buf[off];
|
||
|
++off;
|
||
|
|
||
|
data_tag_len[data_tag_count] = Common.readLength(buf, off, (short)(template_len - (short)(off - template_off)));
|
||
|
off = Common.skipLength(buf, off, (short)(template_len - (short)(off - template_off)));
|
||
|
|
||
|
++data_tag_count;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case (short)0x5f48:
|
||
|
off += 2;
|
||
|
data_len = Common.readLength(buf, off, (short)(len - (short)(off - boff)));
|
||
|
off = Common.skipLength(buf, off, (short)(len - (short)(off - boff)));
|
||
|
data_off = off;
|
||
|
|
||
|
if(data_len > (short)(len - ((short)off - boff))) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
off += data_len;
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
KeyPair nkeys = null;
|
||
|
|
||
|
if(isRsa()) {
|
||
|
nkeys = importRSAKey(buf, data_off, data_len, data_tag_count, data_tag_val, data_tag_len);
|
||
|
} else if(isEc()) {
|
||
|
nkeys = importECKey(ec, buf, data_off, data_len, data_tag_count, data_tag_val, data_tag_len);
|
||
|
}
|
||
|
|
||
|
if(nkeys == null) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(!nkeys.getPrivate().isInitialized() || !nkeys.getPublic().isInitialized()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
resetKeys();
|
||
|
keys = nkeys;
|
||
|
}
|
||
|
|
||
|
|
||
|
protected final short writePublicKeyDo(final byte[] buf, short off) {
|
||
|
|
||
|
if(!isInitialized()) {
|
||
|
ISOException.throwIt(Constants.SW_REFERENCE_DATA_NOT_FOUND);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
final PublicKey pub = keys.getPublic();
|
||
|
|
||
|
off = Util.setShort(buf, off, (short)0x7f49);
|
||
|
|
||
|
if(isRsa()) {
|
||
|
|
||
|
final RSAPublicKey rsapub = (RSAPublicKey)pub;
|
||
|
final short modulus_size = Common.bitsToBytes(rsaModulusBitSize());
|
||
|
final short exponent_size = Common.bitsToBytes(rsaExponentBitSize());
|
||
|
|
||
|
final short mlensize = (short)((modulus_size > (short)0xff) ? 3 : 2);
|
||
|
|
||
|
final short flen =
|
||
|
(short)(1 + mlensize + modulus_size +
|
||
|
1 + 1 + exponent_size);
|
||
|
|
||
|
off = Common.writeLength(buf, off, flen);
|
||
|
|
||
|
buf[off++] = (byte)0x81;
|
||
|
off = Common.writeLength(buf, off, modulus_size);
|
||
|
off += rsapub.getModulus(buf, off);
|
||
|
|
||
|
buf[off++] = (byte)0x82;
|
||
|
off = Common.writeLength(buf, off, exponent_size);
|
||
|
off += rsapub.getExponent(buf, off);
|
||
|
|
||
|
return off;
|
||
|
|
||
|
} else if(isEc()) {
|
||
|
|
||
|
final ECPublicKey ecpub = (ECPublicKey)pub;
|
||
|
final short qsize = (short)(1 + 2 * (short)((ecpub.getSize() / 8) + (((ecpub.getSize() % 8) == 0) ? 0 : 1)));
|
||
|
short rsize = (short)(1 + qsize);
|
||
|
|
||
|
if(qsize > 0x7f) {
|
||
|
rsize = (short)(rsize + 2);
|
||
|
} else {
|
||
|
rsize = (short)(rsize + 1);
|
||
|
}
|
||
|
|
||
|
off = Common.writeLength(buf, off, rsize);
|
||
|
|
||
|
buf[off++] = (byte)0x86;
|
||
|
|
||
|
off = Common.writeLength(buf, off, qsize);
|
||
|
|
||
|
off += ecpub.getW(buf, off);
|
||
|
|
||
|
return off;
|
||
|
|
||
|
}
|
||
|
|
||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||
|
return off;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
protected final short sign(final byte[] buf, final short lc,
|
||
|
final boolean forAuth) {
|
||
|
|
||
|
if(!isInitialized()) {
|
||
|
ISOException.throwIt(Constants.SW_REFERENCE_DATA_NOT_FOUND);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
final PrivateKey priv = keys.getPrivate();
|
||
|
|
||
|
short off = 0;
|
||
|
|
||
|
byte[] sha_header = null;
|
||
|
|
||
|
if(isRsa()) {
|
||
|
|
||
|
if(!forAuth) {
|
||
|
if(lc == (short)(2 + Constants.DSI_SHA224_HEADER[1])) {
|
||
|
sha_header = Constants.DSI_SHA224_HEADER;
|
||
|
} else if(lc == (short)(2 + Constants.DSI_SHA256_HEADER[1])) {
|
||
|
sha_header = Constants.DSI_SHA256_HEADER;
|
||
|
} else if(lc == (short)(2 + Constants.DSI_SHA384_HEADER[1])) {
|
||
|
sha_header = Constants.DSI_SHA384_HEADER;
|
||
|
} else if(lc == (short)(2 + Constants.DSI_SHA512_HEADER[1])) {
|
||
|
sha_header = Constants.DSI_SHA512_HEADER;
|
||
|
} else {
|
||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if(Util.arrayCompare(buf, (short)0, sha_header, (short)0, (byte)sha_header.length) != 0) {
|
||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(lc > (short)(((short)(Common.bitsToBytes(rsaModulusBitSize()) * 2)) / 5)) {
|
||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
final Cipher cipher = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
|
||
|
cipher.init(priv, Cipher.MODE_ENCRYPT);
|
||
|
|
||
|
off = cipher.doFinal(buf, (short)0, lc,
|
||
|
buf, (short)lc);
|
||
|
|
||
|
return Util.arrayCopyNonAtomic(buf, (short)lc,
|
||
|
buf, (short)0,
|
||
|
off);
|
||
|
|
||
|
} else if(isEc()) {
|
||
|
|
||
|
byte alg;
|
||
|
|
||
|
if(lc == MessageDigest.LENGTH_SHA) {
|
||
|
alg = Signature.ALG_ECDSA_SHA;
|
||
|
} else if(lc == MessageDigest.LENGTH_SHA_224) {
|
||
|
alg = Signature.ALG_ECDSA_SHA_224;
|
||
|
} else if(lc == MessageDigest.LENGTH_SHA_256) {
|
||
|
alg = Signature.ALG_ECDSA_SHA_256;
|
||
|
} else if(lc == MessageDigest.LENGTH_SHA_384) {
|
||
|
alg = Signature.ALG_ECDSA_SHA_384;
|
||
|
} else if(lc == MessageDigest.LENGTH_SHA_512) {
|
||
|
alg = Signature.ALG_ECDSA_SHA_512;
|
||
|
} else {
|
||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
final Signature sig = Signature.getInstance(alg, false);
|
||
|
sig.init(priv, Signature.MODE_SIGN);
|
||
|
|
||
|
final short sig_size = sig.signPreComputedHash(buf, (short)0, lc,
|
||
|
buf, lc);
|
||
|
|
||
|
off = (short)(lc + 1);
|
||
|
if((buf[off] & (byte)0x80) != (byte)0) {
|
||
|
++off;
|
||
|
}
|
||
|
++off;
|
||
|
|
||
|
if((buf[off++] != (byte)0x02)) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if((buf[off] & (byte)0x80) != (byte)0) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
final short r_size = Util.makeShort((byte)0, buf[off++]);
|
||
|
final short r_off = off;
|
||
|
|
||
|
off += r_size;
|
||
|
|
||
|
if((buf[off++] != (byte)0x02)) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if((buf[off] & (byte)0x80) != (byte)0) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
final short s_size = Util.makeShort((byte)0, buf[off++]);
|
||
|
final short s_off = off;
|
||
|
|
||
|
off = (short)(lc + sig_size);
|
||
|
|
||
|
if(r_size < s_size) {
|
||
|
off = Util.arrayFillNonAtomic(buf, off, (short)(s_size - r_size), (byte)0);
|
||
|
}
|
||
|
|
||
|
off = Util.arrayCopyNonAtomic(buf, r_off,
|
||
|
buf, off, r_size);
|
||
|
|
||
|
if(s_size < r_size) {
|
||
|
off = Util.arrayFillNonAtomic(buf, off, (short)(r_size - s_size), (byte)0);
|
||
|
}
|
||
|
|
||
|
off = Util.arrayCopyNonAtomic(buf, s_off,
|
||
|
buf, off, s_size);
|
||
|
|
||
|
off = Util.arrayCopyNonAtomic(buf, (short)(lc + sig_size),
|
||
|
buf, (short)0,
|
||
|
(short)(off - lc - sig_size));
|
||
|
|
||
|
Util.arrayFillNonAtomic(buf, off, (short)(lc + sig_size - off), (byte)0);
|
||
|
|
||
|
return off;
|
||
|
}
|
||
|
|
||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
protected final short decipher(final ECCurves ec, final byte[] buf, final short lc) {
|
||
|
|
||
|
if(!isInitialized()) {
|
||
|
ISOException.throwIt(Constants.SW_REFERENCE_DATA_NOT_FOUND);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
final PrivateKey priv = keys.getPrivate();
|
||
|
|
||
|
short off = 0;
|
||
|
|
||
|
if(isRsa()) {
|
||
|
final short modulus_size = Common.bitsToBytes(rsaModulusBitSize());
|
||
|
|
||
|
if(lc != (short)(modulus_size + 1)) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if(buf[0] != (byte)0) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
final Cipher cipher = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
|
||
|
cipher.init(priv, Cipher.MODE_DECRYPT);
|
||
|
|
||
|
final short len = cipher.doFinal(buf, (short)1, (short)(lc - 1),
|
||
|
buf, (short)lc);
|
||
|
|
||
|
off = Util.arrayCopyNonAtomic(buf, lc,
|
||
|
buf, (short)0,
|
||
|
len);
|
||
|
|
||
|
Util.arrayFillNonAtomic(buf, lc, len, (byte)0);
|
||
|
|
||
|
return off;
|
||
|
|
||
|
} else if(isEc()) {
|
||
|
|
||
|
final ECParams params = ecParams(ec);
|
||
|
|
||
|
if((lc <= 7) || (lc > (short)(7 + 1 + (short)(2 * Common.bitsToBytes(params.nb_bits))))) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if(buf[off] != (byte)0xA6) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return 0;
|
||
|
}
|
||
|
++off;
|
||
|
|
||
|
if(buf[off] != (byte)(lc - off - 1)) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||
|
return 0;
|
||
|
}
|
||
|
++off;
|
||
|
|
||
|
if(Util.getShort(buf, off) != (short)(0x7f49)) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return 0;
|
||
|
}
|
||
|
off += 2;
|
||
|
|
||
|
if(buf[off] != (byte)(lc - off - 1)) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||
|
return 0;
|
||
|
}
|
||
|
++off;
|
||
|
|
||
|
if(buf[off] != (byte)0x86) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
|
||
|
return 0;
|
||
|
}
|
||
|
++off;
|
||
|
|
||
|
if(buf[off] != (byte)(lc - off - 1)) {
|
||
|
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
|
||
|
return 0;
|
||
|
}
|
||
|
++off;
|
||
|
|
||
|
final KeyAgreement ka = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DHC_PLAIN, false);
|
||
|
ka.init(priv);
|
||
|
|
||
|
final short len = ka.generateSecret(buf, off, (short)(lc - off),
|
||
|
buf, lc);
|
||
|
|
||
|
off = Util.arrayCopyNonAtomic(buf, lc,
|
||
|
buf, (short)0,
|
||
|
len);
|
||
|
|
||
|
Util.arrayFillNonAtomic(buf, lc, len, (byte)0);
|
||
|
|
||
|
return off;
|
||
|
}
|
||
|
|
||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
protected final void initSignature(final Signature sign) {
|
||
|
if(!isInitialized()) {
|
||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||
|
return;
|
||
|
}
|
||
|
sign.init(keys.getPrivate(), Signature.MODE_SIGN);
|
||
|
}
|
||
|
|
||
|
protected final void initKeyAgreement(final KeyAgreement ka) {
|
||
|
if(!isInitialized()) {
|
||
|
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
|
||
|
return;
|
||
|
}
|
||
|
ka.init(keys.getPrivate());
|
||
|
}
|
||
|
|
||
|
}
|