Skip to content

Commit

Permalink
write large EC public keys correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
philipWendland committed Nov 20, 2014
1 parent 9e14280 commit ca10958
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 70 deletions.
178 changes: 108 additions & 70 deletions src/net/pwendland/javacard/pki/IsoApplet.java
Original file line number Diff line number Diff line change
Expand Up @@ -912,97 +912,136 @@ private void processGetResponse(APDU apdu) {
* \param The apdu to answer. setOutgoing() must not be called already.
*/
private void sendECPublicKey(APDU apdu, ECPublicKey key) {
byte[] buf = apdu.getBuffer();
short le = apdu.setOutgoing();
short pos = 0;
short lengthPos = 0;
short outer_value_len;
short field_bytes = (key.getSize()%8 == 0) ? (short)(key.getSize()/8) : (short)(key.getSize()/8+1);
short r, len;

// Return pubkey. See ISO7816-8 table 3.
buf[pos++] = (byte) 0x7F;
buf[pos++] = (byte) 0x49;

outer_value_len = (short)(7 // We have: 7 tags,
+ (key.getSize() == LENGTH_EC_FP_521 ? 9 : 7) // 7 length fields, of which 2 are 2 byte fields when using 521 bit curves,
+ 8 * field_bytes + 4); // 4 * field_len + 2 * 2 field_len + cofactor (2 bytes) + 2 * uncompressed tag
if(UtilTLV.getLengthFieldLength(outer_value_len) == 2) {
buf[pos++] = (byte) 0x81; // Length field: 2 bytes.
buf[pos++] = (byte) outer_value_len; // Length will be 218 bytes.
len = (short)(7 // We have: 7 tags,
+ (key.getSize() == LENGTH_EC_FP_521 ? 9 : 7) // 7 length fields, of which 2 are 2 byte fields when using 521 bit curves,
+ 8 * field_bytes + 4); // 4 * field_len + 2 * 2 field_len + cofactor (2 bytes) + 2 * uncompressed tag
r = UtilTLV.writeTagAndLen((short)0x7F49, len, ram_buf, pos);
if(r > 0) {
pos += r;
} else {
buf[pos++] = (byte) 0x82; // Length field: 3 bytes.
Util.setShort(buf, pos, outer_value_len);
pos += 2;
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}

// Prime - "P"
buf[pos++] = (byte) 0x81; // Tag
lengthPos = pos++; // Write the length later - we don't know it now.
buf[lengthPos] = (byte) key.getField(buf, pos); // Length + Value
pos += buf[lengthPos];
len = field_bytes;
r = UtilTLV.writeTagAndLen((short)0x81, len, ram_buf, pos);
if(r > 0) {
pos += r;
} else {
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}
r = key.getField(ram_buf, pos);
if(r == len) {
pos += len;
} else {
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}

// First coefficient - "A"
buf[pos++] = (byte) 0x82;
lengthPos = pos++;
buf[lengthPos] = (byte) key.getA(buf, pos);
pos += buf[lengthPos];
len = field_bytes;
r = UtilTLV.writeTagAndLen((short)0x82, len, ram_buf, pos);
if(r > 0) {
pos += r;
} else {
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}
r = key.getA(ram_buf, pos);
if(r == len) {
pos += len;
} else {
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}

// Second coefficient - "B"
buf[pos++] = (byte) 0x83;
lengthPos = pos++;
buf[lengthPos] = (byte) key.getB(buf, pos);
pos += buf[lengthPos];
len = field_bytes;
r = UtilTLV.writeTagAndLen((short)0x83, len, ram_buf, pos);
if(r > 0) {
pos += r;
} else {
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}
r = key.getB(ram_buf, pos);
if(r == len) {
pos += len;
} else {
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}

// Generator - "PB"
buf[pos++] = (byte) 0x84;
lengthPos = pos++;
buf[lengthPos] = (byte) key.getG(buf, pos);
pos += buf[lengthPos];
len = (short)(1 + 2 * field_bytes);
r = UtilTLV.writeTagAndLen((short)0x84, len, ram_buf, pos);
if(r > 0) {
pos += r;
} else {
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}
r = key.getG(ram_buf, pos);
if(r == len) {
pos += len;
} else {
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}

// Order - "Q"
buf[pos++] = (byte) 0x85;
lengthPos = pos++;
buf[lengthPos] = (byte) key.getR(buf, pos);
pos += buf[lengthPos];
len = field_bytes;
r = UtilTLV.writeTagAndLen((short)0x85, len, ram_buf, pos);
if(r > 0) {
pos += r;
} else {
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}
r = key.getR(ram_buf, pos);
if(r == len) {
pos += len;
} else {
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}

// Public key - "PP"
len = (short)(1 + 2 * field_bytes);
r = UtilTLV.writeTagAndLen((short)0x86, len, ram_buf, pos);
if(r > 0) {
pos += r;
} else {
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}
r = key.getW(ram_buf, pos);
if(r == len) {
pos += len;
} else {
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}

// Cofactor
buf[pos++] = (byte) 0x87;
buf[pos++] = (byte) 0x02;
Util.setShort(buf, pos, key.getK());
r = UtilTLV.writeTagAndLen((short)0x87, (short)2, ram_buf, pos);
if(r > 0) {
pos += r;
} else {
ISOException.throwIt(ISO7816.SW_UNKNOWN);
}
Util.setShort(ram_buf, pos, key.getK());
pos+=2;

if((key.getSize() > KeyBuilder.LENGTH_EC_FP_192 && DEF_EXT_APDU)
|| key.getSize() <= KeyBuilder.LENGTH_EC_FP_192) {
// Data fits in a extended or short APDU.
// Public key - "PP"
buf[pos++] = (byte) 0x86;
lengthPos = pos++;
buf[lengthPos] = (byte) key.getW(buf, pos);
pos += buf[lengthPos];

apdu.setOutgoingLength(pos);
apdu.sendBytes((short) 0, pos);
// ram_buf now contains the complete public key.
if(pos <= 256 || DEF_EXT_APDU) {
len = pos;
} else {
// No extended APDUs and >192 bit field length - we have 284 bytes to send for 256 bit length.
// Send the EC public point with the second APDU.
// Send the first part, prepare ram_buf and make the caller use GET RESPONSE.

if(ram_chaining_cache[RAM_CHAINING_CACHE_OFFSET_BYTES_REMAINING] > (short) 0) {
// Should not happen - there is old content to get with GET RESPONSE
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}

apdu.setOutgoingLength(pos);
apdu.sendBytes((short) 0, pos);

pos = (short) 0;
ram_buf[pos++] = (byte) 0x86;
lengthPos = pos++;
ram_buf[lengthPos] = (byte) key.getW(ram_buf, pos);
pos += ram_buf[lengthPos];
len = 256;
}
apdu.setOutgoing();
apdu.setOutgoingLength(len);
apdu.sendBytesLong(ram_buf, (short)0, len);

ram_chaining_cache[RAM_CHAINING_CACHE_OFFSET_BYTES_REMAINING] = pos;
ram_chaining_cache[RAM_CHAINING_CACHE_OFFSET_CURRENT_POS] = (short) 0;
if(len < pos) {
// The data did not fit in a short apdu and no extended apdus are supported.
// Prepare GET RESPONSE.
ram_chaining_cache[RAM_CHAINING_CACHE_OFFSET_BYTES_REMAINING] = (short)(pos - len);
ram_chaining_cache[RAM_CHAINING_CACHE_OFFSET_CURRENT_POS] = len;
short bytesRemaining;
if(ram_chaining_cache[RAM_CHAINING_CACHE_OFFSET_BYTES_REMAINING] > 255) {
bytesRemaining = 0x00FF;
Expand All @@ -1013,7 +1052,6 @@ private void sendECPublicKey(APDU apdu, ECPublicKey key) {
// The second part of the data is now in ram_buf, metadata is in ram_chaining_cache.
// It can be fetched by the host via GET RESPONSE.
}

}

/**
Expand Down
60 changes: 60 additions & 0 deletions src/net/pwendland/javacard/pki/UtilTLV.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,64 @@ public static short getLengthFieldLength(short length) {
}
}

/**
* \brief Write the tag and length to a buffer.
*
* Only the tag and length field are written. The writing of the value field is left to the caller.
*
* \param tag A one- or two-byte tag.
*
* \param len The length that should be written to the length field.
*
* \param out The buffer to write the result to.
*
* \param outLen The length of out.
*
* \param outOffset The offset at which to start writing the tag.
*
* \return -1 in case of an error, or the length that was written.
*/
public static short writeTagAndLen(short tag, short len, byte[] out, short outOffset) {
byte tagLen;
short pos = outOffset;
short outLen = (short)out.length;

if((short)(tag & (short)0xFF00) != 0) {
if((short)(tag & (short)0x1F00) != (short)0x1F00) {
/* Missing escape marker */
return -1;
}
tagLen = 2;
} else {
tagLen = 1;
}

if(len < 0) {
return -1;
}
if((short)(tagLen + getLengthFieldLength(len)) > (short)(outLen - outOffset)) {
return -1;
}

if(tagLen == 1) {
out[pos] = (byte)(tag & (short)0x00FF);
} else {
Util.setShort(out, pos, tag);
}
pos += tagLen;

if(len < 128) {
out[pos++] = (byte)(len & (short)0x007F);
} else if(len < 256) {
out[pos++] = (byte)0x81;
out[pos++] = (byte)(len & (short)0x00FF);
} else {
out[pos++] = (byte)0x82;
Util.setShort(out, pos, len);
pos += 2;
}

return (short)(pos - outOffset);
}

}

0 comments on commit ca10958

Please sign in to comment.