Amadis

Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 9 Next »

Basic transaction

A basic OLA transaction includes the following steps:

OLA classes instantiation (once at startup)

private val 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)

/**
 * Configures OLA layer (key files paths, trace, etc...)
 */
private fun olaConfig() {
    val path = byteArrayOf(0xdf.toByte(), 0x01, 28)
    val pathValue = "/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, 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")) {
        sredMode = byteArrayOf(0xdf.toByte(), 0x17, 0x01, 0x00) // SRED mode not active
    } else {
        sredMode = byteArrayOf(0xdf.toByte(), 0x17, 0x01, 0x01) // SRED mode active
    }
  
    try {
        val out = ByteArrayOutputStream()
        out.write(path)
        out.write(pathValue.toByteArray())
        out.write(cakeys)
        out.write(cakeysValue.toByteArray())
        out.write(crl)
        out.write(crlValue.toByteArray())
        out.write(efl)
        out.write(eflValue.toByteArray())
        out.write(pcsc)
        out.write(lang)
        out.write(runtimeParams)
        out.write(traceLevel)
        out.write(pollingTechno)
        out.write(sredMode)
        
        ola.initializeAtStartUp(out.toByteArray())
  
    } catch (e: IOException) {
    }
}

AID configuration

private fun terminalContactlessConfig() {

    Log.v("Transaction", "terminalContactlessConfig")
    
    val contactlessCfg = arrayOf(
        ContactlessCfg(AID.FromBytes(byteArrayOf(0xA0.toByte(), 0x00, 0x00, 0x00, 0x04, 0x10, 0x10)), 
                       true, 
                       2, 
                       mcCfg),
        ContactlessCfg(AID.FromBytes(byteArrayOf(0xA0.toByte(), 0x00, 0x00, 0x00, 0x04, 0x20, 0x10)),
                       true, 
                       2, 
                       mcCfg),
        ...)
    
    contactless.flushAIDSupported()
    
    for (config in contactlessCfg) {
        contactless.addAIDSupported(
            config.aid,
            config.partial,
            config.kernelId.toByte(),
            config.additionalData
        )
    }
    
    contactless.commitSupportedAIDs()
}

Keys configuration

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()),
                     byteArrayOf(0x03),
                     byteArrayOf(0x24, 0x12, 0x31)),
        ...)
        
    publicKey.flush()

    for (config in publicKeyCfg) {
        val maxReached = SingleObjectHolder(false)
        val key: PublicKeyData = publicKey.PublicKeyData(
            config.rid,
            config.idx,
            config.modulus,
            config.exponentValue,
            config.expirDate
        )
        publicKey.add(key, null, maxReached)
    }

    publicKey.commit()
}

Transaction related data ()

private fun txnSetTransactionRelatedData() {

    Log.v("Transaction", "txnSetTransactionRelatedData")
    emv.setTag(OlaTag.TRANSACTION_DATE, byteArrayOf(0x20, 0x10, 0x28))                          // 9A
    emv.setTag(OlaTag.TRANSACTION_TIME, byteArrayOf(0x12, 0x00, 0x00))                          // 9F21
    emv.setTag(OlaTag.AMOUNT_AUTHORISED, byteArrayOf(0x00, 0x00, 0x00, 0x00, 0x01, 0x50))       // 9F02
    emv.setTag(OlaTag.AMOUNT_OTHER_NUM, byteArrayOf(0x00, 0x00, 0x00, 0x00, 0x00, 0x50))        // 9F03
    emv.setTag(OlaTag.TRANSACTION_CURRENCY_CODE, byteArrayOf(0x08, 0x40))                       // 5F2A
    emv.setTag(OlaTag.TRANSACTION_CURRENCY_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)
      *
      * All other tags must be set through the combinations!
      */
    emv.setTag(0x9F53, byteArrayOf(0x11));
}

Preprocessing

private fun txnPreProcess(): OlaError {

    Log.v("Transaction", "txnPreProcess")
    val ret = contactless.preprocess()
    if (ret != OlaError.OLA_OK) {
        Log.e("Transaction", "txnPreProcess - contactless pre-process failed")
    }
    return ret
}

Card detection

private fun txnGetCard(): Int {

    Log.v("Transaction", "txnGetCard - **** PRESENT CARD ****")
    val foundTechnos = dev.technoPolling(30)
    Log.v("Transaction", "txnGetCard - result of foundTechnos: $foundTechnos")
    return if (foundTechnos == 4) {
        clcEMV_CARD //TODO remove hard-coding
    } else {
        0
    }
}

Do transaction

private fun doTransaction(): OlaError {

    Log.v("Transaction", "doTransaction - buildCandidateList")
    val nbCandidatesHolder = SingleObjectHolder<Int>()
    var ret = contactless.buildCandidateList(nbCandidatesHolder)
    if (ret != OlaError.OLA_OK) {
        Outcome.showOutcome(contactless, ret)
    }
    
    Log.v("Transaction", "doTransaction - nb of candidates: ${nbCandidatesHolder.get()}")
    Log.v("Transaction", "doTransaction - finalSelectCandidate")
    val kernel_id_holder = SingleObjectHolder<Byte>()
    ret = contactless.finalSelectCandidate(1, kernel_id_holder)
    if (ret != OlaError.OLA_OK) {
        Outcome.showOutcome(contactless, ret)
    }

    Log.v("Transaction", "doTransaction - doTransaction")
    ret = contactless.doTransaction()
    Outcome.showOutcome(contactless, ret)
    if (ret == OlaError.OLA_OK) {
        val bytesHolder = SingleObjectHolder<ByteArray>()
        for (olaTag in batch) {
            val result = emv.getTag(olaTag, bytesHolder)
            if (result == OlaError.OLA_OK) {
                Log.v("Transaction", "doTransaction - result for getTag(): $result => " +
                      Utils.bytesToHex(bytesHolder.get()))
            } else {
                Log.v("Transaction", "doTransaction - result for getTag(): $result => tag missing")

            }
        }
    }

    Log.d("Transaction", "doTransaction - **** REMOVE CARD **** ")

    contactless.clean();

    return ret
}

  • No labels