Completed random support functions
This commit is contained in:
parent
cb17d75deb
commit
0431f27e3e
275
README.md
275
README.md
@ -3,279 +3,10 @@
|
|||||||
|
|
||||||
# Kotlin Multiplatform Crypto Library
|
# Kotlin Multiplatform Crypto Library
|
||||||
|
|
||||||
#Note:
|
This repository contains two crypto related projects:
|
||||||
### Next stable release will be published after public release of Kotlin 1.4, until then API will change significantly
|
|
||||||
|
|
||||||
Kotlin Multiplatform Crypto is a library for various cryptographic applications.
|
|
||||||
|
|
||||||
The library comes in two flavors `multiplatform-crypto` and `multiplatform-crypto-delegated`. This project also provides
|
|
||||||
direct libsodium bindings under `multiplatform-crypto-libsodium-bindings`.
|
|
||||||
|
|
||||||
* `multiplatform-crypto` contains pure kotlin implementations, is not reviewed, should be considered unsafe and only
|
|
||||||
for prototyping or experimentation purposes.
|
|
||||||
|
|
||||||
* `multiplatform-crypto-delegated` relies on platform specific implementations, mostly libsodium, but care should still be taken that the kotlin code is not reviewed or proven safe.
|
|
||||||
|
|
||||||
APIs of both variants are identical.
|
|
||||||
|
|
||||||
* `multiplatform-crypto-libsodium-bindings` is a generated bindings library using `kotlin-multiplatform-libsodium-generator`
|
|
||||||
* Under HEAVY development at the moment
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Table of contents
|
|
||||||
1. [Supported platforms](#supported-platforms-by-variant)
|
|
||||||
2. [API](#api)
|
|
||||||
3. TODO
|
|
||||||
|
|
||||||
## 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: |
|
|
||||||
|
|
||||||
## Sample project
|
|
||||||
The library includes sample project that shows usage on different platforms
|
|
||||||
- NOTE: Currently only linux, macOs and windows are included.
|
|
||||||
|
|
||||||
## Notes & Roadmap
|
|
||||||
|
|
||||||
**The API will move fast and break often until v1.0**
|
|
||||||
|
|
||||||
Next steps:
|
|
||||||
- Expand API (ECC, Signing ...)
|
|
||||||
|
|
||||||
## Should I use this in production?
|
|
||||||
**NO.**
|
|
||||||
The library is under HEAVY development.
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|
||||||
## API for Pure and Delegated flavourd
|
|
||||||
|
|
||||||
### Hashing functions
|
|
||||||
* Blake2b
|
|
||||||
* SHA512
|
|
||||||
* SHA256
|
|
||||||
|
|
||||||
### Key Derivation
|
|
||||||
|
|
||||||
* Argon2
|
|
||||||
|
|
||||||
### Authenticated symmetric encryption (AEAD)
|
|
||||||
|
|
||||||
* XChaCha20-Poly1305
|
|
||||||
|
|
||||||
|
|
||||||
### Delegated flavor dependancy table
|
|
||||||
The following table describes which library is used for particular cryptographic primitive
|
|
||||||
|
|
||||||
| Primitive | JVM | JS | Native |
|
|
||||||
| ----------|-----|----|--------|
|
|
||||||
| Blake2b | LazySodium | libsodium.js | libsodium |
|
|
||||||
| SHA256 | LazySodium | libsodium.js | libsodium |
|
|
||||||
| SHA512 | LazySodium | libsodium.js | libsodium |
|
|
||||||
| XChaCha20-Poly1305 | LazySodium | libsodium.js | libsodium |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Integration
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
#### Gradle
|
|
||||||
Kotlin
|
|
||||||
```kotlin
|
|
||||||
implementation("com.ionspin.kotlin:multiplatform-crypto:0.1.0")
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
implementation("com.ionspin.kotlin:multiplatform-crypto-delegated:0.1.0")
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Snapshot builds
|
|
||||||
```kotlin
|
|
||||||
repositories {
|
|
||||||
maven {
|
|
||||||
url = uri("https://oss.sonatype.org/content/repositories/snapshots")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
implementation("com.ionspin.kotlin:multiplatform-crypto:0.1.0-SNAPSHOT")
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
You can use Blake 2b in two modes
|
|
||||||
|
|
||||||
##### Stateless version
|
|
||||||
You need to deliver the complete data that is to be hashed in one go
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
val input = "abc"
|
|
||||||
val result = Crypto.Blake2b.stateless(input.encodeToUByteArray())
|
|
||||||
```
|
|
||||||
|
|
||||||
Result is returned as a `UByteArray`
|
|
||||||
|
|
||||||
##### Updatable instance version
|
|
||||||
You can create an instance and feed the data by using `update(input : UByteArray)` call. Once all data is supplied,
|
|
||||||
you should call `digest()`.
|
|
||||||
|
|
||||||
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"
|
|
||||||
val blake2b = Crypto.Blake2b.updateable(key.encodeToUByteArray())
|
|
||||||
blake2b.update(test.encodeToUByteArray())
|
|
||||||
val result = blake2b.digest().toHexString()
|
|
||||||
```
|
|
||||||
|
|
||||||
After digest is called, the instance is reset and can be reused (Keep in mind key stays the same for the particular instance).
|
|
||||||
#### SHA2 (SHA256 and SHA512)
|
|
||||||
|
|
||||||
##### Stateless version
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
val input = "abc"
|
|
||||||
val result = Crypto.Sha256.stateless(input.encodeToUByteArray())
|
|
||||||
```
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
val input ="abc"
|
|
||||||
val result = Crypto.Sha512.stateless(input.encodeToUByteArray())
|
|
||||||
```
|
|
||||||
|
|
||||||
Result is returned as a `UByteArray`
|
|
||||||
|
|
||||||
##### Updateable version
|
|
||||||
|
|
||||||
Or you can use the updatable instance version
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
val sha256 = Crypto.Sha256.updateable()
|
|
||||||
sha256.update("abc".encodeToUByteArray())
|
|
||||||
val result = sha256.digest()
|
|
||||||
```
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
val sha512 = Crypto.Sha512.updateable()
|
|
||||||
sha512.update("abc".encodeToUByteArray())
|
|
||||||
val result = sha512.digest()
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Symmetric encryption (OUTDATED, won't be exposed in next release, no counterpart in delegated flavor - 0.1.1)
|
|
||||||
|
|
||||||
#### AES
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Libsodium bindings
|
|
||||||
|
|
||||||
* Under development
|
|
||||||
|
|
||||||
|
1. Libsodium bindings for Kotiln Multiplatform
|
||||||
|
2. Pure/Delegated kotlin multiplatform crypto library written from scratch in pure form.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ object Versions {
|
|||||||
|
|
||||||
val kotlinBigNumVersion = "0.1.6-1.4.0-rc-SNAPSHOT"
|
val kotlinBigNumVersion = "0.1.6-1.4.0-rc-SNAPSHOT"
|
||||||
|
|
||||||
val lazySodium = "4.2.6"
|
val lazySodium = "4.3.1-SNAPSHOT"
|
||||||
val jna = "5.5.0"
|
val jna = "5.5.0"
|
||||||
|
|
||||||
val kotlinPoet = "1.6.0"
|
val kotlinPoet = "1.6.0"
|
||||||
@ -81,7 +81,10 @@ object Deps {
|
|||||||
val kotlinPoet = "com.squareup:kotlinpoet:${Versions.kotlinPoet}"
|
val kotlinPoet = "com.squareup:kotlinpoet:${Versions.kotlinPoet}"
|
||||||
|
|
||||||
object Delegated {
|
object Delegated {
|
||||||
val lazysodium = "com.goterl.lazycode:lazysodium-java:${Versions.lazySodium}"
|
// Temporary until reported lazysodium issues are fixed. My snapshot build with
|
||||||
|
// And cause I registered com.ionspin.kotlin as maven central package root now I have to use
|
||||||
|
// that even though this is pure java library. :)
|
||||||
|
val lazysodium = "com.ionspin.kotlin:lazysodium-java:${Versions.lazySodium}"
|
||||||
val jna = "net.java.dev.jna:jna:${Versions.jna}"
|
val jna = "net.java.dev.jna:jna:${Versions.jna}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
285
multiplatform-crypto-api/README.md
Normal file
285
multiplatform-crypto-api/README.md
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
# Kotlin Multiplatform Crypto Library
|
||||||
|
|
||||||
|
|
||||||
|
Kotlin Multiplatform Crypto is a library for various cryptographic applications.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
* `multiplatform-crypto-delegated` relies on platform specific implementations, mostly libsodium, but care should still be taken that the kotlin code is not reviewed or proven safe.
|
||||||
|
|
||||||
|
APIs of both variants are identical.
|
||||||
|
|
||||||
|
|
||||||
|
### Table of contents
|
||||||
|
1. [Supported platforms](#supported-platforms-by-variant)
|
||||||
|
2. [API](#api)
|
||||||
|
3. TODO
|
||||||
|
|
||||||
|
## 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: |
|
||||||
|
|
||||||
|
## Sample project
|
||||||
|
The library includes sample project that shows usage on different platforms
|
||||||
|
- NOTE: Currently only linux, macOs and windows are included.
|
||||||
|
|
||||||
|
## Notes & Roadmap
|
||||||
|
|
||||||
|
**The API will move fast and break often until v1.0**
|
||||||
|
|
||||||
|
Next steps:
|
||||||
|
- Expand API (ECC, Signing ...)
|
||||||
|
|
||||||
|
## Should I use this in production?
|
||||||
|
**NO.**
|
||||||
|
The library is under HEAVY development. Until development is done it will not be reviewed and therefore it shouldn't be used.
|
||||||
|
Contributions are still welcome!
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## API for Pure and Delegated flavours
|
||||||
|
|
||||||
|
### Hashing functions
|
||||||
|
* Blake2b
|
||||||
|
* SHA512
|
||||||
|
* SHA256
|
||||||
|
|
||||||
|
### Key Derivation
|
||||||
|
|
||||||
|
* Argon2
|
||||||
|
|
||||||
|
### Authenticated symmetric encryption (AEAD)
|
||||||
|
|
||||||
|
* XChaCha20-Poly1305
|
||||||
|
|
||||||
|
|
||||||
|
### Delegated flavor dependancy table
|
||||||
|
The following table describes which library is used for particular cryptographic primitive
|
||||||
|
|
||||||
|
| Primitive | JVM | JS | Native |
|
||||||
|
| ----------|-----|----|--------|
|
||||||
|
| Blake2b | LazySodium | libsodium.js | libsodium |
|
||||||
|
| SHA256 | LazySodium | libsodium.js | libsodium |
|
||||||
|
| SHA512 | LazySodium | libsodium.js | libsodium |
|
||||||
|
| XChaCha20-Poly1305 | LazySodium | libsodium.js | libsodium |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Integration
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
#### Gradle
|
||||||
|
Kotlin
|
||||||
|
```kotlin
|
||||||
|
implementation("com.ionspin.kotlin:multiplatform-crypto:0.1.0")
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
implementation("com.ionspin.kotlin:multiplatform-crypto-delegated:0.1.0")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Snapshot builds
|
||||||
|
```kotlin
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
url = uri("https://oss.sonatype.org/content/repositories/snapshots")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
implementation("com.ionspin.kotlin:multiplatform-crypto:0.1.0-SNAPSHOT")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
You can use Blake 2b in two modes
|
||||||
|
|
||||||
|
##### Stateless version
|
||||||
|
You need to deliver the complete data that is to be hashed in one go
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val input = "abc"
|
||||||
|
val result = Crypto.Blake2b.stateless(input.encodeToUByteArray())
|
||||||
|
```
|
||||||
|
|
||||||
|
Result is returned as a `UByteArray`
|
||||||
|
|
||||||
|
##### Updatable instance version
|
||||||
|
You can create an instance and feed the data by using `update(input : UByteArray)` call. Once all data is supplied,
|
||||||
|
you should call `digest()`.
|
||||||
|
|
||||||
|
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"
|
||||||
|
val blake2b = Crypto.Blake2b.updateable(key.encodeToUByteArray())
|
||||||
|
blake2b.update(test.encodeToUByteArray())
|
||||||
|
val result = blake2b.digest().toHexString()
|
||||||
|
```
|
||||||
|
|
||||||
|
After digest is called, the instance is reset and can be reused (Keep in mind key stays the same for the particular instance).
|
||||||
|
#### SHA2 (SHA256 and SHA512)
|
||||||
|
|
||||||
|
##### Stateless version
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val input = "abc"
|
||||||
|
val result = Crypto.Sha256.stateless(input.encodeToUByteArray())
|
||||||
|
```
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val input ="abc"
|
||||||
|
val result = Crypto.Sha512.stateless(input.encodeToUByteArray())
|
||||||
|
```
|
||||||
|
|
||||||
|
Result is returned as a `UByteArray`
|
||||||
|
|
||||||
|
##### Updateable version
|
||||||
|
|
||||||
|
Or you can use the updatable instance version
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val sha256 = Crypto.Sha256.updateable()
|
||||||
|
sha256.update("abc".encodeToUByteArray())
|
||||||
|
val result = sha256.digest()
|
||||||
|
```
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val sha512 = Crypto.Sha512.updateable()
|
||||||
|
sha512.update("abc".encodeToUByteArray())
|
||||||
|
val result = sha512.digest()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Symmetric encryption (OUTDATED, won't be exposed in next release, no counterpart in delegated flavor - 0.1.1)
|
||||||
|
|
||||||
|
#### AES
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Libsodium bindings
|
||||||
|
|
||||||
|
* Under development
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -47,6 +47,9 @@ repositories {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
jcenter()
|
jcenter()
|
||||||
maven("https://dl.bintray.com/terl/lazysodium-maven")
|
maven("https://dl.bintray.com/terl/lazysodium-maven")
|
||||||
|
maven {
|
||||||
|
url = uri("https://oss.sonatype.org/content/repositories/snapshots")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
group = ReleaseInfo.group
|
group = ReleaseInfo.group
|
||||||
|
@ -5,6 +5,8 @@ package com.ionspin.kotlin.crypto.util
|
|||||||
* ugljesa.jovanovic@ionspin.com
|
* ugljesa.jovanovic@ionspin.com
|
||||||
* on 27-Sep-2020
|
* on 27-Sep-2020
|
||||||
*/
|
*/
|
||||||
|
val randombytes_SEEDBYTES = 32
|
||||||
|
|
||||||
expect object LibsodiumRandom {
|
expect object LibsodiumRandom {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.ionspin.kotlin.crypto.util
|
||||||
|
|
||||||
|
import com.ionspin.kotlin.crypto.LibsodiumInitializer
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Ugljesa Jovanovic
|
||||||
|
* ugljesa.jovanovic@ionspin.com
|
||||||
|
* on 03-Oct-2020
|
||||||
|
*/
|
||||||
|
class LibsodiumRandomTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRandom() {
|
||||||
|
//This is just a sanity test, it should fail on occasion though, with probability of 1/2^32
|
||||||
|
LibsodiumInitializer.initializeWithCallback {
|
||||||
|
val random = LibsodiumRandom.random()
|
||||||
|
assertTrue { random != 0U }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRandomUniform() {
|
||||||
|
//This is just a sanity test, it should fail on occasion though, with probability of 1/2^31
|
||||||
|
LibsodiumInitializer.initializeWithCallback {
|
||||||
|
val random = LibsodiumRandom.uniform(UInt.MAX_VALUE / 2U)
|
||||||
|
assertTrue { random != 0U && random < (UInt.MAX_VALUE / 2U)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRandomBuffer() {
|
||||||
|
//This is just a sanity test, it should fail on occasion though, with probability of 1/2^52
|
||||||
|
LibsodiumInitializer.initializeWithCallback {
|
||||||
|
val result = LibsodiumRandom.buf(20)
|
||||||
|
val lowProbability = UByteArray(20) { 0U }
|
||||||
|
assertFalse { result.contentEquals(lowProbability) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRandomBufferDeterministic() {
|
||||||
|
//This is just a sanity test, it should fail on occasion though, with probability of 1/2^52
|
||||||
|
LibsodiumInitializer.initializeWithCallback {
|
||||||
|
val seed = UByteArray(randombytes_SEEDBYTES) { 1U }
|
||||||
|
val result = LibsodiumRandom.bufDeterministic(20, seed)
|
||||||
|
val lowProbability = UByteArray(20) { 0U }
|
||||||
|
assertFalse { result.contentEquals(lowProbability) }
|
||||||
|
val secondResult = LibsodiumRandom.bufDeterministic(20, seed)
|
||||||
|
assertTrue { result.contentEquals(secondResult) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -207,9 +207,18 @@ interface JsSodiumInterface {
|
|||||||
fun from_hex(data : String): Uint8Array
|
fun from_hex(data : String): Uint8Array
|
||||||
fun from_string(data : String): Uint8Array
|
fun from_string(data : String): Uint8Array
|
||||||
|
|
||||||
|
// ---- > ---- Random ---- < -----
|
||||||
|
|
||||||
|
fun randombytes_buf(length: UInt) : Uint8Array
|
||||||
|
fun randombytes_buf_deterministic(length: UInt, seed : Uint8Array) : Uint8Array
|
||||||
|
fun randombytes_random() : UInt
|
||||||
|
fun randombytes_uniform(upper_bound: UInt) : UInt
|
||||||
|
|
||||||
// ---- Utils end ----
|
// ---- Utils end ----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package com.ionspin.kotlin.crypto.util
|
||||||
|
|
||||||
|
import com.ionspin.kotlin.crypto.getSodium
|
||||||
|
import ext.libsodium.com.ionspin.kotlin.crypto.toUByteArray
|
||||||
|
import ext.libsodium.com.ionspin.kotlin.crypto.toUInt8Array
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Ugljesa Jovanovic
|
||||||
|
* ugljesa.jovanovic@ionspin.com
|
||||||
|
* on 27-Sep-2020
|
||||||
|
*/
|
||||||
|
actual object LibsodiumRandom {
|
||||||
|
/**
|
||||||
|
* The randombytes_buf() function fills size bytes starting at buf with an unpredictable sequence of bytes.
|
||||||
|
*/
|
||||||
|
actual fun buf(size: Int): UByteArray {
|
||||||
|
return getSodium().randombytes_buf(size).toUByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The randombytes_buf_deterministic function stores size bytes into buf indistinguishable from random bytes without knowing seed.
|
||||||
|
* For a given seed, this function will always output the same sequence. size can be up to 2^31 (~8GB) because we use kotlin arrays
|
||||||
|
* and they are limited by Int primitive type
|
||||||
|
* seed is randombytes_SEEDBYTES bytes long.
|
||||||
|
* This function is mainly useful for writing tests, and was introduced in libsodium 1.0.12. Under the hood, it uses the ChaCha20 stream cipher.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
actual fun bufDeterministic(size: Int, seed: UByteArray): UByteArray {
|
||||||
|
return getSodium().randombytes_buf_deterministic(size.toUInt(), seed.toUInt8Array()).toUByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The randombytes_random() function returns an unpredictable value between 0 and 0xffffffff (included).
|
||||||
|
*/
|
||||||
|
actual fun random(): UInt {
|
||||||
|
return getSodium().randombytes_random()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The randombytes_uniform() function returns an unpredictable value between 0 and upper_bound (excluded). Unlike r
|
||||||
|
* andombytes_random() % upper_bound, it guarantees a uniform distribution of the possible output values even when
|
||||||
|
* upper_bound is not a power of 2. Note that an upper_bound < 2 leaves only a single element to be chosen, namely 0
|
||||||
|
*/
|
||||||
|
actual fun uniform(upperBound: UInt): UInt {
|
||||||
|
return getSodium().randombytes_uniform(upperBound)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -27,7 +27,7 @@ actual object LibsodiumRandom {
|
|||||||
*/
|
*/
|
||||||
actual fun bufDeterministic(size: Int, seed: UByteArray): UByteArray {
|
actual fun bufDeterministic(size: Int, seed: UByteArray): UByteArray {
|
||||||
val result = ByteArray(size)
|
val result = ByteArray(size)
|
||||||
sodium.randombytes_buf(result, size, seed.asByteArray())
|
sodium.randombytes_buf_deterministic(result, size, seed.asByteArray())
|
||||||
return result.asUByteArray()
|
return result.asUByteArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +36,8 @@ actual object LibsodiumRandom {
|
|||||||
*/
|
*/
|
||||||
actual fun random(): UInt {
|
actual fun random(): UInt {
|
||||||
//Broken in lazysodium-java https://github.com/terl/lazysodium-java/issues/86
|
//Broken in lazysodium-java https://github.com/terl/lazysodium-java/issues/86
|
||||||
TODO()
|
//Using temporary forked and fixed build until pull request is accepted in original repo
|
||||||
|
return sodium.randombytes_random().toUInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -48,7 +49,8 @@ actual object LibsodiumRandom {
|
|||||||
*/
|
*/
|
||||||
actual fun uniform(upperBound: UInt): UInt {
|
actual fun uniform(upperBound: UInt): UInt {
|
||||||
//Broken in lazysodium-java https://github.com/terl/lazysodium-java/issues/86
|
//Broken in lazysodium-java https://github.com/terl/lazysodium-java/issues/86
|
||||||
TODO("not implemented yet")
|
//Using temporary fixed build until pull request is accepted
|
||||||
|
return sodium.randombytes_uniform(upperBound.toInt()).toUInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
|Function name| Implemented |
|
|Function name| Implemented |
|
||||||
|-------------|-------------|
|
|-------------|-------------|
|
||||||
| add | |
|
| add | |
|
||||||
| memcmp | |
|
| memcmp | :heavy_check_mark: |
|
||||||
| memzero | |
|
| memzero | :heavy_check_mark: |
|
||||||
| output_formats | |
|
| output_formats | |
|
||||||
| pad | |
|
| pad | :heavy_check_mark: |
|
||||||
| unpad | |
|
| unpad | :heavy_check_mark: |
|
||||||
| symbols | |
|
| symbols | |
|
||||||
| to_base64 | |
|
| to_base64 | :heavy_check_mark: |
|
||||||
| to_hex | |
|
| to_hex | :heavy_check_mark: |
|
||||||
| to_string | |
|
| from_base64 | :heavy_check_mark: |
|
||||||
|
| from_hex | :heavy_check_mark: |
|
||||||
| crypto_aead_chacha20poly1305_decrypt | :heavy_check_mark: |
|
| crypto_aead_chacha20poly1305_decrypt | :heavy_check_mark: |
|
||||||
| crypto_aead_chacha20poly1305_decrypt_detached | :heavy_check_mark: |
|
| crypto_aead_chacha20poly1305_decrypt_detached | :heavy_check_mark: |
|
||||||
| crypto_aead_chacha20poly1305_encrypt | :heavy_check_mark: |
|
| crypto_aead_chacha20poly1305_encrypt | :heavy_check_mark: |
|
||||||
@ -138,12 +139,12 @@
|
|||||||
| crypto_stream_xchacha20_keygen | not present in LazySodium Android |
|
| crypto_stream_xchacha20_keygen | not present in LazySodium Android |
|
||||||
| crypto_stream_xchacha20_xor | not present in LazySodium Android|
|
| crypto_stream_xchacha20_xor | not present in LazySodium Android|
|
||||||
| crypto_stream_xchacha20_xor_ic | not present in LazySodium Android |
|
| crypto_stream_xchacha20_xor_ic | not present in LazySodium Android |
|
||||||
| randombytes_buf | |
|
| randombytes_buf | :heavy_check_mark: |
|
||||||
| randombytes_buf_deterministic | |
|
| randombytes_buf_deterministic | :heavy_check_mark: |
|
||||||
| randombytes_close | |
|
| randombytes_close | not present in LazySodium |
|
||||||
| randombytes_random | |
|
| randombytes_random | :heavy_check_mark: |
|
||||||
| randombytes_stir | |
|
| randombytes_stir | not present in LazySodium |
|
||||||
| randombytes_uniform | |
|
| randombytes_uniform | :heavy_check_mark: |
|
||||||
| sodium_version_string | |
|
| sodium_version_string | |
|
||||||
| SODIUM_LIBRARY_VERSION_MAJOR | |
|
| SODIUM_LIBRARY_VERSION_MAJOR | |
|
||||||
| SODIUM_LIBRARY_VERSION_MINOR | |
|
| SODIUM_LIBRARY_VERSION_MINOR | |
|
||||||
|
Loading…
x
Reference in New Issue
Block a user