1 / 114

Towards Certifiably Correct Java Card Applets

Towards Certifiably Correct Java Card Applets. Alessandro Coglio. joint work with:. Matthias Anlauff Li-Mei Gilham. David Cyrluk Lambert Meertens. Kestrel Institute. IFIP WG 2.1 Meeting #60 (May 2005). Java. Java. S I Z E. Enterprise Edition. Standard Edition. Micro Edition. Card.

hagop
Télécharger la présentation

Towards Certifiably Correct Java Card Applets

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Towards Certifiably CorrectJava Card Applets Alessandro Coglio joint work with: Matthias Anlauff Li-Mei Gilham David Cyrluk Lambert Meertens Kestrel Institute IFIP WG 2.1 Meeting #60 (May 2005)

  2. Java Java

  3. S I Z E Enterprise Edition Standard Edition Micro Edition Card Java smartcards chip authentication, banking, telephony, health care, … plasticsubstrate

  4. Standard Edition Java • language   ? • API Card

  5. JAVA CARD RUNTIMEENVIRONMENT (JCRE) BYTECODEINTERPRETER API LIBRARY Java bytecode program Java Card program JAVACOMPILER Java Card applet JAVA CARD CONVERTER (checks subset & API) APPLET . . . . . . Java Card applet Java web applet SMART CARD HW/OS

  6. typicallywrittenby hand Java Card program somewhatlow-level  error-prone

  7. Java Card program applet spec

  8. AutoSmart(automatic generator of smart card applets) AUTOSMART Java Card program applet spec • for • high assurance • productivity

  9. AUTOSMART Java Card program applet spec AutoSmart(automatic generator of smart card applets) AUTOSMART Java Card program applet spec

  10. AutoSmart(automatic generator of smart card applets) AUTOSMART Java Card program applet spec proof CHECKER smaller & simplerthan AutoSmart yes/no  easier to trust

  11. AUTOSMART Java Card program applet spec written in SmartSlang

  12. SmartSlang(smart card specification language) SmartSlang

  13. domain-specific language domain = smart cards i.e. constructs specialized to smart card applications under design basic version done, many additional constructs planned precise semantics in terms of state machines applet : CommandStateResponseState but no formal background necessary SmartSlang(smart card specification language)

  14. SmartSlang SmartSlang example: e-wallet

  15. SmartSlang example: e-wallet expressive type system e.g. integer ranges (vs. byte/short/int in JC) type Balance = Int(0,MAX_BALANCE); type Amount = Int(1,MAX_AMOUNT); const MAX_BALANCE = 10000; const MAX_AMOUNT = 100; state { Balance balance } command credit(Amount amount) { if (balance + amount <= MAX_BALANCE) { balance = balance + amount; } else { respond EXCEEDED_BALANCE; } } apdu {0x80,0x30,0,0,ubytes[AMOUNT_BYTES](amount),0}; const EXCEEDED_BALANCE = 0x6A84; ... 1000000 • capture semantics • automatic mappingto JC typese.g. Balance  short Amount  byte (short,short) int pervasive change in Java Card code

  16. SmartSlang example: e-wallet type Balance = Int(0,MAX_BALANCE); type Amount = Int(1,MAX_AMOUNT); const MAX_BALANCE = 10000; const MAX_AMOUNT = 100; state { Balance balance } command credit(Amount amount) { if (balance + amount <= MAX_BALANCE) { balance = balance + amount; } else { respond EXCEEDED_BALANCE; } } apdu {0x80,0x30,0,0,ubytes[AMOUNT_BYTES](amount),0}; const EXCEEDED_BALANCE = 0x6A84; ... explicit state components

  17. SmartSlang example: e-wallet type Balance = Int(0,MAX_BALANCE); type Amount = Int(1,MAX_AMOUNT); const MAX_BALANCE = 10000; const MAX_AMOUNT = 100; state { Balance balance } command credit(Amount amount) { if (balance + amount <= MAX_BALANCE) { balance = balance + amount; } else { respond EXCEEDED_BALANCE; } } apdu {0x80,0x30,0,0,ubytes[AMOUNT_BYTES](amount),0}; const EXCEEDED_BALANCE = 0x6A84; ... explicit symbolic commands with high-level parameters (vs. APDU bytes in JC)

  18. SmartSlang example: e-wallet type Balance = Int(0,MAX_BALANCE); type Amount = Int(1,MAX_AMOUNT); const MAX_BALANCE = 10000; const MAX_AMOUNT = 100; state { Balance balance } command credit(Amount amount) { if (balance + amount <= MAX_BALANCE) { balance = balance + amount; } else { respond EXCEEDED_BALANCE; } } apdu {0x80,0x30,0,0,ubytes[AMOUNT_BYTES](amount),0}; const EXCEEDED_BALANCE = 0x6A84; ... • familiar todevelopers • “superset ofsubset of Java” simple Java-likeexpressions & statements

  19. SmartSlang example: e-wallet type Balance = Int(0,MAX_BALANCE); type Amount = Int(1,MAX_AMOUNT); const MAX_BALANCE = 10000; const MAX_AMOUNT = 100; state { Balance balance } command credit(Amount amount) { if (balance + amount <= MAX_BALANCE) { balance = balance + amount; } else { respond EXCEEDED_BALANCE; } } apdu {0x80,0x30,0,0,ubytes[AMOUNT_BYTES](amount),0}; const EXCEEDED_BALANCE = 0x6A84; ... explicit responses

  20. SmartSlang example: e-wallet type Balance = Int(0,MAX_BALANCE); type Amount = Int(1,MAX_AMOUNT); const MAX_BALANCE = 10000; const MAX_AMOUNT = 100; state { Balance balance } command credit(Amount amount) { if (balance + amount <= MAX_BALANCE) { balance = balance + amount; } else { respond EXCEEDED_BALANCE; } } apdu {0x80,0x30,0,0,ubytes[AMOUNT_BYTES](amount),0}; const EXCEEDED_BALANCE = 0x6A84; ... • all type safetychecked statically(conservatively) • catches user errors type-safe assignment

  21. SmartSlang example: e-wallet type Balance = Int(0,MAX_BALANCE); type Amount = Int(1,MAX_AMOUNT); const MAX_BALANCE = 10000; const MAX_AMOUNT = 100; state { Balance balance } command credit(Amount amount) { balance = balance + amount; } apdu {0x80,0x30,0,0,ubytes[AMOUNT_BYTES](amount),0}; const EXCEEDED_BALANCE = 0x6A84; ... • all type safetychecked statically(conservatively) • catches user errors type-safe assignment user gets warning

  22. SmartSlang example: e-wallet type Balance = Int(0,MAX_BALANCE); type Amount = Int(1,MAX_AMOUNT); const MAX_BALANCE = 10000; const MAX_AMOUNT = 100; state { Balance balance } command credit(Amount amount) { if (balance + amount <= MAX_BALANCE) { balance = balance + amount; } else { respond EXCEEDED_BALANCE; } } apdu {0x80,0x30,0,0,ubytes[AMOUNT_BYTES](amount),0}; const EXCEEDED_BALANCE = 0x6A84; ... • all type safetychecked statically(conservatively) • catches user errors • no runtime errors(e.g. array access) • type checker usesautomated reasoning type-safe assignment no such thing in JC!

  23. SmartSlang example: e-wallet type Balance = Int(0,MAX_BALANCE); type Amount = Int(1,MAX_AMOUNT); const MAX_BALANCE = 10000; const MAX_AMOUNT = 100; state { Balance balance } command credit(Amount amount) { if (balance + amount <= MAX_BALANCE) { balance = balance + amount; } else { respond EXCEEDED_BALANCE; } } apdu {0x80,0x30,0,0,ubytes[AMOUNT_BYTES](amount),0}; const EXCEEDED_BALANCE = 0x6A84; ... declarative APDU encoding (vs. explicit decoding/dispatch in JC) lengthy, error-prone JC decoding/dispatching code automatically generated

  24. SmartSlang example: e-wallet type Balance = Int(0,MAX_BALANCE); type Amount = Int(1,MAX_AMOUNT); const MAX_BALANCE = 10000; const MAX_AMOUNT = 100; state { Balance balance } command credit(Amount amount) { if (balance + amount <= MAX_BALANCE) { balance = balance + amount; } else { respond EXCEEDED_BALANCE; } } apdu {0x80,0x30,0,0,ubytes[AMOUNT_BYTES](amount),0}; const EXCEEDED_BALANCE = 0x6A84; ... amount encoded as byte JC code to handle user input data errors (e.g. if amount > 100) automatically generated

  25. e-wallet example example e-wallet type Balance = Int(0,MAX_BALANCE); type Amount = Int(1,MAX_AMOUNT); const MAX_BALANCE = 10000; const MAX_AMOUNT = 100; state { Balance balance } command credit(Amount amount) { if (balance + amount <= MAX_BALANCE) { balance = balance + amount; } else { respond EXCEEDED_BALANCE; } } apdu {0x80,0x30,0,0,ubytes[AMOUNT_BYTES](amount),0}; const EXCEEDED_BALANCE = 0x6A84; ... const MAX_BALANCE = 10000; const MAX_AMOUNT = 100; type Balance = Int(0,MAX_BALANCE); type Amount = Int(1,MAX_AMOUNT); const BALANCE_BYTES = ubytes(MAX_BALANCE).length; const AMOUNT_BYTES = ubytes(MAX_AMOUNT).length; state { Balance balance } init() { balance = 0; } bytes {}; command credit(Amount amount) { if (balance + amount <= MAX_BALANCE) { balance = balance + amount; } else { respond EXCEEDED_BALANCE; } } apdu {CLA,0x30,0,0,ubytes[AMOUNT_BYTES](amount),0}; command debit(Amount amount) { if (balance - amount >= 0) { balance = balance - amount; } else { respond NEGATIVE_BALANCE; } } apdu {CLA,0x40,0,0,ubytes[AMOUNT_BYTES](amount),0}; command getBalance() { respondok ubytes[BALANCE_BYTES](balance); } apdu {CLA,0x50,0,0,{},BALANCE_BYTES}; const CLA = 0x80; const EXCEEDED_BALANCE = 0x6A84; const NEGATIVE_BALANCE = 0x6A85; SmartSlang spec

  26. package pkg; import javacard.framework.*; import javacard.security.*; import javacardx.crypto.*; class Wallet extends Applet { static final short MAX_BALANCE = 10000; static final short MAX_AMOUNT = 100; static final short BALANCE_BYTES = 2; static final short AMOUNT_BYTES = 1; static final short EXCEEDED_BALANCE = 0x6A84; static final byte CLA = (byte)0x80; static final short NEGATIVE_BALANCE = 0x6A85; short balance; static byte[] _aux1; byte[] inData; short inDataLength; // always <= 255 private A (byte[] initpar, short off, byte len) { _aux1 = new byte[2]; inData = new byte[255]; // command data is 255 bytes max balance = 0; register(initpar,(short)(off + 1),initpar[off]); } public boolean select () { return true; } public void process (APDU apdu) { if (selectingApplet()) return; byte[] buffer = apdu.getBuffer(); if (buffer[ISO7816.OFFSET_CLA] != (byte)0x80) ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); switch (buffer[ISO7816.OFFSET_INS]) { case 0x30: credit(apdu); return; case 0x40: debit(apdu); return; case 0x50: getBalance(apdu); return; default: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } } void credit (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != 0) || (buffer[ISO7816.OFFSET_P2] != 0)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 1) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); short amount = nonNegativeByte(inData[0]); if ((amount < 1) || (amount > 100)) ISOException.throwIt(ISO7816.SW_WRONG_DATA); if ((short)(balance + amount) <= MAX_BALANCE) balance = (short)(balance + amount); else ISOException.throwIt((short)EXCEEDED_BALANCE); } void debit (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != 0) || (buffer[ISO7816.OFFSET_P2] != 0)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 1) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); short amount = nonNegativeByte(inData[0]); if ((amount < 1) || (amount > 100)) ISOException.throwIt(ISO7816.SW_WRONG_DATA); if ((short)(balance - amount) >= 0) balance = (short)(balance - amount); else ISOException.throwIt((short)NEGATIVE_BALANCE); } void getBalance (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != 0) || (buffer[ISO7816.OFFSET_P2] != 0)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); short requiredLe = BALANCE_BYTES; short receivedLe = nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (receivedLe == 0) receivedLe = 256; if (receivedLe != requiredLe) { short swLow = (short)(requiredLe & 0xff); ISOException.throwIt((short)(ISO7816.SW_CORRECT_LENGTH_00 + swLow)); } byte[] _aux1 = Wallet._aux1; ubytes((short)2,balance,_aux1); sendOutgoingData(apdu,_aux1); return; } static void ubytes(short n, short i, byte[] result) { for (short k = (short)(n - 1); k >= 0; k--) { result[k] = (byte) (i & 0xff); i = (short) (i >>> 8); } } static short nonNegativeByte(byte b) { return (b < 0 ? (short)(b + 256) : b); } void receiveIncomingData(APDU apdu) { byte[] buffer = apdu.getBuffer(); short totalRead = 0; short chunkRead = apdu.setIncomingAndReceive(); do { // error if too little or too much data: if (chunkRead == 0 || (short) (totalRead + chunkRead) > inDataLength) ISOException.throwIt(ISO7816.SW_WRONG_DATA); // transfer from APDU buffer to inData array: Util.arrayCopy(buffer,ISO7816.OFFSET_CDATA, inData,totalRead,chunkRead); // increment counter: totalRead += chunkRead; // get more data if necessary: if (totalRead != inDataLength) chunkRead = apdu.receiveBytes(ISO7816.OFFSET_CDATA); } while (totalRead < inDataLength); } void sendOutgoingData(APDU apdu, byte[] data) { apdu.setOutgoing(); apdu.setOutgoingLength((short)data.length); apdu.sendBytesLong(data,(short)0,(short)data.length); } public static void install(byte[] initpar, short off, byte len) { new Wallet(initpar,off,len); } } size(code) = ~ 3–4 size(spec) JavaCardcode e-wallet example ~ 150lines ~ 40 lines const MAX_BALANCE = 10000; const MAX_AMOUNT = 100; type Balance = Int(0,MAX_BALANCE); type Amount = Int(1,MAX_AMOUNT); const BALANCE_BYTES = ubytes(MAX_BALANCE).length; const AMOUNT_BYTES = ubytes(MAX_AMOUNT).length; state { Balance balance } init() { balance = 0; } bytes {}; command credit(Amount amount) { if (balance + amount <= MAX_BALANCE) { balance = balance + amount; } else { respond EXCEEDED_BALANCE; } } apdu {CLA,0x30,0,0,ubytes[AMOUNT_BYTES](amount),0}; command debit(Amount amount) { if (balance - amount >= 0) { balance = balance - amount; } else { respond NEGATIVE_BALANCE; } } apdu {CLA,0x40,0,0,ubytes[AMOUNT_BYTES](amount),0}; command getBalance() { respondok ubytes[BALANCE_BYTES](balance); } apdu {CLA,0x50,0,0,{},BALANCE_BYTES}; const CLA = 0x80; const EXCEEDED_BALANCE = 0x6A84; const NEGATIVE_BALANCE = 0x6A85; AUTOSMART SmartSlang spec (actual files; font size 2)

  27. e-wallet Java Card code e-wallet ... void credit (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != 0) || (buffer[ISO7816.OFFSET_P2] != 0)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 1) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); short amount = nonNegativeByte(inData[0]); if ((amount < 1) || (amount > 100)) ISOException.throwIt(ISO7816.SW_WRONG_DATA); if ((short)(balance + amount) <= MAX_BALANCE) balance = (short)(balance + amount); else ISOException.throwIt((short)EXCEEDED_BALANCE); } ... ... void credit (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != 0) || (buffer[ISO7816.OFFSET_P2] != 0)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 1) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); short amount = nonNegativeByte(inData[0]); if ((amount < 1) || (amount > 100)) ISOException.throwIt(ISO7816.SW_WRONG_DATA); if ((short)(balance + amount) <= MAX_BALANCE) balance = (short)(balance + amount); else ISOException.throwIt((short)EXCEEDED_BALANCE); } ... Java Card code

  28. e-wallet Java Card code ... void credit (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != 0) || (buffer[ISO7816.OFFSET_P2] != 0)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 1) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); short amount = nonNegativeByte(inData[0]); if ((amount < 1) || (amount > 100)) ISOException.throwIt(ISO7816.SW_WRONG_DATA); if ((short)(balance + amount) <= MAX_BALANCE) balance = (short)(balance + amount); else ISOException.throwIt((short)EXCEEDED_BALANCE); } ... interestingcomputation

  29. e-wallet Java Card code ... void credit (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != 0) || (buffer[ISO7816.OFFSET_P2] != 0)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 1) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); short amount = nonNegativeByte(inData[0]); if ((amount < 1) || (amount > 100)) ISOException.throwIt(ISO7816.SW_WRONG_DATA); if ((short)(balance + amount) <= MAX_BALANCE) balance = (short)(balance + amount); else ISOException.throwIt((short)EXCEEDED_BALANCE); } ... APDUchecking& decoding

  30. SmartSlang counterpart ... command credit(Amount amount) { if (balance + amount <= MAX_BALANCE) { balance = balance + amount; } else { respond EXCEEDED_BALANCE; } } apdu {0x80,0x30,0,0,ubytes[AMOUNT_BYTES](amount),0}; ...

  31. SmartSlang example: PKI SmartSlang

  32. SmartSlang example: PKI built-in key types type Key = RSAPrivateKey(1024); type Message = Byte[1024/8]; type Pin = Byte[8]; type PinState = enum {blocked, notVerified(Int(1,3)), ...}; state { Key key, Pin pin, PinState pinState, ... } command privSignDecrypt(Message msg) { if (pinState != verified) { respond SW_SECURITY_NOK; } else { respondok decrypt(key,msg); } } apdu {0x80,0x42,0,0,msg,1024/8}; secure command putPIN(Pin newPin) { pin = newPin; pinState = notVerified(3); } apdu {0x80,0x22,0,0,newPin,0}; ... automatic mapping to JC API classes

  33. SmartSlang example: PKI type Key = RSAPrivateKey(1024); type Message = Byte[1024/8]; type Pin = Byte[8]; type PinState = enum {blocked, notVerified(Int(1,3)), ...}; state { Key key, Pin pin, PinState pinState, ... } command privSignDecrypt(Message msg) { if (pinState != verified) { respond SW_SECURITY_NOK; } else { respondok decrypt(key,msg); } } apdu {0x80,0x42,0,0,msg,1024/8}; secure command putPIN(Pin newPin) { pin = newPin; pinState = notVerified(3); } apdu {0x80,0x22,0,0,newPin,0}; ... built-in cryptographic functions (vs. multiple API method calls in JC) • simple • automatic mappingto JC API method calls

  34. SmartSlang example: PKI type Key = RSAPrivateKey(1024); type Message = Byte[1024/8]; type Pin = Byte[8]; type PinState = enum {blocked, notVerified(Int(1,3)), ...}; state { Key key, Pin pin, PinState pinState, ... } command privSignDecrypt(Message msg) { if (pinState != verified) { respond SW_SECURITY_NOK; } else { respondok decrypt(key,msg); } } apdu {0x80,0x42,0,0,msg,1024/8}; secure command putPIN(Pin newPin) { pin = newPin; pinState = notVerified(3); } apdu {0x80,0x22,0,0,newPin,0}; ... (# tries left) enumerationtypes • with arguments • automatic mappingto byte constants +auxiliary variablesfor arguments in JC

  35. SmartSlang example: PKI type Key = RSAPrivateKey(1024); type Message = Byte[1024/8]; type Pin = Byte[8]; type PinState = enum {blocked, notVerified(Int(1,3)), ...}; state { Key key, Pin pin, PinState pinState, ... } command privSignDecrypt(Message msg) { if (pinState != verified) { respond SW_SECURITY_NOK; } else { respondok decrypt(key,msg); } } apdu {0x80,0x42,0,0,msg,1024/8}; secure command putPIN(Pin newPin) { pin = newPin; pinState = notVerified(3); } apdu {0x80,0x22,0,0,newPin,0}; ... spread change in Java Card code construct for Global Platformsecure channels (vs. multipleAPI method calls in JC) • simple, localized • automatic mapping toJC API method calls

  36. SmartSlang example: PKI type Key = RSAPrivateKey(1024); type Message = Byte[1024/8]; type Pin = Byte[8]; type PinState = enum {blocked, notVerified(Int(1,3)), ...}; state { Key key, Pin pin, PinState pinState, ... } command privSignDecrypt(Message msg) { if (pinState != verified) { respond SW_SECURITY_NOK; } else { respondok decrypt(key,msg); } } apdu {0x80,0x42,0,0,msg,1024/8}; secure command putPIN(Pin newPin) { pin = newPin; pinState = notVerified(3); } apdu {0x80,0x22,0,0,newPin,0}; ... decryption result computed on the fly and returned (vs. pre-allocated intermediate storage in JC) • simple functional model • automatic mapping topre-allocated intermediatestorage in JC automatic static storage management is significant advantage for smart cards!

  37. PKI example PKI example type Key = RSAPrivateKey(1024); type Message = Byte[1024/8]; type Pin = Byte[8]; type PinState = enum {blocked, notVerified(Int(1,3)), ...}; state { Key key, Pin pin, PinState pinState, ... } command privSignDecrypt(Message msg) { if (pinState != verified) { respond SW_SECURITY_NOK; } else { respondok decrypt(key,msg); } } apdu {0x80,0x42,0,0,msg,1024/8}; secure command putPIN(Pin newPin) { pin = newPin; pinState = notVerified(3); } apdu {0x80,0x22,0,0,newPin,0}; ... const KEY_SIZE = 1024; // in bits type KeyState = rec {Bool isExponentSet, Bool isModulusSet}; const MSG_SIZE = KEY_SIZE / 8; // in bytes type Message = Byte[MSG_SIZE]; const PIN_SIZE = 8; type Pin = Byte[PIN_SIZE]; const MAX_TRIES = 3; type PinState = enum {unset, notVerified(Int(1,MAX_TRIES)), verified, blocked}; const MAX_CERT_SIZE = 1000; // in bytes type Certificate = Byte[0..MAX_CERT_SIZE]; const MAX_CERT_CHUNK_SIZE = 100; // in bytes type CertificateChunk = Byte[1..MAX_CERT_CHUNK_SIZE]; const MAX_PROP_SIZE = 200; // in bytes type Properties = Byte[0..MAX_PROP_SIZE]; type AppletState = enum {personalization, deployment, updating}; state { RSAPrivateKey(KEY_SIZE) key, KeyState keyState, Pin pin, PinState pinState, Int(0,MAX_CERT_SIZE) certSize, Certificate certificate, Properties properties, Int(0,MAX_CERT_SIZE) certBytesSent, AppletState appletState } invariant appletState != deployment || (certBytesSent <= (certificate).length-1); init() { /* The current definition of SmartSlang requires every state component to be initialized in the initialization block. This is a bit artificial for certain state components that are really initialized by commands, e.g. keys, forcing us to use meaningless values like all 0s. We may change the definition of SmartSlang to no longer require all state components to be initialized in the initialization block, while leaving the requirement that every state component must be initialized before it is used for the first time. Besides avoiding artificial initializations, this change would support better semantic checking of the SmartSlang spec, because presumably a key shouldn't be used before it is assigned a non-all-0s value. */ key = RSAPrivateKey(KEY_SIZE) (repeat(0,KEY_SIZE/8), repeat(0,KEY_SIZE/8)); keyState = KeyState(false,false); pin = repeat(0,8); pinState = unset; certSize = 0; certificate = {}; properties = {}; certBytesSent = 0; appletState = personalization; } bytes {}; command putExponent(Byte[KEY_SIZE/8] exp) { if (appletState == deployment) { respond SW_WRONG_STATE; } key = RSAPrivateKey(KEY_SIZE) (exp, key.modulus); keyState = KeyState (true, keyState.isModulusSet); // we may extend SmartSlang with direct assignments to record components: // key.exponent = exp; // keyState.isExponentSet =true; } apdu {CLA, INS_PUT_EXP, P1, P2, exp, 0}; command putModulus(Byte[KEY_SIZE/8] mod) { if (appletState == deployment) { respond SW_WRONG_STATE; } key = RSAPrivateKey(KEY_SIZE) (key.exponent, mod); keyState = KeyState (keyState.isExponentSet, true); } apdu {CLA, INS_PUT_MOD, P1, P2, mod, 0}; command putPIN(Pin p) { if (appletState == deployment) { respond SW_WRONG_STATE; } pin = p; pinState = notVerified(MAX_TRIES); } apdu {CLA, INS_PUT_PIN, P1, P2, p, 0}; command putCertificateSize(Int(1,MAX_CERT_SIZE) size) { if (appletState == deployment) { respond SW_WRONG_STATE; } certSize = size; certificate = {}; } apdu {CLA, INS_CERT_SIZE, P1, P2, ubytes[2](size), 0}; command putCertificateChunk(CertificateChunk certChunk) { if (appletState == deployment) { respond SW_WRONG_STATE; } if (certSize == 0) { respond SW_CERT_SIZE_UNSET; } if (certificate.length + certChunk.length > certSize) { respond SW_CERTIFICATE_TOO_LARGE; } certificate = certificate ++ certChunk; } apdu {CLA, INS_PUT_CERT, P1, P2, certChunk, 0}; command putProperties(Properties prop) { if (appletState == deployment) { respond SW_WRONG_STATE; } properties = prop; } apdu {CLA, INS_PUT_PROP, P1, P2, prop, 0}; command unblockPIN() { if (appletState != updating) { respond SW_WRONG_STATE; } pinState = notVerified(MAX_TRIES); } apdu {CLA, INS_UNBLOCK, P1, P2, {}, 0}; command deploy() { if (appletState == deployment) { respond SW_WRONG_STATE; } if (keyState == KeyState(true,true) && pinState != unset && certSize != 0 && certificate.length == certSize && properties != {}) { appletState = deployment; } else { respond SW_UNFINISHED_PERSONALIZATION; } } apdu {CLA, INS_DEPLOY, P1, P2, {}, 0}; command update() { if (appletState != deployment) { respond SW_WRONG_STATE; } appletState = updating; } apdu {CLA, INS_UPDATE, P1, P2, {}, 0}; command getCertificateChunk() { if (appletState != deployment) { respond SW_WRONG_STATE; } CertificateChunk chunk; if (certificate.length - certBytesSent >= MAX_CERT_CHUNK_SIZE) { chunk = certificate[certBytesSent,MAX_CERT_CHUNK_SIZE]; certBytesSent = certBytesSent + MAX_CERT_CHUNK_SIZE; } else { chunk = certificate[certBytesSent..certificate.length-1]; certBytesSent = certificate.length; } if (certificate.length - certBytesSent >= MAX_CERT_CHUNK_SIZE) { respond {chunk, SW1_MORE_DATA, MAX_CERT_CHUNK_SIZE}; } else if (certificate.length - certBytesSent > 0) { respond {chunk, SW1_MORE_DATA, certificate.length - certBytesSent}; } else /* certificate.length == certBytesSent */ { certBytesSent = 0; // reset respondok chunk; } } apdu {CLA, INS_GET_CERT, P1, P2, {}, (certificate.length - certBytesSent >= MAX_CERT_CHUNK_SIZE) ? MAX_CERT_CHUNK_SIZE : certificate.length - certBytesSent}; command getProperties() { if (appletState != deployment) { respond SW_WRONG_STATE; } respondok properties; } apdu {CLA, INS_GET_PROP, P1, P2, {}, properties.length}; command pinVerify(Pin p) { if (appletState != deployment) { respond SW_WRONG_STATE; } switch (pinState) { case verified: { respondok; } case notVerified(triesLeft): { if (p == pin) { pinState = verified; respondok; } else if (triesLeft > 1) { pinState = notVerified(triesLeft - 1); respond {{}, SW1_WRONG_PIN, triesLeft}; } else { pinState = blocked; respond SW_PIN_BLOCKED; } } case blocked: { respond SW_PIN_BLOCKED; } case unset: { // cannot happen -- could be proved using state invariants } } } apdu {CLA, INS_VERIFY, P1, P2, p, 0}; command pinVerified() { if (appletState != deployment) { respond SW_WRONG_STATE; } switch (pinState) { case verified: { respondok; } case notVerified(triesLeft): { respond {{}, SW1_TRIES_LEFT, triesLeft}; } case blocked: { respond SW_PIN_BLOCKED; } case unset: { // cannot happen -- could be proved using state invariants } } } apdu {CLA, INS_VERIFIED, P1, P2, {}, 0}; command privSignDecrypt(Message msg) { if (appletState != deployment) { respond SW_WRONG_STATE; } if (pinState != verified) { respond SW_SECURITY_NOK; } else { respondok rsa(key,msg); } } apdu {CLA, INS_SIGN_DEC, P1, P2, msg, MSG_SIZE}; const CLA = 0x80; const P1 = 0; const P2 = 0; const INS_PUT_EXP = 0x44; const INS_PUT_MOD = 0x46; const INS_PUT_PIN = 0x22; const INS_CERT_SIZE = 0x3A; const INS_PUT_CERT = 0x38; const INS_PUT_PROP = 0x58; const INS_UNBLOCK = 0x24; const INS_DEPLOY = 0x70; const INS_UPDATE = 0x72; const INS_GET_CERT = 0x36; const INS_GET_PROP = 0x56; const INS_VERIFY = 0x20; const INS_VERIFIED = 0x26; const INS_SIGN_DEC = 0x42; const SW_WRONG_STATE = 0x6985; const SW_CERTIFICATE_TOO_LARGE = 0x6A80; const SW_UNFINISHED_PERSONALIZATION = 0x6985; const SW_PIN_BLOCKED = 0x6983; const SW_SECURITY_NOK = 0x6982; const SW_CERT_SIZE_UNSET = 0x6985; const SW1_WRONG_PIN = 0x63; const SW1_TRIES_LEFT = 0x63; const SW1_MORE_DATA = 0x63; select { certBytesSent = 0; // reset } deselect { if (pinState == verified) { pinState = notVerified(MAX_TRIES); } } SmartSlang spec

  38. package pkg; import javacard.framework.*; import javacard.security.*; import javacardx.crypto.*; class A extends Applet { static final short KEY_SIZE = 1024; static final short MSG_SIZE = 128; static final byte PIN_SIZE = 8; static final byte MAX_TRIES = 3; static final short MAX_CERT_SIZE = 1000; static final byte MAX_CERT_CHUNK_SIZE = 100; static final short MAX_PROP_SIZE = 200; static final short SW_WRONG_STATE = 0x6985; static final byte CLA = (byte)0x80; static final byte INS_PUT_EXP = 0x44; static final byte P1 = 0; static final byte P2 = 0; static final byte INS_PUT_MOD = 0x46; static final byte INS_PUT_PIN = 0x22; static final byte INS_CERT_SIZE = 0x3a; static final short SW_CERT_SIZE_UNSET = 0x6985; static final short SW_CERTIFICATE_TOO_LARGE = 0x6a80; static final byte INS_PUT_CERT = 0x38; static final byte INS_PUT_PROP = 0x58; static final byte INS_UNBLOCK = 0x24; static final short SW_UNFINISHED_PERSONALIZATION = 0x6985; static final byte INS_DEPLOY = 0x70; static final byte INS_UPDATE = 0x72; static final byte SW1_MORE_DATA = 0x63; static final byte INS_GET_CERT = 0x36; static final byte INS_GET_PROP = 0x56; static final byte SW1_WRONG_PIN = 0x63; static final short SW_PIN_BLOCKED = 0x6983; static final byte INS_VERIFY = 0x20; static final byte SW1_TRIES_LEFT = 0x63; static final byte INS_VERIFIED = 0x26; static final short SW_SECURITY_NOK = 0x6982; static final byte INS_SIGN_DEC = 0x42; RSAPrivateKey key; KeyState keyState; byte[] pin; PinState pinState; short certSize; ByteVector certificate; ByteVector properties; short certBytesSent; byte appletState; static byte[] exp; static byte[] mod; static byte[] p; static ByteVector certChunk; static ByteVector prop; static byte[] msg; static byte[] _aux1; static byte[] _aux2; static ByteVector chunk; byte[] inData; short inDataLength; // always <= 255 static Cipher rsaCipher; private A (byte[] initpar, short off, byte len) { key = (RSAPrivateKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, (short)1024, false); keyState = new KeyState(); pin = A.byteArrayNew((short)8); pinState = new PinState(); certificate = new ByteVector((short)0,(short)1000); properties = new ByteVector((short)0,(short)200); chunk = new ByteVector((short)1,(short)100); _aux2 = A.byteArrayNew((short)128); _aux1 = A.byteArrayNew((short)128); msg = A.byteArrayNew((short)128); prop = new ByteVector((short)0,(short)200); certChunk = new ByteVector((short)1,(short)100); p = A.byteArrayNew((short)8); mod = A.byteArrayNew((short)128); exp = A.byteArrayNew((short)128); inData = new byte[255]; // command data is 255 bytes max rsaCipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD,false); A.repeat((byte)0, (short)128, _aux1, (short)0); A.repeat((byte)0, (short)128, _aux2, (short)0); A.makeRSAPrivateKey((short)1024, _aux1, _aux2, key); keyState.isExponentSet = false; keyState.isModulusSet = false; A.repeat((byte)0, (short)8, pin, (short)0); pinState.elem = PinState.unset; certSize = 0; certificate.setSize((short)0); properties.setSize((short)0); certBytesSent = 0; appletState = AppletState.personalization; register(initpar, (short)(off + 1), initpar[off]); } public boolean select () { certBytesSent = 0; return true; } public void deselect () { if (pinState.elem == PinState.verified) { pinState.elem = PinState.notVerified; pinState.notVerified_arg = MAX_TRIES; } } public void process (APDU apdu) { if (selectingApplet()) return; byte[] buffer = apdu.getBuffer(); if (buffer[ISO7816.OFFSET_CLA] != CLA) ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); switch (buffer[ISO7816.OFFSET_INS]) { case INS_PUT_EXP: putExponent(apdu); return; case INS_PUT_MOD: putModulus(apdu); return; case INS_PUT_PIN: putPIN(apdu); return; case INS_CERT_SIZE: putCertificateSize(apdu); return; case INS_PUT_CERT: putCertificateChunk(apdu); return; case INS_PUT_PROP: putProperties(apdu); return; case INS_UNBLOCK: unblockPIN(apdu); return; case INS_DEPLOY: deploy(apdu); return; case INS_UPDATE: update(apdu); return; case INS_GET_CERT: getCertificateChunk(apdu); return; case INS_GET_PROP: getProperties(apdu); return; case INS_VERIFY: pinVerify(apdu); return; case INS_VERIFIED: pinVerified(apdu); return; case INS_SIGN_DEC: privSignDecrypt(apdu); return; default: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } } void putExponent (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != P1) || (buffer[ISO7816.OFFSET_P2] != P2)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = A.nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 128) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); Util.arrayCopy(inData, (short)0, exp, (short)0, inDataLength); if (appletState == AppletState.deployment) ISOException.throwIt(SW_WRONG_STATE); A.getRSAModulus(key, _aux1); A.makeRSAPrivateKey((short)1024, exp, _aux1, key); keyState.isExponentSet = true; keyState.isModulusSet = keyState.isModulusSet; } void putModulus (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != P1) || (buffer[ISO7816.OFFSET_P2] != P2)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = A.nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 128) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); Util.arrayCopy(inData, (short)0, mod, (short)0, inDataLength); if (appletState == AppletState.deployment) ISOException.throwIt(SW_WRONG_STATE); A.getRSAExponent(key, _aux1); A.makeRSAPrivateKey((short)1024, _aux1, mod, key); keyState.isExponentSet = keyState.isExponentSet; keyState.isModulusSet = true; } void putPIN (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != P1) || (buffer[ISO7816.OFFSET_P2] != P2)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = A.nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 8) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); Util.arrayCopy(inData, (short)0, p, (short)0, inDataLength); if (appletState == AppletState.deployment) ISOException.throwIt(SW_WRONG_STATE); A.assign(p, pin, (short)0); pinState.elem = PinState.notVerified; pinState.notVerified_arg = MAX_TRIES; } void putCertificateSize (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != P1) || (buffer[ISO7816.OFFSET_P2] != P2)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = A.nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 2) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); short size = Util.makeShort(inData[0], inData[1]); if ((size < 1) || (size > 1000)) ISOException.throwIt(ISO7816.SW_WRONG_DATA); if (appletState == AppletState.deployment) ISOException.throwIt(SW_WRONG_STATE); certSize = size; certificate.setSize((short)0); } void putCertificateChunk (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != P1) || (buffer[ISO7816.OFFSET_P2] != P2)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = A.nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if ((inDataLength < 1) || (inDataLength > 100)) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); Util.arrayCopy(inData, (short)0, certChunk.data, (short)0, inDataLength); if (appletState == AppletState.deployment) ISOException.throwIt(SW_WRONG_STATE); if (certSize == 0) ISOException.throwIt(SW_CERT_SIZE_UNSET); if ((short)(certificate.size() + certChunk.size()) > certSize) ISOException.throwIt(SW_CERTIFICATE_TOO_LARGE); A.concat(certificate, certChunk, certificate, (short)0, false); } void putProperties (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != P1) || (buffer[ISO7816.OFFSET_P2] != P2)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = A.nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if ((inDataLength < 0) || (inDataLength > 200)) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); Util.arrayCopy(inData, (short)0, prop.data, (short)0, inDataLength); if (appletState == AppletState.deployment) ISOException.throwIt(SW_WRONG_STATE); A.assign(prop, properties, (short)0, false); } void unblockPIN (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != P1) || (buffer[ISO7816.OFFSET_P2] != P2)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); if (appletState != AppletState.updating) ISOException.throwIt(SW_WRONG_STATE); pinState.elem = PinState.notVerified; pinState.notVerified_arg = MAX_TRIES; } void deploy (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != P1) || (buffer[ISO7816.OFFSET_P2] != P2)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); if (appletState == AppletState.deployment) ISOException.throwIt(SW_WRONG_STATE); if ((((((keyState.isExponentSet == true) && (keyState.isModulusSet == true)) && (pinState.elem != PinState.unset)) && (certSize != 0)) && (certificate.size() == certSize)) && (properties.size() != 0)) appletState = AppletState.deployment; else ISOException.throwIt(SW_UNFINISHED_PERSONALIZATION); } void update (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != P1) || (buffer[ISO7816.OFFSET_P2] != P2)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); if (appletState != AppletState.deployment) ISOException.throwIt(SW_WRONG_STATE); appletState = AppletState.updating; } void getCertificateChunk (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != P1) || (buffer[ISO7816.OFFSET_P2] != P2)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); short requiredLe = MAX_CERT_CHUNK_SIZE; short receivedLe = A.nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (receivedLe == 0) receivedLe = 256; if (receivedLe != requiredLe) { short swLow = (short)(requiredLe & 0xff); ISOException.throwIt((short)(ISO7816.SW_CORRECT_LENGTH_00 + swLow)); } if (appletState != AppletState.deployment) ISOException.throwIt(SW_WRONG_STATE); if ((short)(certificate.size() - certBytesSent) >= MAX_CERT_CHUNK_SIZE) { A.subarrayFromLength(certificate, certBytesSent, MAX_CERT_CHUNK_SIZE, chunk, (short)0, false); certBytesSent = (short)(certBytesSent + MAX_CERT_CHUNK_SIZE); } else { A.subarrayFromLength(certificate, certBytesSent, (short)(((certificate.size() - 1) - certBytesSent) + 1), chunk, (short)0, false); certBytesSent = certificate.size(); } if ((short)(certificate.size() - certBytesSent) >= MAX_CERT_CHUNK_SIZE) { sendOutgoingData(apdu, chunk.data, chunk.len); ISOException.throwIt(Util.makeShort(SW1_MORE_DATA, MAX_CERT_CHUNK_SIZE)); } else if ((short)(certificate.size() - certBytesSent) > 0) { sendOutgoingData(apdu, chunk.data, chunk.len); ISOException.throwIt(Util.makeShort(SW1_MORE_DATA, (byte)(certificate.size() - certBytesSent))); } else { certBytesSent = 0; sendOutgoingData(apdu, chunk.data, chunk.len); return; } } void getProperties (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != P1) || (buffer[ISO7816.OFFSET_P2] != P2)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); short requiredLe = properties.size(); short receivedLe = A.nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (receivedLe == 0) receivedLe = 256; if (receivedLe != requiredLe) { short swLow = (short)(requiredLe & 0xff); ISOException.throwIt((short)(ISO7816.SW_CORRECT_LENGTH_00 + swLow)); } if (appletState != AppletState.deployment) ISOException.throwIt(SW_WRONG_STATE); sendOutgoingData(apdu, properties.data, properties.len); return; } void pinVerify (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != P1) || (buffer[ISO7816.OFFSET_P2] != P2)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = A.nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 8) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); Util.arrayCopy(inData, (short)0, p, (short)0, inDataLength); if (appletState != AppletState.deployment) ISOException.throwIt(SW_WRONG_STATE); switch (pinState.elem) { case PinState.verified: return; case PinState.notVerified: byte triesLeft = pinState.notVerified_arg; if (A.equals(p, pin)) { pinState.elem = PinState.verified; return; } else if (triesLeft > 1) { pinState.elem = PinState.notVerified; pinState.notVerified_arg = (byte)(triesLeft - 1); ISOException.throwIt(Util.makeShort(SW1_WRONG_PIN, triesLeft)); } else { pinState.elem = PinState.blocked; ISOException.throwIt(SW_PIN_BLOCKED); } case PinState.blocked: ISOException.throwIt(SW_PIN_BLOCKED); case PinState.unset: } } void pinVerified (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != P1) || (buffer[ISO7816.OFFSET_P2] != P2)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); if (appletState != AppletState.deployment) ISOException.throwIt(SW_WRONG_STATE); switch (pinState.elem) { case PinState.verified: return; case PinState.notVerified: byte triesLeft = pinState.notVerified_arg; ISOException.throwIt(Util.makeShort(SW1_TRIES_LEFT, triesLeft)); case PinState.blocked: ISOException.throwIt(SW_PIN_BLOCKED); case PinState.unset: } } void privSignDecrypt (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != P1) || (buffer[ISO7816.OFFSET_P2] != P2)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = A.nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 128) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); Util.arrayCopy(inData, (short)0, msg, (short)0, inDataLength); if (appletState != AppletState.deployment) ISOException.throwIt(SW_WRONG_STATE); if (pinState.elem != PinState.verified) ISOException.throwIt(SW_SECURITY_NOK); else { A.rsa(key, msg, _aux1); sendOutgoingData(apdu, _aux1); return; } } static void assign(ByteVector source, ByteVector target, short offset, boolean setSize) { short len = source.size(); if (setSize) target.setSize(len); Util.arrayCopy(source.data,(short)0,target.data,offset,len); } static void concat(ByteVector vec1, ByteVector vec2, ByteVector result, short offset, boolean setSize) { short len1 = vec1.size(); short len2 = vec2.size(); if (setSize) result.setSize((short)(len1+len2)); Util.arrayCopy(vec2.data,(short)0,result.data,(short)(len1+offset),len2); if (vec1.data != result.data) Util.arrayCopy(vec1.data,(short)0,result.data,offset,len1); } static void subarrayFromLength(ByteVector vec, short i, short n, ByteVector result, short offset, boolean setSize) { if (setSize) result.setSize(n); Util.arrayCopy(vec.data,i,result.data,offset,n); } static void getRSAExponent(RSAPrivateKey key, byte[] result) { key.getExponent(result,(short)0); } static void getRSAModulus(RSAPrivateKey key, byte[] result) { key.getModulus(result,(short)0); } static void assign(byte[] source, byte[] target, short offset) { short len = (short) source.length; Util.arrayCopy(source,(short)0,target,offset,len); } static boolean equals(byte[] arr1, byte[] arr2) { short len = (short) arr1.length; if (arr2.length != len) return false; for (short i = 0; i < len; i++) if (arr1[i] != arr2[i]) return false; return true; } static byte[] byteArrayNew(short size) { return new byte[size]; } static void repeat(byte elem, short n, byte[] result, short offset) { Util.arrayFillNonAtomic(result,offset,n,elem); } static void makeRSAPrivateKey (short len, byte[] exponent, byte[] modulus, RSAPrivateKey result) { result.setExponent(exponent,(short)0,(short)len); result.setModulus(modulus,(short)0,(short)len); } static short nonNegativeByte(byte b) { return (b < 0 ? (short)(b + 256) : b); } void receiveIncomingData(APDU apdu) { byte[] buffer = apdu.getBuffer(); short totalRead = 0; short chunkRead = apdu.setIncomingAndReceive(); do { // error if too little or too much data: if (chunkRead == 0 || (short) (totalRead + chunkRead) > inDataLength) ISOException.throwIt(ISO7816.SW_WRONG_DATA); // transfer from APDU buffer to inData array: Util.arrayCopy(buffer,ISO7816.OFFSET_CDATA, inData,totalRead,chunkRead); // increment counter: totalRead += chunkRead; // get more data if necessary: if (totalRead != inDataLength) chunkRead = apdu.receiveBytes(ISO7816.OFFSET_CDATA); } while (totalRead < inDataLength); } static void rsa(Key key, byte[] data, byte[] result) { short sizeInBytes = (short) (key.getSize() / 8); rsaCipher.init(key,Cipher.MODE_ENCRYPT); rsaCipher.doFinal(data,(short)0,sizeInBytes,result,(short)0); } void sendOutgoingData(APDU apdu, byte[] data) { sendOutgoingData(apdu, data, (short)data.length); } void sendOutgoingData(APDU apdu, byte[] data, short len) { apdu.setOutgoing(); apdu.setOutgoingLength(len); apdu.sendBytesLong(data,(short)0,len); } public static void install(byte[] initpar, short off, byte len) throws ISOException { new A(initpar,off,len); } } class KeyState { boolean isExponentSet; boolean isModulusSet; KeyState () { } } class PinState { static final byte unset = 0; static final byte notVerified = 1; static final byte verified = 2; static final byte blocked = 3; byte elem; byte notVerified_arg = 1; PinState () { } } class AppletState { static final byte personalization = 0; static final byte deployment = 1; static final byte updating = 2; } class VectorBase { short len; void setSize(short newSize) { len = newSize; } short size() { return len; } } class ByteVector extends VectorBase { byte[] data; ByteVector(short minLen, short maxLen) { data = A.byteArrayNew(maxLen); setSize(minLen); } } PKI example ~ 240 lines ~ 700lines size(code) const KEY_SIZE = 1024; // in bits type KeyState = rec {Bool isExponentSet, Bool isModulusSet}; const MSG_SIZE = KEY_SIZE / 8; // in bytes type Message = Byte[MSG_SIZE]; const PIN_SIZE = 8; type Pin = Byte[PIN_SIZE]; const MAX_TRIES = 3; type PinState = enum {unset, notVerified(Int(1,MAX_TRIES)), verified, blocked}; const MAX_CERT_SIZE = 1000; // in bytes type Certificate = Byte[0..MAX_CERT_SIZE]; const MAX_CERT_CHUNK_SIZE = 100; // in bytes type CertificateChunk = Byte[1..MAX_CERT_CHUNK_SIZE]; const MAX_PROP_SIZE = 200; // in bytes type Properties = Byte[0..MAX_PROP_SIZE]; type AppletState = enum {personalization, deployment, updating}; state { RSAPrivateKey(KEY_SIZE) key, KeyState keyState, Pin pin, PinState pinState, Int(0,MAX_CERT_SIZE) certSize, Certificate certificate, Properties properties, Int(0,MAX_CERT_SIZE) certBytesSent, AppletState appletState } invariant appletState != deployment || (certBytesSent <= (certificate).length-1); init() { /* The current definition of SmartSlang requires every state component to be initialized in the initialization block. This is a bit artificial for certain state components that are really initialized by commands, e.g. keys, forcing us to use meaningless values like all 0s. We may change the definition of SmartSlang to no longer require all state components to be initialized in the initialization block, while leaving the requirement that every state component must be initialized before it is used for the first time. Besides avoiding artificial initializations, this change would support better semantic checking of the SmartSlang spec, because presumably a key shouldn't be used before it is assigned a non-all-0s value. */ key = RSAPrivateKey(KEY_SIZE) (repeat(0,KEY_SIZE/8), repeat(0,KEY_SIZE/8)); keyState = KeyState(false,false); pin = repeat(0,8); pinState = unset; certSize = 0; certificate = {}; properties = {}; certBytesSent = 0; appletState = personalization; } bytes {}; command putExponent(Byte[KEY_SIZE/8] exp) { if (appletState == deployment) { respond SW_WRONG_STATE; } key = RSAPrivateKey(KEY_SIZE) (exp, key.modulus); keyState = KeyState (true, keyState.isModulusSet); // we may extend SmartSlang with direct assignments to record components: // key.exponent = exp; // keyState.isExponentSet =true; } apdu {CLA, INS_PUT_EXP, P1, P2, exp, 0}; command putModulus(Byte[KEY_SIZE/8] mod) { if (appletState == deployment) { respond SW_WRONG_STATE; } key = RSAPrivateKey(KEY_SIZE) (key.exponent, mod); keyState = KeyState (keyState.isExponentSet, true); } apdu {CLA, INS_PUT_MOD, P1, P2, mod, 0}; command putPIN(Pin p) { if (appletState == deployment) { respond SW_WRONG_STATE; } pin = p; pinState = notVerified(MAX_TRIES); } apdu {CLA, INS_PUT_PIN, P1, P2, p, 0}; command putCertificateSize(Int(1,MAX_CERT_SIZE) size) { if (appletState == deployment) { respond SW_WRONG_STATE; } certSize = size; certificate = {}; } apdu {CLA, INS_CERT_SIZE, P1, P2, ubytes[2](size), 0}; command putCertificateChunk(CertificateChunk certChunk) { if (appletState == deployment) { respond SW_WRONG_STATE; } if (certSize == 0) { respond SW_CERT_SIZE_UNSET; } if (certificate.length + certChunk.length > certSize) { respond SW_CERTIFICATE_TOO_LARGE; } certificate = certificate ++ certChunk; } apdu {CLA, INS_PUT_CERT, P1, P2, certChunk, 0}; command putProperties(Properties prop) { if (appletState == deployment) { respond SW_WRONG_STATE; } properties = prop; } apdu {CLA, INS_PUT_PROP, P1, P2, prop, 0}; command unblockPIN() { if (appletState != updating) { respond SW_WRONG_STATE; } pinState = notVerified(MAX_TRIES); } apdu {CLA, INS_UNBLOCK, P1, P2, {}, 0}; command deploy() { if (appletState == deployment) { respond SW_WRONG_STATE; } if (keyState == KeyState(true,true) && pinState != unset && certSize != 0 && certificate.length == certSize && properties != {}) { appletState = deployment; } else { respond SW_UNFINISHED_PERSONALIZATION; } } apdu {CLA, INS_DEPLOY, P1, P2, {}, 0}; command update() { if (appletState != deployment) { respond SW_WRONG_STATE; } appletState = updating; } apdu {CLA, INS_UPDATE, P1, P2, {}, 0}; command getCertificateChunk() { if (appletState != deployment) { respond SW_WRONG_STATE; } CertificateChunk chunk; if (certificate.length - certBytesSent >= MAX_CERT_CHUNK_SIZE) { chunk = certificate[certBytesSent,MAX_CERT_CHUNK_SIZE]; certBytesSent = certBytesSent + MAX_CERT_CHUNK_SIZE; } else { chunk = certificate[certBytesSent..certificate.length-1]; certBytesSent = certificate.length; } if (certificate.length - certBytesSent >= MAX_CERT_CHUNK_SIZE) { respond {chunk, SW1_MORE_DATA, MAX_CERT_CHUNK_SIZE}; } else if (certificate.length - certBytesSent > 0) { respond {chunk, SW1_MORE_DATA, certificate.length - certBytesSent}; } else /* certificate.length == certBytesSent */ { certBytesSent = 0; // reset respondok chunk; } } apdu {CLA, INS_GET_CERT, P1, P2, {}, (certificate.length - certBytesSent >= MAX_CERT_CHUNK_SIZE) ? MAX_CERT_CHUNK_SIZE : certificate.length - certBytesSent}; command getProperties() { if (appletState != deployment) { respond SW_WRONG_STATE; } respondok properties; } apdu {CLA, INS_GET_PROP, P1, P2, {}, properties.length}; command pinVerify(Pin p) { if (appletState != deployment) { respond SW_WRONG_STATE; } switch (pinState) { case verified: { respondok; } case notVerified(triesLeft): { if (p == pin) { pinState = verified; respondok; } else if (triesLeft > 1) { pinState = notVerified(triesLeft - 1); respond {{}, SW1_WRONG_PIN, triesLeft}; } else { pinState = blocked; respond SW_PIN_BLOCKED; } } case blocked: { respond SW_PIN_BLOCKED; } case unset: { // cannot happen -- could be proved using state invariants } } } apdu {CLA, INS_VERIFY, P1, P2, p, 0}; command pinVerified() { if (appletState != deployment) { respond SW_WRONG_STATE; } switch (pinState) { case verified: { respondok; } case notVerified(triesLeft): { respond {{}, SW1_TRIES_LEFT, triesLeft}; } case blocked: { respond SW_PIN_BLOCKED; } case unset: { // cannot happen -- could be proved using state invariants } } } apdu {CLA, INS_VERIFIED, P1, P2, {}, 0}; command privSignDecrypt(Message msg) { if (appletState != deployment) { respond SW_WRONG_STATE; } if (pinState != verified) { respond SW_SECURITY_NOK; } else { respondok rsa(key,msg); } } apdu {CLA, INS_SIGN_DEC, P1, P2, msg, MSG_SIZE}; const CLA = 0x80; const P1 = 0; const P2 = 0; const INS_PUT_EXP = 0x44; const INS_PUT_MOD = 0x46; const INS_PUT_PIN = 0x22; const INS_CERT_SIZE = 0x3A; const INS_PUT_CERT = 0x38; const INS_PUT_PROP = 0x58; const INS_UNBLOCK = 0x24; const INS_DEPLOY = 0x70; const INS_UPDATE = 0x72; const INS_GET_CERT = 0x36; const INS_GET_PROP = 0x56; const INS_VERIFY = 0x20; const INS_VERIFIED = 0x26; const INS_SIGN_DEC = 0x42; const SW_WRONG_STATE = 0x6985; const SW_CERTIFICATE_TOO_LARGE = 0x6A80; const SW_UNFINISHED_PERSONALIZATION = 0x6985; const SW_PIN_BLOCKED = 0x6983; const SW_SECURITY_NOK = 0x6982; const SW_CERT_SIZE_UNSET = 0x6985; const SW1_WRONG_PIN = 0x63; const SW1_TRIES_LEFT = 0x63; const SW1_MORE_DATA = 0x63; select { certBytesSent = 0; // reset } deselect { if (pinState == verified) { pinState = notVerified(MAX_TRIES); } } = ~ 3 size(spec) AUTOSMART JavaCardcode SmartSlang spec (actual files; font size 1)

  39. PKI PKI Java Card code ... static byte[] _aux1; static Cipher rsaCipher; ... rsaCipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD,false); _aux1 = new byte[(short)128]; ... void privSignDecrypt (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != 0) || (buffer[ISO7816.OFFSET_P2] != 0)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 128) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); Util.arrayCopy(inData,(short)0,msg,(short)0,inDataLength); if (pinState.elem != PinState.verified) ISOException.throwIt(SW_SECURITY_NOK); else { rsaCipher.init(key,Cipher.MODE_DECRYPT); rsaCipher.doFinal(msg,(short)0,(short)128,_aux1,(short)0); sendOutgoingData(apdu, _aux1); return; } } ... ... static byte[] _aux1; static Cipher rsaCipher; ... rsaCipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD,false); _aux1 = new byte[(short)128]; ... void privSignDecrypt (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != 0) || (buffer[ISO7816.OFFSET_P2] != 0)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 128) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); Util.arrayCopy(inData,(short)0,msg,(short)0,inDataLength); if (pinState.elem != PinState.verified) ISOException.throwIt(SW_SECURITY_NOK); else { rsaCipher.init(key,Cipher.MODE_DECRYPT); rsaCipher.doFinal(msg,(short)0,(short)128,_aux1,(short)0); sendOutgoingData(apdu, _aux1); return; } } ... Java Card code

  40. PKI Java Card code ... static byte[] _aux1; static Cipher rsaCipher; ... rsaCipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD,false); _aux1 = new byte[(short)128]; ... void privSignDecrypt (APDU apdu) { byte[] buffer = apdu.getBuffer(); if ((buffer[ISO7816.OFFSET_P1] != 0) || (buffer[ISO7816.OFFSET_P2] != 0)) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); inDataLength = nonNegativeByte(buffer[ISO7816.OFFSET_LC]); if (inDataLength != 128) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveIncomingData(apdu); Util.arrayCopy(inData,(short)0,msg,(short)0,inDataLength); if (pinState.elem != PinState.verified) ISOException.throwIt(SW_SECURITY_NOK); else { rsaCipher.init(key,Cipher.MODE_DECRYPT); rsaCipher.doFinal(msg,(short)0,(short)128,_aux1,(short)0); sendOutgoingData(apdu, _aux1); return; } } ... intermediate storage pre-allocation APDUchecking& decoding arrays accessed withinbounds by construction interestingcomputation code for “respondok decrypt(key,msg)”

  41. SmartSlang counterpart command privSignDecrypt(Message msg) { if (pinState != verified) { respond SW_SECURITY_NOK; } else { respondok decrypt(key,msg); } } apdu {0x80,0x42,0,0,msg,1024/8};

  42. SmartSlang example: PIN construct SmartSlang

  43. SmartSlang example: PIN construct (under design) identifier (there may be multiple PINs) pin p { length 8; maxtries 3; protect {privSignDecrypt, getCertificate}; apdu verify {0x80,0x20,0,0}; apdu set {0x80,0x22,0,0} secure; } in bytes (determines strength) before blocking commands that requirePIN to be verified first APDU header for command to verify PIN (data = PIN) APDU header for command to (re)set PIN (data = PIN) use secure channel implicit state variables, commands,and checks in other commands (tentative syntax)

  44. SmartSlang example: PIN construct (under design) code = ~7–8 spec pin p { length 8; maxtries 3; protect {privSignDecrypt, getCertificate}; apdu verify {0x80,0x20,0,0}; apdu set {0x80,0x22,0,0} secure; } pin p { length 8; maxtries 3; protect {privSignDecrypt, getCertificate}; apdu verify {0x80,0x20,0,0}; apdu set {0x80,0x22,0,0} secure; } • similar high-level constructs planned for • challenge-response authentication • piecewise data read/write • key establishment • … estimated 

  45. SmartSlang is SmartSlang just a macro language? is AutoSmart just a macro expander?

  46. is SmartSlang just a macro language? is AutoSmart just a macro expander? NO: • expressive types, statically checked • automated reasoning • automatic static storage management by type analysis • APDU decoding/checking • non-local mapping of spec constructs into code • …

  47. inside AutoSmart AutoSmart applet spec applet code AUTOSMART

  48. SPEC CHECKER CODE GENERATOR inside AutoSmart applet spec applet code AUTOSMART

  49. checked applet spec parses spec parser generated from grammarvia our own parser generator checks type safety includes Fourier-Motzkin decisionprocedure for linear arithmetic checks other properties e.g. restrictions to allow generationof code to check and decode APDUs applet spec SPEC CHECKER

  50. checked applet spec formal relationship CODE GENERATOR applet code provable correctness? SmartSlang semanticsin logic Java Card semanticsin logic SMARTSLANG  LOGIC JAVA CARD  LOGIC SmartSlang applet representedin logic Java Card applet representedin logic provable correctness

More Related