aboutsummaryrefslogtreecommitdiff
path: root/src/cz/crcs/ectester/applet/ECTesterApplet.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/cz/crcs/ectester/applet/ECTesterApplet.java')
-rw-r--r--src/cz/crcs/ectester/applet/ECTesterApplet.java333
1 files changed, 231 insertions, 102 deletions
diff --git a/src/cz/crcs/ectester/applet/ECTesterApplet.java b/src/cz/crcs/ectester/applet/ECTesterApplet.java
index 20e3f05..d0ca8f5 100644
--- a/src/cz/crcs/ectester/applet/ECTesterApplet.java
+++ b/src/cz/crcs/ectester/applet/ECTesterApplet.java
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2016-2017 Petr Svenda <petr@svenda.com>
+ * ECTester, tool for testing Elliptic curve cryptography implementations.
+ * Copyright (c) 2016-2018 Petr Svenda <petr@svenda.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -44,16 +45,17 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
public static final byte INS_ALLOCATE = (byte) 0x5a;
public static final byte INS_CLEAR = (byte) 0x5b;
public static final byte INS_SET = (byte) 0x5c;
- public static final byte INS_CORRUPT = (byte) 0x5d;
+ public static final byte INS_TRANSFORM = (byte) 0x5d;
public static final byte INS_GENERATE = (byte) 0x5e;
public static final byte INS_EXPORT = (byte) 0x5f;
public static final byte INS_ECDH = (byte) 0x70;
public static final byte INS_ECDH_DIRECT = (byte) 0x71;
public static final byte INS_ECDSA = (byte) 0x72;
- public static final byte INS_CLEANUP = (byte) 0x73;
- //public static final byte INS_SUPPORT = (byte) 0x74;
- public static final byte INS_ALLOCATE_KA = (byte) 0x75;
- public static final byte INS_ALLOCATE_SIG = (byte) 0x76;
+ public static final byte INS_ECDSA_SIGN = (byte) 0x73;
+ public static final byte INS_ECDSA_VERIFY = (byte) 0x74;
+ public static final byte INS_CLEANUP = (byte) 0x75;
+ public static final byte INS_ALLOCATE_KA = (byte) 0x76;
+ public static final byte INS_ALLOCATE_SIG = (byte) 0x77;
// PARAMETERS for P1 and P2
@@ -70,26 +72,17 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
public static final short SW_KA_NULL = (short) 0x0ee4;
public static final short SW_SIGNATURE_NULL = (short) 0x0ee5;
public static final short SW_OBJECT_NULL = (short) 0x0ee6;
-
-
- // Class javacard.security.KeyAgreement
- // javacard.security.KeyAgreement Fields:
- public static final byte KeyAgreement_ALG_EC_SVDP_DH = 1;
- public static final byte KeyAgreement_ALG_EC_SVDP_DH_KDF = 1;
- public static final byte KeyAgreement_ALG_EC_SVDP_DHC = 2;
- public static final byte KeyAgreement_ALG_EC_SVDP_DHC_KDF = 2;
- public static final byte KeyAgreement_ALG_EC_SVDP_DH_PLAIN = 3;
- public static final byte KeyAgreement_ALG_EC_SVDP_DHC_PLAIN = 4;
- public static final byte KeyAgreement_ALG_EC_PACE_GM = 5;
- public static final byte KeyAgreement_ALG_EC_SVDP_DH_PLAIN_XY = 6;
-
- // Class javacard.security.Signature
- // javacard.security.Signature Fields:
- public static final byte Signature_ALG_ECDSA_SHA = 17;
- public static final byte Signature_ALG_ECDSA_SHA_256 = 33;
- public static final byte Signature_ALG_ECDSA_SHA_384 = 34;
- public static final byte Signature_ALG_ECDSA_SHA_224 = 37;
- public static final byte Signature_ALG_ECDSA_SHA_512 = 38;
+ public static final short SW_Exception = (short) 0xff01;
+ public static final short SW_ArrayIndexOutOfBoundsException = (short) 0xff02;
+ public static final short SW_ArithmeticException = (short) 0xff03;
+ public static final short SW_ArrayStoreException = (short) 0xff04;
+ public static final short SW_NullPointerException = (short) 0xff05;
+ public static final short SW_NegativeArraySizeException = (short) 0xff06;
+ public static final short SW_CryptoException_prefix = (short) 0xf100;
+ public static final short SW_SystemException_prefix = (short) 0xf200;
+ public static final short SW_PINException_prefix = (short) 0xf300;
+ public static final short SW_TransactionException_prefix = (short) 0xf400;
+ public static final short SW_CardRuntimeException_prefix = (short) 0xf500;
private static final short ARRAY_LENGTH = (short) 0xff;
private static final short APDU_MAX_LENGTH = (short) 1024;
@@ -97,8 +90,6 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
private byte[] ramArray = null;
private byte[] ramArray2 = null;
private byte[] apduArray = null;
- // PERSISTENT ARRAY IN EEPROM
- private byte[] dataArray = null; // unused
private RandomData randomData = null;
@@ -123,9 +114,6 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
ramArray2 = JCSystem.makeTransientByteArray(ARRAY_LENGTH, JCSystem.CLEAR_ON_RESET);
apduArray = JCSystem.makeTransientByteArray(APDU_MAX_LENGTH, JCSystem.CLEAR_ON_RESET);
- dataArray = new byte[ARRAY_LENGTH];
- Util.arrayFillNonAtomic(dataArray, (short) 0, ARRAY_LENGTH, (byte) 0);
-
randomData = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
EC_Consts.randomData = randomData;
@@ -152,53 +140,87 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
}
if (cla == CLA_ECTESTERAPPLET) {
- AppletUtil.readAPDU(apdu, apduArray, APDU_MAX_LENGTH);
+ try {
+
+ AppletUtil.readAPDU(apdu, apduArray, APDU_MAX_LENGTH);
+
+ short length = 0;
+ switch (ins) {
+ case INS_ALLOCATE_KA:
+ length = insAllocateKA(apdu);
+ break;
+ case INS_ALLOCATE_SIG:
+ length = insAllocateSig(apdu);
+ break;
+ case INS_ALLOCATE:
+ length = insAllocate(apdu);
+ break;
+ case INS_CLEAR:
+ length = insClear(apdu);
+ break;
+ case INS_SET:
+ length = insSet(apdu);
+ break;
+ case INS_TRANSFORM:
+ length = insTransform(apdu);
+ break;
+ case INS_GENERATE:
+ length = insGenerate(apdu);
+ break;
+ case INS_EXPORT:
+ length = insExport(apdu);
+ break;
+ case INS_ECDH:
+ length = insECDH(apdu);
+ break;
+ case INS_ECDH_DIRECT:
+ length = insECDH_direct(apdu);
+ break;
+ case INS_ECDSA:
+ length = insECDSA(apdu);
+ break;
+ case INS_ECDSA_SIGN:
+ length = insECDSA_sign(apdu);
+ break;
+ case INS_ECDSA_VERIFY:
+ length = insECDSA_verify(apdu);
+ break;
+ case INS_CLEANUP:
+ length = insCleanup(apdu);
+ break;
+ default:
+ // The INS code is not supported by the dispatcher
+ ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
+ break;
+ }
+ apdu.setOutgoingAndSend((short) 0, length);
- short length = 0;
- switch (ins) {
- case INS_ALLOCATE_KA:
- length = insAllocateKA(apdu);
- break;
- case INS_ALLOCATE_SIG:
- length = insAllocateSig(apdu);
- break;
- case INS_ALLOCATE:
- length = insAllocate(apdu);
- break;
- case INS_CLEAR:
- length = insClear(apdu);
- break;
- case INS_SET:
- length = insSet(apdu);
- break;
- case INS_CORRUPT:
- length = insCorrupt(apdu);
- break;
- case INS_GENERATE:
- length = insGenerate(apdu);
- break;
- case INS_EXPORT:
- length = insExport(apdu);
- break;
- case INS_ECDH:
- length = insECDH(apdu);
- break;
- case INS_ECDH_DIRECT:
- length = insECDH_direct(apdu);
- break;
- case INS_ECDSA:
- length = insECDSA(apdu);
- break;
- case INS_CLEANUP:
- length = insCleanup(apdu);
- break;
- default:
- // The INS code is not supported by the dispatcher
- ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
- break;
+ } catch (ISOException e) {
+ throw e; // Our exception from code, just re-emit
+ } catch (ArrayIndexOutOfBoundsException e) {
+ ISOException.throwIt(SW_ArrayIndexOutOfBoundsException);
+ } catch (ArithmeticException e) {
+ ISOException.throwIt(SW_ArithmeticException);
+ } catch (ArrayStoreException e) {
+ ISOException.throwIt(SW_ArrayStoreException);
+ } catch (NullPointerException e) {
+ ISOException.throwIt(SW_NullPointerException);
+ } catch (NegativeArraySizeException e) {
+ ISOException.throwIt(SW_NegativeArraySizeException);
+ } catch (CryptoException e) {
+ ISOException.throwIt((short) (SW_CryptoException_prefix | e.getReason()));
+ } catch (SystemException e) {
+ ISOException.throwIt((short) (SW_SystemException_prefix | e.getReason()));
+ } catch (PINException e) {
+ ISOException.throwIt((short) (SW_PINException_prefix | e.getReason()));
+ } catch (TransactionException e) {
+ ISOException.throwIt((short) (SW_TransactionException_prefix | e.getReason()));
+ } catch (CardRuntimeException e) {
+ ISOException.throwIt((short) (SW_CardRuntimeException_prefix | e.getReason()));
+ } catch (Exception e) {
+ ISOException.throwIt(SW_Exception);
}
- apdu.setOutgoingAndSend((short) 0, length);
} else ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
}
@@ -304,29 +326,29 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
}
/**
- * Corrupts curve paramaters of local and remote keyPairs.
- * returns corruptCurve SWs
+ * Transforms curve paramaters of local and remote keyPairs.
+ * returns transformCurve SWs
*
* @param apdu P1 = byte keyPair (KEYPAIR_* | ...)
* P2 = byte key (EC_Consts.KEY_* | ...)
* DATA = short params (EC_Consts.PARAMETER_* | ...)
- * byte corruption (EC_Consts.CORRUPTION_* || ...)
+ * short transformation (EC_Consts.TRANSFORMATION_* || ...)
* @return length of response
*/
- private short insCorrupt(APDU apdu) {
+ private short insTransform(APDU apdu) {
byte keyPair = apduArray[ISO7816.OFFSET_P1];
byte key = apduArray[ISO7816.OFFSET_P2];
short cdata = apdu.getOffsetCdata();
short params = Util.getShort(apduArray, cdata);
- byte corruption = apduArray[(short) (cdata + 2)];
+ short transformation = Util.getShort(apduArray, (short) (cdata + 2));
short len = 0;
if ((keyPair & KEYPAIR_LOCAL) != 0) {
- len += corrupt(localKeypair, key, params, corruption, apdu.getBuffer(), (short) 0);
+ len += transform(localKeypair, key, params, transformation, apdu.getBuffer(), (short) 0);
}
if ((keyPair & KEYPAIR_REMOTE) != 0) {
- len += corrupt(remoteKeypair, key, params, corruption, apdu.getBuffer(), len);
+ len += transform(remoteKeypair, key, params, transformation, apdu.getBuffer(), len);
}
return len;
@@ -389,7 +411,7 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
* @param apdu P1 = byte pubkey (KEYPAIR_*)
* P2 = byte privkey (KEYPAIR_*)
* DATA = byte export (EXPORT_TRUE || EXPORT_FALSE)
- * short corruption (EC_Consts.CORRUPTION_* | ...)
+ * short transformation (EC_Consts.TRANSFORMATION_* | ...)
* byte type (EC_Consts.KA_* | ...)
* @return length of response
*/
@@ -398,10 +420,10 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
byte privkey = apduArray[ISO7816.OFFSET_P2];
short cdata = apdu.getOffsetCdata();
byte export = apduArray[cdata];
- short corruption = Util.getShort(apduArray, (short) (cdata + 1));
+ short transformation = Util.getShort(apduArray, (short) (cdata + 1));
byte type = apduArray[(short) (cdata + 3)];
- return ecdh(pubkey, privkey, export, corruption, type, apdu.getBuffer(), (short) 0);
+ return ecdh(pubkey, privkey, export, transformation, type, apdu.getBuffer(), (short) 0);
}
/**
@@ -409,7 +431,7 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
*
* @param apdu P1 = byte privkey (KEYPAIR_*)
* P2 = byte export (EXPORT_TRUE || EXPORT_FALSE)
- * DATA = short corruption (EC_Consts.CORRUPTION_* | ...)
+ * DATA = short transformation (EC_Consts.TRANSFORMATION_* | ...)
* byte type (EC_Consts.KA_* | ...)
* short length
* byte[] pubkey
@@ -419,11 +441,11 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
byte privkey = apduArray[ISO7816.OFFSET_P1];
byte export = apduArray[ISO7816.OFFSET_P2];
short cdata = apdu.getOffsetCdata();
- short corruption = Util.getShort(apduArray, cdata);
+ short transformation = Util.getShort(apduArray, cdata);
byte type = apduArray[(short) (cdata + 2)];
short length = Util.getShort(apduArray, (short) (cdata + 3));
- return ecdh_direct(privkey, export, corruption, type, (short) (cdata + 5), length, apdu.getBuffer(), (short) 0);
+ return ecdh_direct(privkey, export, transformation, type, (short) (cdata + 5), length, apdu.getBuffer(), (short) 0);
}
/**
@@ -455,6 +477,57 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
}
/**
+ *
+ * @param apdu P1 = byte keyPair (KEYPAIR_*)
+ * P2 = byte export (EXPORT_TRUE || EXPORT_FALSE)
+ * DATA = byte sigType
+ * short dataLength (00 = random data generated, !00 = data length)
+ * byte[] data
+ * @return length of response
+ */
+ private short insECDSA_sign(APDU apdu) {
+ byte keyPair = apduArray[ISO7816.OFFSET_P1];
+ byte export = apduArray[ISO7816.OFFSET_P2];
+ short cdata = apdu.getOffsetCdata();
+ byte sigType = apduArray[cdata];
+
+ short len = 0;
+ if ((keyPair & KEYPAIR_LOCAL) != 0) {
+ len += ecdsa_sign(localKeypair, sigType, export, apduArray, (short) (cdata + 1), apdu.getBuffer(), (short) 0);
+ }
+ if ((keyPair & KEYPAIR_REMOTE) != 0) {
+ len += ecdsa_sign(remoteKeypair, sigType, export, apduArray, (short) (cdata + 1), apdu.getBuffer(), len);
+ }
+ return len;
+ }
+
+ /**
+ *
+ * @param apdu P1 = byte keyPair (KEYPAIR_*)
+ * P2 = byte sigType
+ * DATA = short dataLength (00 = random data generated, !00 = data length)
+ * byte[] data
+ * short sigLength
+ * byte[] signature
+ * @return length of response
+ */
+ private short insECDSA_verify(APDU apdu) {
+ byte keyPair = apduArray[ISO7816.OFFSET_P1];
+ byte sigType = apduArray[ISO7816.OFFSET_P2];
+ short cdata = apdu.getOffsetCdata();
+
+ short len = 0;
+ if ((keyPair & KEYPAIR_LOCAL) != 0) {
+ len += ecdsa_verify(localKeypair, sigType, apduArray, cdata, apdu.getBuffer(), (short) 0);
+ }
+ if ((keyPair & KEYPAIR_REMOTE) != 0) {
+ len += ecdsa_verify(remoteKeypair, sigType, apduArray, cdata, apdu.getBuffer(), len);
+ }
+ return len;
+ }
+
+
+ /**
* Performs card memory cleanup via JCSystem.requestObjectDeletion()
*
* @param apdu no data
@@ -536,16 +609,16 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
}
/**
- * @param keyPair KeyPair to corrupt
- * @param key key to corrupt (EC_Consts.KEY_* | ...)
- * @param params parameters to corrupt (EC_Consts.PARAMETER_* | ...)
- * @param corruption corruption type (EC_Consts.CORRUPTION_*)
+ * @param keyPair KeyPair to transform
+ * @param key key to transform (EC_Consts.KEY_* | ...)
+ * @param params parameters to transform (EC_Consts.PARAMETER_* | ...)
+ * @param transformation transformation type (EC_Consts.TRANSFORMATION_*)
* @param outBuffer buffer to output sw to
* @param outOffset output offset in buffer
* @return length of data written to the buffer
*/
- private short corrupt(KeyPair keyPair, byte key, short params, byte corruption, byte[] outBuffer, short outOffset) {
- short sw = keyGenerator.corruptCurve(keyPair, key, params, corruption, ramArray, (short) 0);
+ private short transform(KeyPair keyPair, byte key, short params, short transformation, byte[] outBuffer, short outOffset) {
+ short sw = keyGenerator.transformCurve(keyPair, key, params, transformation, ramArray, (short) 0);
Util.setShort(outBuffer, outOffset, sw);
return 2;
}
@@ -581,7 +654,6 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
length += keyGenerator.exportParameters(keyPair, EC_Consts.KEY_PUBLIC, params, outBuffer, outOffset);
sw = keyGenerator.getSW();
}
- //TODO unify this, now that param key == the passed on param.
if ((key & EC_Consts.KEY_PRIVATE) != 0 && sw == ISO7816.SW_NO_ERROR) {
//export params from private
length += keyGenerator.exportParameters(keyPair, EC_Consts.KEY_PRIVATE, params, outBuffer, (short) (outOffset + length));
@@ -596,13 +668,13 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
* @param pubkey keyPair to use for public key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE)
* @param privkey keyPair to use for private key, (KEYPAIR_LOCAL || KEYPAIR_REMOTE)
* @param export whether to export ECDH secret
- * @param corruption whether to invalidate the pubkey before ECDH
+ * @param transformation whether to transform the pubkey before ECDH
* @param type KeyAgreement type to test
* @param outBuffer buffer to write sw to, and export ECDH secret {@code if(export == EXPORT_TRUE)}
* @param outOffset output offset in buffer
* @return length of data written to the buffer
*/
- private short ecdh(byte pubkey, byte privkey, byte export, short corruption, byte type, byte[] outBuffer, short outOffset) {
+ private short ecdh(byte pubkey, byte privkey, byte export, short transformation, byte type, byte[] outBuffer, short outOffset) {
short length = 0;
KeyPair pub = ((pubkey & KEYPAIR_LOCAL) != 0) ? localKeypair : remoteKeypair;
@@ -610,11 +682,11 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
short secretLength = 0;
if (keyTester.getKaType() == type) {
- secretLength = keyTester.testKA(priv, pub, ramArray, (short) 0, ramArray2, (short) 0, corruption);
+ secretLength = keyTester.testKA(priv, pub, ramArray, (short) 0, ramArray2, (short) 0, transformation);
} else {
short allocateSW = keyTester.allocateKA(type);
if (allocateSW == ISO7816.SW_NO_ERROR) {
- secretLength = keyTester.testKA(priv, pub, ramArray, (short) 0, ramArray2, (short) 0, corruption);
+ secretLength = keyTester.testKA(priv, pub, ramArray, (short) 0, ramArray2, (short) 0, transformation);
}
}
Util.setShort(outBuffer, outOffset, keyTester.getSW());
@@ -630,18 +702,18 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
return length;
}
- private short ecdh_direct(byte privkey, byte export, short corruption, byte type, short keyOffset, short keyLength, byte[] outBuffer, short outOffset) {
+ private short ecdh_direct(byte privkey, byte export, short transformation, byte type, short keyOffset, short keyLength, byte[] outBuffer, short outOffset) {
short length = 0;
KeyPair priv = ((privkey & KEYPAIR_LOCAL) != 0) ? localKeypair : remoteKeypair;
short secretLength = 0;
if (keyTester.getKaType() == type) {
- secretLength = keyTester.testKA_direct(priv, apduArray, keyOffset, keyLength, ramArray2, (short) 0, corruption);
+ secretLength = keyTester.testKA_direct(priv, apduArray, keyOffset, keyLength, ramArray2, (short) 0, transformation);
} else {
short allocateSW = keyTester.allocateKA(type);
if (allocateSW == ISO7816.SW_NO_ERROR) {
- secretLength = keyTester.testKA_direct(priv, apduArray, keyOffset, keyLength, ramArray2, (short) 0, corruption);
+ secretLength = keyTester.testKA_direct(priv, apduArray, keyOffset, keyLength, ramArray2, (short) 0, transformation);
}
}
@@ -702,6 +774,63 @@ public class ECTesterApplet extends Applet implements ExtendedLength {
return length;
}
+ private short ecdsa_sign(KeyPair sign, byte sigType, byte export, byte[] inBuffer, short inOffset, byte[] outBuffer, short outOffset) {
+ short length = 0;
+
+ short dataLength = Util.getShort(inBuffer, inOffset);
+ if (dataLength == 0) { //no data to sign
+ //generate random
+ dataLength = 64;
+ randomData.generateData(ramArray, (short) 0, dataLength);
+ } else {
+ Util.arrayCopyNonAtomic(inBuffer, (short) (inOffset + 2), ramArray, (short) 0, dataLength);
+ }
+
+ short signatureLength = 0;
+ if (keyTester.getSigType() == sigType) {
+ signatureLength = keyTester.testECDSA_sign((ECPrivateKey) sign.getPrivate(), ramArray, (short) 0, dataLength, ramArray2, (short) 0);
+ } else {
+ short allocateSW = keyTester.allocateSig(sigType);
+ if (allocateSW == ISO7816.SW_NO_ERROR) {
+ signatureLength = keyTester.testECDSA_sign((ECPrivateKey) sign.getPrivate(), ramArray, (short) 0, dataLength, ramArray2, (short) 0);
+ }
+ }
+ Util.setShort(outBuffer, outOffset, keyTester.getSW());
+ length += 2;
+
+ if (export == EXPORT_TRUE) {
+ Util.setShort(outBuffer, (short) (outOffset + length), signatureLength);
+ length += 2;
+
+ Util.arrayCopyNonAtomic(ramArray2, (short) 0, outBuffer, (short) (outOffset + length), signatureLength);
+ length += signatureLength;
+ }
+
+ return length;
+ }
+
+ private short ecdsa_verify(KeyPair verify, byte sigType, byte[] inBuffer, short inOffset, byte[] outBuffer, short outOffset) {
+ short length = 0;
+
+ short dataLength = Util.getShort(inBuffer, inOffset);
+ short dataOffset = (short)(inOffset + 2);
+ short sigLength = Util.getShort(inBuffer, (short)(dataOffset + dataLength));
+ short sigOffset = (short)(dataOffset + dataLength + 2);
+
+ if (keyTester.getSigType() == sigType) {
+ keyTester.testECDSA_verify((ECPublicKey) verify.getPublic(), inBuffer, dataOffset, dataLength, inBuffer, sigOffset, sigLength);
+ } else {
+ short allocateSW = keyTester.allocateSig(sigType);
+ if (allocateSW == ISO7816.SW_NO_ERROR) {
+ keyTester.testECDSA_verify((ECPublicKey) verify.getPublic(), inBuffer, dataOffset, dataLength, inBuffer, sigOffset, sigLength);
+ }
+ }
+ Util.setShort(outBuffer, outOffset, keyTester.getSW());
+ length += 2;
+
+ return length;
+ }
+
/**
* @param buffer buffer to write sw to
* @param offset output offset in buffer