Merge pull request #13 from ionspin/extract-interfaces
Extract interfaces
This commit is contained in:
commit
c5cb66d087
16
.gitignore
vendored
16
.gitignore
vendored
@ -13,3 +13,19 @@ build/
|
||||
/package.json
|
||||
/multiplatform-crypto/src/jsMain/npm/node_modules
|
||||
/multiplatform-crypto/src/jsMain/npm/package-lock.json
|
||||
/multiplatform-crypto-delegated/node_modules
|
||||
/multiplatform-crypto-delegated/package.json
|
||||
/multiplatform-crypto-delegated/package-lock.json
|
||||
|
||||
/sodiumWrapper/include/
|
||||
/sodiumWrapper/lib/
|
||||
/sodiumWrapper/ios-include/
|
||||
/sodiumWrapper/ios-lib/
|
||||
/sodiumWrapper/static-arm64/
|
||||
/sodiumWrapper/static-arm32/
|
||||
/sodiumWrapper/static-ios/
|
||||
/sodiumWrapper/static-linux-x86-64/
|
||||
/sodiumWrapper/static-macos-x86-64/
|
||||
/sodiumWrapper/static-mingw-x86-64/
|
||||
/sodiumWrapper/static-tvos/
|
||||
/sodiumWrapper/static-watchos/
|
||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "sodiumWrapper/libsodium"]
|
||||
path = sodiumWrapper/libsodium
|
||||
url = https://github.com/ionspin/libsodium.git
|
118
.travis.yml
118
.travis.yml
@ -1,33 +1,74 @@
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
name: linux
|
||||
language: java
|
||||
jdk: openjdk12
|
||||
# before_script:
|
||||
# - wget https://github.com/sormuras/bach/raw/master/install-jdk.sh
|
||||
# - source install-jdk.sh --url 'https://api.adoptopenjdk.net/v2/binary/releases/openjdk12?openjdk_impl=hotspot&os=linux&arch=x64&release=latest&heap_size=normal&type=jdk'
|
||||
# - java --version
|
||||
|
||||
env:
|
||||
KBUILD=linux
|
||||
JAVA_OPTS=-Xmx2g
|
||||
#skip ./gradlew assemble that is normally invoked in installation step
|
||||
install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get -y install automake
|
||||
script:
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./linuxBuild.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./linuxBuildAndPublish.sh; fi'
|
||||
# OSX macos/ios
|
||||
- os: osx
|
||||
name: osx-mac-ios
|
||||
osx_image: xcode11.4
|
||||
language: java
|
||||
jdk: openjdk12
|
||||
# before_script:
|
||||
# - wget https://github.com/sormuras/bach/raw/master/install-jdk.sh
|
||||
# - source install-jdk.sh --url 'https://api.adoptopenjdk.net/v2/binary/releases/openjdk12?openjdk_impl=hotspot&os=linux&arch=x64&release=latest&heap_size=normal&type=jdk'
|
||||
# - java --version
|
||||
install: true
|
||||
env:
|
||||
KBUILD=linux
|
||||
JAVA_OPTS=-Xmx2g
|
||||
script:
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild-mac-ios.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-mac-ios.sh; fi'
|
||||
# OSX watchos
|
||||
- os: osx
|
||||
name: osx-watchos
|
||||
osx_image: xcode11.4
|
||||
language: java
|
||||
jdk: openjdk12
|
||||
install: true
|
||||
env:
|
||||
KBUILD=linux
|
||||
JAVA_OPTS=-Xmx2g
|
||||
script:
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild-watchos.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-watchos.sh; fi'
|
||||
# OSX tvos
|
||||
- os: osx
|
||||
name: osx-tvos
|
||||
osx_image: xcode11.4
|
||||
language: java
|
||||
jdk: openjdk12
|
||||
install: true
|
||||
env:
|
||||
KBUILD=linux
|
||||
JAVA_OPTS=-Xmx2g
|
||||
script:
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild-tvos.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-tvos.sh; fi'
|
||||
# OSX pure
|
||||
- os: osx
|
||||
name: osx-pure
|
||||
osx_image: xcode11.4
|
||||
language: java
|
||||
jdk: openjdk12
|
||||
install: true
|
||||
env:
|
||||
KBUILD=linux
|
||||
JAVA_OPTS=-Xmx2g
|
||||
script:
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild-pure.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-pure.sh; fi'
|
||||
- os: windows
|
||||
name: windwos-pure
|
||||
language: shell
|
||||
jdk: openjdk12
|
||||
env:
|
||||
@ -37,15 +78,68 @@ matrix:
|
||||
before_install:
|
||||
- curl "${GRAVIS}.install-jdk-travis.sh" --output ~/.install-jdk-travis.sh
|
||||
- source ~/.install-jdk-travis.sh
|
||||
install: true
|
||||
script:
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then ./gradlew build ; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then ./gradlew build publishMingwx64PublicationToSnapshotRepository; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then $shell ./windowsBuild-pure.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then ./windowsBuildAndPublish-pure.sh; fi'
|
||||
- os: windows
|
||||
name: windows-delegated
|
||||
language: shell
|
||||
jdk: openjdk12
|
||||
env:
|
||||
- GRAVIS="https://raw.githubusercontent.com/DanySK/Gravis-CI/master/"
|
||||
- JAVA_OPTS=-Xmx2g
|
||||
- JDK="adopt-openj9@1.11"
|
||||
before_install:
|
||||
- curl "${GRAVIS}.install-jdk-travis.sh" --output ~/.install-jdk-travis.sh
|
||||
- source ~/.install-jdk-travis.sh
|
||||
- |-
|
||||
case $TRAVIS_OS_NAME in
|
||||
windows)
|
||||
[[ ! -f C:/tools/msys64/msys2_shell.cmd ]] && rm -rf C:/tools/msys64
|
||||
choco uninstall -y mingw
|
||||
choco upgrade --no-progress -y msys2 bazel
|
||||
export msys2='cmd //C RefreshEnv.cmd '
|
||||
export msys2+='& set MSYS=winsymlinks:nativestrict '
|
||||
export msys2+='& C:\\tools\\msys64\\msys2_shell.cmd -defterm -no-start'
|
||||
export shell="$msys2 -mingw64 -full-path -here -c \$\* --"
|
||||
export msys2+=" -msys2 -c \$\* --"
|
||||
$msys2 pacman --sync --noconfirm --needed \
|
||||
autoconf \
|
||||
automake \
|
||||
mingw-w64-x86_64-libtool \
|
||||
mingw-w64-x86_64-toolchain \
|
||||
perl \
|
||||
unzip
|
||||
taskkill //IM gpg-agent.exe //F
|
||||
export CPPFLAGS=-D__USE_MINGW_ANSI_STDIO=1
|
||||
export PATH=/C/tools/msys64/mingw64/bin:$PATH
|
||||
export GNU_MAKE=mingw32-make
|
||||
export MAKE=mingw32-make
|
||||
export AR=gcc-ar
|
||||
export RANLIB=gcc-ranlib
|
||||
export COVERITY_SCAN_BRANCH_PATTERN=disable_coverity_scan
|
||||
;;
|
||||
esac
|
||||
- export GIT=git
|
||||
- g++ --version
|
||||
- $GNU_MAKE --version
|
||||
- $GIT --version
|
||||
install: true
|
||||
before_cache:
|
||||
- $msys2 pacman --sync --clean --noconfirm
|
||||
script:
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then $shell ./windowsBuild-delegated.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then ./windowsBuildAndPublish-delegated.sh; fi'
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2/
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
- $HOME/.konan/cache
|
||||
- $HOME/.konan/dependencies
|
||||
- $HOME/AppData/Local/Temp/chocolatey
|
||||
- /C/tools/msys64
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
43
EXTERNAL_LICENSES
Normal file
43
EXTERNAL_LICENSES
Normal file
@ -0,0 +1,43 @@
|
||||
Libraries used by multipatform-crypto-delegated follow:
|
||||
|
||||
Libsodium, licensed under ISC License
|
||||
|
||||
/*
|
||||
* ISC License
|
||||
*
|
||||
* Copyright (c) 2013-2020
|
||||
* Frank Denis <j at pureftpd dot org>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
Libsodium.js (temporarily included libsodium-wrappers-sumo-0.7.6.tgz packed npm package with additional sha256 and sha512 multipart wrappers)
|
||||
|
||||
|
||||
Copyright (c) 2015-2020
|
||||
Ahmad Ben Mrad <batikhsouri at gmail dot org>
|
||||
Frank Denis <j at pureftpd dot org>
|
||||
Ryan Lester <ryan at cyph dot com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
115
README.md
115
README.md
@ -5,58 +5,97 @@
|
||||
|
||||
Kotlin Multiplatform Crypto is a library for various cryptographic applications.
|
||||
|
||||
This is an extremely early release, currently only consisting of Blake2b and SHA256 and 512.
|
||||
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, 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: |
|
||||
|
||||
|
||||
API is very opinionated, ment to be used on both encrypting and decrypting side. The idea is that API leaves less room for
|
||||
errors when using it.
|
||||
|
||||
## Notes & Roadmap
|
||||
|
||||
**The API will move fast and break often until v1.0**
|
||||
|
||||
Make SHA hashes "updatable" like Blake2b
|
||||
|
||||
After that tenative plan is to add 25519 curve based signing and key exchange next.
|
||||
Next steps:
|
||||
- Expand API (AEAD, ECC ...)
|
||||
|
||||
## Should I use this in production?
|
||||
|
||||
No.
|
||||
No, until it is reviewed.
|
||||
|
||||
## Should I use this in code that is critical in any way, shape or form?
|
||||
## Should I use this in code that is *critical* in any way, shape or form?
|
||||
|
||||
No.
|
||||
No, but even if after being warned you decide to, then use `multiplatform-crypto-delegated` as it relies on reputable libraries.
|
||||
|
||||
## 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.
|
||||
|
||||
## Integration
|
||||
## Currently supported
|
||||
|
||||
|
||||
|
||||
## Hashing functions
|
||||
### Hashing functions
|
||||
* Blake2b
|
||||
* SHA512
|
||||
* SHA256
|
||||
|
||||
## Symmetric cipher
|
||||
### Symmetric cipher
|
||||
* AES
|
||||
* Modes: CBC, CTR
|
||||
|
||||
## Key Derivation
|
||||
### Key Derivation
|
||||
|
||||
* Argon2
|
||||
|
||||
## AEAD
|
||||
### AEAD
|
||||
|
||||
TODO()
|
||||
|
||||
|
||||
### 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 |
|
||||
| AES-CBC | LazySodium | libsodium.js | libsodium |
|
||||
| AES-CTR | LazySodium | libsodium.js | libsodium |
|
||||
|
||||
|
||||
## Integration
|
||||
|
||||
#### Gradle
|
||||
Kotlin
|
||||
```kotlin
|
||||
implementation("com.ionspin.kotlin:multiplatform-crypto:0.0.2")
|
||||
implementation("com.ionspin.kotlin:multiplatform-crypto:0.0.5")
|
||||
|
||||
or
|
||||
|
||||
implementation("com.ionspin.kotlin:multiplatform-crypto-delegated:0.0.5")
|
||||
```
|
||||
|
||||
#### Snapshot builds
|
||||
@ -66,12 +105,19 @@ repositories {
|
||||
url = uri("https://oss.sonatype.org/content/repositories/snapshots")
|
||||
}
|
||||
}
|
||||
implementation("com.ionspin.kotlin:multiplatform-crypto:0.0.3-SNAPSHOT")
|
||||
implementation("com.ionspin.kotlin:multiplatform-crypto:0.0.6-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,
|
||||
@ -87,23 +133,23 @@ You need to deliver the complete data that is to be hashed in one go
|
||||
|
||||
```kotlin
|
||||
val input = "abc"
|
||||
val result = Blake2b.digest(input)
|
||||
val result = Crypto.Blake2b.stateless(input.encodeToUByteArray())
|
||||
```
|
||||
|
||||
Result is returned as a `Array<Byte>`
|
||||
Result is returned as a `UByteArray`
|
||||
|
||||
##### Updatable instance version
|
||||
You can create an instance and feed the data by using `update(input : Array<Byte>)` call. Once all data is supplied,
|
||||
you should call `digest()` or `digestString()` convenience method that converts the `Array<Byte>` into hexadecimal string.
|
||||
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 = Blake2b(key)
|
||||
blake2b.update(test)
|
||||
val result = blake2b.digest()
|
||||
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).
|
||||
@ -111,36 +157,37 @@ After digest is called, the instance is reset and can be reused (Keep in mind ke
|
||||
|
||||
##### Stateless version
|
||||
|
||||
You need to deliver the complete data that is to be hashed in one go. You can either provide the `Array<Byte>` as input
|
||||
or `String`. Result is always returned as `Array<Byte>` (At least in verision 0.0.1)
|
||||
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 = Sha256.digest(input)
|
||||
val result = Crypto.Sha256.stateless(input.encodeToUByteArray())
|
||||
```
|
||||
|
||||
```kotlin
|
||||
val input ="abc"
|
||||
val result = Sha512.digest(message = input.encodeToByteArray().map { it.toUByte() }.toTypedArray())
|
||||
val result = Crypto.Sha512.stateless(input.encodeToUByteArray())
|
||||
```
|
||||
|
||||
Result is returned as a `Array<Byte>`
|
||||
Result is returned as a `UByteArray`
|
||||
|
||||
##### Updateable version
|
||||
|
||||
Or you can use the updatable instance version
|
||||
|
||||
```kotlin
|
||||
val sha256 = Sha256()
|
||||
sha256.update("abc")
|
||||
val sha256 = Crypto.Sha256.updateable()
|
||||
sha256.update("abc".encodeToUByteArray())
|
||||
val result = sha256.digest()
|
||||
```
|
||||
|
||||
```kotlin
|
||||
val sha512 = Sha512()
|
||||
sha512.update("abc")
|
||||
val sha512 = Crypto.Sha512.updateable()
|
||||
sha512.update("abc".encodeToUByteArray())
|
||||
val result = sha512.digest()
|
||||
```
|
||||
|
||||
### Symmetric encryption
|
||||
|
||||
#### AES
|
||||
|
@ -40,6 +40,7 @@ allprojects {
|
||||
google()
|
||||
maven ("https://kotlin.bintray.com/kotlinx")
|
||||
maven ("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||
maven ("https://kotlin.bintray.com/kotlin-dev")
|
||||
jcenter()
|
||||
maven {
|
||||
url = uri("https://oss.sonatype.org/content/repositories/snapshots")
|
||||
|
@ -21,5 +21,14 @@ plugins {
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven ("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4-M2")
|
||||
}
|
||||
|
||||
System.setProperty("PROJECT_PATH", project.projectDir.parentFile.toString())
|
||||
println("Path ${project.projectDir.parentFile}")
|
||||
|
@ -15,38 +15,57 @@
|
||||
*/
|
||||
|
||||
object Versions {
|
||||
val kotlinCoroutines = "1.3.5-native-mt-1.4-M1"
|
||||
val kotlin = "1.4-M1"
|
||||
val kotlinSerialization = "0.20.0-1.4-M1"
|
||||
val atomicfu = "0.14.2-1.4-M1"
|
||||
val kotlinCoroutines = "1.3.5-native-mt-arm-1.4-M2-SNAPSHOT" //NOTE: my linux arm32 and arm64 build
|
||||
val kotlin = "1.4-M2"
|
||||
val kotlinSerialization = "0.20.0-1.4-M2"
|
||||
val atomicfu = "0.14.3-M2-2-SNAPSHOT" //NOTE: my linux arm32 and arm64 build
|
||||
val nodePlugin = "1.3.0"
|
||||
val dokkaPlugin = "0.9.18"
|
||||
val taskTreePlugin = "1.5"
|
||||
|
||||
val kotlinBigNumVersion = "0.1.6-SNAPSHOT"
|
||||
val kotlinBigNumVersion = "0.1.6-1.4-M2-SNAPSHOT"
|
||||
|
||||
val lazySodium = "4.2.6"
|
||||
val jna = "5.5.0"
|
||||
|
||||
|
||||
}
|
||||
|
||||
object ReleaseInfo {
|
||||
val group = "com.ionspin.kotlin"
|
||||
val version = "0.1.0-SNAPSHOT"
|
||||
}
|
||||
|
||||
object Deps {
|
||||
|
||||
object Common {
|
||||
val stdLib = "stdlib-common"
|
||||
val test = "test-common"
|
||||
val testAnnotation = "test-annotations-common"
|
||||
val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:${Versions.kotlinCoroutines}"
|
||||
// val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:${Versions.kotlinCoroutines}"
|
||||
val coroutines = "com.ionspin.kotlin.coroutines:kotlinx-coroutines-core:${Versions.kotlinCoroutines}"
|
||||
val serialization = "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:${Versions.kotlinSerialization}"
|
||||
val atomicfu = "org.jetbrains.kotlinx:atomicfu:${Versions.atomicfu}"
|
||||
val atomicfu = "com.ionspin.kotlin.atomicfu:atomicfu:${Versions.atomicfu}"
|
||||
|
||||
|
||||
val kotlinBigNum = "com.ionspin.kotlin:bignum:${Versions.kotlinBigNumVersion}"
|
||||
|
||||
val apiProject = ":multiplatform-crypto-api"
|
||||
}
|
||||
|
||||
object Js {
|
||||
val stdLib = "stdlib-js"
|
||||
val test = "test-js"
|
||||
val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:${Versions.kotlinCoroutines}"
|
||||
// val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:${Versions.kotlinCoroutines}"
|
||||
val coroutines = "com.ionspin.kotlin.coroutines:kotlinx-coroutines-core:${Versions.kotlinCoroutines}"
|
||||
val serialization = "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:${Versions.kotlinSerialization}"
|
||||
|
||||
object Npm {
|
||||
val libsodium = Pair("libsodium-wrappers-sumo", "0.7.6")
|
||||
// val libsodiumWrappers = Pair("libsodium-wrappers-sumo", "0.7.6")
|
||||
val libsodiumWrappers = Pair("libsodium-wrappers-sumo", "file:${getProjectPath()}/multiplatform-crypto-delegated/libsodium-wrappers-sumo-0.7.6.tgz")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object Jvm {
|
||||
@ -54,20 +73,29 @@ object Deps {
|
||||
val test = "test"
|
||||
val testJUnit = "test-junit"
|
||||
val reflection = "reflect"
|
||||
val coroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.kotlinCoroutines}"
|
||||
// val coroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.kotlinCoroutines}"
|
||||
val coroutinesCore = "com.ionspin.kotlin.coroutines:kotlinx-coroutines-core:${Versions.kotlinCoroutines}"
|
||||
val coroutinesjdk8 = "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:${Versions.kotlinCoroutines}"
|
||||
val serialization = "org.jetbrains.kotlinx:kotlinx-serialization-runtime:${Versions.kotlinSerialization}"
|
||||
val coroutinesTest = "org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.kotlinCoroutines}"
|
||||
// val coroutinesTest = "org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.kotlinCoroutines}"
|
||||
val coroutinesTest = "com.ionspin.kotlin.coroutines:kotlinx-coroutines-test:${Versions.kotlinCoroutines}"
|
||||
|
||||
object Delegated {
|
||||
val lazysodium = "com.goterl.lazycode:lazysodium-java:${Versions.lazySodium}"
|
||||
val jna = "net.java.dev.jna:jna:${Versions.jna}"
|
||||
}
|
||||
}
|
||||
|
||||
object iOs {
|
||||
val serialization = "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:${Versions.kotlinSerialization}"
|
||||
val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:${Versions.kotlinCoroutines}"
|
||||
// val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:${Versions.kotlinCoroutines}"
|
||||
val coroutines = "com.ionspin.kotlin.coroutines:kotlinx-coroutines-core:${Versions.kotlinCoroutines}"
|
||||
}
|
||||
|
||||
object Native {
|
||||
val serialization = "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:${Versions.kotlinSerialization}"
|
||||
val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:${Versions.kotlinCoroutines}"
|
||||
// val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:${Versions.kotlinCoroutines}"
|
||||
val coroutines = "com.ionspin.kotlin.coroutines:kotlinx-coroutines-core:${Versions.kotlinCoroutines}"
|
||||
|
||||
}
|
||||
|
||||
@ -81,5 +109,6 @@ object PluginsDeps {
|
||||
val mavenPublish = "maven-publish"
|
||||
val signing = "signing"
|
||||
val dokka = "org.jetbrains.dokka"
|
||||
val taskTree = "com.dorongold.task-tree"
|
||||
}
|
||||
|
||||
|
112
buildSrc/src/main/kotlin/Utils.kt
Normal file
112
buildSrc/src/main/kotlin/Utils.kt
Normal file
@ -0,0 +1,112 @@
|
||||
import org.gradle.api.NamedDomainObjectContainer
|
||||
import org.gradle.nativeplatform.platform.internal.Architectures
|
||||
import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 30-May-2020
|
||||
*/
|
||||
fun isInIdea() = System.getProperty("idea.active") == "true"
|
||||
|
||||
fun isInTravis() = System.getenv("TRAVIS") == "true"
|
||||
|
||||
fun getProjectPath() : String {
|
||||
val path = System.getProperty("PROJECT_PATH")
|
||||
return path
|
||||
}
|
||||
|
||||
fun getHostOsName(): String {
|
||||
val target = System.getProperty("os.name")
|
||||
if (target == "Linux") return "linux"
|
||||
if (target.startsWith("Windows")) return "windows"
|
||||
if (target.startsWith("Mac")) return "macos"
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
fun getHostArchitecture(): String {
|
||||
val architecture = System.getProperty("os.arch")
|
||||
DefaultNativePlatform.getCurrentArchitecture()
|
||||
println("Arch: $architecture")
|
||||
val resolvedArch = Architectures.forInput(architecture).name
|
||||
println("Resolved arch: $resolvedArch")
|
||||
return resolvedArch
|
||||
}
|
||||
|
||||
fun KotlinMultiplatformExtension.isRunningInIdea(block: KotlinMultiplatformExtension.() -> Unit) {
|
||||
if (isInIdea()) {
|
||||
block(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinMultiplatformExtension.isNotRunningInIdea(block: KotlinMultiplatformExtension.() -> Unit) {
|
||||
if (!isInIdea()) {
|
||||
block(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinMultiplatformExtension.isRunningInTravis(block: KotlinMultiplatformExtension.() -> Unit) {
|
||||
if (isInTravis()) {
|
||||
block(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinMultiplatformExtension.runningOnLinuxx86_64(block: KotlinMultiplatformExtension.() -> Unit) {
|
||||
if (getHostOsName() == "linux" && getHostArchitecture() == "x86-64") {
|
||||
block(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinMultiplatformExtension.runningOnLinuxArm64(block: KotlinMultiplatformExtension.() -> Unit) {
|
||||
if (getHostOsName() == "linux" && getHostArchitecture() == "aarch64") {
|
||||
block(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinMultiplatformExtension.runningOnLinuxArm32(block: KotlinMultiplatformExtension.() -> Unit) {
|
||||
if (getHostOsName() == "linux" && getHostArchitecture() == "arm-v7") {
|
||||
block(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinMultiplatformExtension.runningOnMacos(block: KotlinMultiplatformExtension.() -> Unit) {
|
||||
if (getHostOsName() == "macos") {
|
||||
block(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinMultiplatformExtension.runningOnWindows(block: KotlinMultiplatformExtension.() -> Unit) {
|
||||
if (getHostOsName() == "windows") {
|
||||
block(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun independentDependencyBlock(nativeDeps: KotlinDependencyHandler.() -> Unit): KotlinDependencyHandler.() -> Unit {
|
||||
return nativeDeps
|
||||
}
|
||||
|
||||
/**
|
||||
* On mac when two targets that have the same parent source set have cinterops defined, gradle creates a "common"
|
||||
* target task for that source set metadata, even though it's a native source set, to work around that, we create
|
||||
* an intermediary source set with the same set of dependancies
|
||||
*
|
||||
*/
|
||||
fun NamedDomainObjectContainer<KotlinSourceSet>.createWorkaroundNativeMainSourceSet(
|
||||
name: String,
|
||||
nativeDeps: KotlinDependencyHandler.() -> Unit
|
||||
): KotlinSourceSet {
|
||||
|
||||
return create("${name}Workaround") {
|
||||
if (!isInIdea()) {
|
||||
kotlin.srcDir("src/nativeMain")
|
||||
dependencies {
|
||||
nativeDeps.invoke(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -17,7 +17,9 @@ org.gradle.parallel=true
|
||||
|
||||
kotlin.code.style=official
|
||||
|
||||
kotlin.js.compiler=both
|
||||
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||
kotlin.js.compiler=ir
|
||||
#kotlin.js.experimental.generateKotlinExternals=true
|
||||
#kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||
kotlin.native.disableCompilerDaemon=true
|
||||
|
||||
org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=4096m
|
||||
|
23
linuxBuild.sh
Normal file → Executable file
23
linuxBuild.sh
Normal file → Executable file
@ -1 +1,22 @@
|
||||
./gradlew build
|
||||
set -e
|
||||
#!/bin/sh
|
||||
#this will hopefully download all konan dependancies that we use in the build scripts
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
#now let's build linux deps
|
||||
export CLANG_BIN=$HOME/.konan/dependencies/clang-llvm-8.0.0-linux-x86-64/bin
|
||||
cd sodiumWrapper
|
||||
./makeLinuxX86-64.sh
|
||||
#Workaround for travis using wrong ld
|
||||
if [ "$TRAVIS" = "true" ]
|
||||
then
|
||||
sudo mv /usr/bin/ld /usr/bin/ld.bck
|
||||
sudo ln -s $CLANG_BIN/ld.lld /usr/bin/ld
|
||||
fi
|
||||
./makeLinuxArm64.sh
|
||||
#now we can do the delegated build
|
||||
cd ..
|
||||
./gradlew multiplatform-crypto-delegated:build
|
||||
#and finally pure build
|
||||
./gradlew multiplatform-crypto:build
|
||||
set +e
|
||||
|
||||
|
25
linuxBuildAndPublish.sh
Normal file → Executable file
25
linuxBuildAndPublish.sh
Normal file → Executable file
@ -1 +1,24 @@
|
||||
./gradlew build publishJvmPublicationToSnapshotRepository publishJsPublicationToSnapshotRepository publishKotlinMultiplatformPublicationToSnapshotRepository publishLinuxPublicationToSnapshotRepository publishMetadataPublicationToSnapshotRepository
|
||||
set -e
|
||||
#!/bin/sh
|
||||
#this will hopefully download all konan dependancies that we use in the build scripts
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
#now let's build linux deps
|
||||
export CLANG_BIN=$HOME/.konan/dependencies/clang-llvm-8.0.0-linux-x86-64/bin
|
||||
cd sodiumWrapper
|
||||
./makeLinuxX86-64.sh
|
||||
#Workaround for travis using wrong ld
|
||||
if [ "$TRAVIS" = "true" ]
|
||||
then
|
||||
sudo mv /usr/bin/ld /usr/bin/ld.bck
|
||||
sudo ln -s $CLANG_BIN/ld.lld /usr/bin/ld
|
||||
fi
|
||||
./makeLinuxArm64.sh
|
||||
#now we can do the delegated build
|
||||
cd ..
|
||||
./gradlew multiplatform-crypto-delegated:build
|
||||
#and finally pure build
|
||||
./gradlew multiplatform-crypto:build
|
||||
./gradlew publishJvmPublicationToSnapshotRepository publishJsPublicationToSnapshotRepository \
|
||||
publishKotlinMultiplatformPublicationToSnapshotRepository publishLinuxX64PublicationToSnapshotRepository \
|
||||
publishLinuxArm64PublicationToSnapshotRepository publishMetadataPublicationToSnapshotRepository
|
||||
set +e
|
18
macBuild-mac-ios.sh
Executable file
18
macBuild-mac-ios.sh
Executable file
@ -0,0 +1,18 @@
|
||||
set -e
|
||||
#!/bin/sh
|
||||
#this will hopefully download all konan dependancies that we use in the build scripts
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
#now let's build linux deps
|
||||
cd sodiumWrapper
|
||||
./makeMacosX86-64.sh
|
||||
./makeIos.sh
|
||||
#now we can do the delegated build of ios and macos libraries
|
||||
cd ..
|
||||
./gradlew multiplatform-crypto-delegated:iosArm32MainKlibrary multiplatform-crypto-delegated:iosArm32TestKlibrary \
|
||||
multiplatform-crypto-delegated:iosArm64MainKlibrary multiplatform-crypto-delegated:iosArm64TestKlibrary \
|
||||
multiplatform-crypto-delegated:iosX64MainKlibrary multiplatform-crypto-delegated:iosX64TestKlibrary \
|
||||
multiplatform-crypto-delegated:macosX64MainKlibrary multiplatform-crypto-delegated:macosX64TestKlibrary
|
||||
./gradlew multiplatform-crypto-delegated:iosX64Test
|
||||
./gradlew multiplatform-crypto-delegated:macosX64Test
|
||||
set +e
|
||||
|
5
macBuild-pure.sh
Executable file
5
macBuild-pure.sh
Executable file
@ -0,0 +1,5 @@
|
||||
set -e
|
||||
#!/bin/sh
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
./gradlew multiplatform-crypto:build
|
||||
set +e
|
13
macBuild-tvos.sh
Executable file
13
macBuild-tvos.sh
Executable file
@ -0,0 +1,13 @@
|
||||
set -e
|
||||
#!/bin/sh
|
||||
#this will hopefully download all konan dependancies that we use in the build scripts
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
#now let's build linux deps
|
||||
cd sodiumWrapper
|
||||
./makeTvos.sh
|
||||
#now we can do the delegated build of ios and macos libraries
|
||||
cd ..
|
||||
./gradlew multiplatform-crypto-delegated:tvosArm64MainKlibrary multiplatform-crypto-delegated:tvosArm64TestKlibrary \
|
||||
multiplatform-crypto-delegated:tvosX64MainKlibrary multiplatform-crypto-delegated:tvosX64TestKlibrary
|
||||
./gradlew multiplatform-crypto-delegated:tvosX64Test
|
||||
set +e
|
14
macBuild-watchos.sh
Executable file
14
macBuild-watchos.sh
Executable file
@ -0,0 +1,14 @@
|
||||
set -e
|
||||
#!/bin/sh
|
||||
#this will hopefully download all konan dependancies that we use in the build scripts
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
#now let's build linux deps
|
||||
cd sodiumWrapper
|
||||
./makeWatchos.sh
|
||||
#now we can do the delegated build of ios and macos libraries
|
||||
cd ..
|
||||
./gradlew multiplatform-crypto-delegated:watchosArm32MainKlibrary multiplatform-crypto-delegated:watchosArm32TestKlibrary \
|
||||
multiplatform-crypto-delegated:watchosArm64MainKlibrary multiplatform-crypto-delegated:watchosArm64TestKlibrary \
|
||||
multiplatform-crypto-delegated:watchosX86MainKlibrary multiplatform-crypto-delegated:watchosX86TestKlibrary
|
||||
./gradlew multiplatform-crypto-delegated:watchosX86Test
|
||||
set +e
|
17
macBuild.sh
Normal file → Executable file
17
macBuild.sh
Normal file → Executable file
@ -1 +1,16 @@
|
||||
./gradlew build
|
||||
set -e
|
||||
#!/bin/sh
|
||||
#this will hopefully download all konan dependancies that we use in the build scripts
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
#now let's build linux deps
|
||||
cd sodiumWrapper
|
||||
./makeMacosX86-64.sh
|
||||
./makeIos.sh
|
||||
./makeTvos.sh
|
||||
./makeWatchos.sh
|
||||
#now we can do the delegated build
|
||||
cd ..
|
||||
./gradlew multiplatform-crypto-delegated:build
|
||||
#and finally pure build
|
||||
./gradlew multiplatform-crypto:build
|
||||
set +e
|
15
macBuildAndPublish-mac-ios.sh
Executable file
15
macBuildAndPublish-mac-ios.sh
Executable file
@ -0,0 +1,15 @@
|
||||
set -e
|
||||
#!/bin/sh
|
||||
#this will hopefully download all konan dependancies that we use in the build scripts
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
#now let's build linux deps
|
||||
cd sodiumWrapper
|
||||
./makeMacosX86-64.sh
|
||||
./makeIos.sh
|
||||
#now we can do the delegated build of ios and macos libraries
|
||||
cd ..
|
||||
./gradlew multiplatform-crypto-delegated:publishIosArm32PublicationToSnapshotRepository \
|
||||
multiplatform-crypto-delegated:publishIosArm64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto-delegated:publishIosX64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto-delegated:publishMacosX64PublicationToSnapshotRepository
|
||||
set +e
|
14
macBuildAndPublish-pure.sh
Executable file
14
macBuildAndPublish-pure.sh
Executable file
@ -0,0 +1,14 @@
|
||||
set -e
|
||||
#!/bin/sh
|
||||
#this will hopefully download all konan dependancies that we use in the build scripts
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
./gradlew multiplatform-crypto:publishIosArm32PublicationToSnapshotRepository \
|
||||
multiplatform-crypto:publishIosArm64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto:publishIosX64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto:publishMacosX64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto:publishTvosArm64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto:publishTvosX64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto:publishWatchosArm32PublicationToSnapshotRepository \
|
||||
multiplatform-crypto:publishWatchosArm64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto:publishWatchosX86PublicationToSnapshotRepository
|
||||
set +e
|
12
macBuildAndPublish-tvos.sh
Executable file
12
macBuildAndPublish-tvos.sh
Executable file
@ -0,0 +1,12 @@
|
||||
set -e
|
||||
#!/bin/sh
|
||||
#this will hopefully download all konan dependancies that we use in the build scripts
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
#now let's build linux deps
|
||||
cd sodiumWrapper
|
||||
./makeTvos.sh
|
||||
#now we can do the delegated build of ios and macos libraries
|
||||
cd ..
|
||||
./gradlew multiplatform-crypto-delegated:publishTvosArm64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto-delegated:publishTvosX64PublicationToSnapshotRepository
|
||||
set +e
|
13
macBuildAndPublish-watchos.sh
Executable file
13
macBuildAndPublish-watchos.sh
Executable file
@ -0,0 +1,13 @@
|
||||
set -e
|
||||
#!/bin/sh
|
||||
#this will hopefully download all konan dependancies that we use in the build scripts
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
#now let's build linux deps
|
||||
cd sodiumWrapper
|
||||
./makeWatchos.sh
|
||||
#now we can do the delegated build of ios and macos libraries
|
||||
cd ..
|
||||
./gradlew multiplatform-crypto-delegated:publishWatchosArm32PublicationToSnapshotRepository \
|
||||
multiplatform-crypto-delegated:publishWatchosArm64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto-delegated:publishWatchosX86PublicationToSnapshotRepository
|
||||
set +e
|
@ -1 +0,0 @@
|
||||
./gradlew build publishIos64ArmPublicationToSnapshotRepository publishIosPublicationToSnapshotRepository publishMacosX64PublicationToSnapshotRepository publishIos32ArmPublicationToSnapshotRepository
|
@ -1 +0,0 @@
|
||||
./gradlew publishAllPublicationsToMavenRepository -x publishMetadataPublicationToMavenRepository -x publishKotlinMultiplatformPublicationToMavenRepository
|
344
multiplatform-crypto-api/build.gradle.kts
Normal file
344
multiplatform-crypto-api/build.gradle.kts
Normal file
@ -0,0 +1,344 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
|
||||
import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest
|
||||
|
||||
plugins {
|
||||
kotlin(PluginsDeps.multiplatform)
|
||||
id (PluginsDeps.mavenPublish)
|
||||
id (PluginsDeps.signing)
|
||||
id (PluginsDeps.dokka) version Versions.dokkaPlugin
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
|
||||
}
|
||||
group = ReleaseInfo.group
|
||||
version = ReleaseInfo.version
|
||||
|
||||
val ideaActive = System.getProperty("idea.active") == "true"
|
||||
|
||||
|
||||
|
||||
kotlin {
|
||||
val hostOsName = getHostOsName()
|
||||
runningOnLinuxx86_64 {
|
||||
jvm()
|
||||
js {
|
||||
browser {
|
||||
testTask {
|
||||
enabled = false //Until I sort out testing on travis
|
||||
useKarma {
|
||||
useChrome()
|
||||
}
|
||||
}
|
||||
}
|
||||
nodejs {
|
||||
testTask {
|
||||
useMocha() {
|
||||
timeout = "10s"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
linuxX64("linux") {
|
||||
binaries {
|
||||
staticLib {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Not supported in OFFICIAL coroutines at the moment
|
||||
linuxArm64() {
|
||||
binaries {
|
||||
staticLib {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Not supported in OFFICAL coroutines at the moment
|
||||
linuxArm32Hfp() {
|
||||
binaries {
|
||||
staticLib {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
runningOnLinuxArm64 {
|
||||
|
||||
}
|
||||
|
||||
runningOnLinuxArm32 {
|
||||
|
||||
}
|
||||
|
||||
runningOnMacos {
|
||||
iosX64() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
iosArm64() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
iosArm32() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macosX64() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tvosX64() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tvosArm64() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watchosArm64() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watchosArm32() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watchosX86() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
runningOnWindows {
|
||||
|
||||
mingwX64() {
|
||||
binaries {
|
||||
staticLib {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mingwX86() {
|
||||
binaries {
|
||||
staticLib {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
println(targets.names)
|
||||
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Common.stdLib))
|
||||
implementation(kotlin(Deps.Common.test))
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Common.test))
|
||||
implementation(kotlin(Deps.Common.testAnnotation))
|
||||
}
|
||||
}
|
||||
|
||||
runningOnLinuxx86_64 {
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Jvm.stdLib))
|
||||
implementation(kotlin(Deps.Jvm.test))
|
||||
implementation(kotlin(Deps.Jvm.testJUnit))
|
||||
}
|
||||
}
|
||||
val jvmTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Jvm.test))
|
||||
implementation(kotlin(Deps.Jvm.testJUnit))
|
||||
implementation(kotlin(Deps.Jvm.reflection))
|
||||
}
|
||||
}
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Js.stdLib))
|
||||
}
|
||||
}
|
||||
val jsTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Js.test))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runningOnMacos {
|
||||
val tvosX64Main by getting {
|
||||
dependsOn(commonMain)
|
||||
}
|
||||
|
||||
val tvosArm64Main by getting {
|
||||
dependsOn(commonMain)
|
||||
}
|
||||
|
||||
val watchosX86Main by getting {
|
||||
dependsOn(commonMain)
|
||||
}
|
||||
|
||||
val watchosArm64Main by getting {
|
||||
dependsOn(commonMain)
|
||||
}
|
||||
|
||||
val watchosArm32Main by getting {
|
||||
dependsOn(commonMain)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
all {
|
||||
languageSettings.enableLanguageFeature("InlineClasses")
|
||||
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
|
||||
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalStdlibApi")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
tasks {
|
||||
create<Jar>("javadocJar") {
|
||||
dependsOn(dokka)
|
||||
archiveClassifier.set("javadoc")
|
||||
from(dokka.get().outputDirectory)
|
||||
}
|
||||
|
||||
dokka {
|
||||
println ("Dokka !")
|
||||
impliedPlatforms = mutableListOf("Common")
|
||||
kotlinTasks {
|
||||
listOf()
|
||||
}
|
||||
sourceRoot {
|
||||
println ("Common !")
|
||||
path = "/home/ionspin/Projects/Future/kotlin-multiplatform-crypto/crypto/src/commonMain" //TODO remove static path!
|
||||
platforms = listOf("Common")
|
||||
}
|
||||
}
|
||||
if (getHostOsName() == "linux" && getHostArchitecture() == "x86-64") {
|
||||
|
||||
val jvmTest by getting(Test::class) {
|
||||
testLogging {
|
||||
events("PASSED", "FAILED", "SKIPPED")
|
||||
}
|
||||
}
|
||||
|
||||
val linuxTest by getting(KotlinNativeTest::class) {
|
||||
|
||||
testLogging {
|
||||
events("PASSED", "FAILED", "SKIPPED")
|
||||
// showStandardStreams = true
|
||||
}
|
||||
}
|
||||
|
||||
val jsNodeTest by getting(KotlinJsTest::class) {
|
||||
testLogging {
|
||||
events("PASSED", "FAILED", "SKIPPED")
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
||||
|
||||
// val legacyjsNodeTest by getting(KotlinJsTest::class) {
|
||||
//
|
||||
// testLogging {
|
||||
// events("PASSED", "FAILED", "SKIPPED")
|
||||
// showStandardStreams = true
|
||||
// }
|
||||
// }
|
||||
|
||||
// val jsIrBrowserTest by getting(KotlinJsTest::class) {
|
||||
// testLogging {
|
||||
// events("PASSED", "FAILED", "SKIPPED")
|
||||
// showStandardStreams = true
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
if (getHostOsName() == "windows") {
|
||||
val mingwX64Test by getting(KotlinNativeTest::class) {
|
||||
|
||||
testLogging {
|
||||
events("PASSED", "FAILED", "SKIPPED")
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,12 @@
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 27-May-2020
|
||||
*/
|
||||
interface CryptoProvider {
|
||||
suspend fun initialize()
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package _multiplatform_crypto_api
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 24-May-2020
|
||||
*/
|
||||
//Workaround for https://youtrack.jetbrains.com/issue/KT-36878
|
||||
val byteArray = byteArrayOf(0)
|
||||
val byte = 0.toByte()
|
||||
val longArray = longArrayOf(0)
|
||||
val long = 0L
|
@ -26,25 +26,20 @@ interface Hash {
|
||||
val MAX_HASH_BYTES : Int
|
||||
}
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
|
||||
interface UpdatableHash : Hash {
|
||||
fun update(data : Array<UByte>)
|
||||
fun update(data : UByteArray)
|
||||
|
||||
fun update(data : String)
|
||||
fun digest() : UByteArray
|
||||
|
||||
fun digest() : Array<UByte>
|
||||
|
||||
fun digestString() : String
|
||||
}
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
|
||||
interface StatelessHash : Hash {
|
||||
fun digest(inputString: String, key: String? = null, hashLength: Int = MAX_HASH_BYTES): Array<UByte>
|
||||
|
||||
fun digest(
|
||||
inputMessage: Array<UByte> = emptyArray(),
|
||||
key: Array<UByte> = emptyArray(),
|
||||
hashLength: Int = MAX_HASH_BYTES
|
||||
): Array<UByte>
|
||||
}
|
||||
|
||||
fun String.encodeToUByteArray() : UByteArray{
|
||||
return encodeToByteArray().toUByteArray()
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
package com.ionspin.kotlin.crypto.hash.blake2b
|
||||
|
||||
import com.ionspin.kotlin.crypto.hash.StatelessHash
|
||||
import com.ionspin.kotlin.crypto.hash.UpdatableHash
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 24-May-2020
|
||||
*/
|
||||
|
||||
object Blake2bProperties {
|
||||
const val MAX_HASH_BYTES = 64
|
||||
}
|
||||
|
||||
interface Blake2b : UpdatableHash {
|
||||
override val MAX_HASH_BYTES: Int
|
||||
get() = Blake2bProperties.MAX_HASH_BYTES
|
||||
}
|
||||
|
||||
interface Blake2bStateless : StatelessHash {
|
||||
override val MAX_HASH_BYTES: Int
|
||||
get() = Blake2bProperties.MAX_HASH_BYTES
|
||||
|
||||
fun digest(
|
||||
inputMessage: UByteArray = ubyteArrayOf(),
|
||||
key: UByteArray = ubyteArrayOf(),
|
||||
hashLength: Int = MAX_HASH_BYTES
|
||||
): UByteArray
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.hash.StatelessHash
|
||||
import com.ionspin.kotlin.crypto.hash.UpdatableHash
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 24-May-2020
|
||||
*/
|
||||
object Sha256Properties {
|
||||
const val MAX_HASH_BYTES = 32
|
||||
}
|
||||
|
||||
interface Sha256 : UpdatableHash {
|
||||
override val MAX_HASH_BYTES: Int
|
||||
get() = Sha256Properties.MAX_HASH_BYTES
|
||||
}
|
||||
interface StatelessSha256 : StatelessHash {
|
||||
override val MAX_HASH_BYTES: Int
|
||||
get() = Sha256Properties.MAX_HASH_BYTES
|
||||
|
||||
fun digest(
|
||||
inputMessage: UByteArray = ubyteArrayOf()
|
||||
): UByteArray
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.hash.StatelessHash
|
||||
import com.ionspin.kotlin.crypto.hash.UpdatableHash
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 24-May-2020
|
||||
*/
|
||||
object Sha512Properties {
|
||||
const val MAX_HASH_BYTES = 64
|
||||
}
|
||||
interface Sha512 : UpdatableHash {
|
||||
override val MAX_HASH_BYTES: Int
|
||||
get() = Sha256Properties.MAX_HASH_BYTES
|
||||
}
|
||||
interface StatelessSha512 : StatelessHash {
|
||||
override val MAX_HASH_BYTES: Int
|
||||
get() = Sha512Properties.MAX_HASH_BYTES
|
||||
|
||||
fun digest(
|
||||
inputMessage: UByteArray = ubyteArrayOf()
|
||||
): UByteArray
|
||||
}
|
@ -22,5 +22,5 @@ package com.ionspin.kotlin.crypto.keyderivation
|
||||
* on 16-May-2020
|
||||
*/
|
||||
interface KeyDerivationFunction {
|
||||
fun derive() : Array<UByte>
|
||||
fun derive() : UByteArray
|
||||
}
|
667
multiplatform-crypto-delegated/build.gradle.kts
Normal file
667
multiplatform-crypto-delegated/build.gradle.kts
Normal file
@ -0,0 +1,667 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
|
||||
import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest
|
||||
|
||||
plugins {
|
||||
kotlin(PluginsDeps.multiplatform)
|
||||
id(PluginsDeps.mavenPublish)
|
||||
id(PluginsDeps.signing)
|
||||
id(PluginsDeps.node) version Versions.nodePlugin
|
||||
id(PluginsDeps.dokka) version Versions.dokkaPlugin
|
||||
id(PluginsDeps.taskTree) version Versions.taskTreePlugin
|
||||
}
|
||||
|
||||
val sonatypeStaging = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||
val sonatypeSnapshots = "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||
|
||||
val sonatypePassword: String? by project
|
||||
|
||||
val sonatypeUsername: String? by project
|
||||
|
||||
val sonatypePasswordEnv: String? = System.getenv()["SONATYPE_PASSWORD"]
|
||||
val sonatypeUsernameEnv: String? = System.getenv()["SONATYPE_USERNAME"]
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
|
||||
}
|
||||
group = ReleaseInfo.group
|
||||
version = ReleaseInfo.version
|
||||
|
||||
val ideaActive = isInIdea()
|
||||
println("Idea active: $ideaActive")
|
||||
|
||||
|
||||
|
||||
kotlin {
|
||||
val hostOsName = getHostOsName()
|
||||
runningOnLinuxx86_64 {
|
||||
println("Configuring Linux X86-64 targets")
|
||||
jvm()
|
||||
js {
|
||||
browser {
|
||||
testTask {
|
||||
isRunningInTravis {
|
||||
enabled = false //Until I sort out testing on travis
|
||||
}
|
||||
useKarma {
|
||||
useChrome()
|
||||
}
|
||||
}
|
||||
}
|
||||
nodejs {
|
||||
testTask {
|
||||
useMocha() {
|
||||
timeout = "10s"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
linuxX64() {
|
||||
compilations.getByName("main") {
|
||||
val libsodiumCinterop by cinterops.creating {
|
||||
defFile(project.file("src/nativeInterop/cinterop/libsodium.def"))
|
||||
compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-linux-x86-64/include/")
|
||||
}
|
||||
kotlinOptions.freeCompilerArgs = listOf(
|
||||
"-include-binary", "${project.rootDir}/sodiumWrapper/static-linux-x86-64/lib/libsodium.a"
|
||||
)
|
||||
}
|
||||
binaries {
|
||||
staticLib {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
linuxArm64() {
|
||||
binaries {
|
||||
staticLib {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Linux 32 is using target-sysroot-2-raspberrypi which is missing getrandom and explicit_bzero in stdlib
|
||||
// so konanc can't build klib because getrandom missing will cause sodium_misuse()
|
||||
// ld.lld: error: undefined symbol: explicit_bzero
|
||||
// >>> referenced by utils.c
|
||||
// >>> libsodium_la-utils.o:(sodium_memzero) in archive /tmp/included11051337748775083797/libsodium.a
|
||||
//
|
||||
// ld.lld: error: undefined symbol: getrandom
|
||||
// >>> referenced by randombytes_sysrandom.c
|
||||
// >>> libsodium_la-randombytes_sysrandom.o:(_randombytes_linux_getrandom) in archive /tmp/included11051337748775083797/libsodium.a
|
||||
|
||||
// linuxArm32Hfp() {
|
||||
// binaries {
|
||||
// staticLib {
|
||||
// }
|
||||
// }
|
||||
// compilations.getByName("main") {
|
||||
// val libsodiumCinterop by cinterops.creating {
|
||||
// defFile(project.file("src/nativeInterop/cinterop/libsodium.def"))
|
||||
// compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-arm32/include/")
|
||||
// }
|
||||
// kotlinOptions.freeCompilerArgs = listOf(
|
||||
// "-include-binary", "${project.rootDir}/sodiumWrapper/static-arm32/lib/libsodium.a"
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
//Not supported in OFFICIAL coroutines at the moment (we're running a custom build)
|
||||
runningOnLinuxArm64 {
|
||||
println("Configuring Linux Arm 64 targets")
|
||||
|
||||
}
|
||||
|
||||
runningOnLinuxArm32 {
|
||||
println("Configuring Linux Arm 32 targets")
|
||||
|
||||
}
|
||||
|
||||
runningOnMacos {
|
||||
println("Configuring macos targets")
|
||||
iosX64() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
iosArm64() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iosArm32() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
macosX64() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
compilations.getByName("main") {
|
||||
val libsodiumCinterop by cinterops.creating {
|
||||
defFile(project.file("src/nativeInterop/cinterop/libsodium.def"))
|
||||
compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-macos-x86-64/include")
|
||||
}
|
||||
kotlinOptions.freeCompilerArgs = listOf(
|
||||
"-include-binary", "${project.rootDir}/sodiumWrapper/static-macos-x86-64/lib/libsodium.a"
|
||||
)
|
||||
}
|
||||
}
|
||||
tvosX64() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tvosArm64() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watchosArm64() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watchosArm32() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watchosX86() {
|
||||
binaries {
|
||||
framework {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
runningOnWindows {
|
||||
println("Configuring Mingw targets")
|
||||
mingwX64() {
|
||||
binaries {
|
||||
staticLib {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
compilations.getByName("main") {
|
||||
val libsodiumCinterop by cinterops.creating {
|
||||
defFile(project.file("src/nativeInterop/cinterop/libsodium.def"))
|
||||
compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-mingw-x86-64/include")
|
||||
}
|
||||
kotlinOptions.freeCompilerArgs = listOf(
|
||||
"-include-binary", "${project.rootDir}/sodiumWrapper/static-mingw-x86-64/lib/libsodium.a"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println(targets.names)
|
||||
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Common.stdLib))
|
||||
implementation(kotlin(Deps.Common.test))
|
||||
implementation(Deps.Common.coroutines)
|
||||
implementation(Deps.Common.kotlinBigNum)
|
||||
api(project(Deps.Common.apiProject))
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Common.test))
|
||||
implementation(kotlin(Deps.Common.testAnnotation))
|
||||
}
|
||||
}
|
||||
|
||||
val nativeDependencies = independentDependencyBlock {
|
||||
implementation(Deps.Native.coroutines)
|
||||
}
|
||||
|
||||
val nativeMain by creating {
|
||||
dependsOn(commonMain)
|
||||
isRunningInIdea {
|
||||
kotlin.setSrcDirs(emptySet<String>())
|
||||
}
|
||||
dependencies {
|
||||
nativeDependencies(this)
|
||||
}
|
||||
}
|
||||
|
||||
val nativeTest by creating {
|
||||
dependsOn(commonTest)
|
||||
isRunningInIdea {
|
||||
kotlin.setSrcDirs(emptySet<String>())
|
||||
}
|
||||
dependencies {
|
||||
implementation(Deps.Native.coroutines)
|
||||
}
|
||||
}
|
||||
|
||||
//Set up shared source sets
|
||||
//linux, linuxArm32Hfp, linuxArm64
|
||||
val linux64Bit = setOf(
|
||||
"linuxX64"
|
||||
)
|
||||
val linuxArm64Bit = setOf(
|
||||
"linuxArm64"
|
||||
)
|
||||
val linux32Bit = setOf(
|
||||
"" // "linuxArm32Hfp"
|
||||
)
|
||||
|
||||
//iosArm32, iosArm64, iosX64, macosX64, metadata, tvosArm64, tvosX64, watchosArm32, watchosArm64, watchosX86
|
||||
val macos64Bit = setOf(
|
||||
"macosX64"
|
||||
)
|
||||
val ios64Bit = setOf(
|
||||
"iosArm64", "iosX64"
|
||||
)
|
||||
val ios32Bit = setOf(
|
||||
"iosArm32"
|
||||
)
|
||||
val mingw64Bit = setOf(
|
||||
"mingwX64"
|
||||
)
|
||||
|
||||
val tvos64Bit = setOf(
|
||||
"tvosArm64", "tvosX64"
|
||||
)
|
||||
|
||||
val watchos32Bit = setOf(
|
||||
"watchosX86", "watchosArm32", "watchosArm64"
|
||||
)
|
||||
|
||||
targets.withType<KotlinNativeTarget> {
|
||||
println("Target $name")
|
||||
|
||||
compilations.getByName("main") {
|
||||
if (linux64Bit.contains(this@withType.name)) {
|
||||
defaultSourceSet.dependsOn(nativeMain)
|
||||
}
|
||||
if (linuxArm64Bit.contains(this@withType.name)) {
|
||||
defaultSourceSet.dependsOn(
|
||||
createWorkaroundNativeMainSourceSet(
|
||||
this@withType.name,
|
||||
nativeDependencies
|
||||
)
|
||||
)
|
||||
|
||||
compilations.getByName("main") {
|
||||
val libsodiumCinterop by cinterops.creating {
|
||||
defFile(project.file("src/nativeInterop/cinterop/libsodium.def"))
|
||||
compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-arm64/include/")
|
||||
}
|
||||
kotlinOptions.freeCompilerArgs = listOf(
|
||||
"-include-binary", "${project.rootDir}/sodiumWrapper/static-arm64/lib/libsodium.a"
|
||||
)
|
||||
}
|
||||
}
|
||||
if (linux32Bit.contains(this@withType.name)) {
|
||||
defaultSourceSet.dependsOn(createWorkaroundNativeMainSourceSet(this@withType.name, nativeDependencies))
|
||||
}
|
||||
if (macos64Bit.contains(this@withType.name)) {
|
||||
defaultSourceSet.dependsOn(createWorkaroundNativeMainSourceSet(this@withType.name, nativeDependencies))
|
||||
}
|
||||
//All ioses share the same static library
|
||||
if (ios64Bit.contains(this@withType.name)) {
|
||||
defaultSourceSet.dependsOn(createWorkaroundNativeMainSourceSet(this@withType.name, nativeDependencies))
|
||||
println("Setting ios cinterop for $this")
|
||||
val libsodiumCinterop by cinterops.creating {
|
||||
defFile(project.file("src/nativeInterop/cinterop/libsodium.def"))
|
||||
compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-ios/include")
|
||||
}
|
||||
kotlinOptions.freeCompilerArgs = listOf(
|
||||
"-include-binary", "${project.rootDir}/sodiumWrapper/static-ios/lib/libsodium.a"
|
||||
)
|
||||
}
|
||||
|
||||
if (ios32Bit.contains(this@withType.name)) {
|
||||
defaultSourceSet.dependsOn(createWorkaroundNativeMainSourceSet(this@withType.name, nativeDependencies))
|
||||
println("Setting ios cinterop for $this")
|
||||
val libsodiumCinterop by cinterops.creating {
|
||||
defFile(project.file("src/nativeInterop/cinterop/libsodium.def"))
|
||||
compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-ios/include")
|
||||
}
|
||||
kotlinOptions.freeCompilerArgs = listOf(
|
||||
"-include-binary", "${project.rootDir}/sodiumWrapper/static-ios/lib/libsodium.a"
|
||||
)
|
||||
}
|
||||
|
||||
if (tvos64Bit.contains(this@withType.name)) {
|
||||
defaultSourceSet.dependsOn(createWorkaroundNativeMainSourceSet(this@withType.name, nativeDependencies))
|
||||
println("Setting ios cinterop for $this")
|
||||
val libsodiumCinterop by cinterops.creating {
|
||||
defFile(project.file("src/nativeInterop/cinterop/libsodium.def"))
|
||||
compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-tvos/include")
|
||||
}
|
||||
kotlinOptions.freeCompilerArgs = listOf(
|
||||
"-include-binary", "${project.rootDir}/sodiumWrapper/static-tvos/lib/libsodium.a"
|
||||
)
|
||||
}
|
||||
|
||||
if (watchos32Bit.contains(this@withType.name)) {
|
||||
defaultSourceSet.dependsOn(createWorkaroundNativeMainSourceSet(this@withType.name, nativeDependencies))
|
||||
println("Setting ios cinterop for $this")
|
||||
val libsodiumCinterop by cinterops.creating {
|
||||
defFile(project.file("src/nativeInterop/cinterop/libsodium.def"))
|
||||
compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-watchos/include")
|
||||
}
|
||||
kotlinOptions.freeCompilerArgs = listOf(
|
||||
"-include-binary", "${project.rootDir}/sodiumWrapper/static-watchos/lib/libsodium.a"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
compilations.getByName("test") {
|
||||
println("Setting native test dep for $this@withType.name")
|
||||
defaultSourceSet.dependsOn(nativeTest)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
runningOnLinuxx86_64 {
|
||||
println("Configuring Linux 64 Bit source sets")
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Jvm.stdLib))
|
||||
implementation(kotlin(Deps.Jvm.test))
|
||||
implementation(kotlin(Deps.Jvm.testJUnit))
|
||||
implementation(Deps.Jvm.coroutinesCore)
|
||||
|
||||
//lazysodium
|
||||
implementation(Deps.Jvm.Delegated.lazysodium)
|
||||
implementation(Deps.Jvm.Delegated.jna)
|
||||
}
|
||||
}
|
||||
val jvmTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Jvm.test))
|
||||
implementation(kotlin(Deps.Jvm.testJUnit))
|
||||
implementation(Deps.Jvm.coroutinesTest)
|
||||
implementation(kotlin(Deps.Jvm.reflection))
|
||||
}
|
||||
}
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Js.stdLib))
|
||||
implementation(Deps.Js.coroutines)
|
||||
implementation(npm(Deps.Js.Npm.libsodiumWrappers.first, Deps.Js.Npm.libsodiumWrappers.second))
|
||||
}
|
||||
}
|
||||
val jsTest by getting {
|
||||
dependencies {
|
||||
implementation(Deps.Js.coroutines)
|
||||
implementation(kotlin(Deps.Js.test))
|
||||
implementation(npm(Deps.Js.Npm.libsodiumWrappers.first, Deps.Js.Npm.libsodiumWrappers.second))
|
||||
}
|
||||
}
|
||||
val linuxX64Main by getting {
|
||||
isRunningInIdea {
|
||||
kotlin.srcDir("src/nativeMain/kotlin")
|
||||
}
|
||||
}
|
||||
val linuxX64Test by getting {
|
||||
dependsOn(nativeTest)
|
||||
isRunningInIdea {
|
||||
kotlin.srcDir("src/nativeTest/kotlin")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
runningOnMacos {
|
||||
println("Configuring Macos source sets")
|
||||
val macosX64Main by getting {
|
||||
dependsOn(nativeMain)
|
||||
if (ideaActive) {
|
||||
kotlin.srcDir("src/nativeMain/kotlin")
|
||||
}
|
||||
|
||||
}
|
||||
val macosX64Test by getting {
|
||||
dependsOn(nativeTest)
|
||||
if (ideaActive) {
|
||||
kotlin.srcDir("src/nativeTest/kotlin")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val tvosX64Main by getting {
|
||||
dependsOn(commonMain)
|
||||
}
|
||||
|
||||
val tvosArm64Main by getting {
|
||||
dependsOn(commonMain)
|
||||
}
|
||||
|
||||
val watchosX86Main by getting {
|
||||
dependsOn(commonMain)
|
||||
}
|
||||
|
||||
val watchosArm64Main by getting {
|
||||
dependsOn(commonMain)
|
||||
}
|
||||
|
||||
val watchosArm32Main by getting {
|
||||
dependsOn(commonMain)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (hostOsName == "windows") {
|
||||
val mingwX64Main by getting {
|
||||
dependsOn(nativeMain)
|
||||
if (ideaActive) {
|
||||
kotlin.srcDir("src/nativeMain/kotlin")
|
||||
}
|
||||
}
|
||||
|
||||
val mingwX64Test by getting {
|
||||
dependsOn(nativeTest)
|
||||
if (ideaActive) {
|
||||
kotlin.srcDir("src/nativeTest/kotlin")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
all {
|
||||
languageSettings.enableLanguageFeature("InlineClasses")
|
||||
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
|
||||
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalStdlibApi")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
tasks {
|
||||
|
||||
|
||||
create<Jar>("javadocJar") {
|
||||
dependsOn(dokka)
|
||||
archiveClassifier.set("javadoc")
|
||||
from(dokka.get().outputDirectory)
|
||||
}
|
||||
|
||||
dokka {
|
||||
}
|
||||
if (getHostOsName() == "linux" && getHostArchitecture() == "x86-64") {
|
||||
val jvmTest by getting(Test::class) {
|
||||
testLogging {
|
||||
events("PASSED", "FAILED", "SKIPPED")
|
||||
}
|
||||
}
|
||||
|
||||
val linuxX64Test by getting(KotlinNativeTest::class) {
|
||||
|
||||
testLogging {
|
||||
events("PASSED", "FAILED", "SKIPPED")
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
||||
|
||||
val jsNodeTest by getting(KotlinJsTest::class) {
|
||||
testLogging {
|
||||
events("PASSED", "FAILED", "SKIPPED")
|
||||
// showStandardStreams = true
|
||||
}
|
||||
}
|
||||
|
||||
// val legacyjsNodeTest by getting(KotlinJsTest::class) {
|
||||
//
|
||||
// testLogging {
|
||||
// events("PASSED", "FAILED", "SKIPPED")
|
||||
// showStandardStreams = true
|
||||
// }
|
||||
// }
|
||||
|
||||
// val jsIrBrowserTest by getting(KotlinJsTest::class) {
|
||||
// testLogging {
|
||||
// events("PASSED", "FAILED", "SKIPPED")
|
||||
// showStandardStreams = true
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
if (getHostOsName() == "windows") {
|
||||
val mingwX64Test by getting(KotlinNativeTest::class) {
|
||||
|
||||
testLogging {
|
||||
events("PASSED", "FAILED", "SKIPPED")
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
signing {
|
||||
isRequired = false
|
||||
sign(publishing.publications)
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications.withType(MavenPublication::class) {
|
||||
artifact(tasks["javadocJar"])
|
||||
pom {
|
||||
name.set("Kotlin Multiplatform Crypto")
|
||||
description.set("Kotlin Multiplatform Crypto library")
|
||||
url.set("https://github.com/ionspin/kotlin-multiplatform-crypto")
|
||||
licenses {
|
||||
license {
|
||||
name.set("The Apache License, Version 2.0")
|
||||
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("ionspin")
|
||||
name.set("Ugljesa Jovanovic")
|
||||
email.set("opensource@ionspin.com")
|
||||
}
|
||||
}
|
||||
scm {
|
||||
url.set("https://github.com/ionspin/kotlin-multiplatform-crypto")
|
||||
connection.set("scm:git:git://git@github.com:ionspin/kotlin-multiplatform-crypto.git")
|
||||
developerConnection.set("scm:git:ssh://git@github.com:ionspin/kotlin-multiplatform-crypto.git")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
|
||||
url = uri(sonatypeStaging)
|
||||
credentials {
|
||||
username = sonatypeUsername ?: sonatypeUsernameEnv ?: ""
|
||||
password = sonatypePassword ?: sonatypePasswordEnv ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
maven {
|
||||
name = "snapshot"
|
||||
url = uri(sonatypeSnapshots)
|
||||
credentials {
|
||||
username = sonatypeUsername ?: sonatypeUsernameEnv ?: ""
|
||||
password = sonatypePassword ?: sonatypePasswordEnv ?: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//configurations.forEach {
|
||||
//
|
||||
// if (it.name == "linuxCompileKlibraries") {
|
||||
// println("Configuration name: ${it.name}")
|
||||
// it.attributes {
|
||||
// this.keySet().forEach { key ->
|
||||
// val attribute = getAttribute(key)
|
||||
// println(" |-- Attribute $key ${attribute}")
|
||||
// attribute(org.jetbrains.kotlin.gradle.plugin.ProjectLocalConfigurations.ATTRIBUTE, "publicZ")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
BIN
multiplatform-crypto-delegated/libsodium-wrappers-sumo-0.7.6.tgz
Normal file
BIN
multiplatform-crypto-delegated/libsodium-wrappers-sumo-0.7.6.tgz
Normal file
Binary file not shown.
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 20-Jul-2019
|
||||
*/
|
||||
object Config {
|
||||
const val DEBUG = false
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bProperties
|
||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bDelegated
|
||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bDelegatedStateless
|
||||
import com.ionspin.kotlin.crypto.hash.sha.*
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 24-May-2020
|
||||
*/
|
||||
|
||||
object Crypto : CryptoProvider {
|
||||
override suspend fun initialize() {
|
||||
Initializer.initialize()
|
||||
}
|
||||
|
||||
fun initializeWithCallback(done: () -> Unit) {
|
||||
Initializer.initializeWithCallback(done)
|
||||
}
|
||||
|
||||
|
||||
object Blake2b {
|
||||
fun updateable(key: UByteArray? = null, hashLength: Int = Blake2bProperties.MAX_HASH_BYTES): com.ionspin.kotlin.crypto.hash.blake2b.Blake2b {
|
||||
checkInitialization()
|
||||
return Blake2bDelegated(key, hashLength)
|
||||
}
|
||||
|
||||
fun stateless(message: UByteArray, key: UByteArray = ubyteArrayOf(), hashLength: Int = Blake2bProperties.MAX_HASH_BYTES): UByteArray {
|
||||
checkInitialization()
|
||||
return Blake2bDelegatedStateless.digest(message, key, hashLength)
|
||||
}
|
||||
}
|
||||
|
||||
object Sha256 {
|
||||
fun updateable(): com.ionspin.kotlin.crypto.hash.sha.Sha256 {
|
||||
checkInitialization()
|
||||
return Sha256Delegated()
|
||||
}
|
||||
|
||||
fun stateless(message: UByteArray) : UByteArray{
|
||||
checkInitialization()
|
||||
return Sha256StatelessDelegated.digest(inputMessage = message)
|
||||
}
|
||||
}
|
||||
|
||||
object Sha512 {
|
||||
fun updateable(): com.ionspin.kotlin.crypto.hash.sha.Sha512 {
|
||||
checkInitialization()
|
||||
return Sha512Delegated()
|
||||
}
|
||||
|
||||
fun stateless(message: UByteArray) : UByteArray {
|
||||
checkInitialization()
|
||||
return Sha512StatelessDelegated.digest(inputMessage = message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkInitialization() {
|
||||
if (!Initializer.isInitialized()) {
|
||||
throw RuntimeException("Platform library not initialized, check if you called Initializer.initialize()")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
object SimpleCrypto {
|
||||
fun hash(message: String): UByteArray {
|
||||
return ubyteArrayOf(0U)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
expect object Initializer {
|
||||
fun isInitialized() : Boolean
|
||||
|
||||
suspend fun initialize()
|
||||
|
||||
fun initializeWithCallback(done: () -> (Unit))
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package _multiplatform_crypto_delegated
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 24-May-2020
|
||||
*/
|
||||
//Workaround for https://youtrack.jetbrains.com/issue/KT-36878
|
||||
val byteArray = byteArrayOf(0)
|
||||
val byte = 0.toByte()
|
||||
val longArray = longArrayOf(0)
|
||||
val long = 0L
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 21-Sep-2019
|
||||
*/
|
||||
expect object SRNG {
|
||||
fun getRandomBytes(amount : Int) : UByteArray
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.hash.blake2b
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
expect class Blake2bDelegated(key: UByteArray? = null, hashLength: Int = Blake2bProperties.MAX_HASH_BYTES) : Blake2b
|
||||
|
||||
|
||||
expect object Blake2bDelegatedStateless : Blake2bStateless
|
||||
|
||||
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
expect class Sha256Delegated() : Sha256
|
||||
|
||||
expect object Sha256StatelessDelegated : StatelessSha256
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
expect class Sha512Delegated() : Sha512
|
||||
|
||||
expect object Sha512StatelessDelegated : StatelessSha512
|
@ -0,0 +1,10 @@
|
||||
package com.ionspin.kotlin.crypto.keyderivation.argon2
|
||||
|
||||
import com.ionspin.kotlin.crypto.keyderivation.KeyDerivationFunction
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 24-May-2020
|
||||
*/
|
||||
interface Argon2 : KeyDerivationFunction
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.parallelization
|
||||
|
||||
import kotlin.time.ExperimentalTime
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-May-2020
|
||||
*/
|
||||
@ExperimentalTime
|
||||
object Coroutines14 {
|
||||
fun argonParallel() : Array<UByte> {
|
||||
// val argon = Argon2()
|
||||
// argon
|
||||
println("Placeholder")
|
||||
return emptyArray()
|
||||
}
|
||||
}
|
@ -17,13 +17,12 @@
|
||||
package com.ionspin.kotlin.crypto.symmetric
|
||||
|
||||
import com.ionspin.kotlin.crypto.SRNG
|
||||
import com.ionspin.kotlin.crypto.util.chunked
|
||||
import com.ionspin.kotlin.crypto.util.xor
|
||||
|
||||
/**
|
||||
* Advanced encryption standard with cipher block chaining and PKCS #5
|
||||
*
|
||||
* For bulk encryption/decryption use [AesCbc.encrypt] and [AesCbc.decrypt]
|
||||
* For bulk encryption/decryption use [AesCbcPure.encrypt] and [AesCbcPure.decrypt]
|
||||
*
|
||||
* To get an instance of AesCbc and then feed it data sequentially with [addData] use [createEncryptor] and [createDecryptor]
|
||||
*
|
||||
@ -31,8 +30,8 @@ import com.ionspin.kotlin.crypto.util.xor
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 21-Sep-2019
|
||||
*/
|
||||
@ExperimentalUnsignedTypes
|
||||
class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializationVector: Array<UByte>? = null) {
|
||||
|
||||
class AesCbcPure internal constructor(val aesKey: AesKey, val mode: Mode, initializationVector: UByteArray? = null) {
|
||||
|
||||
companion object {
|
||||
const val BLOCK_BYTES = 16
|
||||
@ -40,22 +39,22 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
|
||||
* Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all
|
||||
* data call [encrypt]
|
||||
*/
|
||||
fun createEncryptor(aesKey: AesKey) : AesCbc {
|
||||
return AesCbc(aesKey, Mode.ENCRYPT)
|
||||
fun createEncryptor(aesKey: AesKey) : AesCbcPure {
|
||||
return AesCbcPure(aesKey, Mode.ENCRYPT)
|
||||
}
|
||||
/**
|
||||
* Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all
|
||||
* data call [decrypt]
|
||||
*/
|
||||
fun createDecryptor(aesKey : AesKey) : AesCbc {
|
||||
return AesCbc(aesKey, Mode.DECRYPT)
|
||||
fun createDecryptor(aesKey : AesKey) : AesCbcPure {
|
||||
return AesCbcPure(aesKey, Mode.DECRYPT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk encryption, returns encrypted data and a random initialization vector
|
||||
*/
|
||||
fun encrypt(aesKey: AesKey, data: Array<UByte>): EncryptedDataAndInitializationVector {
|
||||
val aesCbc = AesCbc(aesKey, Mode.ENCRYPT)
|
||||
fun encrypt(aesKey: AesKey, data: UByteArray): EncryptedDataAndInitializationVector {
|
||||
val aesCbc = AesCbcPure(aesKey, Mode.ENCRYPT)
|
||||
aesCbc.addData(data)
|
||||
return aesCbc.encrypt()
|
||||
}
|
||||
@ -63,20 +62,20 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
|
||||
/**
|
||||
* Bulk decryption, returns decrypted data
|
||||
*/
|
||||
fun decrypt(aesKey: AesKey, data: Array<UByte>, initialCounter: Array<UByte>? = null): Array<UByte> {
|
||||
val aesCbc = AesCbc(aesKey, Mode.DECRYPT, initialCounter)
|
||||
fun decrypt(aesKey: AesKey, data: UByteArray, initialCounter: UByteArray? = null): UByteArray {
|
||||
val aesCbc = AesCbcPure(aesKey, Mode.DECRYPT, initialCounter)
|
||||
aesCbc.addData(data)
|
||||
return aesCbc.decrypt()
|
||||
}
|
||||
|
||||
private fun padToBlock(unpadded: Array<UByte>): Array<UByte> {
|
||||
private fun padToBlock(unpadded: UByteArray): UByteArray {
|
||||
val paddingSize = 16 - unpadded.size
|
||||
if (unpadded.size == BLOCK_BYTES) {
|
||||
return unpadded
|
||||
}
|
||||
|
||||
if (unpadded.size == BLOCK_BYTES) {
|
||||
return Array(BLOCK_BYTES) {
|
||||
return UByteArray(BLOCK_BYTES) {
|
||||
BLOCK_BYTES.toUByte()
|
||||
}
|
||||
}
|
||||
@ -85,7 +84,7 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
|
||||
throw IllegalStateException("Block larger than 128 bytes")
|
||||
}
|
||||
|
||||
return Array(BLOCK_BYTES) {
|
||||
return UByteArray(BLOCK_BYTES) {
|
||||
when (it) {
|
||||
in unpadded.indices -> unpadded[it]
|
||||
else -> paddingSize.toUByte()
|
||||
@ -95,20 +94,20 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
|
||||
}
|
||||
}
|
||||
|
||||
var currentOutput: Array<UByte> = arrayOf()
|
||||
var previousEncrypted: Array<UByte> = arrayOf()
|
||||
var currentOutput: UByteArray = ubyteArrayOf()
|
||||
var previousEncrypted: UByteArray = ubyteArrayOf()
|
||||
val initVector = if (initializationVector.isNullOrEmpty()) {
|
||||
SRNG.getRandomBytes(16)
|
||||
} else {
|
||||
initializationVector
|
||||
}
|
||||
|
||||
val output = MutableList<Array<UByte>>(0) { arrayOf() }
|
||||
val output = MutableList<UByteArray>(0) { ubyteArrayOf() }
|
||||
|
||||
var buffer: Array<UByte> = UByteArray(16) { 0U }.toTypedArray()
|
||||
var buffer: UByteArray = UByteArray(16) { 0U }
|
||||
var bufferCounter = 0
|
||||
|
||||
fun addData(data: Array<UByte>) {
|
||||
fun addData(data: UByteArray) {
|
||||
//Padding
|
||||
when {
|
||||
bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter)
|
||||
@ -116,16 +115,16 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
|
||||
val chunked = data.chunked(BLOCK_BYTES)
|
||||
chunked.forEach { chunk ->
|
||||
if (bufferCounter + chunk.size < BLOCK_BYTES) {
|
||||
appendToBuffer(chunk, bufferCounter)
|
||||
appendToBuffer(chunk.toUByteArray(), bufferCounter)
|
||||
} else {
|
||||
chunk.copyInto(
|
||||
chunk.toUByteArray().copyInto(
|
||||
destination = buffer,
|
||||
destinationOffset = bufferCounter,
|
||||
startIndex = 0,
|
||||
endIndex = BLOCK_BYTES - bufferCounter
|
||||
)
|
||||
output += consumeBlock(buffer)
|
||||
buffer = Array<UByte>(BLOCK_BYTES) {
|
||||
buffer = UByteArray(BLOCK_BYTES) {
|
||||
when (it) {
|
||||
in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> {
|
||||
chunk[it + (BLOCK_BYTES - bufferCounter)]
|
||||
@ -153,7 +152,7 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
|
||||
if (bufferCounter > 0) {
|
||||
val lastBlockPadded = padToBlock(buffer)
|
||||
if (lastBlockPadded.size > BLOCK_BYTES) {
|
||||
val chunks = lastBlockPadded.chunked(BLOCK_BYTES)
|
||||
val chunks = lastBlockPadded.chunked(BLOCK_BYTES).map { it.toUByteArray() }
|
||||
output += consumeBlock(chunks[0])
|
||||
output += consumeBlock(chunks[1])
|
||||
} else {
|
||||
@ -161,7 +160,7 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
|
||||
}
|
||||
}
|
||||
return EncryptedDataAndInitializationVector(
|
||||
output.reversed().foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
|
||||
output.reversed().foldRight(UByteArray(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
|
||||
initVector
|
||||
)
|
||||
}
|
||||
@ -170,7 +169,7 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
|
||||
* Decrypt data
|
||||
* @return Decrypted data
|
||||
*/
|
||||
fun decrypt(): Array<UByte> {
|
||||
fun decrypt(): UByteArray {
|
||||
val removePaddingCount = output.last().last()
|
||||
|
||||
|
||||
@ -178,37 +177,38 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
|
||||
output.last().dropLast(removePaddingCount.toInt() and 0x7F)
|
||||
} else {
|
||||
output.last().toList()
|
||||
}
|
||||
val preparedOutput = output.dropLast(1).toTypedArray() + removedPadding.toTypedArray()
|
||||
}.toUByteArray()
|
||||
val preparedOutput = (output.dropLast(1) + listOf(removedPadding))
|
||||
//JS compiler freaks out here if we don't supply exact type
|
||||
val reversed : List<Array<UByte>> = preparedOutput.reversed() as List<Array<UByte>>
|
||||
val folded : Array<UByte> = reversed.foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc ->
|
||||
acc + arrayOfUBytes }
|
||||
val reversed : List<UByteArray> = preparedOutput.reversed() as List<UByteArray>
|
||||
val folded : UByteArray = reversed.foldRight(UByteArray(0) { 0U }) { uByteArray, acc ->
|
||||
acc + uByteArray
|
||||
}
|
||||
return folded
|
||||
|
||||
}
|
||||
|
||||
private fun appendToBuffer(array: Array<UByte>, start: Int) {
|
||||
private fun appendToBuffer(array: UByteArray, start: Int) {
|
||||
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||
bufferCounter += array.size
|
||||
}
|
||||
|
||||
private fun consumeBlock(data: Array<UByte>): Array<UByte> {
|
||||
private fun consumeBlock(data: UByteArray): UByteArray {
|
||||
return when (mode) {
|
||||
Mode.ENCRYPT -> {
|
||||
currentOutput = if (currentOutput.isEmpty()) {
|
||||
println("IV: $initVector")
|
||||
Aes.encrypt(aesKey, data xor initVector)
|
||||
AesPure.encrypt(aesKey, data xor initVector)
|
||||
} else {
|
||||
Aes.encrypt(aesKey, data xor currentOutput)
|
||||
AesPure.encrypt(aesKey, data xor currentOutput)
|
||||
}
|
||||
currentOutput
|
||||
}
|
||||
Mode.DECRYPT -> {
|
||||
if (currentOutput.isEmpty()) {
|
||||
currentOutput = Aes.decrypt(aesKey, data) xor initVector
|
||||
currentOutput = AesPure.decrypt(aesKey, data) xor initVector
|
||||
} else {
|
||||
currentOutput = Aes.decrypt(aesKey, data) xor previousEncrypted
|
||||
currentOutput = AesPure.decrypt(aesKey, data) xor previousEncrypted
|
||||
}
|
||||
previousEncrypted = data
|
||||
currentOutput
|
||||
@ -219,8 +219,8 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
|
||||
|
||||
}
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
data class EncryptedDataAndInitializationVector(val encryptedData : Array<UByte>, val initilizationVector : Array<UByte>) {
|
||||
|
||||
data class EncryptedDataAndInitializationVector(val encryptedData : UByteArray, val initilizationVector : UByteArray) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || this::class != other::class) return false
|
@ -20,15 +20,14 @@ import com.ionspin.kotlin.bignum.Endianness
|
||||
import com.ionspin.kotlin.bignum.integer.BigInteger
|
||||
import com.ionspin.kotlin.bignum.modular.ModularBigInteger
|
||||
import com.ionspin.kotlin.crypto.SRNG
|
||||
import com.ionspin.kotlin.crypto.util.chunked
|
||||
import com.ionspin.kotlin.crypto.symmetric.AesCtr.Companion.encrypt
|
||||
import com.ionspin.kotlin.crypto.symmetric.AesCtrPure.Companion.encrypt
|
||||
import com.ionspin.kotlin.crypto.util.xor
|
||||
|
||||
/**
|
||||
*
|
||||
* Advanced encryption standard with counter mode
|
||||
*
|
||||
* For bulk encryption/decryption use [AesCtr.encrypt] and [AesCtr.decrypt]
|
||||
* For bulk encryption/decryption use [AesCtrPure.encrypt] and [AesCtrPure.decrypt]
|
||||
*
|
||||
* To get an instance of AesCtr and then feed it data sequentially with [addData] use [createEncryptor] and [createDecryptor]
|
||||
*
|
||||
@ -36,8 +35,8 @@ import com.ionspin.kotlin.crypto.util.xor
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 22-Sep-2019
|
||||
*/
|
||||
@ExperimentalUnsignedTypes
|
||||
class AesCtr internal constructor(val aesKey: AesKey, val mode: Mode, initialCounter: Array<UByte>? = null) {
|
||||
|
||||
class AesCtrPure internal constructor(val aesKey: AesKey, val mode: Mode, initialCounter: UByteArray? = null) {
|
||||
|
||||
companion object {
|
||||
const val BLOCK_BYTES = 16
|
||||
@ -47,50 +46,50 @@ class AesCtr internal constructor(val aesKey: AesKey, val mode: Mode, initialCou
|
||||
* Creates and returns AesCtr instance that can be fed data using [addData]. Once you have submitted all
|
||||
* data call [encrypt]
|
||||
*/
|
||||
fun createEncryptor(aesKey: AesKey) : AesCtr {
|
||||
return AesCtr(aesKey, Mode.ENCRYPT)
|
||||
fun createEncryptor(aesKey: AesKey) : AesCtrPure {
|
||||
return AesCtrPure(aesKey, Mode.ENCRYPT)
|
||||
}
|
||||
/**
|
||||
* Creates and returns AesCtr instance that can be fed data using [addData]. Once you have submitted all
|
||||
* data call [decrypt]
|
||||
*/
|
||||
fun createDecryptor(aesKey : AesKey) : AesCtr {
|
||||
return AesCtr(aesKey, Mode.DECRYPT)
|
||||
fun createDecryptor(aesKey : AesKey) : AesCtrPure {
|
||||
return AesCtrPure(aesKey, Mode.DECRYPT)
|
||||
}
|
||||
/**
|
||||
* Bulk encryption, returns encrypted data and a random initial counter
|
||||
*/
|
||||
fun encrypt(aesKey: AesKey, data: Array<UByte>): EncryptedDataAndInitialCounter {
|
||||
val aesCtr = AesCtr(aesKey, Mode.ENCRYPT)
|
||||
fun encrypt(aesKey: AesKey, data: UByteArray): EncryptedDataAndInitialCounter {
|
||||
val aesCtr = AesCtrPure(aesKey, Mode.ENCRYPT)
|
||||
aesCtr.addData(data)
|
||||
return aesCtr.encrypt()
|
||||
}
|
||||
/**
|
||||
* Bulk decryption, returns decrypted data
|
||||
*/
|
||||
fun decrypt(aesKey: AesKey, data: Array<UByte>, initialCounter: Array<UByte>? = null): Array<UByte> {
|
||||
val aesCtr = AesCtr(aesKey, Mode.DECRYPT, initialCounter)
|
||||
fun decrypt(aesKey: AesKey, data: UByteArray, initialCounter: UByteArray? = null): UByteArray {
|
||||
val aesCtr = AesCtrPure(aesKey, Mode.DECRYPT, initialCounter)
|
||||
aesCtr.addData(data)
|
||||
return aesCtr.decrypt()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var currentOutput: Array<UByte> = arrayOf()
|
||||
var previousEncrypted: Array<UByte> = arrayOf()
|
||||
var currentOutput: UByteArray = ubyteArrayOf()
|
||||
var previousEncrypted: UByteArray = ubyteArrayOf()
|
||||
val counterStart = if (initialCounter.isNullOrEmpty()) {
|
||||
SRNG.getRandomBytes(16)
|
||||
} else {
|
||||
initialCounter
|
||||
}
|
||||
var blockCounter = modularCreator.fromBigInteger(BigInteger.fromUByteArray(counterStart, Endianness.BIG))
|
||||
var blockCounter = modularCreator.fromBigInteger(BigInteger.fromUByteArray(counterStart.toTypedArray(), Endianness.BIG))
|
||||
|
||||
val output = MutableList<Array<UByte>>(0) { arrayOf() }
|
||||
val output = MutableList<UByteArray>(0) { ubyteArrayOf() }
|
||||
|
||||
var buffer: Array<UByte> = UByteArray(16) { 0U }.toTypedArray()
|
||||
var buffer: UByteArray = UByteArray(16) { 0U }
|
||||
var bufferCounter = 0
|
||||
|
||||
fun addData(data: Array<UByte>) {
|
||||
fun addData(data: UByteArray) {
|
||||
//Padding
|
||||
when {
|
||||
bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter)
|
||||
@ -98,9 +97,9 @@ class AesCtr internal constructor(val aesKey: AesKey, val mode: Mode, initialCou
|
||||
val chunked = data.chunked(BLOCK_BYTES)
|
||||
chunked.forEach { chunk ->
|
||||
if (bufferCounter + chunk.size < BLOCK_BYTES) {
|
||||
appendToBuffer(chunk, bufferCounter)
|
||||
appendToBuffer(chunk.toUByteArray(), bufferCounter)
|
||||
} else {
|
||||
chunk.copyInto(
|
||||
chunk.toUByteArray().copyInto(
|
||||
destination = buffer,
|
||||
destinationOffset = bufferCounter,
|
||||
startIndex = 0,
|
||||
@ -108,7 +107,7 @@ class AesCtr internal constructor(val aesKey: AesKey, val mode: Mode, initialCou
|
||||
)
|
||||
output += consumeBlock(buffer, blockCounter)
|
||||
blockCounter += 1
|
||||
buffer = Array<UByte>(BLOCK_BYTES) {
|
||||
buffer = UByteArray(BLOCK_BYTES) {
|
||||
when (it) {
|
||||
in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> {
|
||||
chunk[it + (BLOCK_BYTES - bufferCounter)]
|
||||
@ -136,7 +135,7 @@ class AesCtr internal constructor(val aesKey: AesKey, val mode: Mode, initialCou
|
||||
output += consumeBlock(buffer, blockCounter)
|
||||
}
|
||||
return EncryptedDataAndInitialCounter(
|
||||
output.reversed().foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
|
||||
output.reversed().foldRight(UByteArray(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
|
||||
counterStart
|
||||
)
|
||||
}
|
||||
@ -144,41 +143,41 @@ class AesCtr internal constructor(val aesKey: AesKey, val mode: Mode, initialCou
|
||||
* Decrypt data
|
||||
* @return Decrypted data
|
||||
*/
|
||||
fun decrypt(): Array<UByte> {
|
||||
fun decrypt(): UByteArray {
|
||||
if (bufferCounter > 0) {
|
||||
output += consumeBlock(buffer, blockCounter)
|
||||
}
|
||||
//JS compiler freaks out here if we don't supply exact type
|
||||
val reversed: List<Array<UByte>> = output.reversed() as List<Array<UByte>>
|
||||
val folded: Array<UByte> = reversed.foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc ->
|
||||
val reversed: List<UByteArray> = output.reversed() as List<UByteArray>
|
||||
val folded: UByteArray = reversed.foldRight(UByteArray(0) { 0U }) { arrayOfUBytes, acc ->
|
||||
acc + arrayOfUBytes
|
||||
}
|
||||
return folded
|
||||
}
|
||||
|
||||
private fun appendToBuffer(array: Array<UByte>, start: Int) {
|
||||
private fun appendToBuffer(array: UByteArray, start: Int) {
|
||||
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||
bufferCounter += array.size
|
||||
}
|
||||
|
||||
private fun consumeBlock(data: Array<UByte>, blockCount: ModularBigInteger): Array<UByte> {
|
||||
val blockCountAsByteArray = blockCount.toUByteArray(Endianness.BIG).expandCounterTo16Bytes()
|
||||
private fun consumeBlock(data: UByteArray, blockCount: ModularBigInteger): UByteArray {
|
||||
val blockCountAsByteArray = blockCount.toUByteArray(Endianness.BIG).toUByteArray().expandCounterTo16Bytes()
|
||||
return when (mode) {
|
||||
Mode.ENCRYPT -> {
|
||||
Aes.encrypt(aesKey, blockCountAsByteArray) xor data
|
||||
AesPure.encrypt(aesKey, blockCountAsByteArray) xor data
|
||||
}
|
||||
Mode.DECRYPT -> {
|
||||
Aes.encrypt(aesKey, blockCountAsByteArray) xor data
|
||||
AesPure.encrypt(aesKey, blockCountAsByteArray) xor data
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun Array<UByte>.expandCounterTo16Bytes() : Array<UByte> {
|
||||
private fun UByteArray.expandCounterTo16Bytes() : UByteArray {
|
||||
return if (this.size < 16) {
|
||||
println("Expanding")
|
||||
val diff = 16 - this.size
|
||||
val pad = Array<UByte>(diff) { 0U }
|
||||
val pad = UByteArray(diff) { 0U }
|
||||
pad + this
|
||||
} else {
|
||||
this
|
||||
@ -187,8 +186,8 @@ class AesCtr internal constructor(val aesKey: AesKey, val mode: Mode, initialCou
|
||||
|
||||
}
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
data class EncryptedDataAndInitialCounter(val encryptedData : Array<UByte>, val initialCounter : Array<UByte>) {
|
||||
|
||||
data class EncryptedDataAndInitialCounter(val encryptedData : UByteArray, val initialCounter : UByteArray) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || this::class != other::class) return false
|
@ -1,10 +1,12 @@
|
||||
package com.ionspin.kotlin.crypto.symmetric
|
||||
|
||||
import com.ionspin.kotlin.crypto.util.flattenToUByteArray
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 07/Sep/2019
|
||||
*/
|
||||
@ExperimentalUnsignedTypes
|
||||
internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UByte>) {
|
||||
|
||||
internal class AesPure internal constructor(val aesKey: AesKey, val input: UByteArray) {
|
||||
companion object {
|
||||
private val debug = false
|
||||
|
||||
@ -54,18 +56,18 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
||||
|
||||
val rcon: UByteArray = ubyteArrayOf(0x8DU, 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, 0x1BU, 0x36U)
|
||||
|
||||
fun encrypt(aesKey: AesKey, input: Array<UByte>): Array<UByte> {
|
||||
return Aes(aesKey, input).encrypt()
|
||||
fun encrypt(aesKey: AesKey, input: UByteArray): UByteArray {
|
||||
return AesPure(aesKey, input).encrypt()
|
||||
}
|
||||
|
||||
fun decrypt(aesKey: AesKey, input: Array<UByte>): Array<UByte> {
|
||||
return Aes(aesKey, input).decrypt()
|
||||
fun decrypt(aesKey: AesKey, input: UByteArray): UByteArray {
|
||||
return AesPure(aesKey, input).decrypt()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val state: Array<Array<UByte>> = (0 until 4).map { outerCounter ->
|
||||
Array<UByte>(4) { innerCounter -> input[innerCounter * 4 + outerCounter] }
|
||||
val state: Array<UByteArray> = (0 until 4).map { outerCounter ->
|
||||
UByteArray(4) { innerCounter -> input[innerCounter * 4 + outerCounter] }
|
||||
}.toTypedArray()
|
||||
|
||||
val numberOfRounds = when (aesKey) {
|
||||
@ -74,7 +76,7 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
||||
is AesKey.Aes256Key -> 14
|
||||
}
|
||||
|
||||
val expandedKey: Array<Array<UByte>> = expandKey()
|
||||
val expandedKey: Array<UByteArray> = expandKey()
|
||||
|
||||
|
||||
var round = 0
|
||||
@ -110,22 +112,22 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
||||
}
|
||||
|
||||
fun shiftRows() {
|
||||
state[0] = arrayOf(state[0][0], state[0][1], state[0][2], state[0][3])
|
||||
state[1] = arrayOf(state[1][1], state[1][2], state[1][3], state[1][0])
|
||||
state[2] = arrayOf(state[2][2], state[2][3], state[2][0], state[2][1])
|
||||
state[3] = arrayOf(state[3][3], state[3][0], state[3][1], state[3][2])
|
||||
state[0] = ubyteArrayOf(state[0][0], state[0][1], state[0][2], state[0][3])
|
||||
state[1] = ubyteArrayOf(state[1][1], state[1][2], state[1][3], state[1][0])
|
||||
state[2] = ubyteArrayOf(state[2][2], state[2][3], state[2][0], state[2][1])
|
||||
state[3] = ubyteArrayOf(state[3][3], state[3][0], state[3][1], state[3][2])
|
||||
}
|
||||
|
||||
fun inversShiftRows() {
|
||||
state[0] = arrayOf(state[0][0], state[0][1], state[0][2], state[0][3])
|
||||
state[1] = arrayOf(state[1][3], state[1][0], state[1][1], state[1][2])
|
||||
state[2] = arrayOf(state[2][2], state[2][3], state[2][0], state[2][1])
|
||||
state[3] = arrayOf(state[3][1], state[3][2], state[3][3], state[3][0])
|
||||
state[0] = ubyteArrayOf(state[0][0], state[0][1], state[0][2], state[0][3])
|
||||
state[1] = ubyteArrayOf(state[1][3], state[1][0], state[1][1], state[1][2])
|
||||
state[2] = ubyteArrayOf(state[2][2], state[2][3], state[2][0], state[2][1])
|
||||
state[3] = ubyteArrayOf(state[3][1], state[3][2], state[3][3], state[3][0])
|
||||
}
|
||||
|
||||
fun mixColumns() {
|
||||
val stateMixed: Array<Array<UByte>> = (0 until 4).map {
|
||||
Array<UByte>(4) { 0U }
|
||||
val stateMixed: Array<UByteArray> = (0 until 4).map {
|
||||
UByteArray(4) { 0U }
|
||||
}.toTypedArray()
|
||||
for (c in 0..3) {
|
||||
|
||||
@ -138,8 +140,8 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
||||
}
|
||||
|
||||
fun inverseMixColumns() {
|
||||
val stateMixed: Array<Array<UByte>> = (0 until 4).map {
|
||||
Array<UByte>(4) { 0U }
|
||||
val stateMixed: Array<UByteArray> = (0 until 4).map {
|
||||
UByteArray(4) { 0U }
|
||||
}.toTypedArray()
|
||||
for (c in 0..3) {
|
||||
stateMixed[0][c] =
|
||||
@ -203,9 +205,9 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
||||
return galoisFieldMultiply(this.toUByte(), second)
|
||||
}
|
||||
|
||||
fun expandKey(): Array<Array<UByte>> {
|
||||
fun expandKey(): Array<UByteArray> {
|
||||
val expandedKey = (0 until 4 * (numberOfRounds + 1)).map {
|
||||
Array<UByte>(4) { 0U }
|
||||
UByteArray(4) { 0U }
|
||||
}.toTypedArray()
|
||||
// First round
|
||||
for (i in 0 until aesKey.numberOf32BitWords) {
|
||||
@ -241,13 +243,13 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
||||
}
|
||||
expandedKey[i] = expandedKey[i - aesKey.numberOf32BitWords].mapIndexed { index, it ->
|
||||
it xor temp[index]
|
||||
}.toTypedArray()
|
||||
}.toUByteArray()
|
||||
clearArray(temp)
|
||||
}
|
||||
return expandedKey
|
||||
}
|
||||
|
||||
fun encrypt(): Array<UByte> {
|
||||
fun encrypt(): UByteArray {
|
||||
if (completed) {
|
||||
throw RuntimeException("Encrypt can only be called once per Aes instance, since the state is cleared at the " +
|
||||
"end of the operation")
|
||||
@ -273,8 +275,8 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
||||
addRoundKey()
|
||||
printState()
|
||||
val transposedMatrix = (0 until 4).map { outerCounter ->
|
||||
Array<UByte>(4) { 0U }
|
||||
}.toTypedArray()
|
||||
UByteArray(4) { 0U }
|
||||
}
|
||||
for (i in 0 until 4) {
|
||||
for (j in 0 until 4) {
|
||||
transposedMatrix[i][j] = state[j][i]
|
||||
@ -282,10 +284,10 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
||||
}
|
||||
state.forEach { clearArray(it) }
|
||||
completed = true
|
||||
return transposedMatrix.flatten().toTypedArray()
|
||||
return transposedMatrix.flattenToUByteArray()
|
||||
}
|
||||
|
||||
fun decrypt(): Array<UByte> {
|
||||
fun decrypt(): UByteArray {
|
||||
if (completed) {
|
||||
throw RuntimeException("Decrypt can only be called once per Aes instance, since the state is cleared at the " +
|
||||
"end of the operation")
|
||||
@ -313,20 +315,19 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
||||
printState()
|
||||
|
||||
val transposedMatrix = (0 until 4).map { outerCounter ->
|
||||
Array<UByte>(4) { 0U }
|
||||
}.toTypedArray()
|
||||
UByteArray(4) { 0U }
|
||||
}
|
||||
for (i in 0 until 4) {
|
||||
for (j in 0 until 4) {
|
||||
transposedMatrix[i][j] = state[j][i]
|
||||
}
|
||||
}
|
||||
printState(transposedMatrix)
|
||||
state.forEach { clearArray(it) }
|
||||
completed = true
|
||||
return transposedMatrix.flatten().toTypedArray()
|
||||
return transposedMatrix.flattenToUByteArray()
|
||||
}
|
||||
|
||||
private fun clearArray(array : Array<UByte>) {
|
||||
private fun clearArray(array : UByteArray) {
|
||||
array.indices.forEach { array[it] = 0U }
|
||||
}
|
||||
|
||||
@ -342,7 +343,7 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
||||
}
|
||||
}
|
||||
|
||||
private fun printState(specific : Array<Array<UByte>>) {
|
||||
private fun printState(specific : List<UByteArray>) {
|
||||
if (!debug) {
|
||||
return
|
||||
}
|
||||
@ -356,7 +357,7 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
||||
}
|
||||
|
||||
sealed class AesKey(val key: String, val keyLength: Int) {
|
||||
val keyArray: Array<UByte> = key.chunked(2).map { it.toUByte(16) }.toTypedArray()
|
||||
val keyArray: UByteArray = key.chunked(2).map { it.toUByte(16) }.toUByteArray()
|
||||
val numberOf32BitWords = keyLength / 32
|
||||
|
||||
class Aes128Key(key: String) : AesKey(key, 128)
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.symmetric
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 18-Sep-2019
|
||||
*/
|
||||
|
||||
enum class Mode {
|
||||
ENCRYPT, DECRYPT
|
||||
}
|
@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package com.ionspin.kotlin.crypto.util
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 15-Jul-2019
|
||||
*/
|
||||
fun Array<Byte>.hexColumsPrint() {
|
||||
val printout = this.map { it.toString(16) }.chunked(16)
|
||||
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
||||
}
|
||||
|
||||
fun Array<UByte>.hexColumsPrint(chunk : Int = 16) {
|
||||
val printout = this.map { it.toString(16).padStart(2, '0') }.chunked(chunk)
|
||||
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
||||
}
|
||||
|
||||
fun UByteArray.hexColumsPrint(chunk : Int = 16) {
|
||||
val printout = this.map { it.toString(16).padStart(2, '0') }.chunked(chunk)
|
||||
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
||||
}
|
||||
|
||||
fun Array<ULong>.hexColumsPrint(chunk: Int = 3) {
|
||||
val printout = this.map { it.toString(16) }.chunked(chunk)
|
||||
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
||||
}
|
||||
|
||||
inline fun <reified T> Array<T>.chunked(sliceSize: Int): Array<Array<T>> {
|
||||
val last = this.size % sliceSize
|
||||
val hasLast = last != 0
|
||||
val numberOfSlices = this.size / sliceSize
|
||||
|
||||
|
||||
val result : MutableList<List<T>> = MutableList<List<T>>(0) { emptyList() }
|
||||
|
||||
for (i in 0 until numberOfSlices) {
|
||||
result.add(this.slice(i * sliceSize until (i + 1) * sliceSize))
|
||||
}
|
||||
if (hasLast) {
|
||||
result.add(this.slice(numberOfSlices * sliceSize until this.size))
|
||||
}
|
||||
|
||||
return result.map { it.toTypedArray() }.toTypedArray()
|
||||
|
||||
}
|
||||
|
||||
|
||||
infix fun UInt.rotateRight(places: Int): UInt {
|
||||
return (this shr places) xor (this shl (32 - places))
|
||||
}
|
||||
|
||||
|
||||
infix fun ULong.rotateRight(places: Int): ULong {
|
||||
return (this shr places) xor (this shl (64 - places))
|
||||
}
|
||||
|
||||
|
||||
infix fun Array<UByte>.xor(other : Array<UByte>) : Array<UByte> {
|
||||
if (this.size != other.size) {
|
||||
throw RuntimeException("Operands of different sizes are not supported yet")
|
||||
}
|
||||
return Array(this.size) { this[it] xor other[it] }
|
||||
}
|
||||
|
||||
|
||||
infix fun UByteArray.xor(other : UByteArray) : UByteArray {
|
||||
if (this.size != other.size) {
|
||||
throw RuntimeException("Operands of different sizes are not supported yet")
|
||||
}
|
||||
return UByteArray(this.size) { this[it] xor other[it] }
|
||||
}
|
||||
|
||||
|
||||
fun String.hexStringToTypedUByteArray() : Array<UByte> {
|
||||
return this.chunked(2).map { it.toUByte(16) }.toTypedArray()
|
||||
}
|
||||
|
||||
|
||||
fun String.hexStringToUByteArray() : UByteArray {
|
||||
return this.chunked(2).map { it.toUByte(16) }.toUByteArray()
|
||||
}
|
||||
|
||||
|
||||
fun Array<UByte>.toHexString() : String {
|
||||
return this.joinToString(separator = "") {
|
||||
if (it <= 0x0FU) {
|
||||
"0${it.toString(16)}"
|
||||
} else {
|
||||
it.toString(16)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun UByteArray.toHexString() : String {
|
||||
return this.joinToString(separator = "") {
|
||||
if (it <= 0x0FU) {
|
||||
"0${it.toString(16)}"
|
||||
} else {
|
||||
it.toString(16)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UInt / Array utils
|
||||
|
||||
fun UInt.toBigEndianUByteArray() : Array<UByte> {
|
||||
return Array<UByte> (4) {
|
||||
((this shr (24 - (it * 8))) and 0xFFU).toUByte()
|
||||
}
|
||||
}
|
||||
|
||||
fun UInt.toLittleEndianTypedUByteArray() : Array<UByte> {
|
||||
return Array<UByte> (4) {
|
||||
((this shr (it * 8)) and 0xFFU).toUByte()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun UInt.toLittleEndianUByteArray() : UByteArray {
|
||||
return UByteArray (4) {
|
||||
((this shr (it * 8)) and 0xFFU).toUByte()
|
||||
}
|
||||
}
|
||||
|
||||
// UInt / Array utils
|
||||
|
||||
fun ULong.toBigEndianUByteArray() : Array<UByte> {
|
||||
return Array<UByte> (8) {
|
||||
((this shr (56 - (it * 8))) and 0xFFU).toUByte()
|
||||
}
|
||||
}
|
||||
|
||||
fun ULong.toLittleEndianTypedUByteArray() : Array<UByte> {
|
||||
return Array<UByte> (8) {
|
||||
((this shr (it * 8)) and 0xFFU).toUByte()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun ULong.toLittleEndianUByteArray() :UByteArray {
|
||||
return UByteArray (8) {
|
||||
((this shr (it * 8)) and 0xFFU).toUByte()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun Array<UByte>.fromLittleEndianArrayToULong() : ULong {
|
||||
if (this.size > 8) {
|
||||
throw RuntimeException("ore than 8 bytes in input, potential overflow")
|
||||
}
|
||||
var ulong = this.foldIndexed(0UL) { index, acc, uByte -> acc or (uByte.toULong() shl (index * 8))}
|
||||
return ulong
|
||||
}
|
||||
|
||||
|
||||
fun UByteArray.fromLittleEndianArrayToULong() : ULong {
|
||||
if (this.size > 8) {
|
||||
throw RuntimeException("ore than 8 bytes in input, potential overflow")
|
||||
}
|
||||
var ulong = this.foldIndexed(0UL) { index, acc, uByte -> acc or (uByte.toULong() shl (index * 8))}
|
||||
return ulong
|
||||
}
|
||||
|
||||
fun UByteArray.arrayChunked(sliceSize: Int): List<UByteArray> {
|
||||
val last = this.size % sliceSize
|
||||
val hasLast = last != 0
|
||||
val numberOfSlices = this.size / sliceSize
|
||||
|
||||
|
||||
val result : MutableList<UByteArray> = MutableList<UByteArray>(0) { ubyteArrayOf() }
|
||||
|
||||
for (i in 0 until numberOfSlices) {
|
||||
result.add(this.sliceArray(i * sliceSize until (i + 1) * sliceSize))
|
||||
}
|
||||
if (hasLast) {
|
||||
result.add(this.sliceArray(numberOfSlices * sliceSize until this.size))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun Array<UByte>.fromBigEndianArrayToULong() : ULong {
|
||||
if (this.size > 8) {
|
||||
throw RuntimeException("ore than 8 bytes in input, potential overflow")
|
||||
}
|
||||
var ulong = this.foldIndexed(0UL) {
|
||||
index, acc, uByte ->
|
||||
val res = acc or (uByte.toULong() shl (56 - (index * 8)))
|
||||
res
|
||||
|
||||
}
|
||||
return ulong
|
||||
}
|
||||
|
||||
|
||||
fun Array<UByte>.fromLittleEndianArrayToUInt() : UInt {
|
||||
if (this.size > 4) {
|
||||
throw RuntimeException("ore than 8 bytes in input, potential overflow")
|
||||
}
|
||||
var uint = this.foldIndexed(0U) { index, acc, uByte -> acc or (uByte.toUInt() shl (index * 8))}
|
||||
return uint
|
||||
}
|
||||
|
||||
|
||||
fun UByteArray.fromLittleEndianArrayToUInt() : UInt {
|
||||
if (this.size > 4) {
|
||||
throw RuntimeException("ore than 8 bytes in input, potential overflow")
|
||||
}
|
||||
var uint = this.foldIndexed(0U) { index, acc, uByte -> acc or (uByte.toUInt() shl (index * 8))}
|
||||
return uint
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
fun Array<UByte>.fromBigEndianArrayToUInt() : UInt {
|
||||
if (this.size > 4) {
|
||||
throw RuntimeException("ore than 8 bytes in input, potential overflow")
|
||||
}
|
||||
var uint = this.foldIndexed(0U) { index, acc, uByte -> acc or (uByte.toUInt() shl (24 - (index * 8))) }
|
||||
return uint
|
||||
}
|
||||
|
||||
|
||||
operator fun UInt.plus(other : UByteArray) : UByteArray {
|
||||
return this.toLittleEndianUByteArray() + other
|
||||
}
|
||||
|
||||
//AES Flatten
|
||||
fun Collection<UByteArray>.flattenToUByteArray(): UByteArray {
|
||||
val result = UByteArray(sumBy { it.size })
|
||||
var position = 0
|
||||
for (element in this) {
|
||||
element.forEach { uByte ->
|
||||
result[position] = uByte
|
||||
position++
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import com.ionspin.kotlin.crypto.util.testBlocking
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 21-Sep-2019
|
||||
*/
|
||||
class SRNGTest {
|
||||
@Test
|
||||
fun testSrng() = testBlocking {
|
||||
Crypto.initialize()
|
||||
//Just a sanity test, need to add better srng tests.
|
||||
val randomBytes1 = SRNG.getRandomBytes(10)
|
||||
val randomBytes2 = SRNG.getRandomBytes(10)
|
||||
assertTrue { !randomBytes1.contentEquals(randomBytes2) }
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.ionspin.kotlin.crypto.hash.blake2b
|
||||
|
||||
import com.ionspin.kotlin.crypto.Crypto
|
||||
import com.ionspin.kotlin.crypto.Initializer
|
||||
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||
import com.ionspin.kotlin.crypto.util.testBlocking
|
||||
import com.ionspin.kotlin.crypto.util.toHexString
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 09-Jun-2020
|
||||
*/
|
||||
class Blake2bTest {
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
fun statelessSimpleTest() = testBlocking {
|
||||
Initializer.initialize()
|
||||
val expected = "a71079d42853dea26e453004338670a53814b78137ffbed07603a41d76a483aa9bc33b582f77d30a65e6f29a89" +
|
||||
"6c0411f38312e1d66e0bf16386c86a89bea572"
|
||||
val result = Crypto.Blake2b.stateless("test".encodeToUByteArray()).toHexString()
|
||||
// println("Result: $result")
|
||||
assertTrue { result == expected }
|
||||
}
|
||||
|
||||
//This is a bad test since it's not larger than one block
|
||||
//but for now I'm testing that the platform library is being correctly called
|
||||
@Test
|
||||
fun updateableSimpleTest() = testBlocking {
|
||||
Initializer.initialize()
|
||||
val expected = "a71079d42853dea26e453004338670a53814b78137ffbed07603a41d76a483aa9bc33b582f77d30a65e6f29a89" +
|
||||
"6c0411f38312e1d66e0bf16386c86a89bea572"
|
||||
val blake2b = Crypto.Blake2b.updateable()
|
||||
blake2b.update("t".encodeToUByteArray())
|
||||
blake2b.update(("est".encodeToUByteArray()))
|
||||
val result = blake2b.digest().toHexString()
|
||||
// println("Result: $result")
|
||||
assertTrue { result == expected }
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.Crypto
|
||||
import com.ionspin.kotlin.crypto.Initializer
|
||||
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||
import com.ionspin.kotlin.crypto.util.testBlocking
|
||||
import com.ionspin.kotlin.crypto.util.toHexString
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 07-Jun-2020
|
||||
*/
|
||||
class Sha256Test {
|
||||
@BeforeTest
|
||||
fun beforeTest() = testBlocking {
|
||||
Initializer.initialize()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun statelessSimpleTest() {
|
||||
val expected = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
|
||||
val result = Crypto.Sha256.stateless("test".encodeToUByteArray()).toHexString()
|
||||
// println("Result: $result")
|
||||
assertTrue { result == expected }
|
||||
}
|
||||
|
||||
//This is a bad test since it's not larger than one block
|
||||
//but for now I'm testing that the platform library is being correctly called
|
||||
@Test
|
||||
fun updateableSimpleTest() {
|
||||
val expected = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
|
||||
val sha256 = Crypto.Sha256.updateable()
|
||||
sha256.update("t".encodeToUByteArray())
|
||||
sha256.update(("est".encodeToUByteArray()))
|
||||
val result = sha256.digest().toHexString()
|
||||
// println("Result: $result")
|
||||
assertTrue { result == expected }
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.Crypto
|
||||
import com.ionspin.kotlin.crypto.Initializer
|
||||
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||
import com.ionspin.kotlin.crypto.util.testBlocking
|
||||
import com.ionspin.kotlin.crypto.util.toHexString
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 07-Jun-2020
|
||||
*/
|
||||
class Sha512Test {
|
||||
@BeforeTest
|
||||
fun beforeTest() = testBlocking {
|
||||
Initializer.initialize()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun statelessSimpleTest() {
|
||||
val expected = "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67" +
|
||||
"b143732c304cc5fa9ad8e6f57f50028a8ff"
|
||||
val result = Crypto.Sha512.stateless("test".encodeToUByteArray()).toHexString()
|
||||
// println("Result: $result")
|
||||
assertTrue { result == expected }
|
||||
}
|
||||
|
||||
//This is a bad test since it's not larger than one block
|
||||
//but for now I'm testing that the platform library is being correctly called
|
||||
@Test
|
||||
fun updateableSimpleTest() {
|
||||
val expected = "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67" +
|
||||
"b143732c304cc5fa9ad8e6f57f50028a8ff"
|
||||
val sha512 = Crypto.Sha512.updateable()
|
||||
sha512.update("t".encodeToUByteArray())
|
||||
sha512.update(("est".encodeToUByteArray()))
|
||||
val result = sha512.digest().toHexString()
|
||||
// println("Result: $result")
|
||||
assertTrue { result == expected }
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.util
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 20-Jul-2019
|
||||
*/
|
||||
expect fun testBlocking(block : suspend () -> Unit)
|
@ -0,0 +1,43 @@
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import ext.libsodium.com.ionspin.kotlin.crypto.JsSodiumInterface
|
||||
import ext.libsodium.com.ionspin.kotlin.crypto.JsSodiumLoader
|
||||
/* 1.4-M1 has some weirdness with static/objects, or I'm misusing something, not sure */
|
||||
lateinit var sodiumPointer : JsSodiumInterface
|
||||
var sodiumLoaded: Boolean = false
|
||||
|
||||
fun getSodium() : JsSodiumInterface = sodiumPointer
|
||||
|
||||
//fun getSodiumAdvanced() : JsSodiumAdvancedInterface = js("sodiumPointer.libsodium")
|
||||
|
||||
fun setSodiumPointer(jsSodiumInterface: JsSodiumInterface) {
|
||||
js("sodiumPointer = jsSodiumInterface")
|
||||
}
|
||||
|
||||
fun getSodiumLoaded() : Boolean = sodiumLoaded
|
||||
|
||||
fun setSodiumLoaded(loaded: Boolean) {
|
||||
js("sodiumLoaded = loaded")
|
||||
}
|
||||
|
||||
actual object Initializer {
|
||||
private var isPlatformInitialized = false
|
||||
|
||||
actual suspend fun initialize() {
|
||||
JsSodiumLoader.load()
|
||||
isPlatformInitialized = true
|
||||
}
|
||||
|
||||
actual fun initializeWithCallback(done: () -> Unit) {
|
||||
JsSodiumLoader.loadWithCallback {
|
||||
isPlatformInitialized = true
|
||||
done()
|
||||
}
|
||||
}
|
||||
|
||||
actual fun isInitialized(): Boolean {
|
||||
return isPlatformInitialized
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package ext.libsodium.com.ionspin.kotlin.crypto
|
||||
|
||||
|
||||
import org.khronos.webgl.Uint8Array
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 27-May-2020
|
||||
*/
|
||||
interface JsSodiumInterface {
|
||||
|
||||
fun randombytes_buf(numberOfBytes: Int): Uint8Array
|
||||
|
||||
fun crypto_generichash(hashLength: Int, inputMessage: Uint8Array, key: Uint8Array,): Uint8Array
|
||||
|
||||
fun crypto_hash_sha256(message: Uint8Array): Uint8Array
|
||||
|
||||
fun crypto_hash_sha512(message: Uint8Array): Uint8Array
|
||||
|
||||
//Updateable
|
||||
|
||||
fun crypto_generichash_init(key : Uint8Array, hashLength: Int) : dynamic
|
||||
|
||||
fun crypto_generichash_update(state: dynamic, inputMessage: Uint8Array)
|
||||
|
||||
fun crypto_generichash_final(state: dynamic, hashLength: Int) : Uint8Array
|
||||
|
||||
|
||||
fun crypto_hash_sha256_init() : dynamic
|
||||
|
||||
fun crypto_hash_sha256_update(state: dynamic, message: Uint8Array)
|
||||
|
||||
fun crypto_hash_sha256_final(state: dynamic): Uint8Array
|
||||
|
||||
fun crypto_hash_sha512_init() : dynamic
|
||||
|
||||
fun crypto_hash_sha512_update(state: dynamic, message: Uint8Array)
|
||||
|
||||
fun crypto_hash_sha512_final(state: dynamic): Uint8Array
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package ext.libsodium.com.ionspin.kotlin.crypto
|
||||
|
||||
import com.ionspin.kotlin.crypto.getSodiumLoaded
|
||||
import com.ionspin.kotlin.crypto.setSodiumPointer
|
||||
import com.ionspin.kotlin.crypto.sodiumLoaded
|
||||
import ext.libsodium.*
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 27-May-2020
|
||||
*/
|
||||
object JsSodiumLoader {
|
||||
|
||||
class _EmitJsSodiumFunction {
|
||||
init {
|
||||
println(::crypto_generichash)
|
||||
println(::crypto_hash_sha256)
|
||||
println(::crypto_hash_sha512)
|
||||
println(::crypto_hash_sha256_init)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun storeSodium(promisedSodium: dynamic, continuation: Continuation<Unit>) {
|
||||
setSodiumPointer(promisedSodium)
|
||||
sodiumLoaded = true
|
||||
continuation.resumeWith(Result.success(Unit))
|
||||
}
|
||||
|
||||
suspend fun load() = suspendCoroutine<Unit> { continuation ->
|
||||
console.log(getSodiumLoaded())
|
||||
if (!getSodiumLoaded()) {
|
||||
val libsodiumModule = js("\$module\$libsodium_wrappers_sumo")
|
||||
_libsodiumPromise.then<dynamic> { storeSodium(libsodiumModule, continuation) }
|
||||
} else {
|
||||
continuation.resumeWith(Result.success(Unit))
|
||||
}
|
||||
}
|
||||
|
||||
fun loadWithCallback(doneCallback: () -> (Unit)) {
|
||||
console.log(getSodiumLoaded())
|
||||
if (!getSodiumLoaded()) {
|
||||
val libsodiumModule = js("\$module\$libsodium_wrappers_sumo")
|
||||
_libsodiumPromise.then<dynamic> {
|
||||
setSodiumPointer(libsodiumModule)
|
||||
sodiumLoaded = true
|
||||
doneCallback.invoke()
|
||||
}
|
||||
} else {
|
||||
doneCallback.invoke()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import org.khronos.webgl.get
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 21-Sep-2019
|
||||
*/
|
||||
actual object SRNG {
|
||||
var counter = 0
|
||||
|
||||
actual fun getRandomBytes(amount: Int): UByteArray {
|
||||
val randomBytes = getSodium().randombytes_buf(amount)
|
||||
val randomBytesUByteArray = UByteArray(amount) {
|
||||
0U
|
||||
}
|
||||
for (i in 0 until amount) {
|
||||
js("""
|
||||
randomBytesUByteArray[i] = randomBytes[i]
|
||||
""")
|
||||
}
|
||||
return randomBytesUByteArray
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.ionspin.kotlin.crypto.hash.blake2b
|
||||
|
||||
import com.ionspin.kotlin.crypto.getSodium
|
||||
import com.ionspin.kotlin.crypto.hash.sha.Sha256StatelessDelegated
|
||||
import com.ionspin.kotlin.crypto.util.toHexString
|
||||
import org.khronos.webgl.Uint8Array
|
||||
import org.khronos.webgl.get
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
actual class Blake2bDelegated actual constructor(key: UByteArray?, val hashLength: Int) : Blake2b {
|
||||
override val MAX_HASH_BYTES: Int = 64
|
||||
|
||||
|
||||
val state : dynamic
|
||||
|
||||
init {
|
||||
state = getSodium().crypto_generichash_init(
|
||||
Uint8Array(key?.toByteArray()?.toTypedArray() ?: arrayOf()),
|
||||
hashLength
|
||||
)
|
||||
}
|
||||
|
||||
override fun update(data: UByteArray) {
|
||||
getSodium().crypto_generichash_update(state, Uint8Array(data.toByteArray().toTypedArray()))
|
||||
}
|
||||
|
||||
override fun digest(): UByteArray {
|
||||
val hashed = getSodium().crypto_generichash_final(state, hashLength)
|
||||
val hash = UByteArray(hashLength)
|
||||
for (i in 0 until hashLength) {
|
||||
hash[i] = hashed[i].toUByte()
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
actual object Blake2bDelegatedStateless : Blake2bStateless {
|
||||
override val MAX_HASH_BYTES: Int = 64
|
||||
|
||||
override fun digest(inputMessage: UByteArray, key: UByteArray, hashLength: Int): UByteArray {
|
||||
val hashed = getSodium().crypto_generichash(hashLength,
|
||||
Uint8Array(inputMessage.toByteArray().toTypedArray()),
|
||||
Uint8Array(key.toByteArray().toTypedArray())
|
||||
)
|
||||
val hash = UByteArray(hashLength)
|
||||
for (i in 0 until hashLength) {
|
||||
hash[i] = hashed[i].toUByte()
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.getSodium
|
||||
|
||||
import org.khronos.webgl.Uint8Array
|
||||
import org.khronos.webgl.get
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
actual class Sha256Delegated : Sha256 {
|
||||
|
||||
val state : dynamic
|
||||
|
||||
init {
|
||||
state = getSodium().crypto_hash_sha256_init()
|
||||
}
|
||||
|
||||
override fun update(data: UByteArray) {
|
||||
getSodium().crypto_hash_sha256_update(state, Uint8Array(data.toByteArray().toTypedArray()))
|
||||
}
|
||||
|
||||
override fun digest(): UByteArray {
|
||||
val hashed = getSodium().crypto_hash_sha256_final(state)
|
||||
val hash = UByteArray(Sha256StatelessDelegated.MAX_HASH_BYTES)
|
||||
console.log(hashed)
|
||||
for (i in 0 until Sha256StatelessDelegated.MAX_HASH_BYTES) {
|
||||
hash[i] = hashed[i].toUByte()
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
actual object Sha256StatelessDelegated : StatelessSha256 {
|
||||
|
||||
override fun digest(inputMessage: UByteArray): UByteArray {
|
||||
val hashed = getSodium().crypto_hash_sha256(Uint8Array(inputMessage.toByteArray().toTypedArray()))
|
||||
val hash = UByteArray(MAX_HASH_BYTES)
|
||||
for (i in 0 until MAX_HASH_BYTES) {
|
||||
hash[i] = hashed[i].toUByte()
|
||||
}
|
||||
return hash
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.getSodium
|
||||
import com.ionspin.kotlin.crypto.getSodium
|
||||
|
||||
import org.khronos.webgl.Uint8Array
|
||||
import org.khronos.webgl.get
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
actual class Sha512Delegated : Sha512 {
|
||||
val state : dynamic
|
||||
|
||||
init {
|
||||
state = getSodium().crypto_hash_sha512_init()
|
||||
}
|
||||
|
||||
override fun update(data: UByteArray) {
|
||||
getSodium().crypto_hash_sha512_update(state, Uint8Array(data.toByteArray().toTypedArray()))
|
||||
}
|
||||
|
||||
override fun digest(): UByteArray {
|
||||
val hashed = getSodium().crypto_hash_sha512_final(state)
|
||||
val hash = UByteArray(Sha512StatelessDelegated.MAX_HASH_BYTES)
|
||||
for (i in 0 until Sha512StatelessDelegated.MAX_HASH_BYTES) {
|
||||
hash[i] = hashed[i].toUByte()
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
actual object Sha512StatelessDelegated : StatelessSha512 {
|
||||
|
||||
override fun digest(inputMessage: UByteArray): UByteArray {
|
||||
val hashed = getSodium().crypto_hash_sha512(Uint8Array(inputMessage.toByteArray().toTypedArray()))
|
||||
val hash = UByteArray(Sha512StatelessDelegated.MAX_HASH_BYTES)
|
||||
for (i in 0 until Sha512StatelessDelegated.MAX_HASH_BYTES) {
|
||||
hash[i] = hashed[i].toUByte()
|
||||
}
|
||||
return hash
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
@file:JsModule("libsodium-wrappers-sumo")
|
||||
@file:JsNonModule
|
||||
package ext.libsodium
|
||||
|
||||
import org.khronos.webgl.Uint8Array
|
||||
import kotlin.js.Promise
|
||||
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 25-May-2020
|
||||
*/
|
||||
|
||||
@JsName("ready")
|
||||
external val _libsodiumPromise : Promise<dynamic>
|
||||
|
||||
external fun crypto_generichash(hashLength: Int, inputMessage: Uint8Array) : Uint8Array
|
||||
|
||||
external fun crypto_hash_sha256(message: Uint8Array) : Uint8Array
|
||||
external fun crypto_hash_sha512(message: Uint8Array) : Uint8Array
|
||||
|
||||
external fun crypto_hash_sha256_init(): dynamic
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import com.ionspin.kotlin.crypto.util.testBlocking
|
||||
import ext.libsodium.com.ionspin.kotlin.crypto.JsSodiumLoader
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 05-Jan-2020
|
||||
*/
|
||||
|
||||
class SRNGJsTest {
|
||||
|
||||
@Test
|
||||
fun testJsSrng() = testBlocking {
|
||||
JsSodiumLoader.load()
|
||||
val bytes1 = SRNG.getRandomBytes(10)
|
||||
val bytes2 = SRNG.getRandomBytes(10)
|
||||
// println("BYTES1\n")
|
||||
// bytes1.forEach {
|
||||
// print(it.toString(16).padStart(2, '0'))
|
||||
// }
|
||||
// println("BYTES2\n")
|
||||
// bytes2.forEach {
|
||||
// print(it.toString(16).padStart(2, '0'))
|
||||
// }
|
||||
assertTrue {
|
||||
!bytes1.contentEquals(bytes2) &&
|
||||
bytes1.size == 10 &&
|
||||
bytes2.size == 10
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.util
|
||||
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.promise
|
||||
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 20-Jul-2019
|
||||
*/
|
||||
actual fun testBlocking(block: suspend ()-> Unit) : dynamic = GlobalScope.promise { block() }
|
@ -0,0 +1,25 @@
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import com.goterl.lazycode.lazysodium.SodiumJava
|
||||
|
||||
actual object Initializer {
|
||||
private var isPlatformInitialized = false
|
||||
|
||||
lateinit var sodium : SodiumJava
|
||||
actual suspend fun initialize() {
|
||||
sodium = SodiumJava()
|
||||
isPlatformInitialized = true
|
||||
}
|
||||
|
||||
actual fun initializeWithCallback(done: () -> Unit) {
|
||||
sodium = SodiumJava()
|
||||
isPlatformInitialized = true
|
||||
done()
|
||||
}
|
||||
|
||||
actual fun isInitialized(): Boolean {
|
||||
return isPlatformInitialized
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import java.security.SecureRandom
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 21-Sep-2019
|
||||
*/
|
||||
|
||||
actual object SRNG {
|
||||
val secureRandom = SecureRandom()
|
||||
actual fun getRandomBytes(amount: Int): UByteArray {
|
||||
val byteArray = ByteArray(amount)
|
||||
secureRandom.nextBytes(byteArray)
|
||||
return byteArray.toUByteArray()
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.ionspin.kotlin.crypto.hash.blake2b
|
||||
|
||||
import com.ionspin.kotlin.crypto.Initializer.sodium
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
actual class Blake2bDelegated actual constructor(key: UByteArray?, val hashLength: Int) : Blake2b {
|
||||
|
||||
val state = ByteArray(sodium.crypto_generichash_statebytes())
|
||||
|
||||
init {
|
||||
sodium.crypto_generichash_init(state,key?.toByteArray() ?: byteArrayOf(), key?.size ?: 0, hashLength)
|
||||
}
|
||||
|
||||
override fun update(data: UByteArray) {
|
||||
sodium.crypto_generichash_update(state, data.toByteArray(), data.size.toLong())
|
||||
}
|
||||
|
||||
override fun digest(): UByteArray {
|
||||
val hashed = ByteArray(hashLength)
|
||||
sodium.crypto_generichash_final(state, hashed, hashLength)
|
||||
return hashed.toUByteArray()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
actual object Blake2bDelegatedStateless : Blake2bStateless {
|
||||
|
||||
|
||||
override fun digest(inputMessage: UByteArray, key: UByteArray, hashLength: Int): UByteArray {
|
||||
val hashed = ByteArray(hashLength)
|
||||
sodium.crypto_generichash(hashed, hashed.size, inputMessage.toByteArray(), inputMessage.size.toLong(), key.toByteArray(), key.size)
|
||||
return hashed.toUByteArray()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.goterl.lazycode.lazysodium.interfaces.Hash
|
||||
import com.ionspin.kotlin.crypto.Initializer.sodium
|
||||
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
actual class Sha256Delegated actual constructor() : Sha256 {
|
||||
|
||||
val state = Hash.State256()
|
||||
|
||||
init {
|
||||
sodium.crypto_hash_sha256_init(state)
|
||||
}
|
||||
|
||||
override fun update(data: UByteArray) {
|
||||
sodium.crypto_hash_sha256_update(state, data.toByteArray(), data.size.toLong())
|
||||
}
|
||||
|
||||
override fun digest(): UByteArray {
|
||||
val hashed = ByteArray(Sha256Properties.MAX_HASH_BYTES)
|
||||
sodium.crypto_hash_sha256_final(state, hashed)
|
||||
return hashed.toUByteArray()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
actual object Sha256StatelessDelegated : StatelessSha256 {
|
||||
|
||||
override fun digest(inputMessage: UByteArray): UByteArray {
|
||||
val hashed = ByteArray(Sha256Properties.MAX_HASH_BYTES)
|
||||
sodium.crypto_hash_sha256(hashed, inputMessage.toByteArray(), inputMessage.size.toLong())
|
||||
return hashed.toUByteArray()
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.goterl.lazycode.lazysodium.interfaces.Hash
|
||||
import com.ionspin.kotlin.crypto.Initializer
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
actual class Sha512Delegated : Sha512 {
|
||||
|
||||
val state = Hash.State512()
|
||||
|
||||
init {
|
||||
Initializer.sodium.crypto_hash_sha512_init(state)
|
||||
}
|
||||
|
||||
override fun update(data: UByteArray) {
|
||||
Initializer.sodium.crypto_hash_sha512_update(state, data.toByteArray(), data.size.toLong())
|
||||
}
|
||||
|
||||
override fun digest(): UByteArray {
|
||||
val hashed = ByteArray(Sha512Properties.MAX_HASH_BYTES)
|
||||
Initializer.sodium.crypto_hash_sha512_final(state, hashed)
|
||||
return hashed.toUByteArray()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
actual object Sha512StatelessDelegated : StatelessSha512 {
|
||||
|
||||
override fun digest(inputMessage: UByteArray): UByteArray {
|
||||
val hashed = ByteArray(Sha512Properties.MAX_HASH_BYTES)
|
||||
Initializer.sodium.crypto_hash_sha512(hashed, inputMessage.toByteArray(), inputMessage.size.toLong())
|
||||
return hashed.toUByteArray()
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.util
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 20-Jul-2019
|
||||
*/
|
||||
actual fun testBlocking(block: suspend () -> Unit) = runBlocking { block() }
|
@ -0,0 +1,9 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 07-Jun-2020
|
||||
*/
|
||||
import platform.posix.*
|
||||
//import cin
|
@ -0,0 +1,44 @@
|
||||
//We'll handle SRNG through libsodium
|
||||
///*
|
||||
// * Copyright 2019 Ugljesa Jovanovic
|
||||
// *
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// */
|
||||
//
|
||||
//package com.ionspin.kotlin.crypto
|
||||
//
|
||||
//import kotlinx.cinterop.*
|
||||
//import platform.windows.*
|
||||
//
|
||||
///**
|
||||
// * Created by Ugljesa Jovanovic
|
||||
// * ugljesa.jovanovic@ionspin.com
|
||||
// * on 21-Sep-2019
|
||||
// */
|
||||
//actual object SRNG {
|
||||
// private val advapi by lazy { LoadLibraryA("ADVAPI32.DLL")}
|
||||
//
|
||||
// private val advapiRandom by lazy {
|
||||
// GetProcAddress(advapi, "SystemFunction036")?.reinterpret<CFunction<Function2<CPointer<ByteVar>, ULong, Int>>>() ?: error("Failed getting advapi random")
|
||||
// }
|
||||
//
|
||||
// @Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
|
||||
// actual fun getRandomBytes(amount: Int): UByteArray {
|
||||
// memScoped {
|
||||
// val randArray = allocArray<ByteVar>(amount)
|
||||
// val pointer = randArray.getPointer(this)
|
||||
// val status = advapiRandom(pointer.reinterpret(), amount.convert())
|
||||
// return UByteArray(amount) { pointer[it].toUByte() }
|
||||
// }
|
||||
// }
|
||||
//}
|
@ -0,0 +1,6 @@
|
||||
headers = sodium.h
|
||||
headerFilter = sodium.h sodium/**
|
||||
#staticLibraries = libsodium.a
|
||||
#libraryPaths = sodiumWrapper/lib
|
||||
#compilerOpts = -I./sodiumWrapper/include
|
||||
linkerOpts =
|
@ -0,0 +1,32 @@
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import kotlinx.atomicfu.AtomicBoolean
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import libsodium.sodium_init
|
||||
import kotlin.native.concurrent.AtomicInt
|
||||
|
||||
actual object Initializer {
|
||||
|
||||
private var isPlatformInitialized : AtomicInt = AtomicInt(0)
|
||||
|
||||
actual suspend fun initialize() {
|
||||
if (isPlatformInitialized.compareAndSet(0, 1)) {
|
||||
sodium_init()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
actual fun initializeWithCallback(done: () -> Unit) {
|
||||
if (isPlatformInitialized.compareAndSet(0, 1)) {
|
||||
sodium_init()
|
||||
}
|
||||
done()
|
||||
}
|
||||
|
||||
actual fun isInitialized(): Boolean {
|
||||
return isPlatformInitialized.value != 0
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import libsodium.randombytes_buf
|
||||
import platform.posix.*
|
||||
//import libsod
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 21-Sep-2019
|
||||
*/
|
||||
actual object SRNG {
|
||||
@Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
|
||||
actual fun getRandomBytes(amount: Int): UByteArray {
|
||||
memScoped {
|
||||
val array = allocArray<UByteVar>(amount)
|
||||
randombytes_buf(array, amount.convert())
|
||||
return UByteArray(amount) {
|
||||
array[it]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package com.ionspin.kotlin.crypto.hash.blake2b
|
||||
import com.ionspin.kotlin.crypto.util.toHexString
|
||||
import kotlinx.cinterop.*
|
||||
import libsodium.*
|
||||
import platform.posix.free
|
||||
import platform.posix.malloc
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
actual class Blake2bDelegated actual constructor(key: UByteArray?, hashLength: Int) : Blake2b {
|
||||
override val MAX_HASH_BYTES: Int = 64
|
||||
|
||||
val requestedHashLength : Int
|
||||
val state : crypto_generichash_state
|
||||
init {
|
||||
requestedHashLength = hashLength
|
||||
val allocated = malloc(crypto_generichash_state.size.convert())!!
|
||||
state = allocated.reinterpret<crypto_generichash_state>().pointed
|
||||
crypto_generichash_init(state.ptr, key?.run { this.toUByteArray().toCValues() }, key?.size?.convert() ?: 0UL.convert(), hashLength.convert())
|
||||
}
|
||||
|
||||
override fun update(data: UByteArray) {
|
||||
crypto_generichash_update(state.ptr, data.toCValues(), data.size.convert())
|
||||
}
|
||||
|
||||
override fun digest(): UByteArray {
|
||||
val hashResult = UByteArray(requestedHashLength)
|
||||
val hashResultPinned = hashResult.pin()
|
||||
crypto_generichash_final(state.ptr, hashResultPinned.addressOf(0), requestedHashLength.convert())
|
||||
free(state.ptr)
|
||||
return hashResult
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
|
||||
actual object Blake2bDelegatedStateless : Blake2bStateless {
|
||||
|
||||
override fun digest(inputMessage: UByteArray, key: UByteArray, hashLength: Int): UByteArray {
|
||||
val hashResult = UByteArray(MAX_HASH_BYTES)
|
||||
val hashResultPinned = hashResult.pin()
|
||||
crypto_generichash(
|
||||
hashResultPinned.addressOf(0),
|
||||
hashLength.convert(),
|
||||
inputMessage.toCValues(),
|
||||
inputMessage.size.convert(),
|
||||
key.toCValues(),
|
||||
key.size.convert()
|
||||
)
|
||||
hashResultPinned.unpin()
|
||||
return hashResult
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bDelegatedStateless
|
||||
import kotlinx.cinterop.*
|
||||
import libsodium.*
|
||||
import platform.posix.free
|
||||
import platform.posix.malloc
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
actual class Sha256Delegated : Sha256 {
|
||||
|
||||
val state : crypto_hash_sha256_state
|
||||
|
||||
init {
|
||||
val allocated = malloc(crypto_hash_sha256_state.size.convert())!!
|
||||
state = allocated.reinterpret<crypto_hash_sha256_state>().pointed
|
||||
crypto_hash_sha256_init(state.ptr)
|
||||
}
|
||||
|
||||
override fun update(data: UByteArray) {
|
||||
crypto_hash_sha256_update(state.ptr, data.toCValues(), data.size.convert())
|
||||
}
|
||||
|
||||
|
||||
|
||||
override fun digest(): UByteArray {
|
||||
val hashResult = UByteArray(Sha256Properties.MAX_HASH_BYTES)
|
||||
val hashResultPinned = hashResult.pin()
|
||||
crypto_hash_sha256_final(state.ptr, hashResultPinned.addressOf(0))
|
||||
free(state.ptr)
|
||||
return hashResult
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
actual object Sha256StatelessDelegated : StatelessSha256 {
|
||||
|
||||
override fun digest(inputMessage: UByteArray): UByteArray {
|
||||
val hashResult = UByteArray(MAX_HASH_BYTES)
|
||||
val hashResultPinned = hashResult.pin()
|
||||
crypto_hash_sha256(hashResultPinned.addressOf(0), inputMessage.toCValues(), inputMessage.size.convert())
|
||||
hashResultPinned.unpin()
|
||||
return hashResult
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import libsodium.*
|
||||
import platform.posix.free
|
||||
import platform.posix.malloc
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
actual class Sha512Delegated : Sha512 {
|
||||
val state : crypto_hash_sha512_state
|
||||
|
||||
init {
|
||||
val allocated = malloc(crypto_hash_sha512_state.size.convert())!!
|
||||
state = allocated.reinterpret<crypto_hash_sha512_state>().pointed
|
||||
crypto_hash_sha512_init(state.ptr)
|
||||
}
|
||||
|
||||
override fun update(data: UByteArray) {
|
||||
crypto_hash_sha512_update(state.ptr, data.toCValues(), data.size.convert())
|
||||
}
|
||||
|
||||
|
||||
|
||||
override fun digest(): UByteArray {
|
||||
val hashResult = UByteArray(Sha512Properties.MAX_HASH_BYTES)
|
||||
val hashResultPinned = hashResult.pin()
|
||||
crypto_hash_sha512_final(state.ptr, hashResultPinned.addressOf(0))
|
||||
free(state.ptr)
|
||||
return hashResult
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
actual object Sha512StatelessDelegated : StatelessSha512 {
|
||||
|
||||
override fun digest(inputMessage: UByteArray): UByteArray {
|
||||
val hashResult = UByteArray(Sha512StatelessDelegated.MAX_HASH_BYTES)
|
||||
val hashResultPinned = hashResult.pin()
|
||||
crypto_hash_sha512(hashResultPinned.addressOf(0), inputMessage.toCValues(), inputMessage.size.convert())
|
||||
hashResultPinned.unpin()
|
||||
return hashResult
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.util
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 20-Jul-2019
|
||||
*/
|
||||
actual fun testBlocking(block: suspend () -> Unit) = runBlocking { block() }
|
@ -16,83 +16,55 @@
|
||||
*/
|
||||
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import com.moowork.gradle.node.task.NodeTask
|
||||
import org.gradle.api.tasks.testing.logging.TestLogging
|
||||
import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest
|
||||
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
|
||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
||||
|
||||
plugins {
|
||||
kotlin(PluginsDeps.multiplatform)
|
||||
id (PluginsDeps.mavenPublish)
|
||||
id (PluginsDeps.signing)
|
||||
id (PluginsDeps.node) version Versions.nodePlugin
|
||||
id (PluginsDeps.dokka) version Versions.dokkaPlugin
|
||||
id(PluginsDeps.mavenPublish)
|
||||
id(PluginsDeps.signing)
|
||||
id(PluginsDeps.node) version Versions.nodePlugin
|
||||
id(PluginsDeps.dokka) version Versions.dokkaPlugin
|
||||
}
|
||||
|
||||
val sonatypeStaging = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||
val sonatypeSnapshots = "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||
|
||||
val sonatypePassword : String? by project
|
||||
val sonatypePassword: String? by project
|
||||
|
||||
val sonatypeUsername : String? by project
|
||||
val sonatypeUsername: String? by project
|
||||
|
||||
val sonatypePasswordEnv : String? = System.getenv()["SONATYPE_PASSWORD"]
|
||||
val sonatypeUsernameEnv : String? = System.getenv()["SONATYPE_USERNAME"]
|
||||
val sonatypePasswordEnv: String? = System.getenv()["SONATYPE_PASSWORD"]
|
||||
val sonatypeUsernameEnv: String? = System.getenv()["SONATYPE_USERNAME"]
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
|
||||
}
|
||||
group = "com.ionspin.kotlin"
|
||||
version = "0.0.4-SNAPSHOT"
|
||||
group = ReleaseInfo.group
|
||||
version = ReleaseInfo.version
|
||||
|
||||
val ideaActive = System.getProperty("idea.active") == "true"
|
||||
|
||||
fun getHostOsName(): String {
|
||||
val target = System.getProperty("os.name")
|
||||
if (target == "Linux") return "linux"
|
||||
if (target.startsWith("Windows")) return "windows"
|
||||
if (target.startsWith("Mac")) return "macos"
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
kotlin {
|
||||
val hostOsName = getHostOsName()
|
||||
if (ideaActive) {
|
||||
when(hostOsName) {
|
||||
"linux" -> linuxX64("native")
|
||||
"macos" -> macosX64("native")
|
||||
"windows" -> mingwX64("native")
|
||||
}
|
||||
}
|
||||
if (hostOsName == "linux") {
|
||||
runningOnLinuxx86_64 {
|
||||
jvm()
|
||||
js {
|
||||
compilations {
|
||||
this.forEach {
|
||||
it.compileKotlinTask.kotlinOptions.sourceMap = true
|
||||
it.compileKotlinTask.kotlinOptions.moduleKind = "commonjs"
|
||||
it.compileKotlinTask.kotlinOptions.metaInfo = true
|
||||
|
||||
if (it.name == "main") {
|
||||
it.compileKotlinTask.kotlinOptions.main = "call"
|
||||
}
|
||||
println("Compilation name ${it.name} set")
|
||||
println("Destination dir ${it.compileKotlinTask.destinationDir}")
|
||||
browser {
|
||||
|
||||
testTask {
|
||||
// isRunningInTravis {
|
||||
enabled = false //Until I sort out testing on travis, and figure out how to increase karma timeout
|
||||
// }
|
||||
useKarma {
|
||||
useChrome()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
//Until I figure out how to run headless chrome on travis
|
||||
// browser {
|
||||
//
|
||||
// testTask {
|
||||
// useKarma {
|
||||
// useChrome()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
nodejs {
|
||||
testTask {
|
||||
useMocha() {
|
||||
@ -105,39 +77,41 @@ kotlin {
|
||||
linuxX64("linux") {
|
||||
binaries {
|
||||
staticLib {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
linuxArm64() {
|
||||
binaries {
|
||||
staticLib {
|
||||
}
|
||||
}
|
||||
}
|
||||
//Not supported in coroutines at the moment
|
||||
// linuxArm32Hfp() {
|
||||
// binaries {
|
||||
// staticLib {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//Not supported in coroutines at the moment
|
||||
// linuxArm64() {
|
||||
// binaries {
|
||||
// staticLib {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
linuxArm32Hfp() {
|
||||
binaries {
|
||||
staticLib {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (hostOsName == "macos") {
|
||||
runningOnMacos {
|
||||
iosX64("ios") {
|
||||
binaries {
|
||||
framework {
|
||||
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
iosArm64("ios64Arm") {
|
||||
binaries {
|
||||
framework {
|
||||
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -145,24 +119,24 @@ kotlin {
|
||||
iosArm32("ios32Arm") {
|
||||
binaries {
|
||||
framework {
|
||||
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
macosX64() {
|
||||
binaries {
|
||||
framework {
|
||||
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hostOsName == "windows") {
|
||||
runningOnWindows {
|
||||
|
||||
mingwX64() {
|
||||
binaries {
|
||||
staticLib {
|
||||
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -186,6 +160,7 @@ kotlin {
|
||||
implementation(kotlin(Deps.Common.test))
|
||||
implementation(Deps.Common.coroutines)
|
||||
implementation(Deps.Common.kotlinBigNum)
|
||||
implementation(project(Deps.Common.apiProject))
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
@ -195,42 +170,49 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
val nativeMain = if (ideaActive) {
|
||||
val nativeMain by getting {
|
||||
dependsOn(commonMain)
|
||||
dependencies {
|
||||
implementation(Deps.Native.coroutines)
|
||||
}
|
||||
}
|
||||
nativeMain
|
||||
} else {
|
||||
|
||||
val nativeMain by creating {
|
||||
dependsOn(commonMain)
|
||||
dependencies {
|
||||
implementation(Deps.Native.coroutines)
|
||||
}
|
||||
}
|
||||
nativeMain
|
||||
}
|
||||
val nativeTest = if (ideaActive) {
|
||||
val nativeTest by getting {
|
||||
dependsOn(commonTest)
|
||||
dependencies {
|
||||
implementation(Deps.Native.coroutines)
|
||||
isRunningInIdea {
|
||||
kotlin.setSrcDirs(emptySet<String>())
|
||||
}
|
||||
}
|
||||
nativeTest
|
||||
} else {
|
||||
|
||||
|
||||
val nativeTest by creating {
|
||||
dependsOn(commonTest)
|
||||
dependencies {
|
||||
implementation(Deps.Native.coroutines)
|
||||
}
|
||||
}
|
||||
nativeTest
|
||||
|
||||
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
|
||||
compilations.getByName("main") {
|
||||
println("Setting native sourceset dependancy for $name")
|
||||
if ((this@withType.name.contains("ios") ||
|
||||
this@withType.name.contains("mingw")).not()
|
||||
) {
|
||||
println("Setting native sourceset dependancy for $this@withType.name")
|
||||
defaultSourceSet.dependsOn(nativeMain)
|
||||
}
|
||||
}
|
||||
compilations.getByName("test") {
|
||||
println("Setting native sourceset dependancy for $name")
|
||||
if ((this@withType.name.contains("ios") ||
|
||||
this@withType.name.contains("mingw")).not()
|
||||
) {
|
||||
println("Setting native sourceset dependancy for $this@withType.name")
|
||||
defaultSourceSet.dependsOn(nativeTest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hostOsName == "linux") {
|
||||
|
||||
|
||||
runningOnLinuxx86_64 {
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Jvm.stdLib))
|
||||
@ -250,41 +232,50 @@ kotlin {
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Js.stdLib))
|
||||
implementation(kotlin(Deps.Js.test))
|
||||
implementation(Deps.Js.coroutines)
|
||||
}
|
||||
}
|
||||
val jsTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test-js"))
|
||||
implementation(Deps.Js.coroutines)
|
||||
implementation(kotlin(Deps.Js.test))
|
||||
}
|
||||
}
|
||||
val linuxMain by getting {
|
||||
dependsOn(nativeMain)
|
||||
//Force idea to consider native sourceset
|
||||
if (ideaActive) {
|
||||
kotlin.srcDir("src/nativeMain/kotlin")
|
||||
}
|
||||
}
|
||||
val linuxTest by getting {
|
||||
dependsOn(nativeTest)
|
||||
// Force idea to consider native sourceset
|
||||
if (ideaActive) {
|
||||
kotlin.srcDir("src/nativeTest/kotlin")
|
||||
}
|
||||
}
|
||||
//Not supported in coroutines at the moment
|
||||
// val linuxArm32HfpMain by getting {
|
||||
// dependsOn(nativeMain)
|
||||
// }
|
||||
//
|
||||
// val linuxArm32HfpTest by getting {
|
||||
// dependsOn(nativeTest)
|
||||
// }
|
||||
|
||||
// val linuxArm64Main by getting {
|
||||
// dependsOn(nativeMain)
|
||||
// }
|
||||
//
|
||||
// val linuxArm64Test by getting {
|
||||
// dependsOn(nativeTest)
|
||||
// }
|
||||
val linuxArm64Main by getting {
|
||||
dependsOn(nativeMain)
|
||||
}
|
||||
|
||||
val linuxArm64Test by getting {
|
||||
dependsOn(nativeTest)
|
||||
}
|
||||
|
||||
val linuxArm32HfpMain by getting {
|
||||
dependsOn(nativeMain)
|
||||
}
|
||||
|
||||
val linuxArm32HfpTest by getting {
|
||||
dependsOn(nativeTest)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (hostOsName == "macos") {
|
||||
|
||||
runningOnMacos{
|
||||
|
||||
val iosMain by getting {
|
||||
dependsOn(nativeMain)
|
||||
@ -308,10 +299,16 @@ kotlin {
|
||||
}
|
||||
|
||||
val macosX64Main by getting {
|
||||
dependsOn(nativeMain)
|
||||
dependsOn(commonMain)
|
||||
if (ideaActive) {
|
||||
kotlin.srcDir("src/nativeMain/kotlin")
|
||||
}
|
||||
}
|
||||
val macosX64Test by getting {
|
||||
dependsOn(nativeTest)
|
||||
dependsOn(commonTest)
|
||||
if (ideaActive) {
|
||||
kotlin.srcDir("src/nativeTest/kotlin")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,7 +324,7 @@ kotlin {
|
||||
// dependsOn(commonTest)
|
||||
// }
|
||||
//
|
||||
if (hostOsName == "windows") {
|
||||
runningOnWindows {
|
||||
val mingwX64Main by getting {
|
||||
dependsOn(commonMain)
|
||||
dependencies {
|
||||
@ -343,6 +340,8 @@ kotlin {
|
||||
|
||||
all {
|
||||
languageSettings.enableLanguageFeature("InlineClasses")
|
||||
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
|
||||
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalStdlibApi")
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,9 +353,9 @@ kotlin {
|
||||
task<Copy>("copyPackageJson") {
|
||||
dependsOn("compileKotlinJs")
|
||||
println("Copying package.json from $projectDir/core/src/jsMain/npm")
|
||||
from ("$projectDir/src/jsMain/npm")
|
||||
from("$projectDir/src/jsMain/npm")
|
||||
println("Node modules dir ${node.nodeModulesDir}")
|
||||
into ("${node.nodeModulesDir}")
|
||||
into("${node.nodeModulesDir}")
|
||||
}
|
||||
|
||||
tasks {
|
||||
@ -369,22 +368,19 @@ tasks {
|
||||
}
|
||||
|
||||
dokka {
|
||||
println ("Dokka !")
|
||||
println("Dokka !")
|
||||
impliedPlatforms = mutableListOf("Common")
|
||||
kotlinTasks {
|
||||
listOf()
|
||||
}
|
||||
sourceRoot {
|
||||
println ("Common !")
|
||||
path = "/home/ionspin/Projects/Future/kotlin-multiplatform-crypto/crypto/src/commonMain" //TODO remove static path!
|
||||
println("Common !")
|
||||
path =
|
||||
"/home/ionspin/Projects/Future/kotlin-multiplatform-crypto/crypto/src/commonMain" //TODO remove static path!
|
||||
platforms = listOf("Common")
|
||||
}
|
||||
}
|
||||
if (getHostOsName() == "linux") {
|
||||
|
||||
val npmInstall by getting
|
||||
val compileKotlinJs by getting(AbstractCompile::class)
|
||||
val compileTestKotlinJs by getting(Kotlin2JsCompile::class)
|
||||
if (getHostOsName() == "linux" && getHostArchitecture() == "x86-64") {
|
||||
|
||||
val jvmTest by getting(Test::class) {
|
||||
testLogging {
|
||||
@ -396,25 +392,24 @@ tasks {
|
||||
|
||||
testLogging {
|
||||
events("PASSED", "FAILED", "SKIPPED")
|
||||
// showStandardStreams = true
|
||||
// showStandardStreams = true
|
||||
}
|
||||
}
|
||||
|
||||
val jsIrNodeTest by getting(KotlinJsTest::class) {
|
||||
|
||||
val jsNodeTest by getting(KotlinJsTest::class) {
|
||||
testLogging {
|
||||
events("PASSED", "FAILED", "SKIPPED")
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
||||
|
||||
val legacyjsNodeTest by getting(KotlinJsTest::class) {
|
||||
|
||||
testLogging {
|
||||
events("PASSED", "FAILED", "SKIPPED")
|
||||
showStandardStreams = true
|
||||
}
|
||||
}
|
||||
// val legacyjsNodeTest by getting(KotlinJsTest::class) {
|
||||
//
|
||||
// testLogging {
|
||||
// events("PASSED", "FAILED", "SKIPPED")
|
||||
// showStandardStreams = true
|
||||
// }
|
||||
// }
|
||||
|
||||
// val jsIrBrowserTest by getting(KotlinJsTest::class) {
|
||||
// testLogging {
|
||||
|
@ -1,21 +0,0 @@
|
||||
#
|
||||
# Copyright 2019 Ugljesa Jovanovic
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
@ -0,0 +1,67 @@
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bProperties
|
||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bPure
|
||||
import com.ionspin.kotlin.crypto.hash.sha.Sha256Pure
|
||||
import com.ionspin.kotlin.crypto.hash.sha.Sha512Pure
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 24-May-2020
|
||||
*/
|
||||
typealias Blake2bPureStateless = Blake2bPure.Companion
|
||||
typealias Sha256PureStateless = Sha256Pure.Companion
|
||||
typealias Sha512PureStateless = Sha512Pure.Companion
|
||||
|
||||
object Crypto : CryptoProvider {
|
||||
override suspend fun initialize() {
|
||||
//Nothing to do atm.
|
||||
}
|
||||
|
||||
fun initializeWithCallback(done: () -> Unit) {
|
||||
done()
|
||||
}
|
||||
|
||||
|
||||
object Blake2b {
|
||||
fun updateable(key: UByteArray? = null, hashLength: Int = Blake2bProperties.MAX_HASH_BYTES): com.ionspin.kotlin.crypto.hash.blake2b.Blake2b {
|
||||
checkInitialization()
|
||||
return Blake2bPure(key, hashLength)
|
||||
}
|
||||
|
||||
fun stateless(message: UByteArray, key: UByteArray = ubyteArrayOf(), hashLength: Int = Blake2bProperties.MAX_HASH_BYTES): UByteArray {
|
||||
checkInitialization()
|
||||
return Blake2bPureStateless.digest(message, key, hashLength)
|
||||
}
|
||||
}
|
||||
|
||||
object Sha256 {
|
||||
fun updateable(): com.ionspin.kotlin.crypto.hash.sha.Sha256 {
|
||||
checkInitialization()
|
||||
return Sha256Pure()
|
||||
}
|
||||
|
||||
fun stateless(message: UByteArray) : UByteArray{
|
||||
checkInitialization()
|
||||
return Sha256PureStateless.digest(inputMessage = message)
|
||||
}
|
||||
}
|
||||
|
||||
object Sha512 {
|
||||
fun updateable(): com.ionspin.kotlin.crypto.hash.sha.Sha512 {
|
||||
checkInitialization()
|
||||
return Sha512Pure()
|
||||
}
|
||||
|
||||
fun stateless(message: UByteArray) : UByteArray {
|
||||
checkInitialization()
|
||||
return Sha512PureStateless.digest(inputMessage = message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkInitialization() {
|
||||
// Nothing to do atm
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package _multiplatform_crypto
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 24-May-2020
|
||||
*/
|
||||
//Workaround for https://youtrack.jetbrains.com/issue/KT-36878
|
||||
val byteArray = byteArrayOf(0)
|
||||
val byte = 0.toByte()
|
||||
val longArray = longArrayOf(0)
|
||||
val long = 0L
|
@ -22,5 +22,5 @@ package com.ionspin.kotlin.crypto
|
||||
* on 21-Sep-2019
|
||||
*/
|
||||
expect object SRNG {
|
||||
fun getRandomBytes(amount : Int) : Array<UByte>
|
||||
fun getRandomBytes(amount : Int) : UByteArray
|
||||
}
|
@ -19,9 +19,6 @@ package com.ionspin.kotlin.crypto.hash.blake2b
|
||||
import com.ionspin.kotlin.bignum.integer.BigInteger
|
||||
import com.ionspin.kotlin.bignum.integer.toBigInteger
|
||||
import com.ionspin.kotlin.crypto.*
|
||||
import com.ionspin.kotlin.crypto.hash.StatelessHash
|
||||
import com.ionspin.kotlin.crypto.hash.UpdatableHash
|
||||
import com.ionspin.kotlin.crypto.util.chunked
|
||||
import com.ionspin.kotlin.crypto.util.rotateRight
|
||||
|
||||
/**
|
||||
@ -30,11 +27,15 @@ import com.ionspin.kotlin.crypto.util.rotateRight
|
||||
* on 14-Jul-2019
|
||||
*/
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : UpdatableHash {
|
||||
|
||||
companion object : StatelessHash {
|
||||
class Blake2bPure(val key: UByteArray? = null, val hashLength: Int = 64) : Blake2b {
|
||||
|
||||
companion object : Blake2bStateless {
|
||||
//Hack start
|
||||
//If this line is not included konanc 1.4-M1 fails to link because it cant find ByteArray which is
|
||||
//a backing class for UByteArray
|
||||
val byteArray: ByteArray = byteArrayOf(0, 1)
|
||||
//Hack end
|
||||
const val BITS_IN_WORD = 64
|
||||
const val ROUNDS_IN_COMPRESS = 12
|
||||
const val BLOCK_BYTES = 128
|
||||
@ -103,7 +104,7 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
||||
|
||||
fun compress(
|
||||
h: Array<ULong>,
|
||||
input: Array<UByte>,
|
||||
input: UByteArray,
|
||||
offsetCounter: BigInteger,
|
||||
finalBlock: Boolean
|
||||
): Array<ULong> {
|
||||
@ -130,7 +131,6 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
||||
v[13] = v[13] xor (offsetCounter shr BITS_IN_WORD).ulongValue()
|
||||
|
||||
if (finalBlock) {
|
||||
// v[14] = v[14] xor 0xFFFFFFFFFFFFFFFFUL
|
||||
v[14] = v[14].inv()
|
||||
}
|
||||
|
||||
@ -144,25 +144,25 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
||||
return h
|
||||
}
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
override fun digest(inputString: String, key: String?, hashLength: Int): Array<UByte> {
|
||||
val array = inputString.encodeToByteArray().map { it.toUByte() }.toList().toTypedArray()
|
||||
|
||||
fun digest(inputString: String, key: String?, hashLength: Int): UByteArray {
|
||||
val array = inputString.encodeToByteArray().toUByteArray()
|
||||
val keyBytes = key?.run {
|
||||
encodeToByteArray().map { it.toUByte() }.toTypedArray()
|
||||
} ?: emptyArray()
|
||||
encodeToByteArray().toUByteArray()
|
||||
} ?: ubyteArrayOf()
|
||||
return digest(inputMessage = array, key = keyBytes, hashLength = hashLength)
|
||||
|
||||
}
|
||||
|
||||
override fun digest(
|
||||
inputMessage: Array<UByte>,
|
||||
key: Array<UByte>,
|
||||
inputMessage: UByteArray,
|
||||
key: UByteArray,
|
||||
hashLength: Int
|
||||
): Array<UByte> {
|
||||
): UByteArray {
|
||||
if (hashLength > MAX_HASH_BYTES) {
|
||||
throw RuntimeException("Invalid hash length. Requested length more than maximum length. Requested length $hashLength")
|
||||
}
|
||||
val chunkedMessage = inputMessage.chunked(BLOCK_BYTES)
|
||||
val chunkedMessage = inputMessage.chunked(BLOCK_BYTES).map { it.toUByteArray() }.toTypedArray()
|
||||
|
||||
val h = iv.copyOf()
|
||||
|
||||
@ -172,7 +172,7 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
||||
val message = if (key.isEmpty()) {
|
||||
if (chunkedMessage.isEmpty()) {
|
||||
Array(1) {
|
||||
Array<UByte>(128) {
|
||||
UByteArray(128) {
|
||||
0U
|
||||
}
|
||||
}
|
||||
@ -199,7 +199,7 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
||||
val lastBlockPadded = if (message.isNotEmpty()) {
|
||||
padToBlock(message[message.size - 1])
|
||||
} else {
|
||||
Array<UByte>(16) { 0U }
|
||||
UByteArray(16) { 0U }
|
||||
}
|
||||
|
||||
compress(h, lastBlockPadded, lastSize.toBigInteger(), true).copyInto(h)
|
||||
@ -208,7 +208,7 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
||||
return formatResult(h).copyOfRange(0, hashLength)
|
||||
}
|
||||
|
||||
private fun formatResult(h: Array<ULong>): Array<UByte> {
|
||||
private fun formatResult(h: Array<ULong>): UByteArray {
|
||||
return h.map {
|
||||
arrayOf(
|
||||
(it and 0xFFUL).toUByte(),
|
||||
@ -222,10 +222,10 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
||||
)
|
||||
}.flatMap {
|
||||
it.toList()
|
||||
}.toTypedArray()
|
||||
}.toUByteArray()
|
||||
}
|
||||
|
||||
private fun padToBlock(unpadded: Array<UByte>): Array<UByte> {
|
||||
private fun padToBlock(unpadded: UByteArray): UByteArray {
|
||||
if (unpadded.size == BLOCK_BYTES) {
|
||||
return unpadded
|
||||
}
|
||||
@ -234,7 +234,7 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
||||
throw IllegalStateException("Block larger than 128 bytes")
|
||||
}
|
||||
|
||||
return Array(BLOCK_BYTES) {
|
||||
return UByteArray(BLOCK_BYTES) {
|
||||
when (it) {
|
||||
in 0 until unpadded.size -> unpadded[it]
|
||||
else -> 0U
|
||||
@ -243,21 +243,21 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
||||
|
||||
}
|
||||
}
|
||||
@ExperimentalStdlibApi
|
||||
|
||||
constructor(
|
||||
key: String?,
|
||||
requestedHashLenght: Int = 64
|
||||
) : this(
|
||||
(key?.encodeToByteArray()?.map { it.toUByte() }?.toTypedArray() ?: emptyArray<UByte>()),
|
||||
(key?.encodeToByteArray()?.toUByteArray() ?: ubyteArrayOf()),
|
||||
requestedHashLenght
|
||||
)
|
||||
|
||||
override val MAX_HASH_BYTES: Int = Blake2b.MAX_HASH_BYTES
|
||||
override val MAX_HASH_BYTES: Int = Blake2bPure.MAX_HASH_BYTES
|
||||
|
||||
var h = iv.copyOf()
|
||||
var counter = BigInteger.ZERO
|
||||
var bufferCounter = 0
|
||||
var buffer = Array<UByte>(BLOCK_BYTES) { 0U }
|
||||
var buffer = UByteArray(BLOCK_BYTES) { 0U }
|
||||
|
||||
|
||||
init {
|
||||
@ -268,7 +268,7 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
||||
}
|
||||
}
|
||||
|
||||
override fun update(data: Array<UByte>) {
|
||||
override fun update(data: UByteArray) {
|
||||
if (data.isEmpty()) {
|
||||
throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
|
||||
}
|
||||
@ -276,7 +276,7 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
||||
when {
|
||||
bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter)
|
||||
bufferCounter + data.size >= BLOCK_BYTES -> {
|
||||
val chunked = data.chunked(BLOCK_BYTES)
|
||||
val chunked = data.chunked(BLOCK_BYTES).map { it.toUByteArray() }
|
||||
chunked.forEach { chunk ->
|
||||
if (bufferCounter + chunk.size < BLOCK_BYTES) {
|
||||
appendToBuffer(chunk, bufferCounter)
|
||||
@ -289,7 +289,7 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
||||
)
|
||||
counter += BLOCK_BYTES
|
||||
consumeBlock(buffer)
|
||||
buffer = Array<UByte>(BLOCK_BYTES) {
|
||||
buffer = UByteArray(BLOCK_BYTES) {
|
||||
when (it) {
|
||||
in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> {
|
||||
chunk[it + (BLOCK_BYTES - bufferCounter)]
|
||||
@ -308,21 +308,21 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
||||
}
|
||||
|
||||
}
|
||||
@ExperimentalStdlibApi
|
||||
override fun update(data: String) {
|
||||
update(data.encodeToByteArray().map { it.toUByte() }.toTypedArray())
|
||||
|
||||
fun update(data: String) {
|
||||
update(data.encodeToByteArray().toUByteArray())
|
||||
}
|
||||
|
||||
private fun appendToBuffer(array: Array<UByte>, start: Int) {
|
||||
private fun appendToBuffer(array: UByteArray, start: Int) {
|
||||
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||
bufferCounter += array.size
|
||||
}
|
||||
|
||||
private fun consumeBlock(block: Array<UByte>) {
|
||||
private fun consumeBlock(block: UByteArray) {
|
||||
h = compress(h, block, counter, false)
|
||||
}
|
||||
|
||||
override fun digest(): Array<UByte> {
|
||||
override fun digest(): UByteArray {
|
||||
val lastBlockPadded = padToBlock(buffer)
|
||||
counter += bufferCounter
|
||||
compress(h, lastBlockPadded, counter, true)
|
||||
@ -333,15 +333,12 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
||||
|
||||
}
|
||||
|
||||
override fun digestString(): String {
|
||||
return digest().map { it.toString(16) }.joinToString(separator = "")
|
||||
}
|
||||
|
||||
private fun reset() {
|
||||
h = iv.copyOf()
|
||||
counter = BigInteger.ZERO
|
||||
bufferCounter = 0
|
||||
buffer = Array<UByte>(BLOCK_BYTES) { 0U }
|
||||
buffer = UByteArray(BLOCK_BYTES) { 0U }
|
||||
}
|
||||
|
||||
|
@ -16,9 +16,6 @@
|
||||
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.util.chunked
|
||||
import com.ionspin.kotlin.crypto.hash.StatelessHash
|
||||
import com.ionspin.kotlin.crypto.hash.UpdatableHash
|
||||
import com.ionspin.kotlin.crypto.util.rotateRight
|
||||
|
||||
|
||||
@ -29,13 +26,13 @@ import com.ionspin.kotlin.crypto.util.rotateRight
|
||||
*/
|
||||
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
class Sha256 : UpdatableHash {
|
||||
|
||||
class Sha256Pure : Sha256 {
|
||||
|
||||
override val MAX_HASH_BYTES: Int = 32
|
||||
|
||||
|
||||
companion object : StatelessHash {
|
||||
companion object : StatelessSha256 {
|
||||
const val BLOCK_SIZE = 512
|
||||
const val BLOCK_SIZE_IN_BYTES = 64
|
||||
const val UINT_MASK = 0xFFFFFFFFU
|
||||
@ -66,15 +63,8 @@ class Sha256 : UpdatableHash {
|
||||
0x748f82eeU, 0x78a5636fU, 0x84c87814U, 0x8cc70208U, 0x90befffaU, 0xa4506cebU, 0xbef9a3f7U, 0xc67178f2U
|
||||
)
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
override fun digest(inputString: String, key: String?, hashLength: Int): Array<UByte> {
|
||||
return digest(
|
||||
inputString.encodeToByteArray().map { it.toUByte() }.toTypedArray(),
|
||||
key?.run { encodeToByteArray().map { it.toUByte() }.toTypedArray() } ?: emptyArray<UByte>(),
|
||||
hashLength)
|
||||
}
|
||||
|
||||
override fun digest(inputMessage: Array<UByte>, key: Array<UByte>, hashLength: Int): Array<UByte> {
|
||||
override fun digest(inputMessage: UByteArray): UByteArray {
|
||||
|
||||
var h = iv.copyOf()
|
||||
|
||||
@ -88,7 +78,7 @@ class Sha256 : UpdatableHash {
|
||||
.chunked(BLOCK_SIZE_IN_BYTES)
|
||||
|
||||
chunks.forEach { chunk ->
|
||||
val w = expandChunk(chunk)
|
||||
val w = expandChunk(chunk.toUByteArray())
|
||||
mix(h, w).copyInto(h)
|
||||
|
||||
}
|
||||
@ -128,7 +118,7 @@ class Sha256 : UpdatableHash {
|
||||
return (((x and y) xor (x and z) xor (y and z)))
|
||||
}
|
||||
|
||||
private fun expandChunk(chunk: Array<UByte>): Array<UInt> {
|
||||
private fun expandChunk(chunk: UByteArray): Array<UInt> {
|
||||
val w = Array<UInt>(BLOCK_SIZE_IN_BYTES) {
|
||||
when (it) {
|
||||
in 0 until 16 -> {
|
||||
@ -188,7 +178,7 @@ class Sha256 : UpdatableHash {
|
||||
}
|
||||
|
||||
|
||||
fun createExpansionArray(originalSizeInBytes: Int): Array<UByte> {
|
||||
fun createExpansionArray(originalSizeInBytes: Int): UByteArray {
|
||||
val originalMessageSizeInBits = originalSizeInBytes * 8
|
||||
|
||||
|
||||
@ -198,7 +188,7 @@ class Sha256 : UpdatableHash {
|
||||
0 -> 0
|
||||
else -> (BLOCK_SIZE - expandedRemainderOf512) / 8
|
||||
}
|
||||
val expansionArray = Array<UByte>(zeroAddAmount + 1) {
|
||||
val expansionArray = UByteArray(zeroAddAmount + 1) {
|
||||
when (it) {
|
||||
0 -> 0b10000000U
|
||||
else -> 0U
|
||||
@ -207,9 +197,9 @@ class Sha256 : UpdatableHash {
|
||||
return expansionArray
|
||||
}
|
||||
|
||||
private fun ULong.toPaddedByteArray(): Array<UByte> {
|
||||
private fun ULong.toPaddedByteArray(): UByteArray {
|
||||
val byteMask = BYTE_MASK_FROM_ULONG
|
||||
return Array(8) {
|
||||
return UByteArray(8) {
|
||||
when (it) {
|
||||
7 -> (this and byteMask).toUByte()
|
||||
6 -> ((this shr 8) and byteMask).toUByte()
|
||||
@ -224,9 +214,9 @@ class Sha256 : UpdatableHash {
|
||||
}
|
||||
}
|
||||
|
||||
private fun UInt.toPaddedByteArray(): Array<UByte> {
|
||||
private fun UInt.toPaddedByteArray(): UByteArray {
|
||||
val byteMask = BYTE_MASK_FROM_UINT
|
||||
return Array(4) {
|
||||
return UByteArray(4) {
|
||||
when (it) {
|
||||
3 -> (this and byteMask).toUByte()
|
||||
2 -> ((this shr 8) and byteMask).toUByte()
|
||||
@ -242,14 +232,14 @@ class Sha256 : UpdatableHash {
|
||||
var h = iv.copyOf()
|
||||
var counter = 0
|
||||
var bufferCounter = 0
|
||||
var buffer = Array<UByte>(BLOCK_SIZE_IN_BYTES) { 0U }
|
||||
var buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { 0U }
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
override fun update(data: String) {
|
||||
return update(data.encodeToByteArray().map { it.toUByte() }.toTypedArray())
|
||||
|
||||
fun update(data: String) {
|
||||
return update(data.encodeToByteArray().toUByteArray())
|
||||
}
|
||||
|
||||
override fun update(data: Array<UByte>) {
|
||||
override fun update(data: UByteArray) {
|
||||
if (data.isEmpty()) {
|
||||
throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
|
||||
}
|
||||
@ -260,9 +250,9 @@ class Sha256 : UpdatableHash {
|
||||
val chunked = data.chunked(BLOCK_SIZE_IN_BYTES)
|
||||
chunked.forEach { chunk ->
|
||||
if (bufferCounter + chunk.size < BLOCK_SIZE_IN_BYTES) {
|
||||
appendToBuffer(chunk, bufferCounter)
|
||||
appendToBuffer(chunk.toUByteArray(), bufferCounter)
|
||||
} else {
|
||||
chunk.copyInto(
|
||||
chunk.toUByteArray().copyInto(
|
||||
destination = buffer,
|
||||
destinationOffset = bufferCounter,
|
||||
startIndex = 0,
|
||||
@ -270,7 +260,7 @@ class Sha256 : UpdatableHash {
|
||||
)
|
||||
counter += BLOCK_SIZE_IN_BYTES
|
||||
consumeBlock(buffer)
|
||||
buffer = Array<UByte>(BLOCK_SIZE_IN_BYTES) {
|
||||
buffer = UByteArray(BLOCK_SIZE_IN_BYTES) {
|
||||
when (it) {
|
||||
in (0 until (chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter))) -> {
|
||||
chunk[it + (BLOCK_SIZE_IN_BYTES - bufferCounter)]
|
||||
@ -289,18 +279,18 @@ class Sha256 : UpdatableHash {
|
||||
}
|
||||
}
|
||||
|
||||
private fun consumeBlock(block: Array<UByte>) {
|
||||
private fun consumeBlock(block: UByteArray) {
|
||||
val w = expandChunk(block)
|
||||
mix(h, w).copyInto(h)
|
||||
}
|
||||
|
||||
override fun digest(): Array<UByte> {
|
||||
override fun digest(): UByteArray {
|
||||
val length = counter + bufferCounter
|
||||
val expansionArray = createExpansionArray(length)
|
||||
val finalBlock =
|
||||
buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPaddedByteArray()
|
||||
finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach {
|
||||
consumeBlock(it)
|
||||
consumeBlock(it.toUByteArray())
|
||||
}
|
||||
|
||||
|
||||
@ -315,11 +305,7 @@ class Sha256 : UpdatableHash {
|
||||
return digest
|
||||
}
|
||||
|
||||
override fun digestString(): String {
|
||||
return digest().map { it.toString(16) }.joinToString(separator = "")
|
||||
}
|
||||
|
||||
private fun appendToBuffer(array: Array<UByte>, start: Int) {
|
||||
private fun appendToBuffer(array: UByteArray, start: Int) {
|
||||
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||
bufferCounter += array.size
|
||||
}
|
@ -16,9 +16,6 @@
|
||||
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.util.chunked
|
||||
import com.ionspin.kotlin.crypto.hash.StatelessHash
|
||||
import com.ionspin.kotlin.crypto.hash.UpdatableHash
|
||||
import com.ionspin.kotlin.crypto.util.rotateRight
|
||||
|
||||
/**
|
||||
@ -27,12 +24,12 @@ import com.ionspin.kotlin.crypto.util.rotateRight
|
||||
* on 18-Jul-2019
|
||||
*/
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
class Sha512 : UpdatableHash {
|
||||
|
||||
class Sha512Pure : Sha512 {
|
||||
|
||||
override val MAX_HASH_BYTES: Int = 32
|
||||
|
||||
companion object : StatelessHash {
|
||||
companion object : StatelessSha512 {
|
||||
const val BLOCK_SIZE = 1024
|
||||
const val BLOCK_SIZE_IN_BYTES = 128
|
||||
const val CHUNK_SIZE = 80
|
||||
@ -134,16 +131,8 @@ class Sha512 : UpdatableHash {
|
||||
0x5be0cd19137e2179UL
|
||||
)
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
override fun digest(inputString: String, key: String?, hashLength: Int): Array<UByte> {
|
||||
return digest(
|
||||
inputString.encodeToByteArray().map { it.toUByte() }.toTypedArray(),
|
||||
key?.run { encodeToByteArray().map { it.toUByte() }.toTypedArray() } ?: emptyArray<UByte>(),
|
||||
hashLength = hashLength
|
||||
)
|
||||
}
|
||||
|
||||
override fun digest(inputMessage: Array<UByte>, key: Array<UByte>, hashLength: Int): Array<UByte> {
|
||||
override fun digest(inputMessage: UByteArray): UByteArray {
|
||||
|
||||
var h = iv.copyOf()
|
||||
|
||||
@ -155,7 +144,7 @@ class Sha512 : UpdatableHash {
|
||||
)
|
||||
|
||||
chunks.forEach { chunk ->
|
||||
val w = expandChunk(chunk)
|
||||
val w = expandChunk(chunk.toUByteArray())
|
||||
mix(h, w)
|
||||
|
||||
}
|
||||
@ -196,7 +185,7 @@ class Sha512 : UpdatableHash {
|
||||
return ((x and y) xor (x and z) xor (y and z))
|
||||
}
|
||||
|
||||
private fun expandChunk(chunk: Array<UByte>): Array<ULong> {
|
||||
private fun expandChunk(chunk: UByteArray): Array<ULong> {
|
||||
val w = Array<ULong>(CHUNK_SIZE) {
|
||||
when (it) {
|
||||
in 0 until 16 -> {
|
||||
@ -259,7 +248,7 @@ class Sha512 : UpdatableHash {
|
||||
return h
|
||||
}
|
||||
|
||||
fun createExpansionArray(originalSizeInBytes: Int): Array<UByte> {
|
||||
fun createExpansionArray(originalSizeInBytes: Int): UByteArray {
|
||||
val originalMessageSizeInBits = originalSizeInBytes * 8
|
||||
|
||||
val expandedRemainderOf1024 = (originalMessageSizeInBits + 129) % BLOCK_SIZE
|
||||
@ -267,7 +256,7 @@ class Sha512 : UpdatableHash {
|
||||
0 -> 0
|
||||
else -> (BLOCK_SIZE - expandedRemainderOf1024) / 8
|
||||
}
|
||||
val expansionArray = Array<UByte>(zeroAddAmount + 1) {
|
||||
val expansionArray = UByteArray(zeroAddAmount + 1) {
|
||||
when (it) {
|
||||
0 -> 0b10000000U
|
||||
else -> 0U
|
||||
@ -277,10 +266,10 @@ class Sha512 : UpdatableHash {
|
||||
}
|
||||
|
||||
|
||||
private fun ULong.toPaddedByteArray(): Array<UByte> {
|
||||
private fun ULong.toPaddedByteArray(): UByteArray {
|
||||
val byteMask = 0xFFUL
|
||||
//Ignore messages longer than 64 bits for now
|
||||
return Array(8) {
|
||||
return UByteArray(8) {
|
||||
when (it) {
|
||||
7 -> (this and byteMask).toUByte()
|
||||
6 -> ((this shr 8) and byteMask).toUByte()
|
||||
@ -295,10 +284,10 @@ class Sha512 : UpdatableHash {
|
||||
}
|
||||
}
|
||||
|
||||
private fun ULong.toPadded128BitByteArray(): Array<UByte> {
|
||||
private fun ULong.toPadded128BitByteArray(): UByteArray {
|
||||
val byteMask = 0xFFUL
|
||||
//Ignore messages longer than 64 bits for now
|
||||
return Array(16) {
|
||||
return UByteArray(16) {
|
||||
when (it) {
|
||||
15 -> (this and byteMask).toUByte()
|
||||
14 -> ((this shr 8) and byteMask).toUByte()
|
||||
@ -317,14 +306,14 @@ class Sha512 : UpdatableHash {
|
||||
var h = iv.copyOf()
|
||||
var counter = 0
|
||||
var bufferCounter = 0
|
||||
var buffer = Array<UByte>(BLOCK_SIZE_IN_BYTES) { 0U }
|
||||
var buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { 0U }
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
override fun update(data: String) {
|
||||
return update(data.encodeToByteArray().map { it.toUByte() }.toTypedArray())
|
||||
|
||||
fun update(data: String) {
|
||||
return update(data.encodeToByteArray().toUByteArray())
|
||||
}
|
||||
|
||||
override fun update(data: Array<UByte>) {
|
||||
override fun update(data: UByteArray) {
|
||||
if (data.isEmpty()) {
|
||||
throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
|
||||
}
|
||||
@ -335,9 +324,9 @@ class Sha512 : UpdatableHash {
|
||||
val chunked = data.chunked(BLOCK_SIZE_IN_BYTES)
|
||||
chunked.forEach { chunk ->
|
||||
if (bufferCounter + chunk.size < BLOCK_SIZE_IN_BYTES) {
|
||||
appendToBuffer(chunk, bufferCounter)
|
||||
appendToBuffer(chunk.toUByteArray(), bufferCounter)
|
||||
} else {
|
||||
chunk.copyInto(
|
||||
chunk.toUByteArray().copyInto(
|
||||
destination = buffer,
|
||||
destinationOffset = bufferCounter,
|
||||
startIndex = 0,
|
||||
@ -345,7 +334,7 @@ class Sha512 : UpdatableHash {
|
||||
)
|
||||
counter += BLOCK_SIZE_IN_BYTES
|
||||
consumeBlock(buffer)
|
||||
buffer = Array<UByte>(BLOCK_SIZE_IN_BYTES) {
|
||||
buffer = UByteArray(BLOCK_SIZE_IN_BYTES) {
|
||||
when (it) {
|
||||
in (0 until (chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter))) -> {
|
||||
chunk[it + (BLOCK_SIZE_IN_BYTES - bufferCounter)]
|
||||
@ -364,18 +353,18 @@ class Sha512 : UpdatableHash {
|
||||
}
|
||||
}
|
||||
|
||||
private fun consumeBlock(block: Array<UByte>) {
|
||||
private fun consumeBlock(block: UByteArray) {
|
||||
val w = expandChunk(block)
|
||||
mix(h, w).copyInto(h)
|
||||
}
|
||||
|
||||
override fun digest(): Array<UByte> {
|
||||
override fun digest(): UByteArray {
|
||||
val length = counter + bufferCounter
|
||||
val expansionArray = createExpansionArray(length)
|
||||
val finalBlock =
|
||||
buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPadded128BitByteArray()
|
||||
finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach {
|
||||
consumeBlock(it)
|
||||
consumeBlock(it.toUByteArray())
|
||||
}
|
||||
|
||||
|
||||
@ -390,11 +379,7 @@ class Sha512 : UpdatableHash {
|
||||
return digest
|
||||
}
|
||||
|
||||
override fun digestString(): String {
|
||||
return digest().map { it.toString(16) }.joinToString(separator = "")
|
||||
}
|
||||
|
||||
private fun appendToBuffer(array: Array<UByte>, start: Int) {
|
||||
private fun appendToBuffer(array: UByteArray, start: Int) {
|
||||
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||
bufferCounter += array.size
|
||||
}
|
@ -1,377 +1,10 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
|
||||
|
||||
package com.ionspin.kotlin.crypto.keyderivation.argon2
|
||||
|
||||
import com.ionspin.kotlin.bignum.integer.toBigInteger
|
||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2b
|
||||
import com.ionspin.kotlin.crypto.keyderivation.KeyDerivationFunction
|
||||
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.argonBlake2bArbitraryLenghtHash
|
||||
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.compressionFunctionG
|
||||
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.validateArgonParameters
|
||||
import com.ionspin.kotlin.crypto.util.fromLittleEndianArrayToUInt
|
||||
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
||||
import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 16-May-2020
|
||||
* on 24-May-2020
|
||||
*/
|
||||
|
||||
enum class ArgonType(val typeId: Int) {
|
||||
Argon2d(0), Argon2i(1), Argon2id(2)
|
||||
}
|
||||
|
||||
data class SegmentPosition(
|
||||
val iteration: Int,
|
||||
val lane: Int,
|
||||
val slice: Int
|
||||
)
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
class Argon2(
|
||||
private val password: Array<UByte>,
|
||||
private val salt: Array<UByte> = emptyArray(),
|
||||
private val parallelism: Int = 1,
|
||||
private val tagLength: UInt = 64U,
|
||||
requestedMemorySize: UInt = 0U,
|
||||
private val numberOfIterations: UInt = 1U,
|
||||
private val key: Array<UByte> = emptyArray(),
|
||||
private val associatedData: Array<UByte> = emptyArray(),
|
||||
private val argonType: ArgonType = ArgonType.Argon2id
|
||||
) : KeyDerivationFunction {
|
||||
|
||||
constructor(
|
||||
password: String,
|
||||
salt: String = "",
|
||||
parallelism: Int = 1,
|
||||
tagLength: UInt = 64U,
|
||||
requestedMemorySize: UInt = 0U,
|
||||
numberOfIterations: UInt = 10U,
|
||||
key: String = "",
|
||||
associatedData: String = "",
|
||||
argonType: ArgonType = ArgonType.Argon2id
|
||||
) : this(
|
||||
password.encodeToByteArray().map { it.toUByte() }.toList().toTypedArray(),
|
||||
salt.encodeToByteArray().map { it.toUByte() }.toList().toTypedArray(),
|
||||
parallelism,
|
||||
tagLength,
|
||||
requestedMemorySize,
|
||||
numberOfIterations,
|
||||
key.encodeToByteArray().map { it.toUByte() }.toList().toTypedArray(),
|
||||
associatedData.encodeToByteArray().map { it.toUByte() }.toList().toTypedArray(),
|
||||
argonType
|
||||
)
|
||||
|
||||
init {
|
||||
validateArgonParameters(
|
||||
password,
|
||||
salt,
|
||||
parallelism,
|
||||
tagLength,
|
||||
requestedMemorySize,
|
||||
numberOfIterations,
|
||||
key,
|
||||
associatedData,
|
||||
argonType
|
||||
)
|
||||
}
|
||||
|
||||
//We support only the latest version
|
||||
private val versionNumber: UInt = 0x13U
|
||||
|
||||
//Use either requested memory size, or default, or throw exception if the requested amount is less than 8*parallelism
|
||||
private val memorySize = if (requestedMemorySize == 0U) {
|
||||
((8 * parallelism) * 2).toUInt()
|
||||
} else {
|
||||
requestedMemorySize
|
||||
}
|
||||
private val blockCount = (memorySize / (4U * parallelism.toUInt())) * (4U * parallelism.toUInt())
|
||||
private val columnCount = (blockCount / parallelism.toUInt()).toInt()
|
||||
private val segmentLength = columnCount / 4
|
||||
|
||||
private val useIndependentAddressing = argonType == ArgonType.Argon2id || argonType == ArgonType.Argon2i
|
||||
|
||||
|
||||
// State
|
||||
private val matrix = Array(parallelism) {
|
||||
Array(columnCount) {
|
||||
Array<UByte>(1024) { 0U }
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearMatrix() {
|
||||
matrix.forEachIndexed { laneIndex, lane ->
|
||||
lane.forEachIndexed { columnIndex, block ->
|
||||
block.forEachIndexed { byteIndex, byte ->
|
||||
matrix[laneIndex][columnIndex][byteIndex] = 0U
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun populateAddressBlock(
|
||||
iteration: Int,
|
||||
slice: Int,
|
||||
lane: Int,
|
||||
addressBlock: Array<UByte>,
|
||||
addressCounter: ULong
|
||||
): Array<UByte> {
|
||||
//Calculate first pass
|
||||
val firstPass = compressionFunctionG(
|
||||
Array<UByte>(1024) { 0U },
|
||||
iteration.toULong().toLittleEndianUByteArray() +
|
||||
lane.toULong().toLittleEndianUByteArray() +
|
||||
slice.toULong().toLittleEndianUByteArray() +
|
||||
blockCount.toULong().toLittleEndianUByteArray() +
|
||||
numberOfIterations.toULong().toLittleEndianUByteArray() +
|
||||
argonType.typeId.toULong().toLittleEndianUByteArray() +
|
||||
addressCounter.toLittleEndianUByteArray() +
|
||||
Array<UByte>(968) { 0U },
|
||||
addressBlock,
|
||||
false
|
||||
)
|
||||
val secondPass = compressionFunctionG(
|
||||
Array<UByte>(1024) { 0U },
|
||||
firstPass,
|
||||
firstPass,
|
||||
false
|
||||
)
|
||||
return secondPass
|
||||
}
|
||||
|
||||
|
||||
private fun computeReferenceBlockIndexes(
|
||||
iteration: Int,
|
||||
slice: Int,
|
||||
lane: Int,
|
||||
column: Int,
|
||||
addressBlock: Array<UByte>?
|
||||
): Pair<Int, Int> {
|
||||
val segmentIndex = (column % segmentLength)
|
||||
val independentIndex = segmentIndex % 128 // 128 is the number of addresses in address block
|
||||
val (j1, j2) = when (argonType) {
|
||||
ArgonType.Argon2d -> {
|
||||
val previousBlock = if (column == 0) {
|
||||
matrix[lane][columnCount - 1] //Get last block in the SAME lane
|
||||
} else {
|
||||
matrix[lane][column - 1]
|
||||
}
|
||||
val first32Bit = previousBlock.sliceArray(0 until 4).fromLittleEndianArrayToUInt()
|
||||
val second32Bit = previousBlock.sliceArray(4 until 8).fromLittleEndianArrayToUInt()
|
||||
Pair(first32Bit, second32Bit)
|
||||
}
|
||||
ArgonType.Argon2i -> {
|
||||
val selectedAddressBlock = addressBlock!!.sliceArray((independentIndex * 8) until (independentIndex * 8) + 8)
|
||||
val first32Bit = selectedAddressBlock.sliceArray(0 until 4).fromLittleEndianArrayToUInt()
|
||||
val second32Bit = selectedAddressBlock.sliceArray(4 until 8).fromLittleEndianArrayToUInt()
|
||||
Pair(first32Bit, second32Bit)
|
||||
}
|
||||
ArgonType.Argon2id -> {
|
||||
if (iteration == 0 && (slice == 0 || slice == 1)) {
|
||||
val selectedAddressBlock =
|
||||
addressBlock!!.sliceArray((independentIndex * 8) until (independentIndex * 8) + 8)
|
||||
val first32Bit = selectedAddressBlock.sliceArray(0 until 4).fromLittleEndianArrayToUInt()
|
||||
val second32Bit = selectedAddressBlock.sliceArray(4 until 8).fromLittleEndianArrayToUInt()
|
||||
Pair(first32Bit, second32Bit)
|
||||
} else {
|
||||
val previousBlock = if (column == 0) {
|
||||
matrix[lane][columnCount - 1] //Get last block in the SAME lane
|
||||
} else {
|
||||
matrix[lane][column - 1]
|
||||
}
|
||||
val first32Bit = previousBlock.sliceArray(0 until 4).fromLittleEndianArrayToUInt()
|
||||
val second32Bit = previousBlock.sliceArray(4 until 8).fromLittleEndianArrayToUInt()
|
||||
Pair(first32Bit, second32Bit)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//If this is first iteration and first slice, block is taken from the current lane
|
||||
val l = if (iteration == 0 && slice == 0) {
|
||||
lane
|
||||
} else {
|
||||
(j2.toBigInteger() % parallelism).intValue()
|
||||
|
||||
}
|
||||
|
||||
|
||||
val referenceAreaSize = if (iteration == 0) {
|
||||
if (slice == 0) {
|
||||
//All indices except the previous
|
||||
segmentIndex - 1
|
||||
} else {
|
||||
if (lane == l) {
|
||||
//Same lane
|
||||
column - 1
|
||||
} else {
|
||||
slice * (columnCount / 4) + if (segmentIndex == 0) { // Check if column is first block of the SEGMENT
|
||||
-1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (lane == l) {
|
||||
columnCount - (columnCount / 4) + (segmentIndex - 1)
|
||||
} else {
|
||||
columnCount - (columnCount / 4) + if (segmentIndex == 0) {
|
||||
-1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val x = (j1.toULong() * j1) shr 32
|
||||
val y = (referenceAreaSize.toULong() * x) shr 32
|
||||
val z = referenceAreaSize.toULong() - 1U - y
|
||||
|
||||
val startPosition = if (iteration == 0) {
|
||||
0
|
||||
} else {
|
||||
if (slice == 3) {
|
||||
0
|
||||
} else {
|
||||
(slice + 1) * segmentLength
|
||||
}
|
||||
}
|
||||
val absolutePosition = (startPosition + z.toInt()) % columnCount
|
||||
|
||||
return Pair(l, absolutePosition)
|
||||
}
|
||||
|
||||
override fun derive(): Array<UByte> {
|
||||
val h0 = Blake2b.digest(
|
||||
parallelism.toUInt()
|
||||
.toLittleEndianUByteArray() + tagLength.toLittleEndianUByteArray() + memorySize.toLittleEndianUByteArray() +
|
||||
numberOfIterations.toLittleEndianUByteArray() + versionNumber.toLittleEndianUByteArray() + argonType.typeId.toUInt()
|
||||
.toLittleEndianUByteArray() +
|
||||
password.size.toUInt().toLittleEndianUByteArray() + password +
|
||||
salt.size.toUInt().toLittleEndianUByteArray() + salt +
|
||||
key.size.toUInt().toLittleEndianUByteArray() + key +
|
||||
associatedData.size.toUInt().toLittleEndianUByteArray() + associatedData
|
||||
)
|
||||
|
||||
//Compute B[i][0]
|
||||
for (i in 0 until parallelism.toInt()) {
|
||||
matrix[i][0] =
|
||||
argonBlake2bArbitraryLenghtHash(
|
||||
h0 + 0.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray(),
|
||||
1024U
|
||||
)
|
||||
}
|
||||
|
||||
//Compute B[i][1]
|
||||
for (i in 0 until parallelism.toInt()) {
|
||||
matrix[i][1] =
|
||||
argonBlake2bArbitraryLenghtHash(
|
||||
h0 + 1.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray(),
|
||||
1024U
|
||||
)
|
||||
}
|
||||
executeArgonWithSingleThread()
|
||||
|
||||
val result = matrix.foldIndexed(emptyArray<UByte>()) { lane, acc, laneArray ->
|
||||
if (acc.size == 0) {
|
||||
acc + laneArray[columnCount - 1] // add last element in first lane to the accumulator
|
||||
} else {
|
||||
// For each element in our accumulator, xor it with an appropriate element from the last column in current lane (from 1 to `parallelism`)
|
||||
acc.mapIndexed { index, it -> it xor laneArray[columnCount - 1][index] }
|
||||
.toTypedArray()
|
||||
}
|
||||
}
|
||||
//Hash the xored last blocks
|
||||
val hash = argonBlake2bArbitraryLenghtHash(result, tagLength)
|
||||
clearMatrix()
|
||||
return hash
|
||||
|
||||
|
||||
}
|
||||
|
||||
private fun executeArgonWithSingleThread() {
|
||||
for (iteration in 0 until numberOfIterations.toInt()) {
|
||||
for (slice in 0 until 4) {
|
||||
for (lane in 0 until parallelism) {
|
||||
val segmentPosition = SegmentPosition(iteration, lane, slice)
|
||||
processSegment(segmentPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processSegment(segmentPosition: SegmentPosition) {
|
||||
val iteration = segmentPosition.iteration
|
||||
val slice = segmentPosition.slice
|
||||
val lane = segmentPosition.lane
|
||||
|
||||
var addressBlock: Array<UByte>? = null
|
||||
var addressCounter = 1UL //Starts from 1 in each segment as defined by the spec
|
||||
|
||||
//Generate initial segment address block
|
||||
if (useIndependentAddressing) {
|
||||
addressBlock = Array<UByte>(1024) {
|
||||
0U
|
||||
}
|
||||
addressBlock = populateAddressBlock(iteration, slice, lane, addressBlock, addressCounter)
|
||||
addressCounter++
|
||||
}
|
||||
val startColumn = if (iteration == 0 && slice == 0) {
|
||||
2
|
||||
} else {
|
||||
slice * segmentLength
|
||||
}
|
||||
|
||||
|
||||
for (column in startColumn until (slice + 1) * segmentLength) {
|
||||
val segmentIndex = column - (slice * segmentLength)
|
||||
//Each address block contains 128 addresses, and we use one per iteration,
|
||||
//so once we do 128 iterations we need to calculate a new address block
|
||||
if (useIndependentAddressing && segmentIndex != 0 && segmentIndex % 128 == 0) {
|
||||
addressBlock = populateAddressBlock(iteration, slice, lane, addressBlock!!, addressCounter)
|
||||
addressCounter++
|
||||
addressBlock.hexColumsPrint(16)
|
||||
}
|
||||
val previousColumn = if (column == 0) {
|
||||
columnCount - 1
|
||||
} else {
|
||||
column - 1
|
||||
}
|
||||
val (l, z) = computeReferenceBlockIndexes(
|
||||
iteration,
|
||||
slice,
|
||||
lane,
|
||||
column,
|
||||
addressBlock
|
||||
)
|
||||
matrix[lane][column] =
|
||||
compressionFunctionG(
|
||||
matrix[lane][previousColumn],
|
||||
matrix[l][z],
|
||||
matrix[lane][column],
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
interface Argon2 : KeyDerivationFunction
|
@ -23,8 +23,8 @@ package com.ionspin.kotlin.crypto.keyderivation.argon2
|
||||
*/
|
||||
class Argon2TagTooShort(tagLength: UInt) : RuntimeException("Too short tag (output) requested. Requested: $tagLength")
|
||||
class Argon2TagTooLong(tagLength: UInt) : RuntimeException("Too long tag (output) requested. Requested: $tagLength")
|
||||
class Argon2TimeTooShort(iterations: UInt) : RuntimeException("Too short time parameter (Too few iterations). Requested iterations: $iterations")
|
||||
class Argon2TimeTooLong(iterations: UInt) : RuntimeException("Too long time parameter (Too many iterations). Requested iterations: $iterations")
|
||||
class Argon2TimeTooShort(iterations: Int) : RuntimeException("Too short time parameter (Too few iterations). Requested iterations: $iterations")
|
||||
class Argon2TimeTooLong(iterations: Int) : RuntimeException("Too long time parameter (Too many iterations). Requested iterations: $iterations")
|
||||
class Argon2MemoryTooLitlle(requestedMemorySize: UInt) : RuntimeException("Requested memory size must be larger than 8 * parallelism. Requested size: $requestedMemorySize")
|
||||
class Argon2MemoryTooMuch(requestedMemorySize: UInt) : RuntimeException("Requested memory size too large. Requested size: $requestedMemorySize")
|
||||
class Argon2LanesTooFew(parallelism: Int) : RuntimeException("Too few, or invalid number of threads requested $parallelism")
|
||||
|
@ -0,0 +1,405 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
|
||||
|
||||
package com.ionspin.kotlin.crypto.keyderivation.argon2
|
||||
|
||||
import com.ionspin.kotlin.bignum.integer.toBigInteger
|
||||
import com.ionspin.kotlin.crypto.Blake2bPureStateless
|
||||
import com.ionspin.kotlin.crypto.SRNG
|
||||
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.argonBlake2bArbitraryLenghtHash
|
||||
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.compressionFunctionG
|
||||
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.validateArgonParameters
|
||||
import com.ionspin.kotlin.crypto.util.*
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 16-May-2020
|
||||
*/
|
||||
|
||||
enum class ArgonType(val typeId: Int) {
|
||||
Argon2d(0), Argon2i(1), Argon2id(2)
|
||||
}
|
||||
|
||||
data class SegmentPosition(
|
||||
val iteration: Int,
|
||||
val lane: Int,
|
||||
val slice: Int
|
||||
)
|
||||
|
||||
data class ArgonResult(
|
||||
val hashBytes: UByteArray,
|
||||
val salt: UByteArray
|
||||
) {
|
||||
val hashString by lazy { hashBytes.map { it.toString(16).padStart(2, '0') }.joinToString(separator = "") }
|
||||
val saltString by lazy { salt.map { it.toString(16).padStart(2, '0') }.joinToString(separator = "") }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
class Argon2Pure(
|
||||
private val password: UByteArray,
|
||||
private val salt: UByteArray = ubyteArrayOf(),
|
||||
private val parallelism: Int = 1,
|
||||
private val tagLength: UInt = 64U,
|
||||
requestedMemorySize: UInt = 0U,
|
||||
private val numberOfIterations: Int = 1,
|
||||
private val key: UByteArray = ubyteArrayOf(),
|
||||
private val associatedData: UByteArray = ubyteArrayOf(),
|
||||
private val argonType: ArgonType = ArgonType.Argon2id
|
||||
) : Argon2 {
|
||||
|
||||
companion object {
|
||||
fun derive(
|
||||
password: String,
|
||||
salt: String? = null,
|
||||
key: String,
|
||||
associatedData: String,
|
||||
parallelism: Int = 16,
|
||||
tagLength: Int = 64,
|
||||
memory: Int = 4096,
|
||||
numberOfIterations: Int = 10,
|
||||
): ArgonResult {
|
||||
val salt = SRNG.getRandomBytes(64)
|
||||
val argon = Argon2Pure(
|
||||
password.encodeToByteArray().toUByteArray(),
|
||||
salt,
|
||||
parallelism,
|
||||
tagLength.toUInt(),
|
||||
memory.toUInt(),
|
||||
numberOfIterations,
|
||||
key.encodeToByteArray().toUByteArray(),
|
||||
associatedData.encodeToByteArray().toUByteArray(),
|
||||
ArgonType.Argon2id
|
||||
)
|
||||
val resultArray = argon.derive()
|
||||
return ArgonResult(resultArray, salt)
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
password: String,
|
||||
salt: String = "",
|
||||
parallelism: Int = 1,
|
||||
tagLength: UInt = 64U,
|
||||
requestedMemorySize: UInt = 0U,
|
||||
numberOfIterations: Int = 10,
|
||||
key: String = "",
|
||||
associatedData: String = "",
|
||||
argonType: ArgonType = ArgonType.Argon2id
|
||||
) : this(
|
||||
password.encodeToByteArray().toUByteArray(),
|
||||
salt.encodeToByteArray().toUByteArray(),
|
||||
parallelism,
|
||||
tagLength,
|
||||
requestedMemorySize,
|
||||
numberOfIterations,
|
||||
key.encodeToByteArray().toUByteArray(),
|
||||
associatedData.encodeToByteArray().toUByteArray(),
|
||||
argonType
|
||||
)
|
||||
|
||||
//We support only the latest version
|
||||
private val versionNumber: UInt = 0x13U
|
||||
|
||||
//Use either requested memory size, or default, or throw exception if the requested amount is less than 8*parallelism
|
||||
private val memorySize = if (requestedMemorySize == 0U) {
|
||||
((8 * parallelism) * 2).toUInt()
|
||||
} else {
|
||||
requestedMemorySize
|
||||
}
|
||||
private val blockCount = (memorySize / (4U * parallelism.toUInt())) * (4U * parallelism.toUInt())
|
||||
private val columnCount = (blockCount / parallelism.toUInt()).toInt()
|
||||
private val segmentLength = columnCount / 4
|
||||
|
||||
private val useIndependentAddressing = argonType == ArgonType.Argon2id || argonType == ArgonType.Argon2i
|
||||
|
||||
// State
|
||||
private val matrix: ArgonMatrix
|
||||
|
||||
init {
|
||||
matrix = ArgonMatrix(columnCount, parallelism)
|
||||
validateArgonParameters(
|
||||
password,
|
||||
salt,
|
||||
parallelism,
|
||||
tagLength,
|
||||
requestedMemorySize,
|
||||
numberOfIterations,
|
||||
key,
|
||||
associatedData,
|
||||
argonType
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun populateAddressBlock(
|
||||
iteration: Int,
|
||||
slice: Int,
|
||||
lane: Int,
|
||||
addressBlock: ArgonBlockPointer,
|
||||
addressCounter: ULong
|
||||
): ArgonBlockPointer {
|
||||
//Calculate first pass
|
||||
val zeroesBlock = ArgonBlock()
|
||||
val firstPass = compressionFunctionG(
|
||||
zeroesBlock.getBlockPointer(),
|
||||
ArgonBlock(iteration.toULong().toLittleEndianUByteArray() +
|
||||
lane.toULong().toLittleEndianUByteArray() +
|
||||
slice.toULong().toLittleEndianUByteArray() +
|
||||
blockCount.toULong().toLittleEndianUByteArray() +
|
||||
numberOfIterations.toULong().toLittleEndianUByteArray() +
|
||||
argonType.typeId.toULong().toLittleEndianUByteArray() +
|
||||
addressCounter.toLittleEndianUByteArray() +
|
||||
UByteArray(968) { 0U }
|
||||
).getBlockPointer(),
|
||||
addressBlock,
|
||||
false
|
||||
)
|
||||
val secondPass = compressionFunctionG(
|
||||
zeroesBlock.getBlockPointer(),
|
||||
firstPass,
|
||||
firstPass,
|
||||
false
|
||||
)
|
||||
return secondPass
|
||||
}
|
||||
|
||||
|
||||
private fun computeReferenceBlockIndexes(
|
||||
iteration: Int,
|
||||
slice: Int,
|
||||
lane: Int,
|
||||
column: Int,
|
||||
addressBlockPointer: ArgonBlockPointer?
|
||||
): Pair<Int, Int> {
|
||||
|
||||
val segmentIndex = (column % segmentLength)
|
||||
val independentIndex = segmentIndex % 128 // 128 is the number of addresses in address block
|
||||
val (j1, j2) = when (argonType) {
|
||||
ArgonType.Argon2d -> {
|
||||
val previousBlockStart = if (column == 0) {
|
||||
matrix.getBlockPointer(lane, columnCount - 1) //Get last block in the SAME lane
|
||||
} else {
|
||||
matrix.getBlockPointer(lane, column - 1)
|
||||
}
|
||||
val first32Bit = matrix.sliceArray(previousBlockStart.asInt() until previousBlockStart.asInt() + 4).fromLittleEndianArrayToUInt()
|
||||
val second32Bit = matrix.sliceArray(previousBlockStart.asInt() + 4 until previousBlockStart.asInt() + 8).fromLittleEndianArrayToUInt()
|
||||
|
||||
Pair(first32Bit, second32Bit)
|
||||
}
|
||||
ArgonType.Argon2i -> {
|
||||
val first32Bit = addressBlockPointer!!.getUIntFromPosition(independentIndex * 8)
|
||||
val second32Bit = addressBlockPointer!!.getUIntFromPosition(independentIndex * 8 + 4)
|
||||
Pair(first32Bit, second32Bit)
|
||||
}
|
||||
ArgonType.Argon2id -> {
|
||||
if (iteration == 0 && (slice == 0 || slice == 1)) {
|
||||
val first32Bit = addressBlockPointer!!.getUIntFromPosition(independentIndex * 8)
|
||||
val second32Bit = addressBlockPointer!!.getUIntFromPosition(independentIndex * 8 + 4)
|
||||
Pair(first32Bit, second32Bit)
|
||||
} else {
|
||||
val previousBlockStart = if (column == 0) {
|
||||
matrix.getBlockPointer(lane, columnCount - 1) //Get last block in the SAME lane
|
||||
} else {
|
||||
matrix.getBlockPointer(lane, column - 1)
|
||||
}
|
||||
val first32Bit = matrix.sliceArray(previousBlockStart.asInt() until previousBlockStart.asInt() + 4).fromLittleEndianArrayToUInt()
|
||||
val second32Bit = matrix.sliceArray(previousBlockStart.asInt() + 4 until previousBlockStart.asInt() + 8).fromLittleEndianArrayToUInt()
|
||||
Pair(first32Bit, second32Bit)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//If this is first iteration and first slice, block is taken from the current lane
|
||||
val l = if (iteration == 0 && slice == 0) {
|
||||
lane
|
||||
} else {
|
||||
(j2.toBigInteger() % parallelism).intValue()
|
||||
|
||||
}
|
||||
|
||||
|
||||
val referenceAreaSize = if (iteration == 0) {
|
||||
if (slice == 0) {
|
||||
//All indices except the previous
|
||||
segmentIndex - 1
|
||||
} else {
|
||||
if (lane == l) {
|
||||
//Same lane
|
||||
column - 1
|
||||
} else {
|
||||
slice * (columnCount / 4) + if (segmentIndex == 0) { // Check if column is first block of the SEGMENT
|
||||
-1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (lane == l) {
|
||||
columnCount - (columnCount / 4) + (segmentIndex - 1)
|
||||
} else {
|
||||
columnCount - (columnCount / 4) + if (segmentIndex == 0) {
|
||||
-1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val x = (j1.toULong() * j1) shr 32
|
||||
val y = (referenceAreaSize.toULong() * x) shr 32
|
||||
val z = referenceAreaSize.toULong() - 1U - y
|
||||
|
||||
val startPosition = if (iteration == 0) {
|
||||
0
|
||||
} else {
|
||||
if (slice == 3) {
|
||||
0
|
||||
} else {
|
||||
(slice + 1) * segmentLength
|
||||
}
|
||||
}
|
||||
val absolutePosition = (startPosition + z.toInt()) % columnCount
|
||||
|
||||
return Pair(l, absolutePosition)
|
||||
}
|
||||
|
||||
override fun derive(): UByteArray {
|
||||
val blakeInput = parallelism.toUInt().toLittleEndianUByteArray() +
|
||||
tagLength.toLittleEndianUByteArray() +
|
||||
memorySize.toLittleEndianUByteArray() +
|
||||
numberOfIterations.toUInt().toLittleEndianUByteArray() +
|
||||
versionNumber.toLittleEndianUByteArray() +
|
||||
argonType.typeId.toUInt().toLittleEndianUByteArray() +
|
||||
password.size.toUInt().toLittleEndianUByteArray() + password +
|
||||
salt.size.toUInt().toLittleEndianUByteArray() + salt +
|
||||
key.size.toUInt().toLittleEndianUByteArray() + key +
|
||||
associatedData.size.toUInt().toLittleEndianUByteArray() + associatedData
|
||||
val h0 = Blake2bPureStateless.digest(
|
||||
blakeInput
|
||||
)
|
||||
|
||||
//Compute B[i][0]
|
||||
for (i in 0 until parallelism) {
|
||||
matrix.setBlockAt(i, 0,
|
||||
argonBlake2bArbitraryLenghtHash(
|
||||
(h0 + 0.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray()).toUByteArray(),
|
||||
1024U
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
//Compute B[i][1]
|
||||
for (i in 0 until parallelism) {
|
||||
matrix.setBlockAt(i, 1,
|
||||
argonBlake2bArbitraryLenghtHash(
|
||||
(h0 + 1.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray()).toUByteArray(),
|
||||
1024U
|
||||
)
|
||||
)
|
||||
}
|
||||
//Run all iterations over all lanes and all segments
|
||||
executeArgonWithSingleThread()
|
||||
|
||||
|
||||
val acc = ArgonBlock(matrix.getBlockAt(0, columnCount - 1))
|
||||
val accPointer = acc.getBlockPointer()
|
||||
for (i in 1 until parallelism) {
|
||||
accPointer.xorInplaceWith(matrix.getBlockPointer(i, columnCount - 1))
|
||||
}
|
||||
//Hash the xored last blocks
|
||||
val hash = argonBlake2bArbitraryLenghtHash(acc.storage, tagLength)
|
||||
matrix.clearMatrix()
|
||||
return hash
|
||||
|
||||
|
||||
}
|
||||
|
||||
private fun executeArgonWithSingleThread() {
|
||||
for (iteration in 0 until numberOfIterations) {
|
||||
for (slice in 0 until 4) {
|
||||
for (lane in 0 until parallelism) {
|
||||
val segmentPosition = SegmentPosition(iteration, lane, slice)
|
||||
processSegment(segmentPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processSegment(segmentPosition: SegmentPosition) {
|
||||
val iteration = segmentPosition.iteration
|
||||
val slice = segmentPosition.slice
|
||||
val lane = segmentPosition.lane
|
||||
|
||||
var addressBlock: ArgonBlockPointer? = null
|
||||
var addressCounter = 1UL //Starts from 1 in each segment as defined by the spec
|
||||
|
||||
//Generate initial segment address block
|
||||
if (useIndependentAddressing) {
|
||||
addressBlock = ArgonBlock().getBlockPointer()
|
||||
addressBlock = populateAddressBlock(iteration, slice, lane, addressBlock, addressCounter)
|
||||
addressCounter++
|
||||
}
|
||||
val startColumn = if (iteration == 0 && slice == 0) {
|
||||
2
|
||||
} else {
|
||||
slice * segmentLength
|
||||
}
|
||||
|
||||
|
||||
for (column in startColumn until (slice + 1) * segmentLength) {
|
||||
val segmentIndex = column - (slice * segmentLength)
|
||||
//Each address block contains 128 addresses, and we use one per iteration,
|
||||
//so once we do 128 iterations we need to calculate a new address block
|
||||
if (useIndependentAddressing && segmentIndex != 0 && segmentIndex % 128 == 0) {
|
||||
addressBlock = populateAddressBlock(iteration, slice, lane, addressBlock!!, addressCounter)
|
||||
addressCounter++
|
||||
}
|
||||
val previousColumn = if (column == 0) {
|
||||
columnCount - 1
|
||||
} else {
|
||||
column - 1
|
||||
}
|
||||
val (l, z) = computeReferenceBlockIndexes(
|
||||
iteration,
|
||||
slice,
|
||||
lane,
|
||||
column,
|
||||
addressBlock
|
||||
)
|
||||
|
||||
matrix.setBlockAt(lane, column,
|
||||
compressionFunctionG(
|
||||
matrix.getBlockPointer(lane, previousColumn),
|
||||
matrix.getBlockPointer(l,z),
|
||||
matrix.getBlockPointer(lane,column),
|
||||
true
|
||||
).getAsUByteArray()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -18,8 +18,10 @@
|
||||
|
||||
package com.ionspin.kotlin.crypto.keyderivation.argon2
|
||||
|
||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2b
|
||||
import com.ionspin.kotlin.crypto.util.*
|
||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bPure
|
||||
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.BLOCK_SIZE
|
||||
import com.ionspin.kotlin.crypto.util.plus
|
||||
import com.ionspin.kotlin.crypto.util.rotateRight
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
@ -27,28 +29,28 @@ import com.ionspin.kotlin.crypto.util.*
|
||||
* on 16-May-2020
|
||||
*/
|
||||
object Argon2Utils {
|
||||
const val BLOCK_SIZE = 1024
|
||||
|
||||
const val R1 = 32
|
||||
const val R2 = 24
|
||||
const val R3 = 16
|
||||
const val R4 = 63
|
||||
|
||||
//based on Blake2b mixRound
|
||||
private fun mixRound(input: Array<UByte>): Array<ULong> {
|
||||
var v = input.chunked(8).map { it.fromLittleEndianArrayToULong() }.toTypedArray()
|
||||
v = mix(v, 0, 4, 8, 12)
|
||||
v = mix(v, 1, 5, 9, 13)
|
||||
v = mix(v, 2, 6, 10, 14)
|
||||
v = mix(v, 3, 7, 11, 15)
|
||||
v = mix(v, 0, 5, 10, 15)
|
||||
v = mix(v, 1, 6, 11, 12)
|
||||
v = mix(v, 2, 7, 8, 13)
|
||||
v = mix(v, 3, 4, 9, 14)
|
||||
return v
|
||||
//Based on Blake2b mix
|
||||
internal fun inplaceMixRound(v : ULongArray) : ULongArray{
|
||||
mix(v, 0, 4, 8, 12)
|
||||
mix(v, 1, 5, 9, 13)
|
||||
mix(v, 2, 6, 10, 14)
|
||||
mix(v, 3, 7, 11, 15)
|
||||
mix(v, 0, 5, 10, 15)
|
||||
mix(v, 1, 6, 11, 12)
|
||||
mix(v, 2, 7, 8, 13)
|
||||
mix(v, 3, 4, 9, 14)
|
||||
return v //Just for chaining, array is mixed in place
|
||||
}
|
||||
|
||||
//Based on Blake2b mix
|
||||
private fun mix(v: Array<ULong>, a: Int, b: Int, c: Int, d: Int): Array<ULong> {
|
||||
private fun mix(v: ULongArray, a: Int, b: Int, c: Int, d: Int) {
|
||||
v[a] = (v[a] + v[b] + 2U * (v[a] and 0xFFFFFFFFUL) * (v[b] and 0xFFFFFFFFUL))
|
||||
v[d] = (v[d] xor v[a]) rotateRight R1
|
||||
v[c] = (v[c] + v[d] + 2U * (v[c] and 0xFFFFFFFFUL) * (v[d] and 0xFFFFFFFFUL))
|
||||
@ -57,11 +59,10 @@ object Argon2Utils {
|
||||
v[d] = (v[d] xor v[a]) rotateRight R3
|
||||
v[c] = (v[c] + v[d] + 2U * (v[c] and 0xFFFFFFFFUL) * (v[d] and 0xFFFFFFFFUL))
|
||||
v[b] = (v[b] xor v[c]) rotateRight R4
|
||||
return v
|
||||
}
|
||||
|
||||
private fun extractColumnFromGBlock(gBlock: Array<UByte>, columnPosition: Int): Array<UByte> {
|
||||
val result = Array<UByte>(128) { 0U }
|
||||
internal fun extractColumnFromGBlock(gBlock: UByteArray, columnPosition: Int): UByteArray {
|
||||
val result = UByteArray(128) { 0U }
|
||||
for (i in 0..7) {
|
||||
gBlock.copyOfRange(i * 128 + (columnPosition * 16), i * 128 + (columnPosition * 16) + 16)
|
||||
.copyInto(result, i * 16)
|
||||
@ -69,69 +70,50 @@ object Argon2Utils {
|
||||
return result
|
||||
}
|
||||
|
||||
private fun copyIntoGBlockColumn(gBlock: Array<UByte>, columnPosition: Int, columnData: Array<UByte>) {
|
||||
for (i in 0..7) {
|
||||
val column = columnData.copyOfRange(i * 16, i * 16 + 16)
|
||||
column.copyInto(gBlock, i * 128 + columnPosition * 16)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun compressionFunctionG(
|
||||
previousBlock: Array<UByte>,
|
||||
referenceBlock: Array<UByte>,
|
||||
currentBlock: Array<UByte>,
|
||||
previousBlock: ArgonBlockPointer,
|
||||
referenceBlock: ArgonBlockPointer,
|
||||
currentBlock: ArgonBlockPointer,
|
||||
xorWithCurrentBlock: Boolean
|
||||
): Array<UByte> {
|
||||
val r = referenceBlock xor previousBlock
|
||||
val q = Array<UByte>(1024) { 0U }
|
||||
val z = Array<UByte>(1024) { 0U }
|
||||
): ArgonBlockPointer {
|
||||
val r = (referenceBlock xorBlocksAndGetPointerToNewBlock previousBlock).getBlockPointer()
|
||||
//Since we are doing inplace xors, we don't need the Q that exists in specification
|
||||
val z = ArgonBlock().getBlockPointer()
|
||||
// Do the argon/blake2b mixing on rows
|
||||
for (i in 0..7) {
|
||||
val startOfRow = (i * 8 * 16)
|
||||
val endOfRow = startOfRow + (8 * 16)
|
||||
val rowToMix = r.copyOfRange(startOfRow, endOfRow)
|
||||
mixRound(rowToMix)
|
||||
.map { it.toLittleEndianUByteArray() }
|
||||
.flatMap { it.asIterable() }
|
||||
.toTypedArray()
|
||||
.copyInto(q, startOfRow)
|
||||
z.setRowFromMixedULongs(i, inplaceMixRound(r.getRowOfULongsForMixing(i)))
|
||||
}
|
||||
// Do the argon/blake2b mixing on columns
|
||||
for (i in 0..7) {
|
||||
copyIntoGBlockColumn(
|
||||
z,
|
||||
i,
|
||||
mixRound(extractColumnFromGBlock(q, i))
|
||||
.map { it.toLittleEndianUByteArray() }
|
||||
.flatMap { it.asIterable() }
|
||||
.toTypedArray()
|
||||
)
|
||||
z.setColumnFromMixedULongs(i, inplaceMixRound(z.getColumnOfULongsForMixing(i)))
|
||||
}
|
||||
val final = if (xorWithCurrentBlock) {
|
||||
(z xor r) xor currentBlock
|
||||
(z xorInplaceWith r) xorInplaceWith currentBlock
|
||||
} else {
|
||||
z xor r
|
||||
z xorInplaceWith r
|
||||
}
|
||||
return final
|
||||
}
|
||||
|
||||
internal fun argonBlake2bArbitraryLenghtHash(input: Array<UByte>, length: UInt): Array<UByte> {
|
||||
internal fun argonBlake2bArbitraryLenghtHash(input: UByteArray, length: UInt): UByteArray {
|
||||
if (length <= 64U) {
|
||||
return Blake2b.digest(inputMessage = length + input, hashLength = length.toInt())
|
||||
return Blake2bPure.digest(inputMessage = length + input, hashLength = length.toInt())
|
||||
}
|
||||
//We can cast to int because UInt even if MAX_VALUE divided by 32 is guaranteed not to overflow
|
||||
val numberOf64ByteBlocks = (1U + ((length - 1U) / 32U) - 2U).toInt() // equivalent to ceil(length/32) - 2
|
||||
val v = Array<Array<UByte>>(numberOf64ByteBlocks) { emptyArray() }
|
||||
v[0] = Blake2b.digest(length + input)
|
||||
val v = Array<UByteArray>(numberOf64ByteBlocks) { ubyteArrayOf() }
|
||||
v[0] = Blake2bPure.digest(length + input)
|
||||
for (i in 1 until numberOf64ByteBlocks) {
|
||||
v[i] = Blake2b.digest(v[i - 1])
|
||||
v[i] = Blake2bPure.digest(v[i - 1])
|
||||
}
|
||||
val remainingPartOfInput = length.toInt() - numberOf64ByteBlocks * 32
|
||||
val vLast = Blake2b.digest(v[numberOf64ByteBlocks - 1], hashLength = remainingPartOfInput)
|
||||
val vLast = Blake2bPure.digest(v[numberOf64ByteBlocks - 1], hashLength = remainingPartOfInput)
|
||||
val concat =
|
||||
(v.map { it.copyOfRange(0, 32) })
|
||||
.plus(listOf(vLast))
|
||||
.foldRight(emptyArray<UByte>()) { arrayOfUBytes, acc -> arrayOfUBytes + acc }
|
||||
.foldRight(ubyteArrayOf()) { arrayOfUBytes, acc -> arrayOfUBytes + acc }
|
||||
|
||||
return concat
|
||||
}
|
||||
@ -143,14 +125,14 @@ object Argon2Utils {
|
||||
* tagLength, requested memory size and number of iterations, so no need to check for upper bound, just lower.
|
||||
*/
|
||||
internal fun validateArgonParameters(
|
||||
password: Array<UByte>,
|
||||
salt: Array<UByte>,
|
||||
password: UByteArray,
|
||||
salt: UByteArray,
|
||||
parallelism: Int ,
|
||||
tagLength: UInt,
|
||||
requestedMemorySize: UInt ,
|
||||
numberOfIterations: UInt ,
|
||||
key: Array<UByte>,
|
||||
associatedData: Array<UByte>,
|
||||
numberOfIterations: Int ,
|
||||
key: UByteArray,
|
||||
associatedData: UByteArray,
|
||||
argonType: ArgonType
|
||||
) {
|
||||
|
||||
@ -170,9 +152,16 @@ object Argon2Utils {
|
||||
throw Argon2MemoryTooLitlle(requestedMemorySize)
|
||||
}
|
||||
//Number of iterations
|
||||
if (numberOfIterations <= 0U) {
|
||||
if (numberOfIterations <= 0) {
|
||||
throw Argon2TimeTooShort(numberOfIterations)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ------------ Arithmetic and other utils
|
||||
|
||||
|
||||
fun UByteArray.xorWithBlock(other : ArgonMatrix, rowPosition: Int, columnPosition: Int) : UByteArray {
|
||||
return UByteArray(BLOCK_SIZE) { this[it] xor other[rowPosition, columnPosition, it] }
|
||||
}
|
@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
|
||||
|
||||
package com.ionspin.kotlin.crypto.keyderivation.argon2
|
||||
|
||||
import com.ionspin.kotlin.crypto.util.xor
|
||||
|
||||
/**
|
||||
* Represents a pointer to a Argon2 Block, this abstracts what the backing structure is, a Argon 2 Matrix or
|
||||
* or a UByteArray block
|
||||
*/
|
||||
interface ArgonBlockPointer {
|
||||
companion object {
|
||||
private val _emitLongArray : LongArray = longArrayOf(1L,2L)
|
||||
}
|
||||
val blockStartPosition: Int
|
||||
|
||||
fun pointerArithmetic(block : (Int, Int) -> Int) : ArgonBlockPointer
|
||||
|
||||
infix fun xorBlocksAndGetPointerToNewBlock(other: ArgonBlockPointer) : ArgonBlock
|
||||
|
||||
operator fun get(blockPosition: Int) : UByte
|
||||
|
||||
operator fun set(blockPosition: Int, value: UByte)
|
||||
|
||||
infix fun xorInplaceWith(other: ArgonBlockPointer) : ArgonBlockPointer {
|
||||
for (it in 0 until 1024) {
|
||||
this[it] = this[it] xor other[it]
|
||||
}
|
||||
return this //For chaining
|
||||
}
|
||||
|
||||
fun asInt() : Int
|
||||
|
||||
fun getAsUByteArray() : UByteArray
|
||||
}
|
||||
|
||||
fun ArgonBlockPointer._emitLongArray() : LongArray = longArrayOf(0,1)
|
||||
|
||||
fun ArgonBlockPointer.getRowOfULongsForMixing(rowIndex: Int) : ULongArray {
|
||||
// Each row has 16 unsigned longs (16 ulongs * 8 bytes = 128 bytes) -- Argon2 considers this as 2 word unsigned
|
||||
// numbers, so strictly speaking argon representation is 8 * 8 matrix of 2 word unsigned numbers (registers
|
||||
val ulongArray = ULongArray(16)
|
||||
for (columnIndex in 0 until 16) {
|
||||
var ulong = 0UL
|
||||
//Now we create the ulong
|
||||
for (bytePosition in 0 until 8) {
|
||||
ulong = ulong or (this[rowIndex * 128 + columnIndex * 8 + bytePosition].toULong() shl (bytePosition * 8))
|
||||
}
|
||||
ulongArray[columnIndex] = ulong
|
||||
}
|
||||
return ulongArray
|
||||
}
|
||||
|
||||
fun ArgonBlockPointer.setRowFromMixedULongs(rowIndex: Int, ulongs: ULongArray) {
|
||||
// Each row has 16 unsigned longs (16 ulongs * 8 bytes = 128 bytes) -- Argon2 considers this as 2 word unsigned
|
||||
// numbers, so strictly speaking argon representation is 8 * 8 matrix of 2 word unsigned numbers (registers
|
||||
for (columnIndex in 0 until 16) {
|
||||
val ulongToConvert = ulongs[columnIndex]
|
||||
for (bytePosition in 0 until 8) {
|
||||
this[rowIndex * 128 + columnIndex * 8 + bytePosition] = ((ulongToConvert shr (bytePosition * 8)) and 0xFFU).toUByte()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ArgonBlockPointer.getColumnOfULongsForMixing(columnIndex: Int) : ULongArray {
|
||||
//In Argon2 representation there are 8 double word registers (numbers, but we work with 16 single word ulongs
|
||||
val ulongArray = ULongArray(16)
|
||||
//There are 8 rows that consist of 2 words (registers, in our case ulongs) each
|
||||
for (rowIndex in 0 until 8) {
|
||||
var ulong = 0UL
|
||||
//Now we create the ulong
|
||||
for (bytePosition in 0 until 8) {
|
||||
ulong = ulong or (this[rowIndex * 128 + columnIndex * 16 + bytePosition].toULong() shl (bytePosition * 8))
|
||||
}
|
||||
ulongArray[rowIndex * 2] = ulong
|
||||
ulong = 0UL
|
||||
// But unlike in columns where we can directly iterate and get all TWO WORD registers, here we also need to grab
|
||||
// the next word
|
||||
for (bytePosition in 8 until 16) {
|
||||
ulong = ulong or (this[rowIndex * 128 + columnIndex * 16 + bytePosition].toULong() shl (bytePosition * 8))
|
||||
}
|
||||
ulongArray[rowIndex * 2 + 1] = ulong
|
||||
}
|
||||
return ulongArray
|
||||
}
|
||||
|
||||
fun ArgonBlockPointer.setColumnFromMixedULongs(columnIndex: Int, ulongs: ULongArray) {
|
||||
//In Argon2 representation there are 8 double word registers (numbers, but we work with 16 single word ulongs
|
||||
//There are 8 rows that consist of 2 words (registers, in our case ulongs) each
|
||||
var ulongToConvert = 0UL
|
||||
for (rowIndex in 0 until 8) {
|
||||
ulongToConvert = ulongs[rowIndex * 2]
|
||||
//Now we create the ulong
|
||||
for (bytePosition in 0 until 8) {
|
||||
this[rowIndex * 128 + columnIndex * 16 + bytePosition] = ((ulongToConvert shr (bytePosition * 8)) and 0xFFU).toUByte()
|
||||
}
|
||||
// But unlike in columns where we can directly iterate and get all TWO WORD registers, here we also need to set
|
||||
// the next word
|
||||
ulongToConvert = ulongs[rowIndex * 2 + 1]
|
||||
for (bytePosition in 8 until 16) {
|
||||
this[rowIndex * 128 + columnIndex * 16 + bytePosition] = ((ulongToConvert shr (bytePosition * 8)) and 0xFFU).toUByte()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ArgonBlockPointer.getUIntFromPosition(positionInBlock: Int) : UInt {
|
||||
var uint = 0U
|
||||
for (i in 0 until 4) {
|
||||
uint = uint or (this[positionInBlock + i].toUInt() shl (i * 8))
|
||||
}
|
||||
return uint
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 21-May-2020
|
||||
*/
|
||||
class ArgonMatrix(val columnCount: Int, val rowCount: Int) {
|
||||
|
||||
internal val storage: UByteArray = UByteArray(columnCount * rowCount * 1024)
|
||||
|
||||
operator fun get(rowPosition: Int, columnPosition: Int, inBlockPosition: Int) : UByte {
|
||||
if (rowPosition > rowCount - 1) {
|
||||
throw RuntimeException("Invalid row (lane) requested: $rowPosition, rowCount: $rowCount")
|
||||
}
|
||||
if (columnPosition > columnCount - 1) {
|
||||
throw RuntimeException("Invalid column requested: $columnPosition, columnCount: $columnCount")
|
||||
}
|
||||
return storage[getBlockStartPositionPointer(rowPosition, columnPosition) + inBlockPosition]
|
||||
}
|
||||
|
||||
val size = storage.size
|
||||
|
||||
operator fun get(absolutePosition: Int) : UByte {
|
||||
return storage[absolutePosition]
|
||||
}
|
||||
|
||||
operator fun set(rowPosition: Int, columnPosition: Int, inBlockPosition: Int, value: UByte) {
|
||||
storage[getBlockStartPositionPointer(rowPosition, columnPosition) + inBlockPosition] = value
|
||||
}
|
||||
|
||||
operator fun set(absolutePosition: Int, value: UByte) {
|
||||
storage[absolutePosition] = value
|
||||
}
|
||||
|
||||
fun getBlockPointer(rowPosition: Int, columnPosition: Int) : ArgonBlockPointer {
|
||||
return ArgonBlockPointerWithMatrix(getBlockStartPositionPointer(rowPosition, columnPosition), this)
|
||||
}
|
||||
|
||||
|
||||
fun sliceArray(indices: IntRange): UByteArray {
|
||||
return storage.sliceArray(indices)
|
||||
}
|
||||
|
||||
fun getBlockAt(rowPosition: Int, columnPosition: Int) : UByteArray {
|
||||
println("Expensive get")
|
||||
return storage.copyOfRange(
|
||||
getBlockStartPositionPointer(rowPosition, columnPosition),
|
||||
getBlockStartPositionPointer(rowPosition, columnPosition) + 1024
|
||||
)
|
||||
}
|
||||
|
||||
fun setBlockAt(rowPosition: Int, columnPosition: Int, blockValue: UByteArray) {
|
||||
blockValue.copyInto(
|
||||
storage,
|
||||
getBlockStartPositionPointer(rowPosition, columnPosition)
|
||||
)
|
||||
}
|
||||
|
||||
private inline fun getBlockStartPositionPointer(rowPosition: Int, columnPosition: Int) : Int {
|
||||
return rowPosition * columnCount * 1024 + columnPosition * 1024
|
||||
}
|
||||
|
||||
internal fun clearMatrix() {
|
||||
for( index in storage.indices) { storage[index] = 0U }
|
||||
}
|
||||
|
||||
private class ArgonBlockPointerWithMatrix constructor(override val blockStartPosition: Int, val matrix: ArgonMatrix) : ArgonBlockPointer {
|
||||
|
||||
override fun pointerArithmetic(block: (Int, Int) -> Int): ArgonBlockPointer {
|
||||
return ArgonBlockPointerWithMatrix(block(blockStartPosition, matrix.size), matrix)
|
||||
}
|
||||
|
||||
override fun asInt(): Int {
|
||||
return blockStartPosition
|
||||
}
|
||||
|
||||
override operator fun get(blockPosition: Int) : UByte {
|
||||
return matrix[blockStartPosition + blockPosition]
|
||||
}
|
||||
|
||||
override fun set(blockPosition: Int, value: UByte) {
|
||||
matrix[blockStartPosition + blockPosition] = value
|
||||
}
|
||||
|
||||
override infix fun xorBlocksAndGetPointerToNewBlock(other: ArgonBlockPointer) : ArgonBlock {
|
||||
return ArgonBlock(UByteArray(1024){
|
||||
matrix[blockStartPosition + it] xor other[it]
|
||||
})
|
||||
}
|
||||
|
||||
override fun getAsUByteArray(): UByteArray {
|
||||
return matrix.storage.slice(blockStartPosition until blockStartPosition + 1024).toUByteArray()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS")
|
||||
inline class ArgonBlock internal constructor(internal val storage: UByteArray) {
|
||||
constructor() : this(UByteArray(1024))
|
||||
operator fun get(index: Int) : UByte {
|
||||
return storage.get(index)
|
||||
}
|
||||
operator fun set(index: Int, value: UByte) {
|
||||
storage.set(index, value)
|
||||
}
|
||||
|
||||
val size: Int get() = storage.size
|
||||
|
||||
internal fun getAsUByteArray() : UByteArray = storage
|
||||
|
||||
fun getBlockPointer() : ArgonBlockPointer{
|
||||
return ArgonBlockPointerWithBlock( this)
|
||||
}
|
||||
|
||||
infix fun xorInplaceWith(other: ArgonBlock) : ArgonBlock {
|
||||
storage.indices.forEach {
|
||||
this[it] = this[it] xor other[it]
|
||||
}
|
||||
return this //For chaining
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class ArgonBlockPointerWithBlock constructor(val storageBlock: ArgonBlock) : ArgonBlockPointer {
|
||||
override val blockStartPosition: Int = 0
|
||||
|
||||
override fun pointerArithmetic(block: (Int, Int) -> Int): ArgonBlockPointer {
|
||||
throw RuntimeException("Haven't really tought out pointer arithmetic with blocks")
|
||||
}
|
||||
|
||||
override fun asInt(): Int {
|
||||
return blockStartPosition
|
||||
}
|
||||
override operator fun get(blockPosition: Int) : UByte {
|
||||
return storageBlock[blockPosition]
|
||||
}
|
||||
|
||||
override fun set(blockPosition: Int, value: UByte) {
|
||||
storageBlock[blockPosition] = value
|
||||
}
|
||||
|
||||
override infix fun xorBlocksAndGetPointerToNewBlock(other: ArgonBlockPointer) : ArgonBlock {
|
||||
return ArgonBlock(UByteArray(1024){
|
||||
storageBlock[it] xor other[it]
|
||||
})
|
||||
}
|
||||
|
||||
override fun getAsUByteArray(): UByteArray {
|
||||
return storageBlock.storage
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.parallelization
|
||||
|
||||
import kotlin.time.ExperimentalTime
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-May-2020
|
||||
*/
|
||||
@ExperimentalTime
|
||||
object Coroutines14 {
|
||||
fun argonParallel() : Array<UByte> {
|
||||
// val argon = Argon2()
|
||||
// argon
|
||||
println("Placeholder")
|
||||
return emptyArray()
|
||||
}
|
||||
}
|
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright 2019 Ugljesa Jovanovic
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.symmetric
|
||||
|
||||
import com.ionspin.kotlin.crypto.SRNG
|
||||
import com.ionspin.kotlin.crypto.util.xor
|
||||
|
||||
/**
|
||||
* Advanced encryption standard with cipher block chaining and PKCS #5
|
||||
*
|
||||
* For bulk encryption/decryption use [AesCbcPure.encrypt] and [AesCbcPure.decrypt]
|
||||
*
|
||||
* To get an instance of AesCbc and then feed it data sequentially with [addData] use [createEncryptor] and [createDecryptor]
|
||||
*
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 21-Sep-2019
|
||||
*/
|
||||
|
||||
class AesCbcPure internal constructor(val aesKey: AesKey, val mode: Mode, initializationVector: UByteArray? = null) {
|
||||
|
||||
companion object {
|
||||
const val BLOCK_BYTES = 16
|
||||
/**
|
||||
* Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all
|
||||
* data call [encrypt]
|
||||
*/
|
||||
fun createEncryptor(aesKey: AesKey) : AesCbcPure {
|
||||
return AesCbcPure(aesKey, Mode.ENCRYPT)
|
||||
}
|
||||
/**
|
||||
* Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all
|
||||
* data call [decrypt]
|
||||
*/
|
||||
fun createDecryptor(aesKey : AesKey) : AesCbcPure {
|
||||
return AesCbcPure(aesKey, Mode.DECRYPT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk encryption, returns encrypted data and a random initialization vector
|
||||
*/
|
||||
fun encrypt(aesKey: AesKey, data: UByteArray): EncryptedDataAndInitializationVector {
|
||||
val aesCbc = AesCbcPure(aesKey, Mode.ENCRYPT)
|
||||
aesCbc.addData(data)
|
||||
return aesCbc.encrypt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk decryption, returns decrypted data
|
||||
*/
|
||||
fun decrypt(aesKey: AesKey, data: UByteArray, initialCounter: UByteArray? = null): UByteArray {
|
||||
val aesCbc = AesCbcPure(aesKey, Mode.DECRYPT, initialCounter)
|
||||
aesCbc.addData(data)
|
||||
return aesCbc.decrypt()
|
||||
}
|
||||
|
||||
private fun padToBlock(unpadded: UByteArray): UByteArray {
|
||||
val paddingSize = 16 - unpadded.size
|
||||
if (unpadded.size == BLOCK_BYTES) {
|
||||
return unpadded
|
||||
}
|
||||
|
||||
if (unpadded.size == BLOCK_BYTES) {
|
||||
return UByteArray(BLOCK_BYTES) {
|
||||
BLOCK_BYTES.toUByte()
|
||||
}
|
||||
}
|
||||
|
||||
if (unpadded.size > BLOCK_BYTES) {
|
||||
throw IllegalStateException("Block larger than 128 bytes")
|
||||
}
|
||||
|
||||
return UByteArray(BLOCK_BYTES) {
|
||||
when (it) {
|
||||
in unpadded.indices -> unpadded[it]
|
||||
else -> paddingSize.toUByte()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var currentOutput: UByteArray = ubyteArrayOf()
|
||||
var previousEncrypted: UByteArray = ubyteArrayOf()
|
||||
val initVector = if (initializationVector.isNullOrEmpty()) {
|
||||
SRNG.getRandomBytes(16)
|
||||
} else {
|
||||
initializationVector
|
||||
}
|
||||
|
||||
val output = MutableList<UByteArray>(0) { ubyteArrayOf() }
|
||||
|
||||
var buffer: UByteArray = UByteArray(16) { 0U }
|
||||
var bufferCounter = 0
|
||||
|
||||
fun addData(data: UByteArray) {
|
||||
//Padding
|
||||
when {
|
||||
bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter)
|
||||
bufferCounter + data.size >= BLOCK_BYTES -> {
|
||||
val chunked = data.chunked(BLOCK_BYTES)
|
||||
chunked.forEach { chunk ->
|
||||
if (bufferCounter + chunk.size < BLOCK_BYTES) {
|
||||
appendToBuffer(chunk.toUByteArray(), bufferCounter)
|
||||
} else {
|
||||
chunk.toUByteArray().copyInto(
|
||||
destination = buffer,
|
||||
destinationOffset = bufferCounter,
|
||||
startIndex = 0,
|
||||
endIndex = BLOCK_BYTES - bufferCounter
|
||||
)
|
||||
output += consumeBlock(buffer)
|
||||
buffer = UByteArray(BLOCK_BYTES) {
|
||||
when (it) {
|
||||
in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> {
|
||||
chunk[it + (BLOCK_BYTES - bufferCounter)]
|
||||
}
|
||||
else -> {
|
||||
0U
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
bufferCounter = chunk.size - (BLOCK_BYTES - bufferCounter)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt fed data and return it alongside the randomly chosen initialization vector
|
||||
* @return Encrypted data and initialization vector
|
||||
*/
|
||||
fun encrypt(): EncryptedDataAndInitializationVector {
|
||||
if (bufferCounter > 0) {
|
||||
val lastBlockPadded = padToBlock(buffer)
|
||||
if (lastBlockPadded.size > BLOCK_BYTES) {
|
||||
val chunks = lastBlockPadded.chunked(BLOCK_BYTES).map { it.toUByteArray() }
|
||||
output += consumeBlock(chunks[0])
|
||||
output += consumeBlock(chunks[1])
|
||||
} else {
|
||||
output += consumeBlock(lastBlockPadded)
|
||||
}
|
||||
}
|
||||
return EncryptedDataAndInitializationVector(
|
||||
output.reversed().foldRight(UByteArray(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
|
||||
initVector
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt data
|
||||
* @return Decrypted data
|
||||
*/
|
||||
fun decrypt(): UByteArray {
|
||||
val removePaddingCount = output.last().last()
|
||||
|
||||
|
||||
val removedPadding = if (removePaddingCount > 0U && removePaddingCount < 16U) {
|
||||
output.last().dropLast(removePaddingCount.toInt() and 0x7F)
|
||||
} else {
|
||||
output.last().toList()
|
||||
}.toUByteArray()
|
||||
val preparedOutput = (output.dropLast(1) + listOf(removedPadding))
|
||||
//JS compiler freaks out here if we don't supply exact type
|
||||
val reversed : List<UByteArray> = preparedOutput.reversed() as List<UByteArray>
|
||||
val folded : UByteArray = reversed.foldRight(UByteArray(0) { 0U }) { uByteArray, acc ->
|
||||
acc + uByteArray
|
||||
}
|
||||
return folded
|
||||
|
||||
}
|
||||
|
||||
private fun appendToBuffer(array: UByteArray, start: Int) {
|
||||
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||
bufferCounter += array.size
|
||||
}
|
||||
|
||||
private fun consumeBlock(data: UByteArray): UByteArray {
|
||||
return when (mode) {
|
||||
Mode.ENCRYPT -> {
|
||||
currentOutput = if (currentOutput.isEmpty()) {
|
||||
println("IV: $initVector")
|
||||
AesPure.encrypt(aesKey, data xor initVector)
|
||||
} else {
|
||||
AesPure.encrypt(aesKey, data xor currentOutput)
|
||||
}
|
||||
currentOutput
|
||||
}
|
||||
Mode.DECRYPT -> {
|
||||
if (currentOutput.isEmpty()) {
|
||||
currentOutput = AesPure.decrypt(aesKey, data) xor initVector
|
||||
} else {
|
||||
currentOutput = AesPure.decrypt(aesKey, data) xor previousEncrypted
|
||||
}
|
||||
previousEncrypted = data
|
||||
currentOutput
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
data class EncryptedDataAndInitializationVector(val encryptedData : UByteArray, val initilizationVector : UByteArray) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || this::class != other::class) return false
|
||||
|
||||
other as EncryptedDataAndInitializationVector
|
||||
|
||||
if (!encryptedData.contentEquals(other.encryptedData)) return false
|
||||
if (!initilizationVector.contentEquals(other.initilizationVector)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = encryptedData.contentHashCode()
|
||||
result = 31 * result + initilizationVector.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user