| /* |
| * 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()); |
| encodeInteger( |
| KMEnumTag.cast(obj).getBuffer(), |
| KMEnumTag.cast(obj).length(), |
| KMEnumTag.cast(obj).getStartOffset(), |
| UINT_TYPE); |
| } |
| |
| private void encodeEnum(short obj) { |
| encodeInteger( |
| KMEnum.cast(obj).getBuffer(), |
| KMEnum.cast(obj).length(), |
| KMEnum.cast(obj).getStartOffset(), |
| UINT_TYPE); |
| } |
| |
| 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); |
| } |
| } |
| } |