Online documentation (Javadoc)
...
Table of Contents | ||||||
---|---|---|---|---|---|---|
|
Basic transaction
A basic OLA transaction includes the following steps:
OLA classes instantiation (once at startup)
Code Block |
---|
private val finalola: Ola ola;private val contact: OlaContact private val contactless: OlaContactless private val emv: OlaEmv private val dev: Dev private val publicKey: OlaPublicKey ... init { ola = Ola.getInstance() contact = OlaContact.getInstance() contactless = OlaContactless.getInstance() emv = OlaEmv.getInstance() publicKey = OlaPublicKey() ... } |
OLA component initialisation (once at startup)
Code Block |
---|
/** * Configures OLA layer (key files paths, trace, etc...) */ private fun olaConfig() { val path = byteArrayOf(0xdf.toByte(), 0x01, 28) val pathValue = private final OlaEmv emv; "/data/data/ca.amadis.tester/" val cakeys = byteArrayOf(0xdf.toByte(), 0x02, 34) val cakeysValue = "/data/data/ca.amadis.tester/CAKeys" val crl = byteArrayOf(0xdf.toByte(), 0x03, 31) val crlValue = "/data/data/ca.amadis.tester/CRL" val efl = byteArrayOf(0xdf.toByte(), 0x04, 31) val eflValue = "/data/data/ca.amadis.tester/EFL" val pcsc = byteArrayOf(0xdf.toByte(), 0x05, 0x00) // pcsc not used val lang = byteArrayOf(0xdf.toByte(), 0x06, 0x00) // lang not used val runtimeParams = byteArrayOf(0xdf.toByte(), 0x07, 0x07, 0x00, 0x0F, 0x04, 0x0F, private final OlaContactless contactless; this.ola = Ola.getInstance(); this.contactless = OlaContactless.getInstance(); this.emv = OlaEmv.getInstance(); |
OLA component initialisation (once at startup)
Code Block |
---|
/** * Configures OLA layer (key files paths, trace, etc...) */ private void olaConfig()0x0A, 0x00, 0x01) val traceLevel = byteArrayOf(0xdf.toByte(), 0x08, 0x01, 0x05) val pollingTechno = byteArrayOf(0xdf.toByte(), 0x10, 0x01, 0x04) // Contactless only var sredMode: ByteArray if (BuildConfig.FLAVOR.lowercase().contains("nosred")) { final byte[] pathsredMode = {byteArrayOf(0xdf.toByte(byte)0xdf, 0x17, 0x01, 28}; 0x00) // SRED mode not active final} Stringelse pathValue{ = "/data/data/ca.amadis.tester/"; final byte[] cakeyssredMode = {byteArrayOf(0xdf.toByte(byte)0xdf, 0x17, 0x020x01, 34}; final String cakeysValue = "/data/data/ca.amadis.tester/CAKeys";0x01) // SRED mode active } final byte[] crl =try {(byte)0xdf, 0x03, 31}; final String val crlValueout = "/data/data/ca.amadis.tester/CRL";ByteArrayOutputStream() final byte[] efl = {out.write(bytepath)0xdf, 0x04, 31}; final String eflValue = "/data/data/ca.amadis.tester/EFL";out.write(pathValue.toByteArray()) out.write(cakeys) final byte[] pcsc = {(byte)0xdf, 0x05, 0x00}; // pcsc not used final byte[] lang = {(byte)0xdf, 0x06, 0x00}; // lang not used out.write(cakeysValue.toByteArray()) out.write(crl) out.write(crlValue.toByteArray()) out.write(efl) final byte[] runtimeParams = {(byte)0xdf, 0x07, 0x07, 0x00, 0x0F, 0x04, 0x0F, 0x0A, 0x00, 0x01}; out.write(eflValue.toByteArray()) out.write(pcsc) final byte[] traceLevel = {(byte)0xdf, 0x08, 0x01, 0x05}; out.write(lang) final byte[] pollingTechno = {(byte)0xdf, 0x10, 0x01, 0x04}; // Contactless only out.write(runtimeParams) out.write(traceLevel) final byte[] sredMode = {out.write(bytepollingTechno)0xdf, 0x17, 0x01, 0x01}; // SRED mode active out.write(sredMode) try { ByteArrayOutputStream out = new ByteArrayOutputStream();ola.initializeAtStartUp(out.toByteArray()) } catch out.write(path); (e: IOException) { } } |
AID configuration
Code Block |
---|
private out.write(pathValue.getBytesfun terminalContactlessConfig()); { outLog.write(cakeys);v("Transaction", "terminalContactlessConfig") out.write(cakeysValue.getBytes());val contactlessCfg = arrayOf( out.write(crl); out.write(crlValue.getBytes());ContactlessCfg(AID.FromBytes(byteArrayOf(0xA0.toByte(), 0x00, 0x00, 0x00, 0x04, 0x10, 0x10)), out.write(efl); out.write(eflValue.getBytes()); true, out.write(pcsc); out.write(lang); out.write(runtimeParams); 2, out.write(traceLevel); out.write(pollingTechno); out.write(sredMode);mcCfg), byte[] tlv = out.toByteArray();ContactlessCfg(AID.FromBytes(byteArrayOf(0xA0.toByte(), 0x00, 0x00, 0x00, 0x04, 0x20, 0x10)), ola.initializeAtStartUp(tlv); } catchtrue, (IOException e) { } } |
AID configuration (once at startup)
Code Block |
---|
private void terminalContactlessConfig() { 2, contactless.flushAIDSupported(); for (OLAConfig.ContactlessCfg config : OLAConfig.contactlessCfg) {mcCfg), contactless.addAIDSupported(...) contactless.flushAIDSupported() config.aid, for (config in contactlessCfg) { contactless.addAIDSupported( config.aid, config.partial, config.kernelId.toByte(), config.additionalData ) } contactless.commitSupportedAIDs() } |
Keys configuration
Code Block |
---|
private fun terminalPublicKeyConfig() { Log.v("Transaction", "terminalPublicKeyConfig") val publicKeyCfg = arrayOf( /* MasterCard */ PublicKeyCfg(byteArrayOf(0xa0.toByte(), 0x00, 0x00, 0x00, 0x03), 0x92.toByte(), byteArrayOf(0x99.toByte(), 0x6a, 0xf5.toByte(), 0x6f, 0x56, 0x91.toByte(), 0x87.toByte(), 0xd0.toByte(), 0x92.toByte(), 0x93.toByte(), 0xc1.toByte(), 0x48, 0x10, 0x45, 0x0e, 0xd8.toByte(), 0xee.toByte(), 0x33, 0x57, 0x39, 0x7b, 0x18, 0xa2.toByte(), 0x45, 0x8e.toByte(), 0xfa.toByte(), 0xa9.toByte(), 0x2d, 0xa3.toByte(), 0xb6.toByte(), 0xdf.toByte(), 0x65, 0x14, 0xec.toByte(), 0x06, 0x01, 0x95.toByte(), 0x31, 0x8f.toByte(), 0xd4.toByte(), 0x3b, 0xe9.toByte(), 0xb8.toByte(), 0xf0.toByte(), 0xcc.toByte(), 0x66, 0x9e.toByte(), 0x3f, 0x84.toByte(), 0x40, 0x57, 0xcb.toByte(), 0xdd.toByte(), 0xf8.toByte(), 0xbd.toByte(), 0xa1.toByte(), 0x91.toByte(), 0xbb.toByte(), 0x64, 0x47, 0x3b, 0xc8.toByte(), 0xdc.toByte(), 0x9a.toByte(), 0x73, 0x0d, 0xb8.toByte(), 0xf6.toByte(), 0xb4.toByte(), 0xed.toByte(), 0xe3.toByte(), 0x92.toByte(), 0x41, 0x86.toByte(), 0xff.toByte(), 0xd9.toByte(), 0xb8.toByte(), 0xc7.toByte(), 0x73, 0x57, 0x89.toByte(), 0xc2.toByte(), 0x3a, 0x36, 0xba.toByte(), 0x0b, 0x8a.toByte(), 0xf6.toByte(), 0x53, 0x72, 0xeb.toByte(), 0x57, 0xea.toByte(), 0x5d, 0x89.toByte(), 0xe7.toByte(), 0xd1.toByte(), 0x4e, 0x9c.toByte(), 0x7b, 0x6b, 0x55, 0x74, 0x60, 0xf1.toByte(), 0x08, 0x85.toByte(), 0xda.toByte(), 0x16, 0xac.toByte(), 0x92.toByte(), 0x3f, 0x15, 0xaf.toByte(), 0x37, 0x58, 0xf0.toByte(), 0xf0.toByte(), 0x3e, 0xbd.toByte(), 0x3c, 0x5c, 0x2c, 0x94.toByte(), 0x9c.toByte(), 0xba.toByte(), 0x30, 0x6d, 0xb4.toByte(), 0x4e, 0x6a, 0x2c, 0x07, 0x6c, 0x5f, 0x67, 0xe2.toByte(), 0x81.toByte(), 0xd7.toByte(), 0xef.toByte(), 0x56, 0x78, 0x5d, 0xc4.toByte(), 0xd7.toByte(), 0x59, 0x45, 0xe4.toByte(), 0x91.toByte(), 0xf0.toByte(), 0x19, 0x18, 0x80.toByte(), 0x0a, 0x9e.toByte(), 0x2d, 0xc6.toByte(), 0x6f, 0x60, 0x08, 0x05, 0x66, 0xce.toByte(), 0x0d, 0xaf.toByte(), 0x8d.toByte(), 0x17, 0xea.toByte(), 0xd4.toByte(), 0x6a, 0xd8.toByte(), 0xe3.toByte(), 0x0a, 0x24, 0x7c, 0x9f.toByte()), config.partial, byteArrayOf(byte0x03)config.kernelId, config.additionalData ); }byteArrayOf(0x24, 0x12, 0x31)), contactless.commitSupportedAIDs(); } |
Keys configuration (once at startup)
Code Block |
---|
private void terminalPublicKeyConfig() { ...) publicKey.flush(); for byte rid[] = {(byte)0xA0, 0x00, 0x00, 0x00, 0x03};(config in publicKeyCfg) { byteval idxmaxReached = SingleObjectHolder(byte)0x92; byte modulus[] = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}; false) val key: PublicKeyData = publicKey.PublicKeyData( byte exponentValue[] = {0x03}; byte expirDate[] = {0x22, 0x12, 0x31}; config.rid, Holders.SingleObjectHolder<Boolean> wasMaxReached_holder = new Holders.SingleObjectHolder<Boolean>(false);config.idx, OlaPublicKey.PublicKeyData key = publicKey.new PublicKeyData(rid, idx, config.modulus, exponentValue, expirDate); publicKey.add(key, null, wasMaxReached_holder); publicKey.commit(); } |
Transaction related data (before each transaction)
Code Block |
---|
private void txnSetTransactionRelatedData() {config.exponentValue, emv.setTag(OlaTag.TRANSACTION_DATE,config.expirDate ) new byte[]{0x20 publicKey.add(key, 0x10null, 0x28}maxReached); } publicKey.commit() } |
Transaction related data ()
Code Block |
---|
private fun txnSetTransactionRelatedData() { Log.v("Transaction", // 9A "txnSetTransactionRelatedData") emv.setTag(OlaTag.TRANSACTION_TIMEDATE, byteArrayOf(0x20, new byte[]{0x12, 0x00, 0x00});0x10, 0x28)) // 9F219A emv.setTag(OlaTag.AMOUNTTRANSACTION_AUTHORISED, new byte[]{0x00, 0x00, 0x00, 0x00, 0x01, 0x50}); // 9F02 emv.setTag(OlaTag.AMOUNT_OTHER_NUM,TIME, byteArrayOf(0x12, 0x00, 0x00)) new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x50}); // 9F039F21 emv.setTag(OlaTag.TRANSACTIONAMOUNT_CURRENCY_CODEAUTHORISED, byteArrayOf(0x00, 0x00, 0x00, new byte[]{0x08, 0x40});0x00, 0x01, 0x50)) // 9F02 emv.setTag(OlaTag.AMOUNT_OTHER_NUM, byteArrayOf(0x00, 0x00, 0x00, 0x00, 0x00, 0x50)) // 5F2A9F03 emv.setTag(OlaTag.TRANSACTION_CURRENCY_EXPONENT, new byte[]{0x02}); CODE, byteArrayOf(0x08, 0x40)) // 5F365F2A emv.setTag(OlaTag.TRANSACTION_CURRENCY_TYPE, new byte[]{0x00});EXPONENT, byteArrayOf(0x02)) // 5F36 emv.setTag(OlaTag.TRANSACTION_TYPE, byteArrayOf(0x00)) // 9C /* * Other possible tags to set here: * Account Type (5F57) * Transaction Category Code (9F53)// 9C /* * AllOther otherpossible tags mustto be set throughhere: the combinations! */ } |
Preprocessing
Code Block |
---|
private OlaError txnPreProcess() { Account Type (5F57) * OlaErrorTransaction retCategory =Code contactless.preprocess(9F53); if (ret != OlaError.OLA_OK)* { * All other tags must be set Log.e("OLA-TESTER", "contactless pre-process failed");through the combinations! }*/ return retemv.setTag(0x9F53, byteArrayOf(0x11)); } |
...
Preprocessing
Code Block |
---|
private shortfun txnGetCardtxnPreProcess(): OlaError { Log.dv("OLA-TESTERTransaction", "PRESENT CARDtxnPreProcess"); val ret = contactless.preprocess() int foundTechnos if (ret != dev.technoPolling(60000);OlaError.OLA_OK) { l("Result of foundTechnos: "+ foundTechnos);Log.e("Transaction", "txnPreProcess - contactless pre-process failed") } if (foundTechnos==4) return { ret } |
Card detection
Code Block |
---|
private fun txnGetCard(): Int { return clcEMV_CARD; //TODO remove hard-coding } Log.v("Transaction", "txnGetCard - **** PRESENT CARD ****") val elsefoundTechnos = dev.technoPolling(30) { Log.v("Transaction", "txnGetCard - result of foundTechnos: return 0;$foundTechnos") return } } |
Do transaction
Code Block |
---|
private OlaError doTransaction(if (foundTechnos == 4) { SingleObjectHolder<Integer> nbCandidatesHolder = new SingleObjectHolder<Integer>(); clcEMV_CARD //TODO remove hard-coding OlaError ret = contactless.buildCandidateList(nbCandidatesHolder); } else { if (ret != OlaError.OLA_OK) 0 { } } |
Do transaction
Code Block |
---|
private fun doTransaction(): OlaError { showOutcome(ret); Log.v("Transaction", "doTransaction }- buildCandidateList") /*val nbCandidatesHolder = SingleObjectHolder<Int>() * For now,var finalSelectCandidateret is useless. Everything from preprocessing to card * processing is done in "buildCandidateList". This will change as soon as exit conditions = contactless.buildCandidateList(nbCandidatesHolder) if (ret != OlaError.OLA_OK) { Outcome.showOutcome(contactless, ret) } * are availble in OLA 2.xLog.v("Transaction", hence"doTransaction we- keepnb allof the function calls herecandidates: ${nbCandidatesHolder.get()}") Log.v("Transaction", "doTransaction - */finalSelectCandidate") SingleObjectHolder<Byte>val kernel_id_holder = new SingleObjectHolder<Byte>(); ret = contactless.finalSelectCandidate(1, kernel_id_holder); if (ret != OlaError.OLA_OK) { {Outcome.showOutcome(contactless, ret) } showOutcome(ret); Log.v("Transaction", "doTransaction - } doTransaction") ret = contactless.doTransaction(); Outcome.showOutcome(contactless, ret); if (ret == OlaError.OLA_OK) { SingleObjectHolder<byte[]>val bytesHolder = new SingleObjectHolder<byte[]>SingleObjectHolder<ByteArray>(); for (int olaTag : batch) for (olaTag in batch) { OlaErrorval result = emv.getTag(olaTag, bytesHolder); if (result == OlaError.OLA_OK) { Log.v("Transaction", "doTransaction - result for getTag(): $result => " + l("Result for ola_contactless_get_tag(): "+result+" => "+ ArraysUtils.toStringbytesToHex(bytesHolder.get())); } else { Log.v("Transaction", "doTransaction - result for getTag(): $result => tag missing") l("Result for ola_contactless_get_tag(): "+result+" => tag missing");} } } Log.d("OLA-TESTERTransaction", "doTransaction - **** REMOVE CARD **** ") contactless.clean(); return ret; } |