How to provide a way to use custom encryption in Android library? - android

I have a library that has default ways to encrypt stuff that it uses, which means user just need to provide a string as a key.
lib.encryption("key")
lib.doEncryptedStuff() // use default encryption
I want to allow users use custom encryption, including those that implement custom java crypto providers. My question is, what should be the correct way to ask user for such encryption methods? For now I came with idea to request two ciphers - one for encryption and one for decryption.
// create and init() ciphers here
lib.encryption(cipherEncrypt, cipherDecrypt)
lib.doEncryptedStuff() // use custom ciphers
But I'm unsure whether this is the correct way. Are Ciphers enough? Or should I provide interface for encryption for user to implement? I remember there being issues with reusing IV, which means cipher re-initialization?
Another issue that I'm facing is that it's really hard for me to generalize interface. Like AES with ECB will work with encrypt()/decrypt() methods, but with CBC it requires IV, which could also be stored inside encrypted data. What a mess.

Seems like I've nailed it. I used Stream-like approach, but not tied to streams. It allows to use any encryption user needs by passing interface as parameter.
My interface:
interface Encryption
{
fun encrypt(
read: (buffer: ByteArray, count: Int) -> Int,
write: (buffer: ByteArray, count: Int) -> Unit
)
fun decrypt(
read: (buffer: ByteArray, count: Int) -> Int,
write: (buffer: ByteArray, count: Int) -> Unit
)
}
Example implementation:
// init cipher and buffer
val cipher = Cipher.getInstance(MODE)
val buffer = ByteArray(BLOCK_SIZE)
cipher.init(Cipher.ENCRYPT_MODE, keystore.getKey(KEY_ALIAS, null))
// write iv (when decrypting read IV from beginning)
val iv = cipher.iv
write(iv, iv.size)
// write data
var count = 0
while (true)
{
count = read(buffer, BLOCK_SIZE)
if (count < BLOCK_SIZE)
break
val encrypted = cipher.update(buffer, 0, count)
write(encrypted, encrypted.size)
}
// flush remains
if (count > 0)
{
val final = cipher.doFinal(buffer, 0, count)
write(final, final.size)
}
Example usage (input and output are streams):
encryption.encrypt(
read = { buffer, count ->
input.read(buffer, 0, count)
},
write = { buffer, count ->
output.write(buffer, 0, count)
}
)

Related

Memory use question: passing map to function vs passing only needed values from map to function

I'm teaching myself coding with kotlin and AS and as a self-assigned project I'm making a little game. I have 6 var integers that are used to get some random numbers and to modify those numbers in certain ways. In order to reduce the amount of code and to simplify passing those integers around to functions I have all 6 stored in a single mutable map. Most of the time when I pass them from one function to the other (or one class to the other) I need all 6, but there is one case where I only need 3 of them.
My question is this: Is it more efficient to pass just the 3 values I need? Or is that actually LESS efficient because I'm passing new copies of already existing values? I'm really not sure how that works internally
example: Would you...
fun main() {
val gameArgs = mutableMapOf("numDice" to 3,
"minNum" to 1,
"maxNum" to 20,
"modNum" to 5,
"targetNum" to 17,
"bonusDice" to -1,)
val rollNumbers = rollDice(gameArgs)
// and then send the rollNumbers and the gameArgs elsewhere to evaluate what
// the numbers mean
}
fun rollDice(args: MutableMap<String, Int>):List<Int>{
val numList = mutableListOf<Int>()
repeat(args["numDice"]!!){
numList.add((args["minNum"]!!..args["maxNum"]!!).random())
}
return numList
}
OR, would you...
fun main() {
val gameArgs = mutableMapOf("numDice" to 3,
"minNum" to 1,
"maxNum" to 20,
"modNum" to 5,
"targetNum" to 17,
"bonusDice" to -1,)
val rollNumbers = rollDice(gameArgs["numDice"]!!, gameArgs["minNum"]!!, gameArgs["maxNum"]!!)
// and then send the number list and the other 3 values from the map elsewhere
}
fun rollDice(numDice: Int, minNum: Int, maxNum: Int):List<Int>{
val numList = mutableListOf<Int>()
repeat(numDice){
numList.add((minNum..maxNum).random())
}
return numList
}
My instincts say the latter is less memory intensive, but my anxiety whispers that the latter might be MORE memory intensive because I'm making copies of 3 values that already exist.
So which is more efficient? And does it make a difference if rollDice() is in another class?
Thanks in advance
[edited to fix one type and one copy/paste error]
You are over-concerned about performance. You could pass ten strings or a hundred copies of short strings like this and it's completely insignificant to performance. Think about when a web page opens on a computer 20 years ago, and thousands of words of text appears on your screen near-instantly. But when you pass a String, it is not copied. Only its memory address is copied, which is even more trivial. Regardless, it is both more efficient and cleaner code to pass the whole thing, not the individual components. Focus primarily on the cleanest-looking code and only try to optimize when you have a performance-critical section of code, like a piece of algorithm that's repeated thousands of times while the user is waiting for the result.
You should not use Maps for this. That's the sort of thing you do in weakly-typed languages like JavaScript. Maps are never used in this way in a strongly-typed language. When you have multiple parameters with different meanings, you use a class to represent them. This makes your code robust. The compiler will complain about and red-underline most of your errors before you even have to install your code to test it. With a map, you won't find your errors until after the program is running and you start having weird bugs that you have to go through the debugging process to find. Replace your map with a class like this:
data class GameSession(
val numDice: Int = 1,
val minNumber: Int = 1,
val maxNumber: Int = 20,
val modNumber: Int = 5,
val targetNumber: Int = 17,
val bonusDice: Int = -1
)
fun main() {
val game = GameSession()
val rollNumbers = rollDice(game)
// ...
}
fun rollDice(game: GameSession): List<Int>{
val numList = mutableListOf<Int>()
repeat(game.numDice){
numList.add((game.minNumber..game.maxNumber).random())
}
return numList
}
To take it a step further, it would make more design sence for rollDice() to be a function of the GameSession class. Also, you can skip making a temporary MutableList and just return a List directly using the List() function. So it could look like this:
data class GameSession(
val numDice: Int = 1,
val minNumber: Int = 1,
val maxNumber: Int = 20,
val modNumber: Int = 5,
val targetNumber: Int = 17,
val bonusDice: Int = -1
) {
fun rollDice(): List<Int> {
return List<Int>(numDice) { (minNumber..maxNumber).random() }
}
}
fun main() {
val game = GameSession()
val rollNumbers = game.rollDice()
// ...
}

RxJava Reduce Single's until some dynamic condition is met

Any RxJava experts that can help me figure this one out?
I have a function that returns a Single<ByteArray> based on some position parameter.
Basically, I want to keep calling that function, until it returns an empty ByteArray.
NOTE: The position parameter is supposed to indicate how many bytes where already read. E.g. first time it will be 0, next time it will be how many bytes where retrieved the first time, and so on.
This is what I have now:
fun readBytes(position: Int) : Single<ByteArray>{ ... }
fun readAllLogs() : Single<LogEntry> {
val oldPosition = AtomicInteger(0)
val position = AtomicInteger(0)
Observable.defer {
readBytes(position.get())
}.reduceWith({ LogEntry() }, { log, bytes ->
position.addAndGet(bytes.size)
log += bytes
log
}).repeatUntil {
val canWeStop = position.get() == oldPosition.get()
oldPosition.set(position.get())
canWeStop
}.toObservable()
}
For completeness, here's my LogEntry accumulator
data class LogEntry {
var data: ByteArray = byteArrayOf()
private set
operator fun plusAssign(bytes: ByteArray) {
data += bytes
}
}
Question 1:
How can I exit this stream elegantly? E.g. not keeping track of separate variables outside the stream?
Question 2:
Any optimizations I can make? I'm going from Single to Observable twice to be able to chain these operators. I'm sure that can be more efficient

Saving a class to internal storage on android/kotlin

I'm trying to save user data via a Player class as seen below:
class Player(name: String, age: Int, gender: String) {
}
and I'm wondering what the best way to save the class instances is. I think internal storage fits best as it's internal app data that the user doesn't need to directly access.
However there are not many resources that explain saving class instances - I only see examples of saving key-value pairs.
Code:
import kotlinx.android.synthetic.main.activity_player_details.*
class PlayerDetails : AppCompatActivity(), View.OnClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_player_details)
val intent = getIntent()
val players = intent.getIntExtra("number_of_players", 1)
println(players)
next_details.setOnClickListener(this)
}
override fun onClick(v: View?) {
val name: String = player_name.text.toString()
val age = if (player_age.text.toString().isNotEmpty()) player_age.text.toString().toInt() else 0
val genderId: Int = gender.checkedRadioButtonId
val gender: String = if (genderId > 0) resources.getResourceEntryName(genderId) else ""
if (name.isNotEmpty() && genderId > 0 && age > 0 ){
println(name)
println(age)
println(gender)
val player = Player(name, age, gender) // I WANT TO SAVE THIS INSTANCE
} else {
blankFields()
}
}
private fun blankFields() {
blank_fields_error.visibility = View.VISIBLE
}
}
Any advice appreciated.
Basically what you're asking is called "serialization".
In Android you have several ways to serialize an object for storage:
Use Java standard serialization (not recommended). Note that this requires a binary storage (e.g. database BLOB) or be converted to Base64 to store in a text format.
Use a serialization library, e.g. JSON, YAML, etc... This is going to be several magnitudes slower that a binary serialization (Android's Parcelable or Java's Serializable), and also slower than binary + Base64, so in my opinion not really a valid option unless you absolutely want the data stored to be human-readable.
Note that Parcelable is not suitable for consitent storage, so it is not an option.
Note that however, in my experience, I tested a lot of serialization methods (mainly for for IPC) and Serializable was fast enough without adding all the bloated code to use Parcelable. Parcelable only provided a negligible speed gain not worth the hassle of implementing and correctly maintaining Parcelable classes.

different code's behavior in unit test and in runtime

The same code works in runtime and doesnt work in test
There is such code
private fun generatePrivateKeyFromText(key: String): Key {
val kf = KeyFactory.getInstance("RSA")
val keySpecPKCS8 = PKCS8EncodedKeySpec(Base64.decodeBase64(key))
return kf.generatePrivate(keySpecPKCS8)
}
When I run or debug app it works ok, but this code fails on generatePrivate while testing
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
#Test
fun decrypt() {
val encrypt = "MoRxCpLJNqxfXGVeU73zZFi+X2j2TLUTyIn1XRqCoEfeN8rNBR/YrEtumAz+8/0AaEsvx0+qTilfbw+edZd8Wfum4McWQ8oWXifvWLgoXybhxWUmCdi2fwA9Gw0parY6CSNYUDA2UuLrLLaDGMz/Jj4s4XmXKp5zuec1zXVdrPM="
val prkey = "MIICXAIBAAKBgQCAaTCQOxAZPfTXfgel2MMPUCLAf32TxfsXu71/c3mVFGtDPB+7IpmxCmEvAv6nlq1ymX1zRR5gIzNt4DZoM0RhE58eoksUXcmFcRnMX5V4bnI8DitHLdB2EZzdvnPX0Umzs+tE7I1ouDIocNQRsEoQeDcNPfz5av2zMPsg0Xl/yQIDAQABAoGAV8UOX5cvQsGZZ+2J1q8ZbI8OodrCf83z+V3mgYXxVZe2VSd0XNmiiWMZ2CNI4k3YUhtdpvtYbsfAsFpvdbuNAXMW82Zwsd0oluPzzoLELGkFvaUJlh2YGmizrBnEwvF0usJYwjsjUbXw3o1xKX8ILk5FBfdr2+L65YIIZ0UhqoECQQD/B0P8iZhoOTx7myhwlFCuVeSadwaOMGy2CgXRLvTFAtstz8YVO+D+yPKsEpAvMlFgEnkTt7tl8DRxMpKnfmO5AkEAgOZudjUD72xWfSJpCEcX76WGSASWE+fLCZ8C/HumIZ+0trW5/bsmBrI/6SldEJcy4b2bHh2nOggC/6R5rEUkkQJAAg779IDj0wuLOnAxLl90G0QkOT72tZUce4evLlYTsbdpL4B619cI5OWYV906frcIQx9DDO6xu4vp0HQZDPMPOQJAOVbH8ntY2ctmmdmRwWXmpusJ1cV8gTROJGSArpHOcAycFd628sCqhLYMKgsFZBjuQG7YrsfgGLdxpgijO1eykQJBAOE8+BrQwFWyOcgnUShPHo8mDOBkeplGr9VZdnWktac2aBr1Biovy+pipUyjSCAaFgOsSU0FDcK0I5ulTOpgMRg="
val decrypt = CryptoService.decrypt(encrypt, prkey)
assertEquals("Pika-pika", decrypt)
}
fun decrypt(ciphertext: String, key: String): String {
var decodedBytes: ByteArray? = null
try {
val c = Cipher.getInstance("RSA")
c.init(Cipher.DECRYPT_MODE, generatePrivateKeyFromText(key))
decodedBytes = c.doFinal(Base64.decodeBase64(ciphertext))
} catch (e: Exception) {
Log.e("Crypto", "RSA decryption error: $e")
}
return String(decodedBytes ?: ByteArray(0))
}
Working function is in Fragment
private fun testCrypto() {
val encrypt = "MoRxCpLJNqxfXGVeU73zZFi+X2j2TLUTyIn1XRqCoEfeN8rNBR/YrEtumAz+8/0AaEsvx0+qTilfbw+edZd8Wfum4McWQ8oWXifvWLgoXybhxWUmCdi2fwA9Gw0parY6CSNYUDA2UuLrLLaDGMz/Jj4s4XmXKp5zuec1zXVdrPM="
val prkey = "MIICXAIBAAKBgQCAaTCQOxAZPfTXfgel2MMPUCLAf32TxfsXu71/c3mVFGtDPB+7IpmxCmEvAv6nlq1ymX1zRR5gIzNt4DZoM0RhE58eoksUXcmFcRnMX5V4bnI8DitHLdB2EZzdvnPX0Umzs+tE7I1ouDIocNQRsEoQeDcNPfz5av2zMPsg0Xl/yQIDAQABAoGAV8UOX5cvQsGZZ+2J1q8ZbI8OodrCf83z+V3mgYXxVZe2VSd0XNmiiWMZ2CNI4k3YUhtdpvtYbsfAsFpvdbuNAXMW82Zwsd0oluPzzoLELGkFvaUJlh2YGmizrBnEwvF0usJYwjsjUbXw3o1xKX8ILk5FBfdr2+L65YIIZ0UhqoECQQD/B0P8iZhoOTx7myhwlFCuVeSadwaOMGy2CgXRLvTFAtstz8YVO+D+yPKsEpAvMlFgEnkTt7tl8DRxMpKnfmO5AkEAgOZudjUD72xWfSJpCEcX76WGSASWE+fLCZ8C/HumIZ+0trW5/bsmBrI/6SldEJcy4b2bHh2nOggC/6R5rEUkkQJAAg779IDj0wuLOnAxLl90G0QkOT72tZUce4evLlYTsbdpL4B619cI5OWYV906frcIQx9DDO6xu4vp0HQZDPMPOQJAOVbH8ntY2ctmmdmRwWXmpusJ1cV8gTROJGSArpHOcAycFd628sCqhLYMKgsFZBjuQG7YrsfgGLdxpgijO1eykQJBAOE8+BrQwFWyOcgnUShPHo8mDOBkeplGr9VZdnWktac2aBr1Biovy+pipUyjSCAaFgOsSU0FDcK0I5ulTOpgMRg="
val decrypt = CryptoService.decrypt(encrypt, prkey)
println(decrypt) // "Pika-pika"
}
I call it on onViewCreated
Updated:
I added BC provider (thanks, #JamesKPolk)
private fun generatePrivateKeyFromText(key: String): Key {
Security.addProvider(BouncyCastleProvider())
val kf = KeyFactory.getInstance(algorithm)
val keySpecPKCS8 = PKCS8EncodedKeySpec(Base64.decodeBase64(key))
return kf.generatePrivate(keySpecPKCS8)
}
But it is still ok in runtime and not while testing
javax.crypto.BadPaddingException: Decryption error
So problem for different running code didnt go.
What the difference between runtime and test which crashes code?
The issue is that the private key is not a PKCS8EncodedKeySpec, but rather an RSAPrivateKey object from PKCS#1. The BC provider, however, will still decode this mistake without complaint. However, other providers will rightfully complain. My guess is that the runtime is using an older version of Android where the default provider is BC, but your test is using a newer version where that isn't the case.
The fix is to make your private key a proper PKCS8EncodedKeySpec. Alternatively, you can explicitly request the "BC" provider. To do so, you need to specify "BC" in the getInstance() call: val keyFactory = KeyFactory.getInstance("RSA", "BC")
However, note that it appears that BC provider support is on its way out.
To convert a private key in the PKCS#1 format, either wrap a 'BEGIN RSA PRIVATE KEY'-style header and footer around the base64 blob or decode the base64 blob and place that in a file, then run:
openssl pkcs8 -topk8 -in privkey.pem -outform der -nocrypt | openssl base64 -A
or
openssl pkcs8 -topk8 -in privkey.der -inform der -nocrypt | openssl base64 -A
A second issue comes from relying on defaults. Instead of doing
val c = Cipher.getInstance("RSA")
which gets you defaults for mode and padding and thus is non-portable, always specify the full "algorithm/mode/padding" transformation string to Cipher.getInstance(). In you case, it appears the data is not padded (an insecure mode) you'd need something like
val c = Cipher.getInstance("RSA/ECB/NoPadding")
However, you really should use proper randomized padding, and currently that is OAEP padding.
Summary
The runtime environment is Android, but I think the test environment is Oracle Java (or maybe openjdk). There are evidently two critical differences in those environments:
Android uses the BC provider for KeyFactory which will handle private keys encoded in PKCS#1 RSAPrivateKey format. Oracle Java only supports PKCS8 encoded keys.
In Android, Cipher.getInstance("RSA") defaults to Cipher.getInstance("RSA/ECB/NoPadding"), but Oracle Java defaults to Cipher.getInstance("RSA/ECB/PKCS1Padding")

Kotlin ByteArray from mixed types

I need to create, in Kotlin, a byte array with mixed types in it.
Specifically, I need to write something like:
0, 1, 0x02, 1, "me#emailaddr.com"
to a characteristic over BLE using Kotlin.
Clearly I'm new to Kotlin. The convenient ways to do it in Swift don't seem to have similar functions in Kotlin.
I've tried:
data class BlePayload(val protoVer: Int, val transId: Int, val cmdType: Int, val valueType: Int, val value: String)
fun packagePayload(): BlePayload {
val checkinInfo: BlePayload = BlePayload(0, 1, 0x02, 1, "me#emailaddr.com")
return checkinInfo
}
...
private fun writeCharacteristic(characteristic: BluetoothGattCharacteristic) {
val payload = BluetoothUtils.packagePayload()
val strByteArray = payload.toString()
val byteArray = strByteArray.toByteArray()
mClientActionListener.log(TAG_GATT_CLIENT_CALLBACK, "Writing to characteristic ${characteristic.uuid}")
mClientActionListener.log(TAG_GATT_CLIENT_CALLBACK, "** strByteArray = $strByteArray")
mClientActionListener.log(TAG_GATT_CLIENT_CALLBACK, "** byteArray = $byteArray")
mClientActionListener.log(TAG_GATT_CLIENT_CALLBACK, "** Writing the Payload: $payload")
characteristic.setValue(byteArray)
}
But that seems to be cumbersome and wrong. However, I can't see another way around it.
It needs to be a byte array for the Peripheral.
I can't find a way to make a byte array without making it a string first.
I don't see a way to go directly to a byte array with mixed types.
I am sure I must be wrong - I just seem to be too new to understand what the Kotlin docs are telling me.
Thanks for looking.
If you're OK pulling in a library to help with this task, I'd consider using Square's Okio for this one. I've used it for this kind of bit-fiddling in the past. Your example would look something like this with Okio:
fun packagePayload(): ByteArray =
Buffer()
.writeInt(0)
.writeInt(1)
.writeInt(2)
.writeInt(1)
.writeString("me#addr.com", Charsets.US_ASCII)
.readByteArray()
Okio also offers little-endian versions of the above methods (i.e. writeIntLe()).
So I ended up abandoning the idea of a data object in favor of:
fun packagePayload(): ByteArray {
var payloadArray: ByteArray = ByteArray(0)
payloadArray = payloadArray.plus(0.toByte())
payloadArray = payloadArray.plus(1.toByte())
payloadArray = payloadArray.plus(2.toByte())
payloadArray = payloadArray.plus(1.toByte())
payloadArray = payloadArray.plus("me#addr.com".toByteArray(Charsets.US_ASCII))
Log.i(TAG_BLEUTIL, "** output of array: " + payloadArray.contentToString())
// [0, 1, 2, 1, 109, 101, 64, 97, 100, 100, 114, 46, 99, 111, 109]
return payloadArray
}
That ended up being a lot more simple.
Here's the catch: if you want 4 byte integers, there is not Kotlin helper fun to make that easy for you.
So you need to do something like this to get the 4 byte Int:
fun Int.toBytes() : ByteArray {
var retArray = ByteArray(0)
for(i in 0..3) {
retArray = retArray.plus((this shr (i * 8) and 0xFF).toByte())
}
return retArray//.reversedArray()
}
In which case you would use it like so:
payloadArray = payloadArray.plus(0.toBytes())
Notice the "//.reversedArray()" at the end of the function. That's in case you need to switch between little endian and big endian.
Hope that helps someone in the future.
Let me know if you have a better solution.
Thanks

Categories

Resources