blob: 2146c8215637706f9a3957d8d7a5b2c2bd160763 [file] [log] [blame]
/*
* 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);
}
}
}