Xavier Leroy
Presentation(day 1)
-Nithya
JavaCard Architecture
Sandbox model
Sun’s Bytecode verification
Our Verification Algorithm
Performance Analysis of both
Differences
Stack based abstract machine
-
Instructions pop their arguments off the stack
- Push their results on the stack
Set of registers (local variables) can be accessed via “load” and “store”
- push the value of a register on the stack
- store the top of the stack in the register
Stack + register -> activation record for a method
:
- Stack initially contains at least 2 elements of type int and it pushes back result of int
:
- Top of the stack contains a reference to an instance of class C
- it then pops it and pushes back a value of type
τ
(the value of the field f)
Type Correctness
No stack overflow or underflow
Code containment
Register initialization
Object initialization
What are Smart Cards?
Security tokens - applications
Traditional Smart Cards
- C or Assembler
Challenged by Open Architectures
- Multos, Java Card
Applications written in Java and so are portable across all Java cards
Java Cards can run multiple applications which can communicate thru shared objects
New applications called applets can be downloaded on the card post issuance
Leaking confidential information outside
(eg: PINS and secret cryptographic keys)
Modifying sensitive information (eg: balance of an electonic purse)
Interfering with other honest application on the card (Trojan Attack)
Put forward by Java Programming
Environment
Execute the applets in a so called
“Sandbox”
Its an insulation layer preventing direct access to the hardware resources
Also implements a suitable access control policy
Applets --> Bytecode
VM manipulates secure abstractions of data than hardware processor
No direct access to hardware resources but to a set of API classes
Upon Downloading, applet subject to static analysis called Bytecode
Verification
1.
2.
3.
4.
To check ->code is well typed
Doesn’t bypass protections 1 & 2 by performing ill typed operations at runtime:
Forging object references from integers
Illegal casting of obj ref from one class to another
Calling private methods of API
Jumping in middle of API method or jumping to data as if it were code
Component 1: Applets executed by JVM
Component 2: Java Card runtime environment provides access control through its “firewall”
Component 3: Bytecode Verifier missing!
-
complex and expensive process requires large amounts of working memory
Relying on off-card tools
Attesting a cryptographic signature on well-typedness of the applet
Oncard downloading restricted to signed applets
Drawbacks:
- to extend the trusted computing base to off card components
- practical issues: (how to deploy the signature keys?)
- legal issues: (who takes liability for a buggy applet?)
To type-check dynamically during applet execution
VM computes bytecode instructions
Keeps track of all data it manipulates
Arguments- correct types?stack overflow or underflow?
Are class member accesses allowed?
Drawback:
- Dynamic Type-checks expensive in terms of execution speed and memory
For Straight line code?
-
Verifier checks every instruction of the method code
- Checks whether stack has enough entries
- Checks whether the entries are of correct types
- Effect of the instruction on Operand stack and registers
Intialisation:
-
Stack ->empty
-
Reg (0…n-1) -> method parameters
-
Reg(n…m-1)-> T (uninitialised reg)
Method invocations ?
For Branches- Forks and Joins
Forks?
must propagate inferred stack & reg type to all successors
Joins?
makes sure the types of the stack and registers along all paths agree
A data structure associating a stack and register type to each program point that is the target of a branch or exception handler
During analysis, Updates ->type associated with target of branch
Replacing by -> LUB(prev type,type inferred for the instruction)
If changed -> the corr.instrn & successors are reanalysed until a fixpoint is reached
This way, the dictionary entry for a branch target contains LUB inferred on all branches of control that lead to this instrn .
1.
1.
Stack heights may differ
An instruction can be reached through several paths with inconsistent operand stacks
Types for a particular stack entry or register may be incompatible eg: branch 1: short branch 2 : Obj Ref
Type set to T in dictionary
Property: Every pair of types possesses a smallest common supertype (LUB)
Does not hold ->
Interfaces are types and classes can implement several interfaces
interface I { ... } interface J { ... } class C1 implements I, J { ... } class C2 implements I, J { ... }
Bytecode verification ignores interfaces
Treats all interface types as class type
“Object”
Verifier contains proper classes and subtyping between them is simply inheritance relation
Subtyping relation is tree shaped
->forms a semilattice.
LUB of 2 classes ->closest common ancestor in inheritance tree
Straight line Code:
Time: as fast as executing instruction
Space: 1 stack and 1 set of reg types
(if each type-3 bytes, then memory=3S+3N bytes)
Similar for method invocation:
Space for method invocation = space for verification
1.
2.
3.
4.
Branchescostly since instructions are analyzed several times to reach fixpoint
Real issue: Memory space for storing dictionary
If B ->no of branch targets, memory =(3S+3N+3)*B bytes [3 bytes of overhead for each Dic. Entry]
Drawbacks:
No. of branch targets proportional to method size -> dictionary size increases linearly
Dictionary entries quite often redundant
Merging several methods into a larger one result in nonverifiable code
Storing the dictionary in EEPROM not an option.
Part II – Novel Bytecode
Verification Algorithm
Operand stack is empty at the beginning and end of every statement
JCVM for e
1
? e
2
: e
3 : code to evaluate e 1 ifeq lbl1 code to evaluate e 2 goto lbl2 lbl1: code to evaluate e 3
Lbl2: …
(Branch to lbl2 occurs with a non-empty operand stack)
In Java source, a local variable has only one type throughout the method:
In JCVM bytecode, register with type T acquires the type
at 1 st store in reg.
For Java code,
A x; if (cond) x = new B(); // B is a subclass of A else x = new C(); // C is another subclass of A
Register x has type A (ie,LUB(B,C)) when two arms merge
{ short x; ... }
{ C y; ... }
Scopes of x & y disjoint ->store x & y in same register
R1: Operand stack is empty at all branch and branch target instructions
R2: Each register has only one type throughout the method code
R3: On method entry,the VM initialises all registers that are not parameters to the bit pattern representing null object reference
Global variables:
Nr,Ns,r[Nr],s[Ns],sp,chg
Initialisation:
Set sp <- 0
Set r [0],…, r[n - 1] to the types of the method arguments
Set r[n ],…, r[Nr - 1] to
┴
Set chg <- true
While chg:
Set chg -> false
For each instruction i of the method, in code order:
If i is the target of a branch instruction:
If sp != 0 and the previous instruction falls through, error
Set sp -> 0
If i is the target of a JSR instruction:
If the previous instruction falls through, error
Set s[0] -> retaddr and sp -> 1
If i is a handler for exceptions of class C:
If the previous instruction falls through, error
Set s[0] -> C and sp -> 1
If two or more of the cases above apply, error
Determine the types a 1,…, an of the arguments of i
If sp < n, error (stack underflow)
For k = 1,…,n: If s[sp - n + k - 1] is not subtype of ak, error
Set sp -> sp - n
Determine the types r 1,…, rm of the results of i
If sp + m > Ns, error (stack overflow)
For k = 1,…,m: Set s[sp + k - 1] -> rk
Set sp -> sp + m
If i is a store to register number k:
Determine the type t of the value written to the register
Set r[k] -> lub(T, r[k])
If r[k] changed, set chg -> true
If i is a branch instruction and sp ! = 0, error
Stack empty?
When checking Store,reg type ->LUB
Typechecking until a fixpoint
Initialisation:
Stack -> Empty reg(0..n-1) -> method parameter types reg(n..m-1)-> ┴ & not T (REQ R
3
)
Subroutines
Object Initialisation
Off card code transformations
Bytecode Transformation on small computers