2019-08-30 14:17:07 +02:00
/ * *
* DesfireProtocol . kt
2019-08-19 12:42:56 +02:00
*
* Copyright ( C ) 2011 Eric Butler
2019-08-30 14:17:07 +02:00
* Copyright ( C ) 2019 < seil0 @mosad . xyz >
2019-08-19 12:42:56 +02:00
*
* Authors :
* Eric Butler < eric @codebutler . com >
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
* /
package com.codebutler.farebot.card.desfire
import android.nfc.tech.IsoDep
import com.codebutler.farebot.Utils
import java.io.ByteArrayOutputStream
import java.io.IOException
import org.apache.commons.lang3.ArrayUtils
import kotlin.experimental.and
class DesfireProtocol ( private val mTagTech : IsoDep ) {
@Suppress ( " unused " )
val manufacturingData : DesfireManufacturingData
@Throws ( DesfireException :: class )
get ( ) {
val respBuffer = sendRequest ( GET _MANUFACTURING _DATA )
if ( respBuffer . size != 28 )
throw DesfireException ( " Invalid response " )
return DesfireManufacturingData ( respBuffer )
}
@Suppress ( " unused " )
val appList : IntArray
@Throws ( DesfireException :: class )
get ( ) {
val appDirBuf = sendRequest ( GET _APPLICATION _DIRECTORY )
val appIds = IntArray ( appDirBuf . size / 3 )
var app = 0
while ( app < appDirBuf . size ) {
val appId = ByteArray ( 3 )
System . arraycopy ( appDirBuf , app , appId , 0 , 3 )
appIds [ app / 3 ] = Utils . byteArrayToInt ( appId )
app += 3
}
return appIds
}
val fileList : IntArray
@Throws ( DesfireException :: class )
get ( ) {
val buf = sendRequest ( GET _FILES )
val fileIds = IntArray ( buf . size )
for ( x in buf . indices ) {
fileIds [ x ] = buf [ x ] . toInt ( )
}
return fileIds
}
@Throws ( DesfireException :: class )
fun selectApp ( appId : Int ) {
val appIdBuff = ByteArray ( 3 )
appIdBuff [ 0 ] = ( appId and 0xFF0000 shr 16 ) . toByte ( )
appIdBuff [ 1 ] = ( appId and 0xFF00 shr 8 ) . toByte ( )
appIdBuff [ 2 ] = ( appId and 0xFF ) . toByte ( )
sendRequest ( SELECT _APPLICATION , appIdBuff )
}
@Throws ( DesfireException :: class )
fun getFileSettings ( fileNo : Int ) : DesfireFileSettings {
val data = sendRequest ( GET _FILE _SETTINGS , byteArrayOf ( fileNo . toByte ( ) ) )
return DesfireFileSettings . create ( data )
}
@Suppress ( " unused " )
@Throws ( DesfireException :: class )
fun readFile ( fileNo : Int ) : ByteArray {
return sendRequest (
READ _DATA ,
byteArrayOf (
fileNo . toByte ( ) ,
0x0 . toByte ( ) ,
0x0 . toByte ( ) ,
0x0 . toByte ( ) ,
0x0 . toByte ( ) ,
0x0 . toByte ( ) ,
0x0 . toByte ( )
)
)
}
@Suppress ( " unused " )
@Throws ( DesfireException :: class )
fun readRecord ( fileNum : Int ) : ByteArray {
return sendRequest (
READ _RECORD ,
byteArrayOf (
fileNum . toByte ( ) ,
0x0 . toByte ( ) ,
0x0 . toByte ( ) ,
0x0 . toByte ( ) ,
0x0 . toByte ( ) ,
0x0 . toByte ( ) ,
0x0 . toByte ( )
)
)
}
@Throws ( DesfireException :: class )
fun readValue ( fileNum : Int ) : Int {
val buf = sendRequest ( READ _VALUE , byteArrayOf ( fileNum . toByte ( ) ) )
ArrayUtils . reverse ( buf )
return Utils . byteArrayToInt ( buf )
}
@Throws ( DesfireException :: class )
private fun sendRequest ( command : Byte , parameters : ByteArray ? = null ) : ByteArray {
val output = ByteArrayOutputStream ( )
var recvBuffer : ByteArray
try {
recvBuffer = mTagTech . transceive ( wrapMessage ( command , parameters ) )
} catch ( e : IOException ) {
throw DesfireException ( e )
}
while ( true ) {
if ( recvBuffer [ recvBuffer . size - 2 ] != 0x91 . toByte ( ) )
throw DesfireException ( " Invalid response " )
output . write ( recvBuffer , 0 , recvBuffer . size - 2 )
val status = recvBuffer [ recvBuffer . size - 1 ]
if ( status == OPERATION _OK ) {
break
} else if ( status == ADDITIONAL _FRAME ) {
try {
recvBuffer = mTagTech . transceive ( wrapMessage ( GET _ADDITIONAL _FRAME , null ) )
} catch ( e : IOException ) {
throw DesfireException ( e )
}
} else if ( status == PERMISSION _DENIED ) {
throw DesfireException ( " Permission denied " )
} else {
throw DesfireException ( " Unknown status code: " + Integer . toHexString ( ( status and 0xFF . toByte ( ) ) . toInt ( ) ) )
}
}
return output . toByteArray ( )
}
@Throws ( DesfireException :: class )
private fun wrapMessage ( command : Byte , parameters : ByteArray ? ) : ByteArray {
val stream = ByteArrayOutputStream ( )
stream . write ( 0x90 . toByte ( ) . toInt ( ) )
stream . write ( command . toInt ( ) )
stream . write ( 0x00 . toByte ( ) . toInt ( ) )
stream . write ( 0x00 . toByte ( ) . toInt ( ) )
if ( parameters != null ) {
stream . write ( parameters . size . toByte ( ) . toInt ( ) )
try {
stream . write ( parameters )
} catch ( e : IOException ) {
throw DesfireException ( e )
}
}
stream . write ( 0x00 . toByte ( ) . toInt ( ) )
return stream . toByteArray ( )
}
companion object {
/* Commands */
internal const val GET _MANUFACTURING _DATA = 0x60 . toByte ( )
internal const val GET _APPLICATION _DIRECTORY = 0x6A . toByte ( )
internal const val GET _ADDITIONAL _FRAME = 0xAF . toByte ( )
internal const val SELECT _APPLICATION = 0x5A . toByte ( )
internal const val READ _DATA = 0xBD . toByte ( )
internal const val READ _RECORD = 0xBB . toByte ( )
internal const val READ _VALUE = 0x6C . toByte ( )
internal const val GET _FILES = 0x6F . toByte ( )
internal const val GET _FILE _SETTINGS = 0xF5 . toByte ( )
/* Status codes */
internal const val OPERATION _OK = 0x00 . toByte ( )
internal const val PERMISSION _DENIED = 0x9D . toByte ( )
internal const val ADDITIONAL _FRAME = 0xAF . toByte ( )
}
}