diff options
Diffstat (limited to 'ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEncoder.java')
-rw-r--r-- | ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEncoder.java | 785 |
1 files changed, 785 insertions, 0 deletions
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEncoder.java new file mode 100644 index 0000000..941a0ac --- /dev/null +++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -0,0 +1,785 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.keymaster; + +import com.android.javacard.seprovider.KMException; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; + +/** + * This class encodes KMType structures to a cbor format data recursively. Encoded bytes are written + * on the buffer provided by the caller. An exception will be thrown if the encoded data length is + * greater than the buffer length provided. + */ +public class KMEncoder { + + // major types + private static final byte UINT_TYPE = 0x00; + private static final byte NEG_INT_TYPE = 0x20; + private static final byte BYTES_TYPE = 0x40; + private static final byte TSTR_TYPE = 0x60; + private static final byte ARRAY_TYPE = (byte) 0x80; + private static final byte MAP_TYPE = (byte) 0xA0; + private static final byte SIMPLE_VALUE_TYPE = (byte) 0xE0; + private static final byte SEMANTIC_TAG_TYPE = (byte) 0xC0; + + // masks + private static final byte ADDITIONAL_MASK = 0x1F; + + // value length + private static final byte UINT8_LENGTH = (byte) 0x18; + private static final byte UINT16_LENGTH = (byte) 0x19; + private static final byte UINT32_LENGTH = (byte) 0x1A; + private static final byte UINT64_LENGTH = (byte) 0x1B; + private static final short TINY_PAYLOAD = 0x17; + private static final short SHORT_PAYLOAD = 0x100; + private static final byte STACK_SIZE = 50; + private static final byte SCRATCH_BUF_SIZE = 6; + private static final byte START_OFFSET = 0; + private static final byte LEN_OFFSET = 2; + private static final byte STACK_PTR_OFFSET = 4; + + private Object[] bufferRef; + private short[] scratchBuf; + private short[] stack; + + public KMEncoder() { + bufferRef = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); + scratchBuf = JCSystem.makeTransientShortArray(SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET); + stack = JCSystem.makeTransientShortArray(STACK_SIZE, JCSystem.CLEAR_ON_RESET); + bufferRef[0] = null; + scratchBuf[START_OFFSET] = (short) 0; + scratchBuf[LEN_OFFSET] = (short) 0; + scratchBuf[STACK_PTR_OFFSET] = (short) 0; + } + + private void push(short objPtr) { + stack[scratchBuf[STACK_PTR_OFFSET]] = objPtr; + scratchBuf[STACK_PTR_OFFSET]++; + } + + private short pop() { + scratchBuf[STACK_PTR_OFFSET]--; + return stack[scratchBuf[STACK_PTR_OFFSET]]; + } + + private void encode(short obj) { + push(obj); + } + + /** + * This functions encodes the given object into the provider buffer space in cbor format. + * + * @param object Object to be encoded into cbor data. + * @param buffer Output where cbor data is copied. + * @param startOff is the start offset of the buffer. + * @param bufLen length of the buffer + * @param encoderOutLimitLen excepted encoded output length. + * @return length of the encoded buffer. + */ + public short encode( + short object, byte[] buffer, short startOff, short bufLen, short encoderOutLimitLen) { + scratchBuf[STACK_PTR_OFFSET] = 0; + bufferRef[0] = buffer; + scratchBuf[START_OFFSET] = startOff; + if ((short) (startOff + encoderOutLimitLen) > bufLen) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + scratchBuf[LEN_OFFSET] = (short) (startOff + encoderOutLimitLen); + push(object); + encode(); + return (short) (scratchBuf[START_OFFSET] - startOff); + } + + public short encode(short object, byte[] buffer, short startOff, short bufLen) { + return encode(object, buffer, startOff, bufLen, (short) (bufLen - startOff)); + } + + // array{KMError.OK,Array{KMByteBlobs}} + public short encodeCert(byte[] certBuffer, short bufferStart, short certStart, short certLength) { + if (bufferStart > certStart) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + bufferRef[0] = certBuffer; + scratchBuf[START_OFFSET] = certStart; + scratchBuf[LEN_OFFSET] = (short) (certStart + 1); + // Byte Header + cert length + scratchBuf[START_OFFSET] -= getEncodedBytesLength(certLength); + // Array header - 1 elements i.e. 1 byte + scratchBuf[START_OFFSET]--; + if (scratchBuf[START_OFFSET] < bufferStart) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + bufferStart = scratchBuf[START_OFFSET]; + writeMajorTypeWithLength(ARRAY_TYPE, (short) 1); // Array of 1 elements + writeMajorTypeWithLength(BYTES_TYPE, certLength); // Cert Byte Blob of length + return bufferStart; + } + + private void encode() { + while (scratchBuf[STACK_PTR_OFFSET] > 0) { + short exp = pop(); + byte type = KMType.getType(exp); + switch (type) { + case KMType.BYTE_BLOB_TYPE: + encodeByteBlob(exp); + break; + case KMType.TEXT_STRING_TYPE: + encodeTextString(exp); + break; + case KMType.INTEGER_TYPE: + encodeUnsignedInteger(exp); + break; + case KMType.SIMPLE_VALUE_TYPE: + encodeSimpleValue(exp); + break; + case KMType.NEG_INTEGER_TYPE: + encodeNegInteger(exp); + break; + case KMType.ARRAY_TYPE: + encodeArray(exp); + break; + case KMType.MAP_TYPE: + encodeMap(exp); + break; + case KMType.ENUM_TYPE: + encodeEnum(exp); + break; + case KMType.KEY_PARAM_TYPE: + encodeKeyParam(exp); + break; + case KMType.SEMANTIC_TAG_TYPE: + encodeSemanticTag(exp); + break; + case KMType.COSE_KEY_TYPE: + case KMType.COSE_HEADERS_TYPE: + case KMType.COSE_CERT_PAYLOAD_TYPE: + encodeCoseMap(exp); + break; + case KMType.KEY_CHAR_TYPE: + encodeKeyChar(exp); + break; + case KMType.VERIFICATION_TOKEN_TYPE: + encodeVeriToken(exp); + break; + case KMType.HMAC_SHARING_PARAM_TYPE: + encodeHmacSharingParam(exp); + break; + case KMType.HW_AUTH_TOKEN_TYPE: + encodeHwAuthToken(exp); + break; + case KMType.TAG_TYPE: + short tagType = KMTag.getTagType(exp); + encodeTag(tagType, exp); + break; + case KMType.COSE_PAIR_TAG_TYPE: + short cosePairTagType = KMCosePairTagType.getTagValueType(exp); + encodeCosePairTag(cosePairTagType, exp); + break; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + } + } + + private void encodeCosePairIntegerTag(short exp) { + KMCosePairIntegerTag cosePairIntTag = KMCosePairIntegerTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairIntTag.getValuePtr()); + encode(cosePairIntTag.getKeyPtr()); + } + + private void encodeCosePairByteBlobTag(short exp) { + KMCosePairByteBlobTag cosePairByteBlobTag = KMCosePairByteBlobTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairByteBlobTag.getValuePtr()); + encode(cosePairByteBlobTag.getKeyPtr()); + } + + private void encodeCosePairCoseKeyTag(short exp) { + KMCosePairCoseKeyTag cosePairCoseKeyTag = KMCosePairCoseKeyTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairCoseKeyTag.getValuePtr()); + encode(cosePairCoseKeyTag.getKeyPtr()); + } + + private void encodeCosePairTextStringTag(short exp) { + KMCosePairTextStringTag cosePairTextStringTag = KMCosePairTextStringTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairTextStringTag.getValuePtr()); + encode(cosePairTextStringTag.getKeyPtr()); + } + + private void encodeCosePairSimpleValueTag(short exp) { + KMCosePairSimpleValueTag cosePairSimpleValueTag = KMCosePairSimpleValueTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairSimpleValueTag.getValuePtr()); + encode(cosePairSimpleValueTag.getKeyPtr()); + } + + private void encodeCosePairNegIntegerTag(short exp) { + KMCosePairNegIntegerTag cosePairNegIntegerTag = KMCosePairNegIntegerTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairNegIntegerTag.getValuePtr()); + encode(cosePairNegIntegerTag.getKeyPtr()); + } + + private void encodeCosePairTag(short tagType, short exp) { + switch (tagType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + encodeCosePairByteBlobTag(exp); + return; + case KMType.COSE_PAIR_INT_TAG_TYPE: + encodeCosePairIntegerTag(exp); + return; + case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: + encodeCosePairNegIntegerTag(exp); + return; + case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: + encodeCosePairSimpleValueTag(exp); + return; + case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: + encodeCosePairTextStringTag(exp); + return; + case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: + encodeCosePairCoseKeyTag(exp); + return; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + } + + private void encodeTag(short tagType, short exp) { + switch (tagType) { + case KMType.BIGNUM_TAG: + encodeBignumTag(exp); + return; + case KMType.BYTES_TAG: + encodeBytesTag(exp); + return; + case KMType.BOOL_TAG: + encodeBoolTag(exp); + return; + case KMType.UINT_TAG: + case KMType.ULONG_TAG: + case KMType.DATE_TAG: + encodeIntegerTag(exp); + return; + case KMType.ULONG_ARRAY_TAG: + case KMType.UINT_ARRAY_TAG: + encodeIntegerArrayTag(exp); + return; + case KMType.ENUM_TAG: + encodeEnumTag(exp); + return; + case KMType.ENUM_ARRAY_TAG: + encodeEnumArrayTag(exp); + return; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + } + + private void encodeCoseMap(short obj) { + encodeAsMap(KMCoseMap.getVals(obj)); + } + + private void encodeKeyParam(short obj) { + encodeAsMap(KMKeyParameters.cast(obj).getVals()); + } + + private void encodeKeyChar(short obj) { + encode(KMKeyCharacteristics.cast(obj).getVals()); + } + + private void encodeVeriToken(short obj) { + encode(KMVerificationToken.cast(obj).getVals()); + } + + private void encodeHwAuthToken(short obj) { + encode(KMHardwareAuthToken.cast(obj).getVals()); + } + + private void encodeHmacSharingParam(short obj) { + encode(KMHmacSharingParameters.cast(obj).getVals()); + } + + private void encodeArray(short obj) { + writeMajorTypeWithLength(ARRAY_TYPE, KMArray.cast(obj).length()); + short len = KMArray.cast(obj).length(); + short index = (short) (len - 1); + short subObj; + while (index >= 0) { + subObj = KMArray.cast(obj).get(index); + if (subObj != KMType.INVALID_VALUE) { + encode(subObj); + } + index--; + } + } + + public void encodeArrayOnlyLength(short arrLength, byte[] buffer, short offset, short length) { + bufferRef[0] = buffer; + scratchBuf[START_OFFSET] = offset; + scratchBuf[LEN_OFFSET] = (short) (offset + length + 1); + writeMajorTypeWithLength(ARRAY_TYPE, length); + } + + private void encodeMap(short obj) { + writeMajorTypeWithLength(MAP_TYPE, KMMap.cast(obj).length()); + short len = KMMap.cast(obj).length(); + short index = (short) (len - 1); + while (index >= 0) { + encode(KMMap.cast(obj).getKeyValue(index)); + encode(KMMap.cast(obj).getKey(index)); + index--; + } + } + + private void encodeAsMap(short obj) { + writeMajorTypeWithLength(MAP_TYPE, KMArray.cast(obj).length()); + short len = KMArray.cast(obj).length(); + short index = (short) (len - 1); + short inst; + while (index >= 0) { + inst = KMArray.cast(obj).get(index); + encode(inst); + index--; + } + } + + private void encodeIntegerArrayTag(short obj) { + writeTag(KMIntegerArrayTag.cast(obj).getTagType(), KMIntegerArrayTag.cast(obj).getKey()); + encode(KMIntegerArrayTag.cast(obj).getValues()); + } + + private void encodeEnumArrayTag(short obj) { + writeTag(KMEnumArrayTag.cast(obj).getTagType(), KMEnumArrayTag.cast(obj).getKey()); + encode(KMEnumArrayTag.cast(obj).getValues()); + } + + private void encodeIntegerTag(short obj) { + writeTag(KMIntegerTag.cast(obj).getTagType(), KMIntegerTag.cast(obj).getKey()); + encode(KMIntegerTag.cast(obj).getValue()); + } + + private void encodeBignumTag(short obj) { + writeTag(KMBignumTag.getTagType(obj), KMBignumTag.getKey(obj)); + encode(KMBignumTag.cast(obj).getValue()); + } + + private void encodeBytesTag(short obj) { + writeTag(KMByteTag.cast(obj).getTagType(), KMByteTag.cast(obj).getKey()); + encode(KMByteTag.cast(obj).getValue()); + } + + private void encodeBoolTag(short obj) { + writeTag(KMBoolTag.cast(obj).getTagType(), KMBoolTag.cast(obj).getKey()); + writeByteValue(KMBoolTag.cast(obj).getVal()); + } + + private void encodeEnumTag(short obj) { + writeTag(KMEnumTag.cast(obj).getTagType(), KMEnumTag.cast(obj).getKey()); + writeByteValue(KMEnumTag.cast(obj).getValue()); + } + + private void encodeEnum(short obj) { + writeByteValue(KMEnum.cast(obj).getVal()); + } + + private void encodeInteger(byte[] val, short len, short startOff, short majorType) { + // find out the most significant byte + short msbIndex = findMsb(val, startOff, len); + // find the difference between most significant byte and len + short diff = (short) (len - msbIndex); + if (diff == 0) { + writeByte((byte) (majorType | 0)); + } else if ((diff == 1) + && (val[(short) (startOff + msbIndex)] < UINT8_LENGTH) + && (val[(short) (startOff + msbIndex)] >= 0)) { + writeByte((byte) (majorType | val[(short) (startOff + msbIndex)])); + } else if (diff == 1) { + writeByte((byte) (majorType | UINT8_LENGTH)); + writeByte(val[(short) (startOff + msbIndex)]); + } else if (diff == 2) { + writeByte((byte) (majorType | UINT16_LENGTH)); + writeBytes(val, (short) (startOff + msbIndex), (short) 2); + } else if (diff <= 4) { + writeByte((byte) (majorType | UINT32_LENGTH)); + writeBytes(val, (short) (startOff + len - 4), (short) 4); + } else { + writeByte((byte) (majorType | UINT64_LENGTH)); + writeBytes(val, startOff, (short) 8); + } + } + + // find out the most significant byte + public short findMsb(byte[] buf, short offset, short len) { + byte index = 0; + // find out the most significant byte + while (index < len) { + if (buf[(short) (offset + index)] > 0) { + break; + } else if (buf[(short) (offset + index)] < 0) { + break; + } + index++; // index will be equal to len if value is 0. + } + return index; + } + + public void computeOnesCompliment(short msbIndex, byte[] buf, short offset, short len) { + // find the difference between most significant byte and len + short diff = (short) (len - msbIndex); + short correctedOffset = offset; + short correctedLen = len; + // The offset and length of the buffer for Short and Byte types should be + // corrected before computing the 1s compliment. The reason for doing this + // is to avoid computation of 1s compliment on the MSB bytes. + if (diff == 0) { + // Fail + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } else if (diff == 1) { + correctedOffset = (short) (offset + 3); + correctedLen = 1; + } else if (diff == 2) { + correctedOffset = (short) (offset + 2); + correctedLen = 2; + } + // For int and long values the len and offset values are always proper. + // int - 4 bytes + // long - 8 bytes. + KMUtils.computeOnesCompliment(buf, correctedOffset, correctedLen); + } + + // Encoding rule for negative Integers is taken from + // https://datatracker.ietf.org/doc/html/rfc7049#section-2.1, Major type 1. + public short handleNegIntegerEncodingRule(byte[] buf, short offset, short len) { + short msbIndex = findMsb(buf, offset, len); + // Do -1-N, where N is the negative integer + // The value of -1-N is equal to the 1s compliment of N. + computeOnesCompliment(msbIndex, buf, offset, len); + return msbIndex; + } + + // Note: This function modifies the buffer's actual value. So after encoding, restore the original + // value by calling removeNegIntegerEncodingRule(). + public short applyNegIntegerEncodingRule(byte[] buf, short offset, short len) { + return handleNegIntegerEncodingRule(buf, offset, len); + } + + public void removeNegIntegerEncodingRule( + byte[] buf, short offset, short len, short origMsbIndex) { + // Do -1-N, where N is the negative integer + // The value of -1-N is equal to the 1s compliment of N. + computeOnesCompliment(origMsbIndex, buf, offset, len); + } + + private void encodeNegInteger(short obj) { + byte[] val = KMNInteger.cast(obj).getBuffer(); + short len = KMNInteger.cast(obj).length(); + short startOff = KMNInteger.cast(obj).getStartOff(); + short msbIndex = applyNegIntegerEncodingRule(val, startOff, len); + encodeInteger(val, len, startOff, NEG_INT_TYPE); + removeNegIntegerEncodingRule(val, startOff, len, msbIndex); + } + + private void encodeSemanticTag(short obj) { + short tag = KMSemanticTag.cast(obj).getKeyPtr(); + encode(KMSemanticTag.cast(obj).getValuePtr()); + encodeInteger( + KMInteger.cast(tag).getBuffer(), + KMInteger.cast(tag).length(), + KMInteger.cast(tag).getStartOff(), + SEMANTIC_TAG_TYPE); + } + + private void encodeUnsignedInteger(short obj) { + byte[] val = KMInteger.cast(obj).getBuffer(); + short len = KMInteger.cast(obj).length(); + short startOff = KMInteger.cast(obj).getStartOff(); + encodeInteger(val, len, startOff, UINT_TYPE); + } + + private void encodeSimpleValue(short obj) { + byte value = KMSimpleValue.cast(obj).getValue(); + writeByte((byte) (SIMPLE_VALUE_TYPE | value)); + } + + private void encodeTextString(short obj) { + writeMajorTypeWithLength(TSTR_TYPE, KMTextString.cast(obj).length()); + writeBytes( + KMTextString.cast(obj).getBuffer(), + KMTextString.cast(obj).getStartOff(), + KMTextString.cast(obj).length()); + } + + public short encodeByteBlobHeader(short bufLen, byte[] buffer, short startOff, short length) { + bufferRef[0] = buffer; + scratchBuf[START_OFFSET] = startOff; + scratchBuf[LEN_OFFSET] = (short) (startOff + length + 1); + writeMajorTypeWithLength(BYTES_TYPE, bufLen); + return (short) (scratchBuf[START_OFFSET] - startOff); + } + + private void encodeByteBlob(short obj) { + writeMajorTypeWithLength(BYTES_TYPE, KMByteBlob.cast(obj).length()); + writeBytes( + KMByteBlob.cast(obj).getBuffer(), + KMByteBlob.cast(obj).getStartOff(), + KMByteBlob.cast(obj).length()); + } + + public short getEncodedLength(short ptr) { + short len = 0; + short type = KMType.getType(ptr); + switch (type) { + case KMType.BYTE_BLOB_TYPE: + len += getEncodedByteBlobLength(ptr); + break; + case KMType.TEXT_STRING_TYPE: + len += getEncodedTextStringLength(ptr); + break; + case KMType.INTEGER_TYPE: + len += getEncodedIntegerLength(ptr); + break; + case KMType.NEG_INTEGER_TYPE: + len += getEncodedNegIntegerLength(ptr); + break; + case KMType.ARRAY_TYPE: + len += getEncodedArrayLen(ptr); + break; + case KMType.MAP_TYPE: + len += getEncodedMapLen(ptr); + break; + case KMType.COSE_PAIR_TAG_TYPE: + short cosePairTagType = KMCosePairTagType.getTagValueType(ptr); + len += getEncodedCosePairTagLen(cosePairTagType, ptr); + break; + case KMType.COSE_KEY_TYPE: + case KMType.COSE_HEADERS_TYPE: + case KMType.COSE_CERT_PAYLOAD_TYPE: + len += getEncodedArrayLen(KMCoseMap.getVals(ptr)); + break; + default: + KMException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return len; + } + + private short getEncodedCosePairTagLen(short tagType, short exp) { + short length = 0; + switch (tagType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + KMCosePairByteBlobTag cosePairByteBlobTag = KMCosePairByteBlobTag.cast(exp); + length = getEncodedLength(cosePairByteBlobTag.getKeyPtr()); + length += getEncodedLength(cosePairByteBlobTag.getValuePtr()); + break; + case KMType.COSE_PAIR_INT_TAG_TYPE: + KMCosePairIntegerTag cosePairIntTag = KMCosePairIntegerTag.cast(exp); + length = getEncodedLength(cosePairIntTag.getValuePtr()); + length += getEncodedLength(cosePairIntTag.getKeyPtr()); + break; + case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: + KMCosePairNegIntegerTag cosePairNegIntegerTag = KMCosePairNegIntegerTag.cast(exp); + length = getEncodedLength(cosePairNegIntegerTag.getValuePtr()); + length += getEncodedLength(cosePairNegIntegerTag.getKeyPtr()); + break; + case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: + KMCosePairSimpleValueTag cosePairSimpleValueTag = KMCosePairSimpleValueTag.cast(exp); + length = getEncodedLength(cosePairSimpleValueTag.getValuePtr()); + length += getEncodedLength(cosePairSimpleValueTag.getKeyPtr()); + break; + case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: + KMCosePairTextStringTag cosePairTextStringTag = KMCosePairTextStringTag.cast(exp); + length = getEncodedLength(cosePairTextStringTag.getValuePtr()); + length += getEncodedLength(cosePairTextStringTag.getKeyPtr()); + break; + case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: + KMCosePairCoseKeyTag cosePairCoseKeyTag = KMCosePairCoseKeyTag.cast(exp); + length = getEncodedLength(cosePairCoseKeyTag.getValuePtr()); + length += getEncodedLength(cosePairCoseKeyTag.getKeyPtr()); + break; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return length; + } + + private short getEncodedMapLen(short obj) { + short mapLen = KMMap.cast(obj).length(); + short len = getEncodedBytesLength(mapLen); + short index = 0; + while (index < mapLen) { + len += getEncodedLength(KMMap.cast(obj).getKey(index)); + len += getEncodedLength(KMMap.cast(obj).getKeyValue(index)); + index++; + } + return len; + } + + private short getEncodedArrayLen(short obj) { + short arrLen = KMArray.cast(obj).length(); + short len = getEncodedBytesLength(arrLen); + short index = 0; + short subObj; + while (index < arrLen) { + subObj = KMArray.cast(obj).get(index); + if (subObj != KMType.INVALID_VALUE) { + len += getEncodedLength(subObj); + } + index++; + } + return len; + } + + public short getEncodedBytesLength(short len) { + short ret = 0; + if (len < KMEncoder.UINT8_LENGTH && len >= 0) { + ret = 1; + } else if (len >= KMEncoder.UINT8_LENGTH && len <= (short) 0x00FF) { + ret = 2; + } else if (len > (short) 0x00FF && len <= (short) 0x7FFF) { + ret = 3; + } else { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + return ret; + } + + private short getEncodedByteBlobLength(short obj) { + short len = KMByteBlob.cast(obj).length(); + len += getEncodedBytesLength(len); + return len; + } + + private short getEncodedTextStringLength(short obj) { + short len = KMTextString.cast(obj).length(); + len += getEncodedBytesLength(len); + return len; + } + + private short getEncodedNegIntegerLength(short obj) { + byte[] buf = KMNInteger.cast(obj).getBuffer(); + short len = KMNInteger.cast(obj).length(); + short offset = KMNInteger.cast(obj).getStartOff(); + short msbIndex = applyNegIntegerEncodingRule(buf, offset, len); + short ret = getEncodedIntegerLength(buf, offset, len); + removeNegIntegerEncodingRule(buf, offset, len, msbIndex); + return ret; + } + + private short getEncodedIntegerLength(byte[] val, short startOff, short len) { + short msbIndex = findMsb(val, startOff, len); + // find the difference between most significant byte and len + short diff = (short) (len - msbIndex); + switch (diff) { + case 0: + case 1: // Byte + if ((val[(short) (startOff + msbIndex)] < KMEncoder.UINT8_LENGTH) + && (val[(short) (startOff + msbIndex)] >= 0)) { + return (short) 1; + } else { + return (short) 2; + } + case 2: // Short + return (short) 3; + case 3: + case 4: // UInt32 + return (short) 5; + case 5: + case 6: + case 7: + case 8: // UInt64 + return (short) 9; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return 0; + } + + private short getEncodedIntegerLength(short obj) { + byte[] val = KMInteger.cast(obj).getBuffer(); + short len = KMInteger.cast(obj).length(); + short startOff = KMInteger.cast(obj).getStartOff(); + return getEncodedIntegerLength(val, startOff, len); + } + + private void writeByteValue(byte val) { + if ((val < UINT8_LENGTH) && (val >= 0)) { + writeByte((byte) (UINT_TYPE | val)); + } else { + writeByte((byte) (UINT_TYPE | UINT8_LENGTH)); + writeByte(val); + } + } + + private void writeTag(short tagType, short tagKey) { + writeByte((byte) (UINT_TYPE | UINT32_LENGTH)); + writeShort(tagType); + writeShort(tagKey); + } + + private void writeMajorTypeWithLength(byte majorType, short len) { + if (len <= TINY_PAYLOAD) { + writeByte((byte) (majorType | (byte) (len & ADDITIONAL_MASK))); + } else if (len < SHORT_PAYLOAD) { + writeByte((byte) (majorType | UINT8_LENGTH)); + writeByte((byte) (len & 0xFF)); + } else { + writeByte((byte) (majorType | UINT16_LENGTH)); + writeShort(len); + } + } + + private void writeBytes(byte[] buf, short start, short len) { + byte[] buffer = (byte[]) bufferRef[0]; + Util.arrayCopyNonAtomic(buf, start, buffer, scratchBuf[START_OFFSET], len); + incrementStartOff(len); + } + + private void writeShort(short val) { + byte[] buffer = (byte[]) bufferRef[0]; + buffer[scratchBuf[START_OFFSET]] = (byte) ((val >> 8) & 0xFF); + incrementStartOff((short) 1); + buffer[scratchBuf[START_OFFSET]] = (byte) ((val & 0xFF)); + incrementStartOff((short) 1); + } + + private void writeByte(byte val) { + byte[] buffer = (byte[]) bufferRef[0]; + buffer[scratchBuf[START_OFFSET]] = val; + incrementStartOff((short) 1); + } + + private void incrementStartOff(short inc) { + scratchBuf[START_OFFSET] += inc; + if (scratchBuf[START_OFFSET] >= scratchBuf[LEN_OFFSET]) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + } + + public short encodeArrayHeader(short bufLen, byte[] buffer, short startOff, short length) { + bufferRef[0] = buffer; + scratchBuf[START_OFFSET] = startOff; + scratchBuf[LEN_OFFSET] = (short) (startOff + length + 1); + writeMajorTypeWithLength(ARRAY_TYPE, bufLen); + return (short) (scratchBuf[START_OFFSET] - startOff); + } +} |