From ca109589ecc653adbb7a05da28a18764cd23c85c Mon Sep 17 00:00:00 2001 From: Philip Wendland Date: Thu, 20 Nov 2014 00:15:31 +0100 Subject: [PATCH] write large EC public keys correctly --- src/net/pwendland/javacard/pki/IsoApplet.java | 178 +++++++++++------- src/net/pwendland/javacard/pki/UtilTLV.java | 60 ++++++ 2 files changed, 168 insertions(+), 70 deletions(-) diff --git a/src/net/pwendland/javacard/pki/IsoApplet.java b/src/net/pwendland/javacard/pki/IsoApplet.java index a9d66fea..9b0b7bed 100644 --- a/src/net/pwendland/javacard/pki/IsoApplet.java +++ b/src/net/pwendland/javacard/pki/IsoApplet.java @@ -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; @@ -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. } - } /** diff --git a/src/net/pwendland/javacard/pki/UtilTLV.java b/src/net/pwendland/javacard/pki/UtilTLV.java index 0f9af76c..929f69d0 100644 --- a/src/net/pwendland/javacard/pki/UtilTLV.java +++ b/src/net/pwendland/javacard/pki/UtilTLV.java @@ -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); + } + }