2019-07-20 20:02:37 +02:00
[](https://travis-ci.com/ionspin/kotlin-multiplatform-crypto)

2019-07-20 11:39:26 +02:00
# Kotlin Multiplatform Crypto Library
Kotlin Multiplatform Crypto is a library for various cryptographic applications.
2020-06-05 18:35:49 +02:00
The library comes in two flavors `multiplatform-crypto` and `multiplatform-crypto-delegated`
* `multiplatform-crypto` contains pure kotlin implementations, is not reviewed, should be considered unsafe and only
for prototyping or experimentation purposes.
2020-06-11 20:29:03 +02:00
* `multiplatform-crypto-delegated` relies on platform specific implementations, like libsodium, but care should still be taken that the kotlin code is not reviewed or proven safe.
APIs of both variants are identical.
## Supported platforms by variant
|Platform|Pure variant| Delegated variant|
|--------|------------|------------------|
|Linux X86 64| :heavy_check_mark: | :heavy_check_mark: |
|Linux Arm 64| :heavy_check_mark: | :heavy_check_mark: |
|Linux Arm 32| :heavy_check_mark: | :x: |
|macOS X86 64| :heavy_check_mark: | :heavy_check_mark: |
|iOS x86 64 | :heavy_check_mark: | :heavy_check_mark: |
|iOS Arm 64 | :heavy_check_mark: | :heavy_check_mark: |
|iOS Arm 32 | :heavy_check_mark: | :heavy_check_mark: |
|watchOS X86 32 | :heavy_check_mark: | :heavy_check_mark: |
|watchOS Arm 64(_32) | :heavy_check_mark: | :heavy_check_mark: |
|watchos Arm 32 | :heavy_check_mark: | :heavy_check_mark: |
|tvOS X86 64 | :heavy_check_mark: | :heavy_check_mark: |
|tvOS Arm 64 | :heavy_check_mark: | :heavy_check_mark: |
|minGW X86 64| :heavy_check_mark: | :heavy_check_mark: |
|minGW X86 32| :x: | :x: |
2020-06-13 16:31:14 +02:00
## Sample project
The library includes sample project that shows usage on different platforms
- NOTE: Currently only linux, macOs and windows are included.
2020-06-05 18:35:49 +02:00
2019-07-20 11:39:26 +02:00
## Notes & Roadmap
**The API will move fast and break often until v1.0**
2020-06-05 18:35:49 +02:00
Next steps:
- Expand API (AEAD, ECC ...)
2020-06-13 16:31:14 +02:00
- Include AES and Argon2 in new API approach
- Add primitives missing in `delegated` variant that are supported in `pure` (at the moment AES and Argon2)
2019-07-20 11:39:26 +02:00
## Should I use this in production?
2020-06-05 18:35:49 +02:00
No, until it is reviewed.
2019-09-19 23:53:23 +02:00
2020-06-05 18:35:49 +02:00
## Should I use this in code that is *critical* in any way, shape or form?
2019-09-19 23:53:23 +02:00
2020-06-11 20:29:03 +02:00
No, but even if after being warned you decide to, then use `multiplatform-crypto-delegated` as it relies on reputable libraries.
2019-09-19 23:53:23 +02:00
## Why?
This is an experimental implementation, mostly for expanding personal understanding of cryptography.
It's not peer reviewed, not guaranteed to be bug free, and not guaranteed to be secure.
2019-07-20 11:39:26 +02:00
2020-05-21 14:23:45 +02:00
## Currently supported
2019-09-25 00:42:01 +02:00
2020-05-21 14:23:45 +02:00
### Hashing functions
2019-07-20 18:59:37 +02:00
* Blake2b
* SHA512
* SHA256
2020-01-10 23:58:28 +01:00
2020-05-21 14:23:45 +02:00
### Key Derivation
2020-01-10 23:58:28 +01:00
* Argon2
2020-06-14 12:49:46 +02:00
### Authenticated symmetric encryption (AEAD)
2019-09-19 23:53:23 +02:00
2020-06-14 12:49:46 +02:00
* TODO
2019-07-20 18:59:37 +02:00
2020-06-05 18:35:49 +02:00
### Delegated flavor dependancy table
The following table describes which library is used for particular cryptographic primitive
| Primitive | JVM | JS | Native |
| ----------|-----|----|--------|
2020-06-11 20:29:03 +02:00
| Blake2b | LazySodium | libsodium.js | libsodium |
| SHA256 | LazySodium | libsodium.js | libsodium |
| SHA512 | LazySodium | libsodium.js | libsodium |
2020-06-14 12:49:46 +02:00
2020-06-05 18:35:49 +02:00
2019-07-20 11:39:26 +02:00
## Integration
2020-06-13 16:31:14 +02:00
NOTE: Latest version of the library is built with Kotlin 1.4-M2 and therefore only SNAPSHOT variant is available. Next
stable version will be released when Kotlin 1.4. is released
2019-07-20 11:39:26 +02:00
#### Gradle
2020-06-05 18:35:49 +02:00
Kotlin
2019-07-20 11:39:26 +02:00
```kotlin
2020-06-13 16:31:14 +02:00
implementation("com.ionspin.kotlin:multiplatform-crypto:0.1.0")
2020-06-05 18:35:49 +02:00
or
2020-06-13 16:31:14 +02:00
implementation("com.ionspin.kotlin:multiplatform-crypto-delegated:0.1.0")
2019-07-20 11:39:26 +02:00
```
#### Snapshot builds
```kotlin
repositories {
maven {
url = uri("https://oss.sonatype.org/content/repositories/snapshots")
}
}
2020-06-13 16:31:14 +02:00
implementation("com.ionspin.kotlin:multiplatform-crypto:0.1.0-SNAPSHOT")
2019-07-20 11:39:26 +02:00
```
## Usage
2020-06-11 20:29:03 +02:00
### Helper functions
All API take `UByteArray` as message/key/nonce/etc parameter. For convenience when working with strings we provide
`String.enocdeToUbyteArray()` extensions function, and `UByteArray.toHexString` extension function.
More convenience functions will be added.
2019-07-21 12:36:10 +02:00
### Hashes
Hashes are provided in two versions, "stateless", usually the companion object of the hash,
which takes the data to be hashed in one go, and "updatable" which can be fed data in chunks.
#### Blake2b
2019-07-20 18:59:37 +02:00
You can use Blake 2b in two modes
2019-07-21 12:36:10 +02:00
##### Stateless version
2019-07-20 18:59:37 +02:00
You need to deliver the complete data that is to be hashed in one go
```kotlin
val input = "abc"
2020-06-11 20:29:03 +02:00
val result = Crypto.Blake2b.stateless(input.encodeToUByteArray())
2019-07-20 18:59:37 +02:00
```
2020-05-21 14:23:45 +02:00
Result is returned as a `UByteArray`
2019-07-20 18:59:37 +02:00
2019-07-21 12:36:10 +02:00
##### Updatable instance version
2020-05-21 14:23:45 +02:00
You can create an instance and feed the data by using `update(input : UByteArray)` call. Once all data is supplied,
2020-06-07 00:05:14 +02:00
you should call `digest()` .
2019-07-20 18:59:37 +02:00
If you want to use Blake2b with a key, you should supply it when creating the `Blake2b` instance.
```kotlin
val test = "abc"
val key = "key"
2020-06-11 20:29:03 +02:00
val blake2b = Crypto.Blake2b.updateable(key.encodeToUByteArray())
blake2b.update(test.encodeToUByteArray())
val result = blake2b.digest().toHexString()
2019-07-20 18:59:37 +02:00
```
After digest is called, the instance is reset and can be reused (Keep in mind key stays the same for the particular instance).
2019-07-21 12:36:10 +02:00
#### SHA2 (SHA256 and SHA512)
##### Stateless version
2019-07-20 18:59:37 +02:00
2020-05-21 14:23:45 +02:00
You need to deliver the complete data that is to be hashed in one go. You can either provide the `UByteArray` as input
or `String` . Result is always returned as `UByteArray` (At least in verision 0.0.1)
2019-07-20 18:59:37 +02:00
```kotlin
val input = "abc"
2020-06-11 20:29:03 +02:00
val result = Crypto.Sha256.stateless(input.encodeToUByteArray())
2019-07-20 18:59:37 +02:00
```
```kotlin
val input ="abc"
2020-06-11 20:29:03 +02:00
val result = Crypto.Sha512.stateless(input.encodeToUByteArray())
2019-07-20 18:59:37 +02:00
```
2020-05-21 14:23:45 +02:00
Result is returned as a `UByteArray`
2019-07-20 18:59:37 +02:00
2019-07-21 12:36:10 +02:00
##### Updateable version
Or you can use the updatable instance version
```kotlin
2020-06-11 20:29:03 +02:00
val sha256 = Crypto.Sha256.updateable()
sha256.update("abc".encodeToUByteArray())
2019-07-21 12:36:10 +02:00
val result = sha256.digest()
```
```kotlin
2020-06-11 20:29:03 +02:00
val sha512 = Crypto.Sha512.updateable()
sha512.update("abc".encodeToUByteArray())
2019-07-21 12:36:10 +02:00
val result = sha512.digest()
```
2019-09-23 23:14:55 +02:00
2020-06-11 20:29:03 +02:00
### Symmetric encryption
#### AES
2019-09-23 23:14:55 +02:00
Aes is available with CBC and CTR mode through `AesCbc` and `AesCtr` classes/objects.
Similarly to hashes you can either use stateless or updateable version.
Initialization vector, or counter states are chosen by the SDK automaticaly, and returned alongside encrypted data
##### Stateless AesCbc and AesCtr
AesCtr
```kotlin
val keyString = "4278b840fb44aaa757c1bf04acbe1a3e"
val key = AesKey.Aes128Key(keyString)
val plainText = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"
val encryptedDataAndInitializationVector = AesCtr.encrypt(key, plainText.hexStringToUByteArray())
val decrypted = AesCtr.decrypt(
key,
encryptedDataAndInitializationVector.encryptedData,
encryptedDataAndInitializationVector.initialCounter
)
plainText == decrypted.toHexString()
```
AesCbc
```kotlin
val keyString = "4278b840fb44aaa757c1bf04acbe1a3e"
val key = AesKey.Aes128Key(keyString)
val plainText = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5"
val encryptedDataAndInitializationVector = AesCbc.encrypt(key, plainText.hexStringToUByteArray())
val decrypted = AesCbc.decrypt(
key,
encryptedDataAndInitializationVector.encryptedData,
encryptedDataAndInitializationVector.initilizationVector
)
plainText == decrypted.toHexString()
```
2020-05-16 21:12:25 +02:00
### Key derivation
#### Argon2
NOTE: This implementation is tested against KAT generated by reference Argon2 implementation, which does not follow
specification completely. See this issue https://github.com/P-H-C/phc-winner-argon2/issues/183
```kotlin
val argon2Instance = Argon2(
password = "Password",
salt = "RandomSalt",
parallelism = 8,
tagLength = 64U,
requestedMemorySize = 256U, //4GB
numberOfIterations = 4U,
key = "",
associatedData = "",
argonType = ArgonType.Argon2id
)
val tag = argon2Instance.derive()
val tagString = tag.map { it.toString(16).padStart(2, '0') }.joinToString(separator = "")
val expectedTagString = "c255e3e94305817d5e09a7c771e574e3a81cc78fef5da4a9644b6df0" +
"0ba1c9b424e3dd0ce7e600b1269b14c84430708186a8a60403e1bfbda935991592b9ff37"
println("Tag: ${tagString}")
assertEquals(tagString, expectedTagString)
```
2019-09-23 23:14:55 +02:00
2019-07-21 12:36:10 +02:00
2019-07-20 18:59:37 +02:00
2019-07-20 11:39:26 +02:00