Skip to content

Commit

Permalink
Merge pull request #46 from jadar/binary-data-support
Browse files Browse the repository at this point in the history
Add support for storing raw binary data in the Vault.
  • Loading branch information
benjohnde authored Oct 11, 2023
2 parents e2d2098 + 3777adc commit 873466c
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/androidMain/kotlin/com/liftric/kvault/KVault.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.liftric.kvault

import android.content.Context
import android.content.SharedPreferences
import android.util.Base64
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey

Expand Down Expand Up @@ -97,6 +98,17 @@ actual open class KVault(context: Context, fileName: String? = null) {
.commit()
}

/**
* Saves a byte array value in the store.
* @param key The key to store
* @param dataValue The value to store
*/
actual fun set(key: String, dataValue: ByteArray): Boolean =
encSharedPrefs
.edit()
.putString(key, Base64.encodeToString(dataValue, Base64.DEFAULT))
.commit()

/**
* Checks if object with key exists in the SharedPreferences.
* @param forKey The key to query
Expand Down Expand Up @@ -180,6 +192,17 @@ actual open class KVault(context: Context, fileName: String? = null) {
}
}

/**
* Returns the data value of an object in the store.
* @param forKey The key to query
* @return The stored bytes value
*/
actual fun data(forKey: String): ByteArray? {
return encSharedPrefs.getString(forKey, null)?.let {
Base64.decode(it, Base64.DEFAULT)
}
}

/**
* Returns all keys of the objects in the SharedPreferences.
* @return A list with all keys
Expand Down
14 changes: 14 additions & 0 deletions src/commonMain/kotlin/com/liftric/kvault/KVault.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ expect open class KVault {
*/
fun set(key: String, boolValue: Boolean): Boolean

/**
* Saves a byte array value in the store.
* @param key The key to store
* @param dataValue The value to store
*/
fun set(key: String, dataValue: ByteArray): Boolean

/**
* Checks if object with key exists in the store.
* @param forKey The key to query
Expand Down Expand Up @@ -92,6 +99,13 @@ expect open class KVault {
*/
fun bool(forKey: String): Boolean?

/**
* Returns the data value of an object in the store.
* @param forKey The key to query
* @return The stored bytes value
*/
fun data(forKey: String): ByteArray?

/**
* Returns all keys of the stored objects.
* @return A list with all keys
Expand Down
17 changes: 17 additions & 0 deletions src/commonTest/kotlin/com/liftric/kvault/KVaultTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,23 @@ abstract class AbstractKVaultTest(private val keychain: KVault) {
}
}

@Test
fun testSetGetData() {
val testData = mapOf(
"data1" to "Hello World!".encodeToByteArray(),
"data2" to "Foo bar".encodeToByteArray(),
"data3" to "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.".encodeToByteArray()
)
testData.values.toTypedArray().last() // - Why this line?
testData.forEach {
keychain.set(it.key, it.value)
assertContentEquals(it.value, keychain.data(it.key), "${it.key} should resolve to ${it.value.decodeToString()}")
}
funnelAssertion<ByteArray>(testData.keys.toList()) {
keychain.data(it)
}
}

@Test
fun testSetGetAll() {
val boolData = Pair("bool", true)
Expand Down
33 changes: 33 additions & 0 deletions src/iosMain/kotlin/com/liftric/kvault/KVault.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import platform.Foundation.*
import platform.Security.*
import platform.darwin.OSStatus
import platform.darwin.noErr
import platform.posix.memcpy

/**
* Keychain wrapper.
Expand Down Expand Up @@ -96,6 +97,15 @@ actual open class KVault(
return addOrUpdate(key, NSNumber(bool = boolValue).toNSData())
}

/**
* Saves a byte array value in the store.
* @param key The key to store
* @param dataValue The value to store
*/
actual fun set(key: String, dataValue: ByteArray): Boolean {
return addOrUpdate(key, dataValue.toNSData())
}

/**
* Returns the string value of an object in the Keychain.
* @param forKey The key to query
Expand Down Expand Up @@ -150,6 +160,15 @@ actual open class KVault(
return value(forKey)?.toNSNumber()?.boolValue
}

/**
* Returns the data value of an object in the store.
* @param forKey The key to query
* @return The stored bytes value
*/
actual fun data(forKey: String): ByteArray? {
return value(forKey)?.toByteArray()
}

/**
* Returns all keys of the objects in the Keychain.
* @return A list with all keys
Expand Down Expand Up @@ -311,5 +330,19 @@ actual open class KVault(
private val NSData.stringValue: String?
get() = NSString.create(this, NSUTF8StringEncoding) as String?

private fun NSData.toByteArray(): ByteArray =
ByteArray(length.toInt()).apply {
if (isNotEmpty()) {
usePinned {
memcpy(it.addressOf(0), this@toByteArray.bytes, this@toByteArray.length)
}
}
}

private fun ByteArray.toNSData(): NSData =
memScoped {
NSData.create(bytes = allocArrayOf(this@toNSData), length = this@toNSData.size.convert())
}

private fun OSStatus.validate(): Boolean = toUInt() == noErr
}

0 comments on commit 873466c

Please sign in to comment.