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
|
/package.json
|
||||||
/multiplatform-crypto/src/jsMain/npm/node_modules
|
/multiplatform-crypto/src/jsMain/npm/node_modules
|
||||||
/multiplatform-crypto/src/jsMain/npm/package-lock.json
|
/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:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: linux
|
- os: linux
|
||||||
|
name: linux
|
||||||
language: java
|
language: java
|
||||||
jdk: openjdk12
|
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:
|
env:
|
||||||
KBUILD=linux
|
KBUILD=linux
|
||||||
JAVA_OPTS=-Xmx2g
|
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:
|
script:
|
||||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./linuxBuild.sh; fi'
|
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./linuxBuild.sh; fi'
|
||||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./linuxBuildAndPublish.sh; fi'
|
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./linuxBuildAndPublish.sh; fi'
|
||||||
|
# OSX macos/ios
|
||||||
- os: osx
|
- os: osx
|
||||||
|
name: osx-mac-ios
|
||||||
osx_image: xcode11.4
|
osx_image: xcode11.4
|
||||||
language: java
|
language: java
|
||||||
jdk: openjdk12
|
jdk: openjdk12
|
||||||
# before_script:
|
install: true
|
||||||
# - 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:
|
env:
|
||||||
KBUILD=linux
|
KBUILD=linux
|
||||||
JAVA_OPTS=-Xmx2g
|
JAVA_OPTS=-Xmx2g
|
||||||
script:
|
script:
|
||||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild.sh; fi'
|
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild-mac-ios.sh; fi'
|
||||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish.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
|
- os: windows
|
||||||
|
name: windwos-pure
|
||||||
language: shell
|
language: shell
|
||||||
jdk: openjdk12
|
jdk: openjdk12
|
||||||
env:
|
env:
|
||||||
@ -37,15 +78,68 @@ matrix:
|
|||||||
before_install:
|
before_install:
|
||||||
- curl "${GRAVIS}.install-jdk-travis.sh" --output ~/.install-jdk-travis.sh
|
- curl "${GRAVIS}.install-jdk-travis.sh" --output ~/.install-jdk-travis.sh
|
||||||
- source ~/.install-jdk-travis.sh
|
- source ~/.install-jdk-travis.sh
|
||||||
|
install: true
|
||||||
script:
|
script:
|
||||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then ./gradlew build ; fi'
|
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then $shell ./windowsBuild-pure.sh; fi'
|
||||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then ./gradlew build publishMingwx64PublicationToSnapshotRepository; 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:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- $HOME/.m2/
|
- $HOME/.m2/
|
||||||
- $HOME/.gradle/caches/
|
- $HOME/.gradle/caches/
|
||||||
- $HOME/.gradle/wrapper/
|
- $HOME/.gradle/wrapper/
|
||||||
- $HOME/.konan/cache
|
- $HOME/.konan/cache
|
||||||
|
- $HOME/.konan/dependencies
|
||||||
|
- $HOME/AppData/Local/Temp/chocolatey
|
||||||
|
- /C/tools/msys64
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- 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.
|
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
|
## Notes & Roadmap
|
||||||
|
|
||||||
**The API will move fast and break often until v1.0**
|
**The API will move fast and break often until v1.0**
|
||||||
|
|
||||||
Make SHA hashes "updatable" like Blake2b
|
Next steps:
|
||||||
|
- Expand API (AEAD, ECC ...)
|
||||||
After that tenative plan is to add 25519 curve based signing and key exchange next.
|
|
||||||
|
|
||||||
## Should I use this in production?
|
## 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?
|
## Why?
|
||||||
|
|
||||||
This is an experimental implementation, mostly for expanding personal understanding of cryptography.
|
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.
|
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
|
* Blake2b
|
||||||
* SHA512
|
* SHA512
|
||||||
* SHA256
|
* SHA256
|
||||||
|
|
||||||
## Symmetric cipher
|
### Symmetric cipher
|
||||||
* AES
|
* AES
|
||||||
* Modes: CBC, CTR
|
* Modes: CBC, CTR
|
||||||
|
|
||||||
## Key Derivation
|
### Key Derivation
|
||||||
|
|
||||||
* Argon2
|
* Argon2
|
||||||
|
|
||||||
## AEAD
|
### AEAD
|
||||||
|
|
||||||
TODO()
|
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
|
## Integration
|
||||||
|
|
||||||
#### Gradle
|
#### Gradle
|
||||||
|
Kotlin
|
||||||
```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
|
#### Snapshot builds
|
||||||
@ -66,12 +105,19 @@ repositories {
|
|||||||
url = uri("https://oss.sonatype.org/content/repositories/snapshots")
|
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
|
## 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
|
||||||
|
|
||||||
Hashes are provided in two versions, "stateless", usually the companion object of the hash,
|
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
|
```kotlin
|
||||||
val input = "abc"
|
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
|
##### 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 can create an instance and feed the data by using `update(input : UByteArray)` call. Once all data is supplied,
|
||||||
you should call `digest()` or `digestString()` convenience method that converts the `Array<Byte>` into hexadecimal string.
|
you should call `digest()`.
|
||||||
|
|
||||||
If you want to use Blake2b with a key, you should supply it when creating the `Blake2b` instance.
|
If you want to use Blake2b with a key, you should supply it when creating the `Blake2b` instance.
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
val test = "abc"
|
val test = "abc"
|
||||||
val key = "key"
|
val key = "key"
|
||||||
val blake2b = Blake2b(key)
|
val blake2b = Crypto.Blake2b.updateable(key.encodeToUByteArray())
|
||||||
blake2b.update(test)
|
blake2b.update(test.encodeToUByteArray())
|
||||||
val result = blake2b.digest()
|
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).
|
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
|
##### 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
|
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 `Array<Byte>` (At least in verision 0.0.1)
|
or `String`. Result is always returned as `UByteArray` (At least in verision 0.0.1)
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
val input = "abc"
|
val input = "abc"
|
||||||
val result = Sha256.digest(input)
|
val result = Crypto.Sha256.stateless(input.encodeToUByteArray())
|
||||||
```
|
```
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
val input ="abc"
|
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
|
##### Updateable version
|
||||||
|
|
||||||
Or you can use the updatable instance version
|
Or you can use the updatable instance version
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
val sha256 = Sha256()
|
val sha256 = Crypto.Sha256.updateable()
|
||||||
sha256.update("abc")
|
sha256.update("abc".encodeToUByteArray())
|
||||||
val result = sha256.digest()
|
val result = sha256.digest()
|
||||||
```
|
```
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
val sha512 = Sha512()
|
val sha512 = Crypto.Sha512.updateable()
|
||||||
sha512.update("abc")
|
sha512.update("abc".encodeToUByteArray())
|
||||||
val result = sha512.digest()
|
val result = sha512.digest()
|
||||||
```
|
```
|
||||||
|
|
||||||
### Symmetric encryption
|
### Symmetric encryption
|
||||||
|
|
||||||
#### AES
|
#### AES
|
||||||
|
@ -40,6 +40,7 @@ allprojects {
|
|||||||
google()
|
google()
|
||||||
maven ("https://kotlin.bintray.com/kotlinx")
|
maven ("https://kotlin.bintray.com/kotlinx")
|
||||||
maven ("https://dl.bintray.com/kotlin/kotlin-eap")
|
maven ("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||||
|
maven ("https://kotlin.bintray.com/kotlin-dev")
|
||||||
jcenter()
|
jcenter()
|
||||||
maven {
|
maven {
|
||||||
url = uri("https://oss.sonatype.org/content/repositories/snapshots")
|
url = uri("https://oss.sonatype.org/content/repositories/snapshots")
|
||||||
|
@ -21,5 +21,14 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven ("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||||
jcenter()
|
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 {
|
object Versions {
|
||||||
val kotlinCoroutines = "1.3.5-native-mt-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-M1"
|
val kotlin = "1.4-M2"
|
||||||
val kotlinSerialization = "0.20.0-1.4-M1"
|
val kotlinSerialization = "0.20.0-1.4-M2"
|
||||||
val atomicfu = "0.14.2-1.4-M1"
|
val atomicfu = "0.14.3-M2-2-SNAPSHOT" //NOTE: my linux arm32 and arm64 build
|
||||||
val nodePlugin = "1.3.0"
|
val nodePlugin = "1.3.0"
|
||||||
val dokkaPlugin = "0.9.18"
|
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 Deps {
|
||||||
|
|
||||||
object Common {
|
object Common {
|
||||||
val stdLib = "stdlib-common"
|
val stdLib = "stdlib-common"
|
||||||
val test = "test-common"
|
val test = "test-common"
|
||||||
val testAnnotation = "test-annotations-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 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 kotlinBigNum = "com.ionspin.kotlin:bignum:${Versions.kotlinBigNumVersion}"
|
||||||
|
|
||||||
|
val apiProject = ":multiplatform-crypto-api"
|
||||||
}
|
}
|
||||||
|
|
||||||
object Js {
|
object Js {
|
||||||
val stdLib = "stdlib-js"
|
val stdLib = "stdlib-js"
|
||||||
val test = "test-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}"
|
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 {
|
object Jvm {
|
||||||
@ -54,20 +73,29 @@ object Deps {
|
|||||||
val test = "test"
|
val test = "test"
|
||||||
val testJUnit = "test-junit"
|
val testJUnit = "test-junit"
|
||||||
val reflection = "reflect"
|
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 coroutinesjdk8 = "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:${Versions.kotlinCoroutines}"
|
||||||
val serialization = "org.jetbrains.kotlinx:kotlinx-serialization-runtime:${Versions.kotlinSerialization}"
|
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 {
|
object iOs {
|
||||||
val serialization = "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:${Versions.kotlinSerialization}"
|
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 {
|
object Native {
|
||||||
val serialization = "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:${Versions.kotlinSerialization}"
|
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 mavenPublish = "maven-publish"
|
||||||
val signing = "signing"
|
val signing = "signing"
|
||||||
val dokka = "org.jetbrains.dokka"
|
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.code.style=official
|
||||||
|
|
||||||
kotlin.js.compiler=both
|
kotlin.js.compiler=ir
|
||||||
kotlin.mpp.enableGranularSourceSetsMetadata=true
|
#kotlin.js.experimental.generateKotlinExternals=true
|
||||||
|
#kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||||
|
kotlin.native.disableCompilerDaemon=true
|
||||||
|
|
||||||
org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=4096m
|
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
|
val MAX_HASH_BYTES : Int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExperimentalUnsignedTypes
|
|
||||||
interface UpdatableHash : Hash {
|
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 {
|
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(),
|
fun String.encodeToUByteArray() : UByteArray{
|
||||||
hashLength: Int = MAX_HASH_BYTES
|
return encodeToByteArray().toUByteArray()
|
||||||
): Array<UByte>
|
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
* on 16-May-2020
|
||||||
*/
|
*/
|
||||||
interface KeyDerivationFunction {
|
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
|
package com.ionspin.kotlin.crypto.symmetric
|
||||||
|
|
||||||
import com.ionspin.kotlin.crypto.SRNG
|
import com.ionspin.kotlin.crypto.SRNG
|
||||||
import com.ionspin.kotlin.crypto.util.chunked
|
|
||||||
import com.ionspin.kotlin.crypto.util.xor
|
import com.ionspin.kotlin.crypto.util.xor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Advanced encryption standard with cipher block chaining and PKCS #5
|
* 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]
|
* 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
|
* ugljesa.jovanovic@ionspin.com
|
||||||
* on 21-Sep-2019
|
* 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 {
|
companion object {
|
||||||
const val BLOCK_BYTES = 16
|
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
|
* Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all
|
||||||
* data call [encrypt]
|
* data call [encrypt]
|
||||||
*/
|
*/
|
||||||
fun createEncryptor(aesKey: AesKey) : AesCbc {
|
fun createEncryptor(aesKey: AesKey) : AesCbcPure {
|
||||||
return AesCbc(aesKey, Mode.ENCRYPT)
|
return AesCbcPure(aesKey, Mode.ENCRYPT)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all
|
* Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all
|
||||||
* data call [decrypt]
|
* data call [decrypt]
|
||||||
*/
|
*/
|
||||||
fun createDecryptor(aesKey : AesKey) : AesCbc {
|
fun createDecryptor(aesKey : AesKey) : AesCbcPure {
|
||||||
return AesCbc(aesKey, Mode.DECRYPT)
|
return AesCbcPure(aesKey, Mode.DECRYPT)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bulk encryption, returns encrypted data and a random initialization vector
|
* Bulk encryption, returns encrypted data and a random initialization vector
|
||||||
*/
|
*/
|
||||||
fun encrypt(aesKey: AesKey, data: Array<UByte>): EncryptedDataAndInitializationVector {
|
fun encrypt(aesKey: AesKey, data: UByteArray): EncryptedDataAndInitializationVector {
|
||||||
val aesCbc = AesCbc(aesKey, Mode.ENCRYPT)
|
val aesCbc = AesCbcPure(aesKey, Mode.ENCRYPT)
|
||||||
aesCbc.addData(data)
|
aesCbc.addData(data)
|
||||||
return aesCbc.encrypt()
|
return aesCbc.encrypt()
|
||||||
}
|
}
|
||||||
@ -63,20 +62,20 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
|
|||||||
/**
|
/**
|
||||||
* Bulk decryption, returns decrypted data
|
* Bulk decryption, returns decrypted data
|
||||||
*/
|
*/
|
||||||
fun decrypt(aesKey: AesKey, data: Array<UByte>, initialCounter: Array<UByte>? = null): Array<UByte> {
|
fun decrypt(aesKey: AesKey, data: UByteArray, initialCounter: UByteArray? = null): UByteArray {
|
||||||
val aesCbc = AesCbc(aesKey, Mode.DECRYPT, initialCounter)
|
val aesCbc = AesCbcPure(aesKey, Mode.DECRYPT, initialCounter)
|
||||||
aesCbc.addData(data)
|
aesCbc.addData(data)
|
||||||
return aesCbc.decrypt()
|
return aesCbc.decrypt()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun padToBlock(unpadded: Array<UByte>): Array<UByte> {
|
private fun padToBlock(unpadded: UByteArray): UByteArray {
|
||||||
val paddingSize = 16 - unpadded.size
|
val paddingSize = 16 - unpadded.size
|
||||||
if (unpadded.size == BLOCK_BYTES) {
|
if (unpadded.size == BLOCK_BYTES) {
|
||||||
return unpadded
|
return unpadded
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unpadded.size == BLOCK_BYTES) {
|
if (unpadded.size == BLOCK_BYTES) {
|
||||||
return Array(BLOCK_BYTES) {
|
return UByteArray(BLOCK_BYTES) {
|
||||||
BLOCK_BYTES.toUByte()
|
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")
|
throw IllegalStateException("Block larger than 128 bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array(BLOCK_BYTES) {
|
return UByteArray(BLOCK_BYTES) {
|
||||||
when (it) {
|
when (it) {
|
||||||
in unpadded.indices -> unpadded[it]
|
in unpadded.indices -> unpadded[it]
|
||||||
else -> paddingSize.toUByte()
|
else -> paddingSize.toUByte()
|
||||||
@ -95,20 +94,20 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentOutput: Array<UByte> = arrayOf()
|
var currentOutput: UByteArray = ubyteArrayOf()
|
||||||
var previousEncrypted: Array<UByte> = arrayOf()
|
var previousEncrypted: UByteArray = ubyteArrayOf()
|
||||||
val initVector = if (initializationVector.isNullOrEmpty()) {
|
val initVector = if (initializationVector.isNullOrEmpty()) {
|
||||||
SRNG.getRandomBytes(16)
|
SRNG.getRandomBytes(16)
|
||||||
} else {
|
} else {
|
||||||
initializationVector
|
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
|
var bufferCounter = 0
|
||||||
|
|
||||||
fun addData(data: Array<UByte>) {
|
fun addData(data: UByteArray) {
|
||||||
//Padding
|
//Padding
|
||||||
when {
|
when {
|
||||||
bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter)
|
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)
|
val chunked = data.chunked(BLOCK_BYTES)
|
||||||
chunked.forEach { chunk ->
|
chunked.forEach { chunk ->
|
||||||
if (bufferCounter + chunk.size < BLOCK_BYTES) {
|
if (bufferCounter + chunk.size < BLOCK_BYTES) {
|
||||||
appendToBuffer(chunk, bufferCounter)
|
appendToBuffer(chunk.toUByteArray(), bufferCounter)
|
||||||
} else {
|
} else {
|
||||||
chunk.copyInto(
|
chunk.toUByteArray().copyInto(
|
||||||
destination = buffer,
|
destination = buffer,
|
||||||
destinationOffset = bufferCounter,
|
destinationOffset = bufferCounter,
|
||||||
startIndex = 0,
|
startIndex = 0,
|
||||||
endIndex = BLOCK_BYTES - bufferCounter
|
endIndex = BLOCK_BYTES - bufferCounter
|
||||||
)
|
)
|
||||||
output += consumeBlock(buffer)
|
output += consumeBlock(buffer)
|
||||||
buffer = Array<UByte>(BLOCK_BYTES) {
|
buffer = UByteArray(BLOCK_BYTES) {
|
||||||
when (it) {
|
when (it) {
|
||||||
in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> {
|
in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> {
|
||||||
chunk[it + (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) {
|
if (bufferCounter > 0) {
|
||||||
val lastBlockPadded = padToBlock(buffer)
|
val lastBlockPadded = padToBlock(buffer)
|
||||||
if (lastBlockPadded.size > BLOCK_BYTES) {
|
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[0])
|
||||||
output += consumeBlock(chunks[1])
|
output += consumeBlock(chunks[1])
|
||||||
} else {
|
} else {
|
||||||
@ -161,7 +160,7 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return EncryptedDataAndInitializationVector(
|
return EncryptedDataAndInitializationVector(
|
||||||
output.reversed().foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
|
output.reversed().foldRight(UByteArray(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
|
||||||
initVector
|
initVector
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -170,7 +169,7 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
|
|||||||
* Decrypt data
|
* Decrypt data
|
||||||
* @return Decrypted data
|
* @return Decrypted data
|
||||||
*/
|
*/
|
||||||
fun decrypt(): Array<UByte> {
|
fun decrypt(): UByteArray {
|
||||||
val removePaddingCount = output.last().last()
|
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)
|
output.last().dropLast(removePaddingCount.toInt() and 0x7F)
|
||||||
} else {
|
} else {
|
||||||
output.last().toList()
|
output.last().toList()
|
||||||
}
|
}.toUByteArray()
|
||||||
val preparedOutput = output.dropLast(1).toTypedArray() + removedPadding.toTypedArray()
|
val preparedOutput = (output.dropLast(1) + listOf(removedPadding))
|
||||||
//JS compiler freaks out here if we don't supply exact type
|
//JS compiler freaks out here if we don't supply exact type
|
||||||
val reversed : List<Array<UByte>> = preparedOutput.reversed() as List<Array<UByte>>
|
val reversed : List<UByteArray> = preparedOutput.reversed() as List<UByteArray>
|
||||||
val folded : Array<UByte> = reversed.foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc ->
|
val folded : UByteArray = reversed.foldRight(UByteArray(0) { 0U }) { uByteArray, acc ->
|
||||||
acc + arrayOfUBytes }
|
acc + uByteArray
|
||||||
|
}
|
||||||
return folded
|
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)
|
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||||
bufferCounter += array.size
|
bufferCounter += array.size
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun consumeBlock(data: Array<UByte>): Array<UByte> {
|
private fun consumeBlock(data: UByteArray): UByteArray {
|
||||||
return when (mode) {
|
return when (mode) {
|
||||||
Mode.ENCRYPT -> {
|
Mode.ENCRYPT -> {
|
||||||
currentOutput = if (currentOutput.isEmpty()) {
|
currentOutput = if (currentOutput.isEmpty()) {
|
||||||
println("IV: $initVector")
|
println("IV: $initVector")
|
||||||
Aes.encrypt(aesKey, data xor initVector)
|
AesPure.encrypt(aesKey, data xor initVector)
|
||||||
} else {
|
} else {
|
||||||
Aes.encrypt(aesKey, data xor currentOutput)
|
AesPure.encrypt(aesKey, data xor currentOutput)
|
||||||
}
|
}
|
||||||
currentOutput
|
currentOutput
|
||||||
}
|
}
|
||||||
Mode.DECRYPT -> {
|
Mode.DECRYPT -> {
|
||||||
if (currentOutput.isEmpty()) {
|
if (currentOutput.isEmpty()) {
|
||||||
currentOutput = Aes.decrypt(aesKey, data) xor initVector
|
currentOutput = AesPure.decrypt(aesKey, data) xor initVector
|
||||||
} else {
|
} else {
|
||||||
currentOutput = Aes.decrypt(aesKey, data) xor previousEncrypted
|
currentOutput = AesPure.decrypt(aesKey, data) xor previousEncrypted
|
||||||
}
|
}
|
||||||
previousEncrypted = data
|
previousEncrypted = data
|
||||||
currentOutput
|
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 {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (other == null || this::class != other::class) return false
|
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.integer.BigInteger
|
||||||
import com.ionspin.kotlin.bignum.modular.ModularBigInteger
|
import com.ionspin.kotlin.bignum.modular.ModularBigInteger
|
||||||
import com.ionspin.kotlin.crypto.SRNG
|
import com.ionspin.kotlin.crypto.SRNG
|
||||||
import com.ionspin.kotlin.crypto.util.chunked
|
import com.ionspin.kotlin.crypto.symmetric.AesCtrPure.Companion.encrypt
|
||||||
import com.ionspin.kotlin.crypto.symmetric.AesCtr.Companion.encrypt
|
|
||||||
import com.ionspin.kotlin.crypto.util.xor
|
import com.ionspin.kotlin.crypto.util.xor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Advanced encryption standard with counter mode
|
* 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]
|
* 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
|
* ugljesa.jovanovic@ionspin.com
|
||||||
* on 22-Sep-2019
|
* 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 {
|
companion object {
|
||||||
const val BLOCK_BYTES = 16
|
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
|
* Creates and returns AesCtr instance that can be fed data using [addData]. Once you have submitted all
|
||||||
* data call [encrypt]
|
* data call [encrypt]
|
||||||
*/
|
*/
|
||||||
fun createEncryptor(aesKey: AesKey) : AesCtr {
|
fun createEncryptor(aesKey: AesKey) : AesCtrPure {
|
||||||
return AesCtr(aesKey, Mode.ENCRYPT)
|
return AesCtrPure(aesKey, Mode.ENCRYPT)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Creates and returns AesCtr instance that can be fed data using [addData]. Once you have submitted all
|
* Creates and returns AesCtr instance that can be fed data using [addData]. Once you have submitted all
|
||||||
* data call [decrypt]
|
* data call [decrypt]
|
||||||
*/
|
*/
|
||||||
fun createDecryptor(aesKey : AesKey) : AesCtr {
|
fun createDecryptor(aesKey : AesKey) : AesCtrPure {
|
||||||
return AesCtr(aesKey, Mode.DECRYPT)
|
return AesCtrPure(aesKey, Mode.DECRYPT)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Bulk encryption, returns encrypted data and a random initial counter
|
* Bulk encryption, returns encrypted data and a random initial counter
|
||||||
*/
|
*/
|
||||||
fun encrypt(aesKey: AesKey, data: Array<UByte>): EncryptedDataAndInitialCounter {
|
fun encrypt(aesKey: AesKey, data: UByteArray): EncryptedDataAndInitialCounter {
|
||||||
val aesCtr = AesCtr(aesKey, Mode.ENCRYPT)
|
val aesCtr = AesCtrPure(aesKey, Mode.ENCRYPT)
|
||||||
aesCtr.addData(data)
|
aesCtr.addData(data)
|
||||||
return aesCtr.encrypt()
|
return aesCtr.encrypt()
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Bulk decryption, returns decrypted data
|
* Bulk decryption, returns decrypted data
|
||||||
*/
|
*/
|
||||||
fun decrypt(aesKey: AesKey, data: Array<UByte>, initialCounter: Array<UByte>? = null): Array<UByte> {
|
fun decrypt(aesKey: AesKey, data: UByteArray, initialCounter: UByteArray? = null): UByteArray {
|
||||||
val aesCtr = AesCtr(aesKey, Mode.DECRYPT, initialCounter)
|
val aesCtr = AesCtrPure(aesKey, Mode.DECRYPT, initialCounter)
|
||||||
aesCtr.addData(data)
|
aesCtr.addData(data)
|
||||||
return aesCtr.decrypt()
|
return aesCtr.decrypt()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentOutput: Array<UByte> = arrayOf()
|
var currentOutput: UByteArray = ubyteArrayOf()
|
||||||
var previousEncrypted: Array<UByte> = arrayOf()
|
var previousEncrypted: UByteArray = ubyteArrayOf()
|
||||||
val counterStart = if (initialCounter.isNullOrEmpty()) {
|
val counterStart = if (initialCounter.isNullOrEmpty()) {
|
||||||
SRNG.getRandomBytes(16)
|
SRNG.getRandomBytes(16)
|
||||||
} else {
|
} else {
|
||||||
initialCounter
|
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
|
var bufferCounter = 0
|
||||||
|
|
||||||
fun addData(data: Array<UByte>) {
|
fun addData(data: UByteArray) {
|
||||||
//Padding
|
//Padding
|
||||||
when {
|
when {
|
||||||
bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter)
|
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)
|
val chunked = data.chunked(BLOCK_BYTES)
|
||||||
chunked.forEach { chunk ->
|
chunked.forEach { chunk ->
|
||||||
if (bufferCounter + chunk.size < BLOCK_BYTES) {
|
if (bufferCounter + chunk.size < BLOCK_BYTES) {
|
||||||
appendToBuffer(chunk, bufferCounter)
|
appendToBuffer(chunk.toUByteArray(), bufferCounter)
|
||||||
} else {
|
} else {
|
||||||
chunk.copyInto(
|
chunk.toUByteArray().copyInto(
|
||||||
destination = buffer,
|
destination = buffer,
|
||||||
destinationOffset = bufferCounter,
|
destinationOffset = bufferCounter,
|
||||||
startIndex = 0,
|
startIndex = 0,
|
||||||
@ -108,7 +107,7 @@ class AesCtr internal constructor(val aesKey: AesKey, val mode: Mode, initialCou
|
|||||||
)
|
)
|
||||||
output += consumeBlock(buffer, blockCounter)
|
output += consumeBlock(buffer, blockCounter)
|
||||||
blockCounter += 1
|
blockCounter += 1
|
||||||
buffer = Array<UByte>(BLOCK_BYTES) {
|
buffer = UByteArray(BLOCK_BYTES) {
|
||||||
when (it) {
|
when (it) {
|
||||||
in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> {
|
in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> {
|
||||||
chunk[it + (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)
|
output += consumeBlock(buffer, blockCounter)
|
||||||
}
|
}
|
||||||
return EncryptedDataAndInitialCounter(
|
return EncryptedDataAndInitialCounter(
|
||||||
output.reversed().foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
|
output.reversed().foldRight(UByteArray(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
|
||||||
counterStart
|
counterStart
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -144,41 +143,41 @@ class AesCtr internal constructor(val aesKey: AesKey, val mode: Mode, initialCou
|
|||||||
* Decrypt data
|
* Decrypt data
|
||||||
* @return Decrypted data
|
* @return Decrypted data
|
||||||
*/
|
*/
|
||||||
fun decrypt(): Array<UByte> {
|
fun decrypt(): UByteArray {
|
||||||
if (bufferCounter > 0) {
|
if (bufferCounter > 0) {
|
||||||
output += consumeBlock(buffer, blockCounter)
|
output += consumeBlock(buffer, blockCounter)
|
||||||
}
|
}
|
||||||
//JS compiler freaks out here if we don't supply exact type
|
//JS compiler freaks out here if we don't supply exact type
|
||||||
val reversed: List<Array<UByte>> = output.reversed() as List<Array<UByte>>
|
val reversed: List<UByteArray> = output.reversed() as List<UByteArray>
|
||||||
val folded: Array<UByte> = reversed.foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc ->
|
val folded: UByteArray = reversed.foldRight(UByteArray(0) { 0U }) { arrayOfUBytes, acc ->
|
||||||
acc + arrayOfUBytes
|
acc + arrayOfUBytes
|
||||||
}
|
}
|
||||||
return folded
|
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)
|
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||||
bufferCounter += array.size
|
bufferCounter += array.size
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun consumeBlock(data: Array<UByte>, blockCount: ModularBigInteger): Array<UByte> {
|
private fun consumeBlock(data: UByteArray, blockCount: ModularBigInteger): UByteArray {
|
||||||
val blockCountAsByteArray = blockCount.toUByteArray(Endianness.BIG).expandCounterTo16Bytes()
|
val blockCountAsByteArray = blockCount.toUByteArray(Endianness.BIG).toUByteArray().expandCounterTo16Bytes()
|
||||||
return when (mode) {
|
return when (mode) {
|
||||||
Mode.ENCRYPT -> {
|
Mode.ENCRYPT -> {
|
||||||
Aes.encrypt(aesKey, blockCountAsByteArray) xor data
|
AesPure.encrypt(aesKey, blockCountAsByteArray) xor data
|
||||||
}
|
}
|
||||||
Mode.DECRYPT -> {
|
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) {
|
return if (this.size < 16) {
|
||||||
println("Expanding")
|
println("Expanding")
|
||||||
val diff = 16 - this.size
|
val diff = 16 - this.size
|
||||||
val pad = Array<UByte>(diff) { 0U }
|
val pad = UByteArray(diff) { 0U }
|
||||||
pad + this
|
pad + this
|
||||||
} else {
|
} else {
|
||||||
this
|
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 {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (this === other) return true
|
||||||
if (other == null || this::class != other::class) return false
|
if (other == null || this::class != other::class) return false
|
@ -1,10 +1,12 @@
|
|||||||
package com.ionspin.kotlin.crypto.symmetric
|
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
|
* 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 {
|
companion object {
|
||||||
private val debug = false
|
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)
|
val rcon: UByteArray = ubyteArrayOf(0x8DU, 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, 0x1BU, 0x36U)
|
||||||
|
|
||||||
fun encrypt(aesKey: AesKey, input: Array<UByte>): Array<UByte> {
|
fun encrypt(aesKey: AesKey, input: UByteArray): UByteArray {
|
||||||
return Aes(aesKey, input).encrypt()
|
return AesPure(aesKey, input).encrypt()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decrypt(aesKey: AesKey, input: Array<UByte>): Array<UByte> {
|
fun decrypt(aesKey: AesKey, input: UByteArray): UByteArray {
|
||||||
return Aes(aesKey, input).decrypt()
|
return AesPure(aesKey, input).decrypt()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val state: Array<Array<UByte>> = (0 until 4).map { outerCounter ->
|
val state: Array<UByteArray> = (0 until 4).map { outerCounter ->
|
||||||
Array<UByte>(4) { innerCounter -> input[innerCounter * 4 + outerCounter] }
|
UByteArray(4) { innerCounter -> input[innerCounter * 4 + outerCounter] }
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
|
|
||||||
val numberOfRounds = when (aesKey) {
|
val numberOfRounds = when (aesKey) {
|
||||||
@ -74,7 +76,7 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
|||||||
is AesKey.Aes256Key -> 14
|
is AesKey.Aes256Key -> 14
|
||||||
}
|
}
|
||||||
|
|
||||||
val expandedKey: Array<Array<UByte>> = expandKey()
|
val expandedKey: Array<UByteArray> = expandKey()
|
||||||
|
|
||||||
|
|
||||||
var round = 0
|
var round = 0
|
||||||
@ -110,22 +112,22 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun shiftRows() {
|
fun shiftRows() {
|
||||||
state[0] = arrayOf(state[0][0], state[0][1], state[0][2], state[0][3])
|
state[0] = ubyteArrayOf(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[1] = ubyteArrayOf(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[2] = ubyteArrayOf(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[3] = ubyteArrayOf(state[3][3], state[3][0], state[3][1], state[3][2])
|
||||||
}
|
}
|
||||||
|
|
||||||
fun inversShiftRows() {
|
fun inversShiftRows() {
|
||||||
state[0] = arrayOf(state[0][0], state[0][1], state[0][2], state[0][3])
|
state[0] = ubyteArrayOf(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[1] = ubyteArrayOf(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[2] = ubyteArrayOf(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[3] = ubyteArrayOf(state[3][1], state[3][2], state[3][3], state[3][0])
|
||||||
}
|
}
|
||||||
|
|
||||||
fun mixColumns() {
|
fun mixColumns() {
|
||||||
val stateMixed: Array<Array<UByte>> = (0 until 4).map {
|
val stateMixed: Array<UByteArray> = (0 until 4).map {
|
||||||
Array<UByte>(4) { 0U }
|
UByteArray(4) { 0U }
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
for (c in 0..3) {
|
for (c in 0..3) {
|
||||||
|
|
||||||
@ -138,8 +140,8 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun inverseMixColumns() {
|
fun inverseMixColumns() {
|
||||||
val stateMixed: Array<Array<UByte>> = (0 until 4).map {
|
val stateMixed: Array<UByteArray> = (0 until 4).map {
|
||||||
Array<UByte>(4) { 0U }
|
UByteArray(4) { 0U }
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
for (c in 0..3) {
|
for (c in 0..3) {
|
||||||
stateMixed[0][c] =
|
stateMixed[0][c] =
|
||||||
@ -203,9 +205,9 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
|||||||
return galoisFieldMultiply(this.toUByte(), second)
|
return galoisFieldMultiply(this.toUByte(), second)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun expandKey(): Array<Array<UByte>> {
|
fun expandKey(): Array<UByteArray> {
|
||||||
val expandedKey = (0 until 4 * (numberOfRounds + 1)).map {
|
val expandedKey = (0 until 4 * (numberOfRounds + 1)).map {
|
||||||
Array<UByte>(4) { 0U }
|
UByteArray(4) { 0U }
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
// First round
|
// First round
|
||||||
for (i in 0 until aesKey.numberOf32BitWords) {
|
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 ->
|
expandedKey[i] = expandedKey[i - aesKey.numberOf32BitWords].mapIndexed { index, it ->
|
||||||
it xor temp[index]
|
it xor temp[index]
|
||||||
}.toTypedArray()
|
}.toUByteArray()
|
||||||
clearArray(temp)
|
clearArray(temp)
|
||||||
}
|
}
|
||||||
return expandedKey
|
return expandedKey
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encrypt(): Array<UByte> {
|
fun encrypt(): UByteArray {
|
||||||
if (completed) {
|
if (completed) {
|
||||||
throw RuntimeException("Encrypt can only be called once per Aes instance, since the state is cleared at the " +
|
throw RuntimeException("Encrypt can only be called once per Aes instance, since the state is cleared at the " +
|
||||||
"end of the operation")
|
"end of the operation")
|
||||||
@ -273,8 +275,8 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
|||||||
addRoundKey()
|
addRoundKey()
|
||||||
printState()
|
printState()
|
||||||
val transposedMatrix = (0 until 4).map { outerCounter ->
|
val transposedMatrix = (0 until 4).map { outerCounter ->
|
||||||
Array<UByte>(4) { 0U }
|
UByteArray(4) { 0U }
|
||||||
}.toTypedArray()
|
}
|
||||||
for (i in 0 until 4) {
|
for (i in 0 until 4) {
|
||||||
for (j in 0 until 4) {
|
for (j in 0 until 4) {
|
||||||
transposedMatrix[i][j] = state[j][i]
|
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) }
|
state.forEach { clearArray(it) }
|
||||||
completed = true
|
completed = true
|
||||||
return transposedMatrix.flatten().toTypedArray()
|
return transposedMatrix.flattenToUByteArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decrypt(): Array<UByte> {
|
fun decrypt(): UByteArray {
|
||||||
if (completed) {
|
if (completed) {
|
||||||
throw RuntimeException("Decrypt can only be called once per Aes instance, since the state is cleared at the " +
|
throw RuntimeException("Decrypt can only be called once per Aes instance, since the state is cleared at the " +
|
||||||
"end of the operation")
|
"end of the operation")
|
||||||
@ -313,20 +315,19 @@ internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UBy
|
|||||||
printState()
|
printState()
|
||||||
|
|
||||||
val transposedMatrix = (0 until 4).map { outerCounter ->
|
val transposedMatrix = (0 until 4).map { outerCounter ->
|
||||||
Array<UByte>(4) { 0U }
|
UByteArray(4) { 0U }
|
||||||
}.toTypedArray()
|
}
|
||||||
for (i in 0 until 4) {
|
for (i in 0 until 4) {
|
||||||
for (j in 0 until 4) {
|
for (j in 0 until 4) {
|
||||||
transposedMatrix[i][j] = state[j][i]
|
transposedMatrix[i][j] = state[j][i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printState(transposedMatrix)
|
|
||||||
state.forEach { clearArray(it) }
|
state.forEach { clearArray(it) }
|
||||||
completed = true
|
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 }
|
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) {
|
if (!debug) {
|
||||||
return
|
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) {
|
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
|
val numberOf32BitWords = keyLength / 32
|
||||||
|
|
||||||
class Aes128Key(key: String) : AesKey(key, 128)
|
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")
|
@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.native.tasks.KotlinNativeTest
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
|
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
|
||||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin(PluginsDeps.multiplatform)
|
kotlin(PluginsDeps.multiplatform)
|
||||||
id (PluginsDeps.mavenPublish)
|
id(PluginsDeps.mavenPublish)
|
||||||
id (PluginsDeps.signing)
|
id(PluginsDeps.signing)
|
||||||
id (PluginsDeps.node) version Versions.nodePlugin
|
id(PluginsDeps.node) version Versions.nodePlugin
|
||||||
id (PluginsDeps.dokka) version Versions.dokkaPlugin
|
id(PluginsDeps.dokka) version Versions.dokkaPlugin
|
||||||
}
|
}
|
||||||
|
|
||||||
val sonatypeStaging = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
|
val sonatypeStaging = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||||
val sonatypeSnapshots = "https://oss.sonatype.org/content/repositories/snapshots/"
|
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 sonatypePasswordEnv: String? = System.getenv()["SONATYPE_PASSWORD"]
|
||||||
val sonatypeUsernameEnv : String? = System.getenv()["SONATYPE_USERNAME"]
|
val sonatypeUsernameEnv: String? = System.getenv()["SONATYPE_USERNAME"]
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
jcenter()
|
jcenter()
|
||||||
|
|
||||||
}
|
}
|
||||||
group = "com.ionspin.kotlin"
|
group = ReleaseInfo.group
|
||||||
version = "0.0.4-SNAPSHOT"
|
version = ReleaseInfo.version
|
||||||
|
|
||||||
val ideaActive = System.getProperty("idea.active") == "true"
|
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 {
|
kotlin {
|
||||||
val hostOsName = getHostOsName()
|
val hostOsName = getHostOsName()
|
||||||
if (ideaActive) {
|
runningOnLinuxx86_64 {
|
||||||
when(hostOsName) {
|
|
||||||
"linux" -> linuxX64("native")
|
|
||||||
"macos" -> macosX64("native")
|
|
||||||
"windows" -> mingwX64("native")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hostOsName == "linux") {
|
|
||||||
jvm()
|
jvm()
|
||||||
js {
|
js {
|
||||||
compilations {
|
|
||||||
this.forEach {
|
|
||||||
it.compileKotlinTask.kotlinOptions.sourceMap = true
|
|
||||||
it.compileKotlinTask.kotlinOptions.moduleKind = "commonjs"
|
|
||||||
it.compileKotlinTask.kotlinOptions.metaInfo = true
|
|
||||||
|
|
||||||
if (it.name == "main") {
|
browser {
|
||||||
it.compileKotlinTask.kotlinOptions.main = "call"
|
|
||||||
}
|
testTask {
|
||||||
println("Compilation name ${it.name} set")
|
// isRunningInTravis {
|
||||||
println("Destination dir ${it.compileKotlinTask.destinationDir}")
|
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 {
|
nodejs {
|
||||||
testTask {
|
testTask {
|
||||||
useMocha() {
|
useMocha() {
|
||||||
@ -105,39 +77,41 @@ kotlin {
|
|||||||
linuxX64("linux") {
|
linuxX64("linux") {
|
||||||
binaries {
|
binaries {
|
||||||
staticLib {
|
staticLib {
|
||||||
|
optimized = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
linuxArm64() {
|
||||||
|
binaries {
|
||||||
|
staticLib {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Not supported in coroutines at the moment
|
|
||||||
// linuxArm32Hfp() {
|
linuxArm32Hfp() {
|
||||||
// binaries {
|
binaries {
|
||||||
// staticLib {
|
staticLib {
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//Not supported in coroutines at the moment
|
|
||||||
// linuxArm64() {
|
|
||||||
// binaries {
|
|
||||||
// staticLib {
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hostOsName == "macos") {
|
runningOnMacos {
|
||||||
iosX64("ios") {
|
iosX64("ios") {
|
||||||
binaries {
|
binaries {
|
||||||
framework {
|
framework {
|
||||||
|
optimized = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iosArm64("ios64Arm") {
|
iosArm64("ios64Arm") {
|
||||||
binaries {
|
binaries {
|
||||||
framework {
|
framework {
|
||||||
|
optimized = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,24 +119,24 @@ kotlin {
|
|||||||
iosArm32("ios32Arm") {
|
iosArm32("ios32Arm") {
|
||||||
binaries {
|
binaries {
|
||||||
framework {
|
framework {
|
||||||
|
optimized = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
macosX64() {
|
macosX64() {
|
||||||
binaries {
|
binaries {
|
||||||
framework {
|
framework {
|
||||||
|
optimized = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hostOsName == "windows") {
|
runningOnWindows {
|
||||||
|
|
||||||
mingwX64() {
|
mingwX64() {
|
||||||
binaries {
|
binaries {
|
||||||
staticLib {
|
staticLib {
|
||||||
|
optimized = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,6 +160,7 @@ kotlin {
|
|||||||
implementation(kotlin(Deps.Common.test))
|
implementation(kotlin(Deps.Common.test))
|
||||||
implementation(Deps.Common.coroutines)
|
implementation(Deps.Common.coroutines)
|
||||||
implementation(Deps.Common.kotlinBigNum)
|
implementation(Deps.Common.kotlinBigNum)
|
||||||
|
implementation(project(Deps.Common.apiProject))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val commonTest by getting {
|
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 {
|
val nativeMain by creating {
|
||||||
dependsOn(commonMain)
|
dependsOn(commonMain)
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(Deps.Native.coroutines)
|
implementation(Deps.Native.coroutines)
|
||||||
}
|
}
|
||||||
}
|
isRunningInIdea {
|
||||||
nativeMain
|
kotlin.setSrcDirs(emptySet<String>())
|
||||||
}
|
|
||||||
val nativeTest = if (ideaActive) {
|
|
||||||
val nativeTest by getting {
|
|
||||||
dependsOn(commonTest)
|
|
||||||
dependencies {
|
|
||||||
implementation(Deps.Native.coroutines)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nativeTest
|
|
||||||
} else {
|
|
||||||
val nativeTest by creating {
|
val nativeTest by creating {
|
||||||
dependsOn(commonTest)
|
dependsOn(commonTest)
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(Deps.Native.coroutines)
|
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 {
|
val jvmMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin(Deps.Jvm.stdLib))
|
implementation(kotlin(Deps.Jvm.stdLib))
|
||||||
@ -250,41 +232,50 @@ kotlin {
|
|||||||
val jsMain by getting {
|
val jsMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin(Deps.Js.stdLib))
|
implementation(kotlin(Deps.Js.stdLib))
|
||||||
implementation(kotlin(Deps.Js.test))
|
|
||||||
implementation(Deps.Js.coroutines)
|
implementation(Deps.Js.coroutines)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val jsTest by getting {
|
val jsTest by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("test-js"))
|
implementation(Deps.Js.coroutines)
|
||||||
|
implementation(kotlin(Deps.Js.test))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val linuxMain by getting {
|
val linuxMain by getting {
|
||||||
dependsOn(nativeMain)
|
dependsOn(nativeMain)
|
||||||
|
//Force idea to consider native sourceset
|
||||||
|
if (ideaActive) {
|
||||||
|
kotlin.srcDir("src/nativeMain/kotlin")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val linuxTest by getting {
|
val linuxTest by getting {
|
||||||
dependsOn(nativeTest)
|
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 {
|
val linuxArm64Main by getting {
|
||||||
// dependsOn(nativeMain)
|
dependsOn(nativeMain)
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// val linuxArm64Test by getting {
|
val linuxArm64Test by getting {
|
||||||
// dependsOn(nativeTest)
|
dependsOn(nativeTest)
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
val linuxArm32HfpMain by getting {
|
||||||
|
dependsOn(nativeMain)
|
||||||
|
}
|
||||||
|
|
||||||
|
val linuxArm32HfpTest by getting {
|
||||||
|
dependsOn(nativeTest)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hostOsName == "macos") {
|
|
||||||
|
runningOnMacos{
|
||||||
|
|
||||||
val iosMain by getting {
|
val iosMain by getting {
|
||||||
dependsOn(nativeMain)
|
dependsOn(nativeMain)
|
||||||
@ -308,10 +299,16 @@ kotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val macosX64Main by getting {
|
val macosX64Main by getting {
|
||||||
dependsOn(nativeMain)
|
dependsOn(commonMain)
|
||||||
|
if (ideaActive) {
|
||||||
|
kotlin.srcDir("src/nativeMain/kotlin")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val macosX64Test by getting {
|
val macosX64Test by getting {
|
||||||
dependsOn(nativeTest)
|
dependsOn(commonTest)
|
||||||
|
if (ideaActive) {
|
||||||
|
kotlin.srcDir("src/nativeTest/kotlin")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,7 +324,7 @@ kotlin {
|
|||||||
// dependsOn(commonTest)
|
// dependsOn(commonTest)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
if (hostOsName == "windows") {
|
runningOnWindows {
|
||||||
val mingwX64Main by getting {
|
val mingwX64Main by getting {
|
||||||
dependsOn(commonMain)
|
dependsOn(commonMain)
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -343,6 +340,8 @@ kotlin {
|
|||||||
|
|
||||||
all {
|
all {
|
||||||
languageSettings.enableLanguageFeature("InlineClasses")
|
languageSettings.enableLanguageFeature("InlineClasses")
|
||||||
|
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
|
||||||
|
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalStdlibApi")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,9 +353,9 @@ kotlin {
|
|||||||
task<Copy>("copyPackageJson") {
|
task<Copy>("copyPackageJson") {
|
||||||
dependsOn("compileKotlinJs")
|
dependsOn("compileKotlinJs")
|
||||||
println("Copying package.json from $projectDir/core/src/jsMain/npm")
|
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}")
|
println("Node modules dir ${node.nodeModulesDir}")
|
||||||
into ("${node.nodeModulesDir}")
|
into("${node.nodeModulesDir}")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
@ -369,22 +368,19 @@ tasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dokka {
|
dokka {
|
||||||
println ("Dokka !")
|
println("Dokka !")
|
||||||
impliedPlatforms = mutableListOf("Common")
|
impliedPlatforms = mutableListOf("Common")
|
||||||
kotlinTasks {
|
kotlinTasks {
|
||||||
listOf()
|
listOf()
|
||||||
}
|
}
|
||||||
sourceRoot {
|
sourceRoot {
|
||||||
println ("Common !")
|
println("Common !")
|
||||||
path = "/home/ionspin/Projects/Future/kotlin-multiplatform-crypto/crypto/src/commonMain" //TODO remove static path!
|
path =
|
||||||
|
"/home/ionspin/Projects/Future/kotlin-multiplatform-crypto/crypto/src/commonMain" //TODO remove static path!
|
||||||
platforms = listOf("Common")
|
platforms = listOf("Common")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (getHostOsName() == "linux") {
|
if (getHostOsName() == "linux" && getHostArchitecture() == "x86-64") {
|
||||||
|
|
||||||
val npmInstall by getting
|
|
||||||
val compileKotlinJs by getting(AbstractCompile::class)
|
|
||||||
val compileTestKotlinJs by getting(Kotlin2JsCompile::class)
|
|
||||||
|
|
||||||
val jvmTest by getting(Test::class) {
|
val jvmTest by getting(Test::class) {
|
||||||
testLogging {
|
testLogging {
|
||||||
@ -396,25 +392,24 @@ tasks {
|
|||||||
|
|
||||||
testLogging {
|
testLogging {
|
||||||
events("PASSED", "FAILED", "SKIPPED")
|
events("PASSED", "FAILED", "SKIPPED")
|
||||||
// showStandardStreams = true
|
// showStandardStreams = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val jsIrNodeTest by getting(KotlinJsTest::class) {
|
val jsNodeTest by getting(KotlinJsTest::class) {
|
||||||
|
|
||||||
testLogging {
|
testLogging {
|
||||||
events("PASSED", "FAILED", "SKIPPED")
|
events("PASSED", "FAILED", "SKIPPED")
|
||||||
showStandardStreams = true
|
showStandardStreams = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val legacyjsNodeTest by getting(KotlinJsTest::class) {
|
// val legacyjsNodeTest by getting(KotlinJsTest::class) {
|
||||||
|
//
|
||||||
testLogging {
|
// testLogging {
|
||||||
events("PASSED", "FAILED", "SKIPPED")
|
// events("PASSED", "FAILED", "SKIPPED")
|
||||||
showStandardStreams = true
|
// showStandardStreams = true
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// val jsIrBrowserTest by getting(KotlinJsTest::class) {
|
// val jsIrBrowserTest by getting(KotlinJsTest::class) {
|
||||||
// testLogging {
|
// 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
|
* on 21-Sep-2019
|
||||||
*/
|
*/
|
||||||
expect object SRNG {
|
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.BigInteger
|
||||||
import com.ionspin.kotlin.bignum.integer.toBigInteger
|
import com.ionspin.kotlin.bignum.integer.toBigInteger
|
||||||
import com.ionspin.kotlin.crypto.*
|
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
|
import com.ionspin.kotlin.crypto.util.rotateRight
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,11 +27,15 @@ import com.ionspin.kotlin.crypto.util.rotateRight
|
|||||||
* on 14-Jul-2019
|
* 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 BITS_IN_WORD = 64
|
||||||
const val ROUNDS_IN_COMPRESS = 12
|
const val ROUNDS_IN_COMPRESS = 12
|
||||||
const val BLOCK_BYTES = 128
|
const val BLOCK_BYTES = 128
|
||||||
@ -103,7 +104,7 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
|||||||
|
|
||||||
fun compress(
|
fun compress(
|
||||||
h: Array<ULong>,
|
h: Array<ULong>,
|
||||||
input: Array<UByte>,
|
input: UByteArray,
|
||||||
offsetCounter: BigInteger,
|
offsetCounter: BigInteger,
|
||||||
finalBlock: Boolean
|
finalBlock: Boolean
|
||||||
): Array<ULong> {
|
): 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()
|
v[13] = v[13] xor (offsetCounter shr BITS_IN_WORD).ulongValue()
|
||||||
|
|
||||||
if (finalBlock) {
|
if (finalBlock) {
|
||||||
// v[14] = v[14] xor 0xFFFFFFFFFFFFFFFFUL
|
|
||||||
v[14] = v[14].inv()
|
v[14] = v[14].inv()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,25 +144,25 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
|||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExperimentalStdlibApi
|
|
||||||
override fun digest(inputString: String, key: String?, hashLength: Int): Array<UByte> {
|
fun digest(inputString: String, key: String?, hashLength: Int): UByteArray {
|
||||||
val array = inputString.encodeToByteArray().map { it.toUByte() }.toList().toTypedArray()
|
val array = inputString.encodeToByteArray().toUByteArray()
|
||||||
val keyBytes = key?.run {
|
val keyBytes = key?.run {
|
||||||
encodeToByteArray().map { it.toUByte() }.toTypedArray()
|
encodeToByteArray().toUByteArray()
|
||||||
} ?: emptyArray()
|
} ?: ubyteArrayOf()
|
||||||
return digest(inputMessage = array, key = keyBytes, hashLength = hashLength)
|
return digest(inputMessage = array, key = keyBytes, hashLength = hashLength)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun digest(
|
override fun digest(
|
||||||
inputMessage: Array<UByte>,
|
inputMessage: UByteArray,
|
||||||
key: Array<UByte>,
|
key: UByteArray,
|
||||||
hashLength: Int
|
hashLength: Int
|
||||||
): Array<UByte> {
|
): UByteArray {
|
||||||
if (hashLength > MAX_HASH_BYTES) {
|
if (hashLength > MAX_HASH_BYTES) {
|
||||||
throw RuntimeException("Invalid hash length. Requested length more than maximum length. Requested length $hashLength")
|
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()
|
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()) {
|
val message = if (key.isEmpty()) {
|
||||||
if (chunkedMessage.isEmpty()) {
|
if (chunkedMessage.isEmpty()) {
|
||||||
Array(1) {
|
Array(1) {
|
||||||
Array<UByte>(128) {
|
UByteArray(128) {
|
||||||
0U
|
0U
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,7 +199,7 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
|||||||
val lastBlockPadded = if (message.isNotEmpty()) {
|
val lastBlockPadded = if (message.isNotEmpty()) {
|
||||||
padToBlock(message[message.size - 1])
|
padToBlock(message[message.size - 1])
|
||||||
} else {
|
} else {
|
||||||
Array<UByte>(16) { 0U }
|
UByteArray(16) { 0U }
|
||||||
}
|
}
|
||||||
|
|
||||||
compress(h, lastBlockPadded, lastSize.toBigInteger(), true).copyInto(h)
|
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)
|
return formatResult(h).copyOfRange(0, hashLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun formatResult(h: Array<ULong>): Array<UByte> {
|
private fun formatResult(h: Array<ULong>): UByteArray {
|
||||||
return h.map {
|
return h.map {
|
||||||
arrayOf(
|
arrayOf(
|
||||||
(it and 0xFFUL).toUByte(),
|
(it and 0xFFUL).toUByte(),
|
||||||
@ -222,10 +222,10 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
|||||||
)
|
)
|
||||||
}.flatMap {
|
}.flatMap {
|
||||||
it.toList()
|
it.toList()
|
||||||
}.toTypedArray()
|
}.toUByteArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun padToBlock(unpadded: Array<UByte>): Array<UByte> {
|
private fun padToBlock(unpadded: UByteArray): UByteArray {
|
||||||
if (unpadded.size == BLOCK_BYTES) {
|
if (unpadded.size == BLOCK_BYTES) {
|
||||||
return unpadded
|
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")
|
throw IllegalStateException("Block larger than 128 bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array(BLOCK_BYTES) {
|
return UByteArray(BLOCK_BYTES) {
|
||||||
when (it) {
|
when (it) {
|
||||||
in 0 until unpadded.size -> unpadded[it]
|
in 0 until unpadded.size -> unpadded[it]
|
||||||
else -> 0U
|
else -> 0U
|
||||||
@ -243,21 +243,21 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ExperimentalStdlibApi
|
|
||||||
constructor(
|
constructor(
|
||||||
key: String?,
|
key: String?,
|
||||||
requestedHashLenght: Int = 64
|
requestedHashLenght: Int = 64
|
||||||
) : this(
|
) : this(
|
||||||
(key?.encodeToByteArray()?.map { it.toUByte() }?.toTypedArray() ?: emptyArray<UByte>()),
|
(key?.encodeToByteArray()?.toUByteArray() ?: ubyteArrayOf()),
|
||||||
requestedHashLenght
|
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 h = iv.copyOf()
|
||||||
var counter = BigInteger.ZERO
|
var counter = BigInteger.ZERO
|
||||||
var bufferCounter = 0
|
var bufferCounter = 0
|
||||||
var buffer = Array<UByte>(BLOCK_BYTES) { 0U }
|
var buffer = UByteArray(BLOCK_BYTES) { 0U }
|
||||||
|
|
||||||
|
|
||||||
init {
|
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()) {
|
if (data.isEmpty()) {
|
||||||
throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
|
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 {
|
when {
|
||||||
bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter)
|
bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter)
|
||||||
bufferCounter + data.size >= BLOCK_BYTES -> {
|
bufferCounter + data.size >= BLOCK_BYTES -> {
|
||||||
val chunked = data.chunked(BLOCK_BYTES)
|
val chunked = data.chunked(BLOCK_BYTES).map { it.toUByteArray() }
|
||||||
chunked.forEach { chunk ->
|
chunked.forEach { chunk ->
|
||||||
if (bufferCounter + chunk.size < BLOCK_BYTES) {
|
if (bufferCounter + chunk.size < BLOCK_BYTES) {
|
||||||
appendToBuffer(chunk, bufferCounter)
|
appendToBuffer(chunk, bufferCounter)
|
||||||
@ -289,7 +289,7 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatab
|
|||||||
)
|
)
|
||||||
counter += BLOCK_BYTES
|
counter += BLOCK_BYTES
|
||||||
consumeBlock(buffer)
|
consumeBlock(buffer)
|
||||||
buffer = Array<UByte>(BLOCK_BYTES) {
|
buffer = UByteArray(BLOCK_BYTES) {
|
||||||
when (it) {
|
when (it) {
|
||||||
in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> {
|
in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> {
|
||||||
chunk[it + (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) {
|
fun update(data: String) {
|
||||||
update(data.encodeToByteArray().map { it.toUByte() }.toTypedArray())
|
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)
|
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||||
bufferCounter += array.size
|
bufferCounter += array.size
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun consumeBlock(block: Array<UByte>) {
|
private fun consumeBlock(block: UByteArray) {
|
||||||
h = compress(h, block, counter, false)
|
h = compress(h, block, counter, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun digest(): Array<UByte> {
|
override fun digest(): UByteArray {
|
||||||
val lastBlockPadded = padToBlock(buffer)
|
val lastBlockPadded = padToBlock(buffer)
|
||||||
counter += bufferCounter
|
counter += bufferCounter
|
||||||
compress(h, lastBlockPadded, counter, true)
|
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() {
|
private fun reset() {
|
||||||
h = iv.copyOf()
|
h = iv.copyOf()
|
||||||
counter = BigInteger.ZERO
|
counter = BigInteger.ZERO
|
||||||
bufferCounter = 0
|
bufferCounter = 0
|
||||||
buffer = Array<UByte>(BLOCK_BYTES) { 0U }
|
buffer = UByteArray(BLOCK_BYTES) { 0U }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -16,9 +16,6 @@
|
|||||||
|
|
||||||
package com.ionspin.kotlin.crypto.hash.sha
|
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
|
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
|
override val MAX_HASH_BYTES: Int = 32
|
||||||
|
|
||||||
|
|
||||||
companion object : StatelessHash {
|
companion object : StatelessSha256 {
|
||||||
const val BLOCK_SIZE = 512
|
const val BLOCK_SIZE = 512
|
||||||
const val BLOCK_SIZE_IN_BYTES = 64
|
const val BLOCK_SIZE_IN_BYTES = 64
|
||||||
const val UINT_MASK = 0xFFFFFFFFU
|
const val UINT_MASK = 0xFFFFFFFFU
|
||||||
@ -66,15 +63,8 @@ class Sha256 : UpdatableHash {
|
|||||||
0x748f82eeU, 0x78a5636fU, 0x84c87814U, 0x8cc70208U, 0x90befffaU, 0xa4506cebU, 0xbef9a3f7U, 0xc67178f2U
|
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()
|
var h = iv.copyOf()
|
||||||
|
|
||||||
@ -88,7 +78,7 @@ class Sha256 : UpdatableHash {
|
|||||||
.chunked(BLOCK_SIZE_IN_BYTES)
|
.chunked(BLOCK_SIZE_IN_BYTES)
|
||||||
|
|
||||||
chunks.forEach { chunk ->
|
chunks.forEach { chunk ->
|
||||||
val w = expandChunk(chunk)
|
val w = expandChunk(chunk.toUByteArray())
|
||||||
mix(h, w).copyInto(h)
|
mix(h, w).copyInto(h)
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -128,7 +118,7 @@ class Sha256 : UpdatableHash {
|
|||||||
return (((x and y) xor (x and z) xor (y and z)))
|
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) {
|
val w = Array<UInt>(BLOCK_SIZE_IN_BYTES) {
|
||||||
when (it) {
|
when (it) {
|
||||||
in 0 until 16 -> {
|
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
|
val originalMessageSizeInBits = originalSizeInBytes * 8
|
||||||
|
|
||||||
|
|
||||||
@ -198,7 +188,7 @@ class Sha256 : UpdatableHash {
|
|||||||
0 -> 0
|
0 -> 0
|
||||||
else -> (BLOCK_SIZE - expandedRemainderOf512) / 8
|
else -> (BLOCK_SIZE - expandedRemainderOf512) / 8
|
||||||
}
|
}
|
||||||
val expansionArray = Array<UByte>(zeroAddAmount + 1) {
|
val expansionArray = UByteArray(zeroAddAmount + 1) {
|
||||||
when (it) {
|
when (it) {
|
||||||
0 -> 0b10000000U
|
0 -> 0b10000000U
|
||||||
else -> 0U
|
else -> 0U
|
||||||
@ -207,9 +197,9 @@ class Sha256 : UpdatableHash {
|
|||||||
return expansionArray
|
return expansionArray
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ULong.toPaddedByteArray(): Array<UByte> {
|
private fun ULong.toPaddedByteArray(): UByteArray {
|
||||||
val byteMask = BYTE_MASK_FROM_ULONG
|
val byteMask = BYTE_MASK_FROM_ULONG
|
||||||
return Array(8) {
|
return UByteArray(8) {
|
||||||
when (it) {
|
when (it) {
|
||||||
7 -> (this and byteMask).toUByte()
|
7 -> (this and byteMask).toUByte()
|
||||||
6 -> ((this shr 8) 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
|
val byteMask = BYTE_MASK_FROM_UINT
|
||||||
return Array(4) {
|
return UByteArray(4) {
|
||||||
when (it) {
|
when (it) {
|
||||||
3 -> (this and byteMask).toUByte()
|
3 -> (this and byteMask).toUByte()
|
||||||
2 -> ((this shr 8) and byteMask).toUByte()
|
2 -> ((this shr 8) and byteMask).toUByte()
|
||||||
@ -242,14 +232,14 @@ class Sha256 : UpdatableHash {
|
|||||||
var h = iv.copyOf()
|
var h = iv.copyOf()
|
||||||
var counter = 0
|
var counter = 0
|
||||||
var bufferCounter = 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) {
|
fun update(data: String) {
|
||||||
return update(data.encodeToByteArray().map { it.toUByte() }.toTypedArray())
|
return update(data.encodeToByteArray().toUByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun update(data: Array<UByte>) {
|
override fun update(data: UByteArray) {
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
|
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)
|
val chunked = data.chunked(BLOCK_SIZE_IN_BYTES)
|
||||||
chunked.forEach { chunk ->
|
chunked.forEach { chunk ->
|
||||||
if (bufferCounter + chunk.size < BLOCK_SIZE_IN_BYTES) {
|
if (bufferCounter + chunk.size < BLOCK_SIZE_IN_BYTES) {
|
||||||
appendToBuffer(chunk, bufferCounter)
|
appendToBuffer(chunk.toUByteArray(), bufferCounter)
|
||||||
} else {
|
} else {
|
||||||
chunk.copyInto(
|
chunk.toUByteArray().copyInto(
|
||||||
destination = buffer,
|
destination = buffer,
|
||||||
destinationOffset = bufferCounter,
|
destinationOffset = bufferCounter,
|
||||||
startIndex = 0,
|
startIndex = 0,
|
||||||
@ -270,7 +260,7 @@ class Sha256 : UpdatableHash {
|
|||||||
)
|
)
|
||||||
counter += BLOCK_SIZE_IN_BYTES
|
counter += BLOCK_SIZE_IN_BYTES
|
||||||
consumeBlock(buffer)
|
consumeBlock(buffer)
|
||||||
buffer = Array<UByte>(BLOCK_SIZE_IN_BYTES) {
|
buffer = UByteArray(BLOCK_SIZE_IN_BYTES) {
|
||||||
when (it) {
|
when (it) {
|
||||||
in (0 until (chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter))) -> {
|
in (0 until (chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter))) -> {
|
||||||
chunk[it + (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)
|
val w = expandChunk(block)
|
||||||
mix(h, w).copyInto(h)
|
mix(h, w).copyInto(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun digest(): Array<UByte> {
|
override fun digest(): UByteArray {
|
||||||
val length = counter + bufferCounter
|
val length = counter + bufferCounter
|
||||||
val expansionArray = createExpansionArray(length)
|
val expansionArray = createExpansionArray(length)
|
||||||
val finalBlock =
|
val finalBlock =
|
||||||
buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPaddedByteArray()
|
buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPaddedByteArray()
|
||||||
finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach {
|
finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach {
|
||||||
consumeBlock(it)
|
consumeBlock(it.toUByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -315,11 +305,7 @@ class Sha256 : UpdatableHash {
|
|||||||
return digest
|
return digest
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun digestString(): String {
|
private fun appendToBuffer(array: UByteArray, start: Int) {
|
||||||
return digest().map { it.toString(16) }.joinToString(separator = "")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun appendToBuffer(array: Array<UByte>, start: Int) {
|
|
||||||
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||||
bufferCounter += array.size
|
bufferCounter += array.size
|
||||||
}
|
}
|
@ -16,9 +16,6 @@
|
|||||||
|
|
||||||
package com.ionspin.kotlin.crypto.hash.sha
|
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
|
import com.ionspin.kotlin.crypto.util.rotateRight
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,12 +24,12 @@ import com.ionspin.kotlin.crypto.util.rotateRight
|
|||||||
* on 18-Jul-2019
|
* on 18-Jul-2019
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ExperimentalUnsignedTypes
|
|
||||||
class Sha512 : UpdatableHash {
|
class Sha512Pure : Sha512 {
|
||||||
|
|
||||||
override val MAX_HASH_BYTES: Int = 32
|
override val MAX_HASH_BYTES: Int = 32
|
||||||
|
|
||||||
companion object : StatelessHash {
|
companion object : StatelessSha512 {
|
||||||
const val BLOCK_SIZE = 1024
|
const val BLOCK_SIZE = 1024
|
||||||
const val BLOCK_SIZE_IN_BYTES = 128
|
const val BLOCK_SIZE_IN_BYTES = 128
|
||||||
const val CHUNK_SIZE = 80
|
const val CHUNK_SIZE = 80
|
||||||
@ -134,16 +131,8 @@ class Sha512 : UpdatableHash {
|
|||||||
0x5be0cd19137e2179UL
|
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()
|
var h = iv.copyOf()
|
||||||
|
|
||||||
@ -155,7 +144,7 @@ class Sha512 : UpdatableHash {
|
|||||||
)
|
)
|
||||||
|
|
||||||
chunks.forEach { chunk ->
|
chunks.forEach { chunk ->
|
||||||
val w = expandChunk(chunk)
|
val w = expandChunk(chunk.toUByteArray())
|
||||||
mix(h, w)
|
mix(h, w)
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -196,7 +185,7 @@ class Sha512 : UpdatableHash {
|
|||||||
return ((x and y) xor (x and z) xor (y and z))
|
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) {
|
val w = Array<ULong>(CHUNK_SIZE) {
|
||||||
when (it) {
|
when (it) {
|
||||||
in 0 until 16 -> {
|
in 0 until 16 -> {
|
||||||
@ -259,7 +248,7 @@ class Sha512 : UpdatableHash {
|
|||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createExpansionArray(originalSizeInBytes: Int): Array<UByte> {
|
fun createExpansionArray(originalSizeInBytes: Int): UByteArray {
|
||||||
val originalMessageSizeInBits = originalSizeInBytes * 8
|
val originalMessageSizeInBits = originalSizeInBytes * 8
|
||||||
|
|
||||||
val expandedRemainderOf1024 = (originalMessageSizeInBits + 129) % BLOCK_SIZE
|
val expandedRemainderOf1024 = (originalMessageSizeInBits + 129) % BLOCK_SIZE
|
||||||
@ -267,7 +256,7 @@ class Sha512 : UpdatableHash {
|
|||||||
0 -> 0
|
0 -> 0
|
||||||
else -> (BLOCK_SIZE - expandedRemainderOf1024) / 8
|
else -> (BLOCK_SIZE - expandedRemainderOf1024) / 8
|
||||||
}
|
}
|
||||||
val expansionArray = Array<UByte>(zeroAddAmount + 1) {
|
val expansionArray = UByteArray(zeroAddAmount + 1) {
|
||||||
when (it) {
|
when (it) {
|
||||||
0 -> 0b10000000U
|
0 -> 0b10000000U
|
||||||
else -> 0U
|
else -> 0U
|
||||||
@ -277,10 +266,10 @@ class Sha512 : UpdatableHash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun ULong.toPaddedByteArray(): Array<UByte> {
|
private fun ULong.toPaddedByteArray(): UByteArray {
|
||||||
val byteMask = 0xFFUL
|
val byteMask = 0xFFUL
|
||||||
//Ignore messages longer than 64 bits for now
|
//Ignore messages longer than 64 bits for now
|
||||||
return Array(8) {
|
return UByteArray(8) {
|
||||||
when (it) {
|
when (it) {
|
||||||
7 -> (this and byteMask).toUByte()
|
7 -> (this and byteMask).toUByte()
|
||||||
6 -> ((this shr 8) 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
|
val byteMask = 0xFFUL
|
||||||
//Ignore messages longer than 64 bits for now
|
//Ignore messages longer than 64 bits for now
|
||||||
return Array(16) {
|
return UByteArray(16) {
|
||||||
when (it) {
|
when (it) {
|
||||||
15 -> (this and byteMask).toUByte()
|
15 -> (this and byteMask).toUByte()
|
||||||
14 -> ((this shr 8) and byteMask).toUByte()
|
14 -> ((this shr 8) and byteMask).toUByte()
|
||||||
@ -317,14 +306,14 @@ class Sha512 : UpdatableHash {
|
|||||||
var h = iv.copyOf()
|
var h = iv.copyOf()
|
||||||
var counter = 0
|
var counter = 0
|
||||||
var bufferCounter = 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) {
|
fun update(data: String) {
|
||||||
return update(data.encodeToByteArray().map { it.toUByte() }.toTypedArray())
|
return update(data.encodeToByteArray().toUByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun update(data: Array<UByte>) {
|
override fun update(data: UByteArray) {
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
|
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)
|
val chunked = data.chunked(BLOCK_SIZE_IN_BYTES)
|
||||||
chunked.forEach { chunk ->
|
chunked.forEach { chunk ->
|
||||||
if (bufferCounter + chunk.size < BLOCK_SIZE_IN_BYTES) {
|
if (bufferCounter + chunk.size < BLOCK_SIZE_IN_BYTES) {
|
||||||
appendToBuffer(chunk, bufferCounter)
|
appendToBuffer(chunk.toUByteArray(), bufferCounter)
|
||||||
} else {
|
} else {
|
||||||
chunk.copyInto(
|
chunk.toUByteArray().copyInto(
|
||||||
destination = buffer,
|
destination = buffer,
|
||||||
destinationOffset = bufferCounter,
|
destinationOffset = bufferCounter,
|
||||||
startIndex = 0,
|
startIndex = 0,
|
||||||
@ -345,7 +334,7 @@ class Sha512 : UpdatableHash {
|
|||||||
)
|
)
|
||||||
counter += BLOCK_SIZE_IN_BYTES
|
counter += BLOCK_SIZE_IN_BYTES
|
||||||
consumeBlock(buffer)
|
consumeBlock(buffer)
|
||||||
buffer = Array<UByte>(BLOCK_SIZE_IN_BYTES) {
|
buffer = UByteArray(BLOCK_SIZE_IN_BYTES) {
|
||||||
when (it) {
|
when (it) {
|
||||||
in (0 until (chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter))) -> {
|
in (0 until (chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter))) -> {
|
||||||
chunk[it + (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)
|
val w = expandChunk(block)
|
||||||
mix(h, w).copyInto(h)
|
mix(h, w).copyInto(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun digest(): Array<UByte> {
|
override fun digest(): UByteArray {
|
||||||
val length = counter + bufferCounter
|
val length = counter + bufferCounter
|
||||||
val expansionArray = createExpansionArray(length)
|
val expansionArray = createExpansionArray(length)
|
||||||
val finalBlock =
|
val finalBlock =
|
||||||
buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPadded128BitByteArray()
|
buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPadded128BitByteArray()
|
||||||
finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach {
|
finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach {
|
||||||
consumeBlock(it)
|
consumeBlock(it.toUByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -390,11 +379,7 @@ class Sha512 : UpdatableHash {
|
|||||||
return digest
|
return digest
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun digestString(): String {
|
private fun appendToBuffer(array: UByteArray, start: Int) {
|
||||||
return digest().map { it.toString(16) }.joinToString(separator = "")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun appendToBuffer(array: Array<UByte>, start: Int) {
|
|
||||||
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||||
bufferCounter += 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
|
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.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
|
* Created by Ugljesa Jovanovic
|
||||||
* ugljesa.jovanovic@ionspin.com
|
* ugljesa.jovanovic@ionspin.com
|
||||||
* on 16-May-2020
|
* on 24-May-2020
|
||||||
*/
|
*/
|
||||||
|
interface Argon2 : KeyDerivationFunction
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -23,8 +23,8 @@ package com.ionspin.kotlin.crypto.keyderivation.argon2
|
|||||||
*/
|
*/
|
||||||
class Argon2TagTooShort(tagLength: UInt) : RuntimeException("Too short tag (output) requested. Requested: $tagLength")
|
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 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 Argon2TimeTooShort(iterations: Int) : 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 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 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 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")
|
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
|
package com.ionspin.kotlin.crypto.keyderivation.argon2
|
||||||
|
|
||||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2b
|
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bPure
|
||||||
import com.ionspin.kotlin.crypto.util.*
|
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
|
* Created by Ugljesa Jovanovic
|
||||||
@ -27,28 +29,28 @@ import com.ionspin.kotlin.crypto.util.*
|
|||||||
* on 16-May-2020
|
* on 16-May-2020
|
||||||
*/
|
*/
|
||||||
object Argon2Utils {
|
object Argon2Utils {
|
||||||
|
const val BLOCK_SIZE = 1024
|
||||||
|
|
||||||
const val R1 = 32
|
const val R1 = 32
|
||||||
const val R2 = 24
|
const val R2 = 24
|
||||||
const val R3 = 16
|
const val R3 = 16
|
||||||
const val R4 = 63
|
const val R4 = 63
|
||||||
|
|
||||||
//based on Blake2b mixRound
|
//Based on Blake2b mix
|
||||||
private fun mixRound(input: Array<UByte>): Array<ULong> {
|
internal fun inplaceMixRound(v : ULongArray) : ULongArray{
|
||||||
var v = input.chunked(8).map { it.fromLittleEndianArrayToULong() }.toTypedArray()
|
mix(v, 0, 4, 8, 12)
|
||||||
v = mix(v, 0, 4, 8, 12)
|
mix(v, 1, 5, 9, 13)
|
||||||
v = mix(v, 1, 5, 9, 13)
|
mix(v, 2, 6, 10, 14)
|
||||||
v = mix(v, 2, 6, 10, 14)
|
mix(v, 3, 7, 11, 15)
|
||||||
v = mix(v, 3, 7, 11, 15)
|
mix(v, 0, 5, 10, 15)
|
||||||
v = mix(v, 0, 5, 10, 15)
|
mix(v, 1, 6, 11, 12)
|
||||||
v = mix(v, 1, 6, 11, 12)
|
mix(v, 2, 7, 8, 13)
|
||||||
v = mix(v, 2, 7, 8, 13)
|
mix(v, 3, 4, 9, 14)
|
||||||
v = mix(v, 3, 4, 9, 14)
|
return v //Just for chaining, array is mixed in place
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Based on Blake2b mix
|
//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[a] = (v[a] + v[b] + 2U * (v[a] and 0xFFFFFFFFUL) * (v[b] and 0xFFFFFFFFUL))
|
||||||
v[d] = (v[d] xor v[a]) rotateRight R1
|
v[d] = (v[d] xor v[a]) rotateRight R1
|
||||||
v[c] = (v[c] + v[d] + 2U * (v[c] and 0xFFFFFFFFUL) * (v[d] and 0xFFFFFFFFUL))
|
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[d] = (v[d] xor v[a]) rotateRight R3
|
||||||
v[c] = (v[c] + v[d] + 2U * (v[c] and 0xFFFFFFFFUL) * (v[d] and 0xFFFFFFFFUL))
|
v[c] = (v[c] + v[d] + 2U * (v[c] and 0xFFFFFFFFUL) * (v[d] and 0xFFFFFFFFUL))
|
||||||
v[b] = (v[b] xor v[c]) rotateRight R4
|
v[b] = (v[b] xor v[c]) rotateRight R4
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractColumnFromGBlock(gBlock: Array<UByte>, columnPosition: Int): Array<UByte> {
|
internal fun extractColumnFromGBlock(gBlock: UByteArray, columnPosition: Int): UByteArray {
|
||||||
val result = Array<UByte>(128) { 0U }
|
val result = UByteArray(128) { 0U }
|
||||||
for (i in 0..7) {
|
for (i in 0..7) {
|
||||||
gBlock.copyOfRange(i * 128 + (columnPosition * 16), i * 128 + (columnPosition * 16) + 16)
|
gBlock.copyOfRange(i * 128 + (columnPosition * 16), i * 128 + (columnPosition * 16) + 16)
|
||||||
.copyInto(result, i * 16)
|
.copyInto(result, i * 16)
|
||||||
@ -69,69 +70,50 @@ object Argon2Utils {
|
|||||||
return result
|
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(
|
internal fun compressionFunctionG(
|
||||||
previousBlock: Array<UByte>,
|
previousBlock: ArgonBlockPointer,
|
||||||
referenceBlock: Array<UByte>,
|
referenceBlock: ArgonBlockPointer,
|
||||||
currentBlock: Array<UByte>,
|
currentBlock: ArgonBlockPointer,
|
||||||
xorWithCurrentBlock: Boolean
|
xorWithCurrentBlock: Boolean
|
||||||
): Array<UByte> {
|
): ArgonBlockPointer {
|
||||||
val r = referenceBlock xor previousBlock
|
val r = (referenceBlock xorBlocksAndGetPointerToNewBlock previousBlock).getBlockPointer()
|
||||||
val q = Array<UByte>(1024) { 0U }
|
//Since we are doing inplace xors, we don't need the Q that exists in specification
|
||||||
val z = Array<UByte>(1024) { 0U }
|
val z = ArgonBlock().getBlockPointer()
|
||||||
// Do the argon/blake2b mixing on rows
|
// Do the argon/blake2b mixing on rows
|
||||||
for (i in 0..7) {
|
for (i in 0..7) {
|
||||||
val startOfRow = (i * 8 * 16)
|
z.setRowFromMixedULongs(i, inplaceMixRound(r.getRowOfULongsForMixing(i)))
|
||||||
val endOfRow = startOfRow + (8 * 16)
|
|
||||||
val rowToMix = r.copyOfRange(startOfRow, endOfRow)
|
|
||||||
mixRound(rowToMix)
|
|
||||||
.map { it.toLittleEndianUByteArray() }
|
|
||||||
.flatMap { it.asIterable() }
|
|
||||||
.toTypedArray()
|
|
||||||
.copyInto(q, startOfRow)
|
|
||||||
}
|
}
|
||||||
// Do the argon/blake2b mixing on columns
|
// Do the argon/blake2b mixing on columns
|
||||||
for (i in 0..7) {
|
for (i in 0..7) {
|
||||||
copyIntoGBlockColumn(
|
z.setColumnFromMixedULongs(i, inplaceMixRound(z.getColumnOfULongsForMixing(i)))
|
||||||
z,
|
|
||||||
i,
|
|
||||||
mixRound(extractColumnFromGBlock(q, i))
|
|
||||||
.map { it.toLittleEndianUByteArray() }
|
|
||||||
.flatMap { it.asIterable() }
|
|
||||||
.toTypedArray()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
val final = if (xorWithCurrentBlock) {
|
val final = if (xorWithCurrentBlock) {
|
||||||
(z xor r) xor currentBlock
|
(z xorInplaceWith r) xorInplaceWith currentBlock
|
||||||
} else {
|
} else {
|
||||||
z xor r
|
z xorInplaceWith r
|
||||||
}
|
}
|
||||||
return final
|
return final
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun argonBlake2bArbitraryLenghtHash(input: Array<UByte>, length: UInt): Array<UByte> {
|
internal fun argonBlake2bArbitraryLenghtHash(input: UByteArray, length: UInt): UByteArray {
|
||||||
if (length <= 64U) {
|
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
|
//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 numberOf64ByteBlocks = (1U + ((length - 1U) / 32U) - 2U).toInt() // equivalent to ceil(length/32) - 2
|
||||||
val v = Array<Array<UByte>>(numberOf64ByteBlocks) { emptyArray() }
|
val v = Array<UByteArray>(numberOf64ByteBlocks) { ubyteArrayOf() }
|
||||||
v[0] = Blake2b.digest(length + input)
|
v[0] = Blake2bPure.digest(length + input)
|
||||||
for (i in 1 until numberOf64ByteBlocks) {
|
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 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 =
|
val concat =
|
||||||
(v.map { it.copyOfRange(0, 32) })
|
(v.map { it.copyOfRange(0, 32) })
|
||||||
.plus(listOf(vLast))
|
.plus(listOf(vLast))
|
||||||
.foldRight(emptyArray<UByte>()) { arrayOfUBytes, acc -> arrayOfUBytes + acc }
|
.foldRight(ubyteArrayOf()) { arrayOfUBytes, acc -> arrayOfUBytes + acc }
|
||||||
|
|
||||||
return concat
|
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.
|
* tagLength, requested memory size and number of iterations, so no need to check for upper bound, just lower.
|
||||||
*/
|
*/
|
||||||
internal fun validateArgonParameters(
|
internal fun validateArgonParameters(
|
||||||
password: Array<UByte>,
|
password: UByteArray,
|
||||||
salt: Array<UByte>,
|
salt: UByteArray,
|
||||||
parallelism: Int ,
|
parallelism: Int ,
|
||||||
tagLength: UInt,
|
tagLength: UInt,
|
||||||
requestedMemorySize: UInt ,
|
requestedMemorySize: UInt ,
|
||||||
numberOfIterations: UInt ,
|
numberOfIterations: Int ,
|
||||||
key: Array<UByte>,
|
key: UByteArray,
|
||||||
associatedData: Array<UByte>,
|
associatedData: UByteArray,
|
||||||
argonType: ArgonType
|
argonType: ArgonType
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -170,9 +152,16 @@ object Argon2Utils {
|
|||||||
throw Argon2MemoryTooLitlle(requestedMemorySize)
|
throw Argon2MemoryTooLitlle(requestedMemorySize)
|
||||||
}
|
}
|
||||||
//Number of iterations
|
//Number of iterations
|
||||||
if (numberOfIterations <= 0U) {
|
if (numberOfIterations <= 0) {
|
||||||
throw Argon2TimeTooShort(numberOfIterations)
|
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