Remove pure and delegated, leave api just to trigger fetching konan deps in advance, remove most of it later
This commit is contained in:
parent
b0c6b43f2e
commit
85ae96165f
@ -54,7 +54,6 @@ macPublishToSnapshot:
|
||||
script:
|
||||
- ./macBuild.sh
|
||||
- ./macBuildAndPublishSnapshot-bindings.sh
|
||||
- ./macBuildAndPublishSnapshot-delegated.sh
|
||||
only:
|
||||
- master
|
||||
tags:
|
||||
@ -69,7 +68,6 @@ buildWindows:
|
||||
script:
|
||||
- $env:CHERE_INVOKING = 'yes'
|
||||
- C:\msys64\usr\bin\bash -lc "./windowsBuild-delegated.sh"
|
||||
- C:\msys64\usr\bin\bash -lc "./windowsBuild-pure.sh"
|
||||
tags:
|
||||
- windowsX64
|
||||
|
||||
@ -78,7 +76,6 @@ windowsPublishToSnapshot:
|
||||
script:
|
||||
- $env:CHERE_INVOKING = 'yes'
|
||||
- C:\msys64\usr\bin\bash -lc "./windowsBuildAndPublish-delegated.sh"
|
||||
- C:\msys64\usr\bin\bash -lc "./windowsBuildAndPublish-pure.sh"
|
||||
only:
|
||||
- master
|
||||
tags:
|
||||
|
197
.travis.yml
197
.travis.yml
@ -1,197 +0,0 @@
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
name: linux
|
||||
language: java
|
||||
jdk: openjdk12
|
||||
|
||||
env:
|
||||
KBUILD=linux
|
||||
JAVA_OPTS=-Xmx2g
|
||||
#skip ./gradlew assemble that is normally invoked in installation step
|
||||
install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get -y install automake
|
||||
script:
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./linuxBuild.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./linuxBuildAndPublish.sh; fi'
|
||||
# OSX macos
|
||||
- os: osx
|
||||
name: osx-mac
|
||||
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-mac.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-mac.sh; fi'
|
||||
# OSX ios
|
||||
- os: osx
|
||||
name: osx-ios
|
||||
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-ios.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-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 macos PURE
|
||||
- os: osx
|
||||
name: osx-mac-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-mac.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-pure-mac.sh; fi'
|
||||
# OSX ios PURE
|
||||
- os: osx
|
||||
name: osx-ios-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-ios.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-pure-ios.sh; fi'
|
||||
# OSX watchos PURE
|
||||
- os: osx
|
||||
name: osx-watchos-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-watchos.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-pure-watchos.sh; fi'
|
||||
# OSX tvos PURE
|
||||
- os: osx
|
||||
name: osx-tvos-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-tvos.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-pure-tvos.sh; fi'
|
||||
|
||||
- os: windows
|
||||
name: windows-pure
|
||||
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
|
||||
install: true
|
||||
script:
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then $shell ./windowsBuild-pure.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then ./windowsBuildAndPublish-pure.sh; fi'
|
||||
- os: windows
|
||||
name: windows-delegated
|
||||
language: shell
|
||||
jdk: openjdk12
|
||||
env:
|
||||
- GRAVIS="https://raw.githubusercontent.com/DanySK/Gravis-CI/master/"
|
||||
- JAVA_OPTS=-Xmx2g
|
||||
- JDK="adopt-openj9@1.11"
|
||||
before_install:
|
||||
- curl "${GRAVIS}.install-jdk-travis.sh" --output ~/.install-jdk-travis.sh
|
||||
- source ~/.install-jdk-travis.sh
|
||||
- |-
|
||||
case $TRAVIS_OS_NAME in
|
||||
windows)
|
||||
[[ ! -f C:/tools/msys64/msys2_shell.cmd ]] && rm -rf C:/tools/msys64
|
||||
choco uninstall -y mingw
|
||||
choco upgrade --no-progress -y msys2 bazel
|
||||
export msys2='cmd //C RefreshEnv.cmd '
|
||||
export msys2+='& set MSYS=winsymlinks:nativestrict '
|
||||
export msys2+='& C:\\tools\\msys64\\msys2_shell.cmd -defterm -no-start'
|
||||
export shell="$msys2 -mingw64 -full-path -here -c \$\* --"
|
||||
export msys2+=" -msys2 -c \$\* --"
|
||||
$msys2 pacman --sync --noconfirm --needed \
|
||||
autoconf \
|
||||
automake \
|
||||
mingw-w64-x86_64-libtool \
|
||||
mingw-w64-x86_64-toolchain \
|
||||
perl \
|
||||
unzip
|
||||
taskkill //IM gpg-agent.exe //F
|
||||
export CPPFLAGS=-D__USE_MINGW_ANSI_STDIO=1
|
||||
export PATH=/C/tools/msys64/mingw64/bin:$PATH
|
||||
export GNU_MAKE=mingw32-make
|
||||
export MAKE=mingw32-make
|
||||
export AR=gcc-ar
|
||||
export RANLIB=gcc-ranlib
|
||||
export COVERITY_SCAN_BRANCH_PATTERN=disable_coverity_scan
|
||||
;;
|
||||
esac
|
||||
- export GIT=git
|
||||
- g++ --version
|
||||
- $GNU_MAKE --version
|
||||
- $GIT --version
|
||||
install: true
|
||||
before_cache:
|
||||
- $msys2 pacman --sync --clean --noconfirm
|
||||
script:
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then $shell ./windowsBuild-delegated.sh; fi'
|
||||
- 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then $shell ./windowsBuildAndPublish-delegated.sh; fi'
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2/
|
||||
- $HOME/.gradle
|
||||
- $HOME/.konan
|
||||
- $HOME/AppData/Local/Temp/chocolatey
|
||||
- /C/tools/msys64
|
||||
branches:
|
||||
only:
|
||||
- master
|
@ -15,10 +15,8 @@ fi
|
||||
./makeLinuxArm64.sh
|
||||
#now we can do the delegated build
|
||||
cd ..
|
||||
./gradlew multiplatform-crypto-delegated:build
|
||||
|
||||
#build libsodium bindings
|
||||
./gradlew multiplatform-crypto-libsodium-bindings:build
|
||||
#and finally pure build
|
||||
./gradlew multiplatform-crypto:build
|
||||
set +e
|
||||
|
||||
|
@ -15,11 +15,9 @@ fi
|
||||
./makeLinuxArm64.sh
|
||||
#now we can do the delegated build
|
||||
cd ..
|
||||
./gradlew multiplatform-crypto-delegated:build
|
||||
#build libsodium bindings
|
||||
./gradlew multiplatform-crypto-libsodium-bindings:build
|
||||
#and finally pure build
|
||||
./gradlew multiplatform-crypto:build
|
||||
|
||||
./gradlew publishJvmPublicationToSnapshotRepository publishJsPublicationToSnapshotRepository \
|
||||
publishKotlinMultiplatformPublicationToSnapshotRepository publishLinuxX64PublicationToSnapshotRepository \
|
||||
publishLinuxArm64PublicationToSnapshotRepository publishMetadataPublicationToSnapshotRepository
|
||||
|
@ -8,10 +8,6 @@ cd sodiumWrapper
|
||||
./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
|
||||
./gradlew multiplatform-crypto-delegated:iosX64Test
|
||||
|
||||
./gradlew multiplatform-crypto-libsodium-bindings:iosArm32MainKlibrary multiplatform-crypto-libsodium-bindings:iosArm32TestKlibrary \
|
||||
multiplatform-crypto-libsodium-bindings:iosArm64MainKlibrary multiplatform-crypto-libsodium-bindings:iosArm64TestKlibrary \
|
||||
|
@ -8,9 +8,6 @@ cd sodiumWrapper
|
||||
./makeIos.sh
|
||||
#now we can do the delegated build of ios and macos libraries
|
||||
cd ..
|
||||
./gradlew multiplatform-crypto-delegated:macosX64MainKlibrary multiplatform-crypto-delegated:macosX64TestKlibrary
|
||||
./gradlew multiplatform-crypto-delegated:macosX64Test
|
||||
|
||||
./gradlew multiplatform-crypto-libsodium-bindings:macosX64MainKlibrary multiplatform-crypto-libsodium-bindings:macosX64TestKlibrary
|
||||
./gradlew multiplatform-crypto-libsodium-bindings:macosX64Test
|
||||
set +e
|
||||
|
@ -1,9 +0,0 @@
|
||||
set -e
|
||||
#!/bin/sh
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
./gradlew multiplatform-crypto:iosArm32MainKlibrary multiplatform-crypto:iosArm32TestKlibrary \
|
||||
multiplatform-crypto:iosArm64MainKlibrary multiplatform-crypto:iosArm64TestKlibrary \
|
||||
multiplatform-crypto:iosX64MainKlibrary multiplatform-crypto:iosX64TestKlibrary
|
||||
./gradlew multiplatform-crypto:iosX64Test
|
||||
|
||||
set +e
|
@ -1,7 +0,0 @@
|
||||
set -e
|
||||
#!/bin/sh
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
./gradlew multiplatform-crypto:macosX64MainKlibrary multiplatform-crypto:macosX64TestKlibrary
|
||||
./gradlew multiplatform-crypto:macosX64Test
|
||||
|
||||
set +e
|
@ -1,7 +0,0 @@
|
||||
set -e
|
||||
#!/bin/sh
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
./gradlew multiplatform-crypto:tvosArm64MainKlibrary multiplatform-crypto:tvosArm64TestKlibrary \
|
||||
multiplatform-crypto:tvosX64MainKlibrary multiplatform-crypto:tvosX64TestKlibrary
|
||||
./gradlew multiplatform-crypto:tvosX64Test
|
||||
set +e
|
@ -1,8 +0,0 @@
|
||||
set -e
|
||||
#!/bin/sh
|
||||
./gradlew multiplatform-crypto-api:build
|
||||
./gradlew multiplatform-crypto:watchosArm32MainKlibrary multiplatform-crypto:watchosArm32TestKlibrary \
|
||||
multiplatform-crypto:watchosArm64MainKlibrary multiplatform-crypto:watchosArm64TestKlibrary \
|
||||
multiplatform-crypto:watchosX86MainKlibrary multiplatform-crypto:watchosX86TestKlibrary
|
||||
./gradlew multiplatform-crypto:watchosX86Test
|
||||
set +e
|
@ -7,9 +7,6 @@ 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
|
||||
|
||||
./gradlew multiplatform-crypto-libsodium-bindings:tvosArm64MainKlibrary multiplatform-crypto-libsodium-bindings:tvosArm64TestKlibrary \
|
||||
multiplatform-crypto-libsodium-bindings:tvosX64MainKlibrary multiplatform-crypto-libsodium-bindings:tvosX64TestKlibrary
|
||||
|
@ -7,10 +7,6 @@ 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
|
||||
|
||||
./gradlew multiplatform-crypto-libsodium-bindings:watchosArm32MainKlibrary multiplatform-crypto-libsodium-bindings:watchosArm32TestKlibrary \
|
||||
multiplatform-crypto-libsodium-bindings:watchosArm64MainKlibrary multiplatform-crypto-libsodium-bindings:watchosArm64TestKlibrary \
|
||||
|
@ -8,9 +8,6 @@ cd sodiumWrapper
|
||||
./makeIosWatchosTvos.sh
|
||||
#now we can do the delegated build
|
||||
cd ..
|
||||
./gradlew multiplatform-crypto-delegated:build
|
||||
#pure build
|
||||
./gradlew multiplatform-crypto:build
|
||||
#libsodium bindings
|
||||
./gradlew multiplatform-crypto-libsodium-bindings:build
|
||||
set +e
|
||||
|
@ -8,9 +8,6 @@ cd sodiumWrapper
|
||||
./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
|
||||
|
||||
./gradlew multiplatform-crypto-libsodium-bindings:publishIosArm32PublicationToSnapshotRepository \
|
||||
multiplatform-crypto-libsodium-bindings:publishIosArm64PublicationToSnapshotRepository \
|
||||
|
@ -7,7 +7,6 @@ cd sodiumWrapper
|
||||
./makeMacosX86-64.sh
|
||||
#now we can do the delegated build of ios and macos libraries
|
||||
cd ..
|
||||
./gradlew multiplatform-crypto-delegated:publishMacosX64PublicationToSnapshotRepository
|
||||
|
||||
./gradlew multiplatform-crypto-libsodium-bindings:publishMacosX64PublicationToSnapshotRepository
|
||||
set +e
|
||||
|
@ -1,8 +0,0 @@
|
||||
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
|
||||
set +e
|
@ -1,6 +0,0 @@
|
||||
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:publishMacosX64PublicationToSnapshotRepository
|
||||
set +e
|
@ -1,8 +0,0 @@
|
||||
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:publishTvosArm64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto:publishTvosX64PublicationToSnapshotRepository
|
||||
|
||||
set +e
|
@ -1,8 +0,0 @@
|
||||
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:publishWatchosArm32PublicationToSnapshotRepository \
|
||||
multiplatform-crypto:publishWatchosArm64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto:publishWatchosX86PublicationToSnapshotRepository
|
||||
set +e
|
@ -1,8 +0,0 @@
|
||||
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:publishTvosArm64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto:publishTvosX64PublicationToSnapshotRepository
|
||||
|
||||
set +e
|
@ -7,9 +7,6 @@ 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
|
||||
|
||||
./gradlew multiplatform-crypto-libsodium-bindings:publishWatchosArm32PublicationToSnapshotRepository \
|
||||
multiplatform-crypto-libsodium-bindings:publishWatchosArm64PublicationToSnapshotRepository \
|
||||
|
@ -1,15 +0,0 @@
|
||||
set -e
|
||||
#!/bin/sh
|
||||
./gradlew multiplatform-crypto-delegated:publishMacosX64PublicationToSnapshotRepository
|
||||
|
||||
./gradlew multiplatform-crypto-delegated:publishIosArm32PublicationToSnapshotRepository \
|
||||
multiplatform-crypto-delegated:publishIosArm64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto-delegated:publishIosX64PublicationToSnapshotRepository
|
||||
|
||||
./gradlew multiplatform-crypto-delegated:publishWatchosArm32PublicationToSnapshotRepository \
|
||||
multiplatform-crypto-delegated:publishWatchosArm64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto-delegated:publishWatchosX86PublicationToSnapshotRepository
|
||||
|
||||
./gradlew multiplatform-crypto-delegated:publishTvosArm64PublicationToSnapshotRepository \
|
||||
multiplatform-crypto-delegated:publishTvosX64PublicationToSnapshotRepository
|
||||
set +e
|
@ -1,698 +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.
|
||||
*
|
||||
*/
|
||||
|
||||
@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
|
||||
import org.jetbrains.dokka.Platform
|
||||
|
||||
plugins {
|
||||
kotlin(PluginsDeps.multiplatform)
|
||||
id(PluginsDeps.mavenPublish)
|
||||
id(PluginsDeps.signing)
|
||||
id(PluginsDeps.node) version Versions.nodePlugin
|
||||
id(PluginsDeps.dokka)
|
||||
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 {
|
||||
isRunningInGitlabCi {
|
||||
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"
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
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.kotlinBigNum)
|
||||
api(project(Deps.Common.apiProject))
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Common.test))
|
||||
implementation(kotlin(Deps.Common.testAnnotation))
|
||||
}
|
||||
}
|
||||
|
||||
val nativeDependencies = independentDependencyBlock {
|
||||
}
|
||||
|
||||
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 {
|
||||
}
|
||||
}
|
||||
|
||||
//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 iosArm = setOf(
|
||||
"iosArm64", "iosArm32"
|
||||
)
|
||||
val iosSimulator = setOf(
|
||||
"iosX64"
|
||||
)
|
||||
val mingw64Bit = setOf(
|
||||
"mingwX64"
|
||||
)
|
||||
|
||||
val tvosArm = setOf(
|
||||
"tvosArm64"
|
||||
)
|
||||
val tvosSimulator = setOf(
|
||||
"tvosX64"
|
||||
)
|
||||
|
||||
val watchosArm = setOf(
|
||||
"watchosArm32", "watchosArm64"
|
||||
)
|
||||
val watchosSimulator = setOf(
|
||||
"watchosX86"
|
||||
)
|
||||
|
||||
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 (iosArm.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 (iosSimulator.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-simulators/include")
|
||||
}
|
||||
kotlinOptions.freeCompilerArgs = listOf(
|
||||
"-include-binary", "${project.rootDir}/sodiumWrapper/static-ios-simulators/lib/libsodium.a"
|
||||
)
|
||||
}
|
||||
|
||||
if (tvosArm.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 (tvosSimulator.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-simulators/include")
|
||||
}
|
||||
kotlinOptions.freeCompilerArgs = listOf(
|
||||
"-include-binary", "${project.rootDir}/sodiumWrapper/static-tvos-simulators/lib/libsodium.a"
|
||||
)
|
||||
}
|
||||
|
||||
if (watchosArm.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"
|
||||
)
|
||||
}
|
||||
|
||||
if (watchosSimulator.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-simulators/include")
|
||||
}
|
||||
kotlinOptions.freeCompilerArgs = listOf(
|
||||
"-include-binary", "${project.rootDir}/sodiumWrapper/static-watchos-simulators/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))
|
||||
|
||||
//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(kotlin(Deps.Jvm.reflection))
|
||||
}
|
||||
}
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Js.stdLib))
|
||||
implementation(npm(Deps.Js.Npm.libsodiumWrappers.first, Deps.Js.Npm.libsodiumWrappers.second))
|
||||
}
|
||||
}
|
||||
val jsTest by getting {
|
||||
dependencies {
|
||||
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(dokkaJavadoc)
|
||||
archiveClassifier.set("javadoc")
|
||||
from(dokkaJavadoc.get().outputDirectory)
|
||||
}
|
||||
|
||||
dokkaJavadoc {
|
||||
println("Dokka !")
|
||||
dokkaSourceSets {
|
||||
named("commonMain") {
|
||||
displayName.set("common")
|
||||
platform.set(Platform.common)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
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")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
Binary file not shown.
@ -1,26 +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.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 20-Jul-2019
|
||||
*/
|
||||
object Config {
|
||||
const val DEBUG = false
|
||||
}
|
@ -1,216 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import com.ionspin.kotlin.crypto.authenticated.XChaCha20Poly1305Delegated
|
||||
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.blake2b.Blake2bMultipart
|
||||
import com.ionspin.kotlin.crypto.hash.sha.*
|
||||
import com.ionspin.kotlin.crypto.keyderivation.ArgonResult
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 24-May-2020
|
||||
*/
|
||||
|
||||
object CryptoInitializerDelegated : CryptoInitializer {
|
||||
override suspend fun initialize() {
|
||||
Initializer.initialize()
|
||||
}
|
||||
|
||||
fun initializeWithCallback(done: () -> Unit) {
|
||||
Initializer.initializeWithCallback(done)
|
||||
}
|
||||
|
||||
override fun isInitialized(): Boolean {
|
||||
return Initializer.isInitialized()
|
||||
}
|
||||
}
|
||||
|
||||
object CryptoPrimitives : PrimitivesApi {
|
||||
|
||||
object Blake2b {
|
||||
fun updateable(key: UByteArray? = null, hashLength: Int = Blake2bProperties.MAX_HASH_BYTES): com.ionspin.kotlin.crypto.hash.blake2b.Blake2bMultipart {
|
||||
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(): Sha512Multipart {
|
||||
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()")
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashBlake2bMultipart(key: UByteArray?, hashLength: Int): Blake2bMultipart {
|
||||
checkInitialization()
|
||||
return Blake2bDelegated(key, hashLength)
|
||||
}
|
||||
|
||||
override fun hashBlake2b(message: UByteArray, key: UByteArray, hashLength: Int): UByteArray {
|
||||
checkInitialization()
|
||||
return Blake2bDelegatedStateless.digest(message, key, hashLength)
|
||||
}
|
||||
|
||||
override fun hashSha256Multipart(): com.ionspin.kotlin.crypto.hash.sha.Sha256 {
|
||||
checkInitialization()
|
||||
return Sha256Delegated()
|
||||
}
|
||||
|
||||
override fun hashSha256(message: UByteArray): UByteArray {
|
||||
checkInitialization()
|
||||
return Sha256StatelessDelegated.digest(inputMessage = message)
|
||||
}
|
||||
|
||||
override fun hashSha512Multipart(): com.ionspin.kotlin.crypto.hash.sha.Sha512Multipart {
|
||||
checkInitialization()
|
||||
return Sha512Delegated()
|
||||
}
|
||||
|
||||
override fun hashSha512(message: UByteArray): UByteArray {
|
||||
checkInitialization()
|
||||
return Sha512StatelessDelegated.digest(inputMessage = message)
|
||||
}
|
||||
|
||||
override fun deriveKey(
|
||||
password: String,
|
||||
salt: String?,
|
||||
key: String,
|
||||
associatedData: String,
|
||||
parallelism: Int,
|
||||
tagLength: Int,
|
||||
memory: Int,
|
||||
numberOfIterations: Int
|
||||
): ArgonResult {
|
||||
// return Argon2Delegated.derive(
|
||||
// password,
|
||||
// salt,
|
||||
// key,
|
||||
// associatedData,
|
||||
// parallelism
|
||||
// tagLength,
|
||||
// memory,
|
||||
// numberOfIterations
|
||||
// )
|
||||
TODO()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
fun SymmetricKey.Companion.randomKey() : SymmetricKey {
|
||||
return SymmetricKey(SRNG.getRandomBytes(32))
|
||||
}
|
||||
|
||||
object Crypto {
|
||||
|
||||
object Hash : HashApi {
|
||||
override fun hash(data: UByteArray, key : UByteArray) : HashedData {
|
||||
return HashedData(Blake2bDelegatedStateless.digest(data, key))
|
||||
}
|
||||
|
||||
override fun multipartHash(key: UByteArray?) : com.ionspin.kotlin.crypto.hash.MultipartHash {
|
||||
return Blake2bDelegated(key)
|
||||
}
|
||||
}
|
||||
|
||||
object Encryption : EncryptionApi {
|
||||
override fun encrypt(key: SymmetricKey, data : Encryptable<*>, associatedData : UByteArray) : EncryptedData {
|
||||
if (key.value.size != 32) {
|
||||
throw RuntimeException("Invalid key size! Required 32, supplied ${key.value.size}")
|
||||
}
|
||||
val nonce = SRNG.getRandomBytes(24)
|
||||
return EncryptedData(XChaCha20Poly1305Delegated.encrypt(key.value, nonce, data.toEncryptableForm(), associatedData), nonce)
|
||||
|
||||
}
|
||||
|
||||
override fun <T: Encryptable<T>> decrypt(key: SymmetricKey, encryptedData : EncryptedData, associatedData: UByteArray, byteArrayDeserializer : (UByteArray) -> T) : T {
|
||||
return byteArrayDeserializer(XChaCha20Poly1305Delegated.decrypt(key.value, encryptedData.nonce, encryptedData.ciphertext, associatedData))
|
||||
|
||||
}
|
||||
|
||||
override fun createMultipartEncryptor(key: SymmetricKey): MultipartAuthenticatedEncryption {
|
||||
return MultipartAuthenticatedEncryptor(key)
|
||||
}
|
||||
|
||||
override fun createMultipartDecryptor(key: SymmetricKey, header: MultipartEncryptionHeader) : MultipartAuthenticatedDecryption {
|
||||
val decryptor = XChaCha20Poly1305Delegated()
|
||||
decryptor.initializeForDecryption(key.value, header.nonce)
|
||||
return MultipartAuthenticatedDecryptor(decryptor)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class MultipartAuthenticatedEncryptor internal constructor(val key : SymmetricKey) : MultipartAuthenticatedEncryption {
|
||||
|
||||
val header : MultipartEncryptionHeader
|
||||
val primitive = XChaCha20Poly1305Delegated()
|
||||
init {
|
||||
header = MultipartEncryptionHeader(primitive.initializeForEncryption(key.value))
|
||||
}
|
||||
|
||||
override fun startEncryption(): MultipartEncryptionHeader {
|
||||
return header
|
||||
}
|
||||
|
||||
override fun encryptPartialData(data: UByteArray, associatedData: UByteArray): EncryptedDataPart {
|
||||
return EncryptedDataPart(primitive.encrypt(data, associatedData))
|
||||
}
|
||||
|
||||
override fun cleanup() {
|
||||
primitive.cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MultipartAuthenticatedDecryptor internal constructor(val decryptor: XChaCha20Poly1305Delegated) : MultipartAuthenticatedDecryption {
|
||||
override fun decryptPartialData(data: EncryptedDataPart, associatedData: UByteArray): DecryptedDataPart {
|
||||
return DecryptedDataPart(decryptor.decrypt(data.data, associatedData))
|
||||
}
|
||||
|
||||
override fun cleanup() {
|
||||
decryptor.cleanup()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
expect object Initializer {
|
||||
fun isInitialized() : Boolean
|
||||
|
||||
suspend fun initialize()
|
||||
|
||||
fun initializeWithCallback(done: () -> (Unit))
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
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
|
@ -1,26 +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.
|
||||
*/
|
||||
|
||||
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
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.authenticated
|
||||
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jun-2020
|
||||
*/
|
||||
expect class XChaCha20Poly1305Delegated internal constructor() {
|
||||
internal constructor(key: UByteArray, testState : UByteArray, testHeader: UByteArray, isDecryptor: Boolean)
|
||||
companion object {
|
||||
fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, associatedData: UByteArray) : UByteArray
|
||||
fun decrypt(key: UByteArray, nonce: UByteArray, ciphertext: UByteArray, associatedData: UByteArray) : UByteArray
|
||||
}
|
||||
fun initializeForEncryption(key: UByteArray) : UByteArray
|
||||
fun initializeForDecryption(key: UByteArray, header: UByteArray)
|
||||
fun encrypt(data: UByteArray, associatedData: UByteArray = ubyteArrayOf()) : UByteArray
|
||||
fun decrypt(data: UByteArray, associatedData: UByteArray = ubyteArrayOf()) : UByteArray
|
||||
fun cleanup()
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,31 +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.
|
||||
*/
|
||||
|
||||
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) : Blake2bMultipart
|
||||
|
||||
|
||||
expect object Blake2bDelegatedStateless : Blake2b
|
||||
|
||||
|
@ -1,29 +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.
|
||||
*/
|
||||
|
||||
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
|
@ -1,29 +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.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
expect class Sha512Delegated() : Sha512Multipart
|
||||
|
||||
expect object Sha512StatelessDelegated : Sha512
|
@ -1,10 +0,0 @@
|
||||
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
|
@ -1,34 +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.
|
||||
*/
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
@ -1,216 +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.
|
||||
*/
|
||||
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package com.ionspin.kotlin.crypto.util
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 15-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
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] }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 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
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import kotlin.test.Test
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-Jul-2020
|
||||
*/
|
||||
class DebugTest {
|
||||
|
||||
|
||||
}
|
@ -1,37 +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.
|
||||
*/
|
||||
|
||||
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 {
|
||||
CryptoInitializerDelegated.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) }
|
||||
}
|
||||
}
|
@ -1,240 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.authenticated
|
||||
|
||||
import com.ionspin.kotlin.crypto.CryptoInitializerDelegated
|
||||
import com.ionspin.kotlin.crypto.Initializer
|
||||
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
||||
import com.ionspin.kotlin.crypto.util.testBlocking
|
||||
import kotlin.test.Ignore
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertFails
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-Jun-2020
|
||||
*/
|
||||
class XChaCha20Poly1305Test {
|
||||
|
||||
|
||||
@Test
|
||||
fun xChaCha20Poly1305() = testBlocking {
|
||||
CryptoInitializerDelegated.initialize()
|
||||
|
||||
assertTrue {
|
||||
val message = ("Ladies and Gentlemen of the class of '99: If I could offer you " +
|
||||
"only one tip for the future, sunscreen would be it.").encodeToUByteArray()
|
||||
|
||||
val associatedData = ubyteArrayOf(
|
||||
0x50U, 0x51U, 0x52U, 0x53U, 0xc0U, 0xc1U, 0xc2U, 0xc3U, 0xc4U, 0xc5U, 0xc6U, 0xc7U
|
||||
)
|
||||
val key = ubyteArrayOf(
|
||||
0x80U, 0x81U, 0x82U, 0x83U, 0x84U, 0x85U, 0x86U, 0x87U,
|
||||
0x88U, 0x89U, 0x8aU, 0x8bU, 0x8cU, 0x8dU, 0x8eU, 0x8fU,
|
||||
0x90U, 0x91U, 0x92U, 0x93U, 0x94U, 0x95U, 0x96U, 0x97U,
|
||||
0x98U, 0x99U, 0x9aU, 0x9bU, 0x9cU, 0x9dU, 0x9eU, 0x9fU,
|
||||
)
|
||||
|
||||
val nonce = ubyteArrayOf(
|
||||
0x40U, 0x41U, 0x42U, 0x43U, 0x44U, 0x45U, 0x46U, 0x47U,
|
||||
0x48U, 0x49U, 0x4aU, 0x4bU, 0x4cU, 0x4dU, 0x4eU, 0x4fU,
|
||||
0x50U, 0x51U, 0x52U, 0x53U, 0x54U, 0x55U, 0x56U, 0x57U,
|
||||
)
|
||||
|
||||
val expected = ubyteArrayOf(
|
||||
0xbdU, 0x6dU, 0x17U, 0x9dU, 0x3eU, 0x83U, 0xd4U, 0x3bU,
|
||||
0x95U, 0x76U, 0x57U, 0x94U, 0x93U, 0xc0U, 0xe9U, 0x39U,
|
||||
0x57U, 0x2aU, 0x17U, 0x00U, 0x25U, 0x2bU, 0xfaU, 0xccU,
|
||||
0xbeU, 0xd2U, 0x90U, 0x2cU, 0x21U, 0x39U, 0x6cU, 0xbbU,
|
||||
0x73U, 0x1cU, 0x7fU, 0x1bU, 0x0bU, 0x4aU, 0xa6U, 0x44U,
|
||||
0x0bU, 0xf3U, 0xa8U, 0x2fU, 0x4eU, 0xdaU, 0x7eU, 0x39U,
|
||||
0xaeU, 0x64U, 0xc6U, 0x70U, 0x8cU, 0x54U, 0xc2U, 0x16U,
|
||||
0xcbU, 0x96U, 0xb7U, 0x2eU, 0x12U, 0x13U, 0xb4U, 0x52U,
|
||||
0x2fU, 0x8cU, 0x9bU, 0xa4U, 0x0dU, 0xb5U, 0xd9U, 0x45U,
|
||||
0xb1U, 0x1bU, 0x69U, 0xb9U, 0x82U, 0xc1U, 0xbbU, 0x9eU,
|
||||
0x3fU, 0x3fU, 0xacU, 0x2bU, 0xc3U, 0x69U, 0x48U, 0x8fU,
|
||||
0x76U, 0xb2U, 0x38U, 0x35U, 0x65U, 0xd3U, 0xffU, 0xf9U,
|
||||
0x21U, 0xf9U, 0x66U, 0x4cU, 0x97U, 0x63U, 0x7dU, 0xa9U,
|
||||
0x76U, 0x88U, 0x12U, 0xf6U, 0x15U, 0xc6U, 0x8bU, 0x13U,
|
||||
0xb5U, 0x2eU, 0xc0U, 0x87U, 0x59U, 0x24U, 0xc1U, 0xc7U,
|
||||
0x98U, 0x79U, 0x47U, 0xdeU, 0xafU, 0xd8U, 0x78U, 0x0aU,
|
||||
0xcfU, 0x49U
|
||||
)
|
||||
val encrypted = XChaCha20Poly1305Delegated.encrypt(key, nonce, message, associatedData)
|
||||
encrypted.hexColumsPrint()
|
||||
val decrypted = XChaCha20Poly1305Delegated.decrypt(key, nonce, encrypted, associatedData)
|
||||
println("Decrypted")
|
||||
decrypted.hexColumsPrint()
|
||||
println("----------")
|
||||
encrypted.contentEquals(expected) && decrypted.contentEquals(message)
|
||||
}
|
||||
|
||||
assertTrue {
|
||||
val message = ubyteArrayOf(
|
||||
0x00U
|
||||
)
|
||||
val associatedData = ubyteArrayOf(
|
||||
0x00U
|
||||
)
|
||||
val key = ubyteArrayOf(
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
)
|
||||
|
||||
val nonce = ubyteArrayOf(
|
||||
0x00U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U, 0x08U, 0x09U, 0x0aU, 0x0bU,
|
||||
0x0cU, 0x0dU, 0x0eU, 0x0fU, 0x10U, 0x11U, 0x12U, 0x13U, 0x14U, 0x15U, 0x16U, 0x17U,
|
||||
)
|
||||
|
||||
val expected = ubyteArrayOf(
|
||||
0xbdU, 0x3bU, 0x8aU, 0xd7U, 0xa1U, 0x9dU, 0xe8U, 0xc4U, 0x55U,
|
||||
0x84U, 0x6fU, 0xfcU, 0x75U, 0x31U, 0xbfU, 0x0cU, 0x2dU
|
||||
)
|
||||
val encrypted = XChaCha20Poly1305Delegated.encrypt(key, nonce, message, associatedData)
|
||||
val decrypted = XChaCha20Poly1305Delegated.decrypt(key, nonce, encrypted, associatedData)
|
||||
|
||||
encrypted.contentEquals(expected) && decrypted.contentEquals(message)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Ignore() //"Will fail because nonce is not a parameter any more"
|
||||
@Test
|
||||
fun updateableXChaCha20Poly1305() {
|
||||
assertTrue {
|
||||
val message = ("Ladies and Gentlemen of the class of '99: If I could offer you " +
|
||||
"only one tip for the future, sunscreen would be it.").encodeToUByteArray()
|
||||
|
||||
val associatedData = ubyteArrayOf(
|
||||
0x50U, 0x51U, 0x52U, 0x53U, 0xc0U, 0xc1U, 0xc2U, 0xc3U, 0xc4U, 0xc5U, 0xc6U, 0xc7U
|
||||
)
|
||||
val key = ubyteArrayOf(
|
||||
0x80U, 0x81U, 0x82U, 0x83U, 0x84U, 0x85U, 0x86U, 0x87U,
|
||||
0x88U, 0x89U, 0x8aU, 0x8bU, 0x8cU, 0x8dU, 0x8eU, 0x8fU,
|
||||
0x90U, 0x91U, 0x92U, 0x93U, 0x94U, 0x95U, 0x96U, 0x97U,
|
||||
0x98U, 0x99U, 0x9aU, 0x9bU, 0x9cU, 0x9dU, 0x9eU, 0x9fU,
|
||||
)
|
||||
|
||||
val nonce = ubyteArrayOf(
|
||||
0x40U, 0x41U, 0x42U, 0x43U, 0x44U, 0x45U, 0x46U, 0x47U,
|
||||
0x48U, 0x49U, 0x4aU, 0x4bU, 0x4cU, 0x4dU, 0x4eU, 0x4fU,
|
||||
0x50U, 0x51U, 0x52U, 0x53U, 0x54U, 0x55U, 0x56U, 0x57U,
|
||||
)
|
||||
|
||||
val expected = ubyteArrayOf(
|
||||
0xbdU, 0x6dU, 0x17U, 0x9dU, 0x3eU, 0x83U, 0xd4U, 0x3bU,
|
||||
0x95U, 0x76U, 0x57U, 0x94U, 0x93U, 0xc0U, 0xe9U, 0x39U,
|
||||
0x57U, 0x2aU, 0x17U, 0x00U, 0x25U, 0x2bU, 0xfaU, 0xccU,
|
||||
0xbeU, 0xd2U, 0x90U, 0x2cU, 0x21U, 0x39U, 0x6cU, 0xbbU,
|
||||
0x73U, 0x1cU, 0x7fU, 0x1bU, 0x0bU, 0x4aU, 0xa6U, 0x44U,
|
||||
0x0bU, 0xf3U, 0xa8U, 0x2fU, 0x4eU, 0xdaU, 0x7eU, 0x39U,
|
||||
0xaeU, 0x64U, 0xc6U, 0x70U, 0x8cU, 0x54U, 0xc2U, 0x16U,
|
||||
0xcbU, 0x96U, 0xb7U, 0x2eU, 0x12U, 0x13U, 0xb4U, 0x52U,
|
||||
0x2fU, 0x8cU, 0x9bU, 0xa4U, 0x0dU, 0xb5U, 0xd9U, 0x45U,
|
||||
0xb1U, 0x1bU, 0x69U, 0xb9U, 0x82U, 0xc1U, 0xbbU, 0x9eU,
|
||||
0x3fU, 0x3fU, 0xacU, 0x2bU, 0xc3U, 0x69U, 0x48U, 0x8fU,
|
||||
0x76U, 0xb2U, 0x38U, 0x35U, 0x65U, 0xd3U, 0xffU, 0xf9U,
|
||||
0x21U, 0xf9U, 0x66U, 0x4cU, 0x97U, 0x63U, 0x7dU, 0xa9U,
|
||||
0x76U, 0x88U, 0x12U, 0xf6U, 0x15U, 0xc6U, 0x8bU, 0x13U,
|
||||
0xb5U, 0x2eU, 0xc0U, 0x87U, 0x59U, 0x24U, 0xc1U, 0xc7U,
|
||||
0x98U, 0x79U, 0x47U, 0xdeU, 0xafU, 0xd8U, 0x78U, 0x0aU,
|
||||
0xcfU, 0x49U
|
||||
)
|
||||
// val xChaChaPoly = XChaCha20Poly1305Delegated(key, associatedData)
|
||||
// val firstChunk = xChaChaPoly.encrypt(message)
|
||||
// val finalChunk = xChaChaPoly.finishEncryption().first
|
||||
// val result = firstChunk + finalChunk
|
||||
|
||||
// result.contentEquals(expected)
|
||||
1 == 1
|
||||
}
|
||||
|
||||
assertTrue {
|
||||
val message = ubyteArrayOf(
|
||||
0x00U
|
||||
)
|
||||
val associatedData = ubyteArrayOf(
|
||||
0x00U
|
||||
)
|
||||
val key = ubyteArrayOf(
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
)
|
||||
|
||||
val nonce = ubyteArrayOf(
|
||||
0x00U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U, 0x08U, 0x09U, 0x0aU, 0x0bU,
|
||||
0x0cU, 0x0dU, 0x0eU, 0x0fU, 0x10U, 0x11U, 0x12U, 0x13U, 0x14U, 0x15U, 0x16U, 0x17U,
|
||||
)
|
||||
|
||||
val expected = ubyteArrayOf(
|
||||
0xbdU, 0x3bU, 0x8aU, 0xd7U, 0xa1U, 0x9dU, 0xe8U, 0xc4U, 0x55U,
|
||||
0x84U, 0x6fU, 0xfcU, 0x75U, 0x31U, 0xbfU, 0x0cU, 0x2dU
|
||||
)
|
||||
// val xChaChaPoly = XChaCha20Poly1305Delegated(key, associatedData)
|
||||
// val firstChunk = xChaChaPoly.encrypt(message)
|
||||
// val finalChunk = xChaChaPoly.finishEncryption().first
|
||||
// val result = firstChunk + finalChunk
|
||||
// result.contentEquals(expected)
|
||||
1 == 1
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStreamingImpl() = testBlocking {
|
||||
Initializer.initialize()
|
||||
val key = UByteArray(32) { 0U }
|
||||
val state = ubyteArrayOf(
|
||||
0x2DU, 0xDBU, 0xC7U, 0xB2U, 0x03U, 0xBCU, 0xC3U, 0x22U, 0xBDU, 0x0CU, 0xBAU, 0x82U, 0xADU, 0x77U, 0x79U, 0x44U,
|
||||
0xE6U, 0x8FU, 0xA9U, 0x94U, 0x89U, 0xB1U, 0xDFU, 0xBEU, 0x00U, 0x9FU, 0x69U, 0xECU, 0x21U, 0x88U, 0x47U, 0x55U,
|
||||
0x01U, 0x00U, 0x00U, 0x00U, 0xC5U, 0x55U, 0x06U, 0x38U, 0xEBU, 0xA3U, 0x12U, 0x7BU, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||
0x00U, 0x00U, 0x00U, 0x00U,
|
||||
)
|
||||
val header = ubyteArrayOf(
|
||||
0x49U, 0x62U, 0x22U, 0x03U, 0xB7U, 0x46U, 0x11U, 0x97U, 0x8FU, 0x46U, 0x4AU, 0x3BU, 0x2FU, 0x2AU, 0x81U, 0x03U,
|
||||
0xC5U, 0x55U, 0x06U, 0x38U, 0xEBU, 0xA3U, 0x12U, 0x7BU,
|
||||
)
|
||||
val expected = ubyteArrayOf(
|
||||
0xAFU, 0xD3U, 0x2DU, 0x59U, 0xB8U, 0xC4U, 0x66U, 0x2EU, 0x47U, 0x29U, 0xC6U, 0xF9U, 0x93U, 0x4BU, 0x09U, 0x27U,
|
||||
0x24U, 0xDDU, 0xF3U, 0x05U, 0x48U, 0x94U, 0x67U, 0x10U, 0x00U, 0x21U, 0x85U, 0x22U, 0x96U, 0x3CU, 0xCEU, 0x8EU,
|
||||
0xB7U, 0x53U, 0x9DU, 0x46U, 0xF5U, 0x3CU, 0x5EU, 0x48U, 0x9BU, 0x8CU, 0x13U, 0xB7U, 0x28U, 0x6BU, 0xB3U, 0x6CU,
|
||||
0x3AU, 0x04U, 0xB7U, 0x25U, 0xB9U, 0x50U, 0x45U, 0x08U, 0x0BU, 0x89U, 0xA2U, 0x0FU, 0x70U, 0xCCU, 0x60U, 0x1BU,
|
||||
0xC3U, 0x17U, 0x35U, 0x9FU, 0xAEU, 0x82U, 0x51U, 0x43U, 0x1BU, 0x9DU, 0x53U, 0x9EU, 0xE2U, 0xAFU, 0x20U, 0x1FU,
|
||||
0xFDU, 0x03U, 0x59U, 0x11U, 0x51U, 0x9EU, 0xACU, 0x83U, 0xCDU, 0x78U, 0xD1U, 0xD0U, 0xE5U, 0xD7U, 0x0EU, 0x41U,
|
||||
0xDEU, 0xFBU, 0x5CU, 0x7FU, 0x1CU, 0x26U, 0x32U, 0x2CU, 0x51U, 0xF6U, 0xEFU, 0xC6U, 0x34U, 0xC4U, 0xACU, 0x6CU,
|
||||
0xE8U, 0xF9U, 0x4BU, 0xABU, 0xA3U,
|
||||
)
|
||||
val encryptor = XChaCha20Poly1305Delegated(key, state, header, false)
|
||||
val decryptor = XChaCha20Poly1305Delegated(key, state, header, true)
|
||||
val data = UByteArray(100) { 0U }
|
||||
val result = encryptor.encrypt(data)
|
||||
val decrypted = decryptor.decrypt(result)
|
||||
println("Encrypted -----------")
|
||||
result.hexColumsPrint()
|
||||
println("Encrypted end -----------")
|
||||
println("Decrypted -----------")
|
||||
decrypted.hexColumsPrint()
|
||||
println("Decrypted end -----------")
|
||||
assertTrue {
|
||||
expected.contentEquals(result) && decrypted.contentEquals(data)
|
||||
}
|
||||
val messedUpTag = result.copyOf()
|
||||
messedUpTag[messedUpTag.size - 3] = 0U
|
||||
messedUpTag[messedUpTag.size - 2] = 0U
|
||||
messedUpTag[messedUpTag.size - 1] = 0U
|
||||
assertFails {
|
||||
val decryptorForWrongTag = XChaCha20Poly1305Delegated(key, state, header, true)
|
||||
val plaintext = decryptorForWrongTag.decrypt(messedUpTag)
|
||||
println("Decrypted with wrong tag -----------")
|
||||
plaintext.hexColumsPrint()
|
||||
println("Decrypted with wrong tag end -----------")
|
||||
}
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.hash.blake2b
|
||||
|
||||
import com.ionspin.kotlin.crypto.Crypto
|
||||
import com.ionspin.kotlin.crypto.CryptoPrimitives
|
||||
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 = CryptoPrimitives.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 = CryptoPrimitives.Blake2b.updateable()
|
||||
blake2b.update("t".encodeToUByteArray())
|
||||
blake2b.update(("est".encodeToUByteArray()))
|
||||
val result = blake2b.digest().toHexString()
|
||||
// println("Result: $result")
|
||||
assertTrue { result == expected }
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.Crypto
|
||||
import com.ionspin.kotlin.crypto.CryptoPrimitives
|
||||
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() = testBlocking {
|
||||
Initializer.initialize()
|
||||
val expected = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
|
||||
val result = CryptoPrimitives.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() = testBlocking {
|
||||
Initializer.initialize()
|
||||
val expected = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
|
||||
val sha256 = CryptoPrimitives.Sha256.updateable()
|
||||
sha256.update("t".encodeToUByteArray())
|
||||
sha256.update(("est".encodeToUByteArray()))
|
||||
val result = sha256.digest().toHexString()
|
||||
// println("Result: $result")
|
||||
assertTrue { result == expected }
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.Crypto
|
||||
import com.ionspin.kotlin.crypto.CryptoPrimitives
|
||||
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() = testBlocking {
|
||||
Initializer.initialize()
|
||||
val expected = "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67" +
|
||||
"b143732c304cc5fa9ad8e6f57f50028a8ff"
|
||||
val result = CryptoPrimitives.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() = testBlocking {
|
||||
Initializer.initialize()
|
||||
val expected = "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67" +
|
||||
"b143732c304cc5fa9ad8e6f57f50028a8ff"
|
||||
val sha512 = CryptoPrimitives.Sha512.updateable()
|
||||
sha512.update("t".encodeToUByteArray())
|
||||
sha512.update(("est".encodeToUByteArray()))
|
||||
val result = sha512.digest().toHexString()
|
||||
// println("Result: $result")
|
||||
assertTrue { result == expected }
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.highlevel
|
||||
|
||||
import com.ionspin.kotlin.crypto.Crypto
|
||||
import com.ionspin.kotlin.crypto.Initializer
|
||||
import com.ionspin.kotlin.crypto.SymmetricKey
|
||||
import com.ionspin.kotlin.crypto.hash.decodeToString
|
||||
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
||||
import com.ionspin.kotlin.crypto.util.testBlocking
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 09-Jul-2020
|
||||
*/
|
||||
class EncryptionTest {
|
||||
@Test
|
||||
fun testMultipartEncryption() = testBlocking {
|
||||
Initializer.initialize()
|
||||
val plaintext = ("pUoR4JVXJUeMKNkt6ZGGzEdTo33ajNGXwXpivBKA0XKs8toGRYI9Eul4bELRDkaQDNhd4vZseEFU" +
|
||||
"ojsAn3c9zIifIrMnydSivHVZ2pBtpAQwYoJhYmEsfE0tROGnOwFWyB9K6LRSv1gB3YqKR9VyM8mpRoUM3UCRRjyiX7bnKdCE1" +
|
||||
"EiX0myiwcY1nUKTgB3keERWtMU07hX7bCtao5nRvDofSj3o3IInHRQh6opltr5asQwn4m1qn029QF").encodeToUByteArray()
|
||||
val associatedData = "Additional data 1".encodeToUByteArray()
|
||||
val keyValue = UByteArray(32) { it.toUByte() }
|
||||
val key = SymmetricKey(keyValue)
|
||||
val encryptor = Crypto.Encryption.createMultipartEncryptor(key)
|
||||
val header = encryptor.startEncryption()
|
||||
val ciphertext1 = encryptor.encryptPartialData(plaintext.sliceArray(0 until 100), associatedData)
|
||||
val ciphertext2 = encryptor.encryptPartialData(plaintext.sliceArray(100 until 200))
|
||||
val ciphertext3 = encryptor.encryptPartialData(plaintext.sliceArray(200 until 250))
|
||||
//decrypt
|
||||
val decryptor = Crypto.Encryption.createMultipartDecryptor(key, header)
|
||||
val plaintext1 = decryptor.decryptPartialData(ciphertext1, associatedData)
|
||||
val plaintext2 = decryptor.decryptPartialData(ciphertext2)
|
||||
val plaintext3 = decryptor.decryptPartialData(ciphertext3)
|
||||
|
||||
val combinedPlaintext = plaintext1.data + plaintext2.data + plaintext3.data
|
||||
assertTrue {
|
||||
plaintext.contentEquals(combinedPlaintext)
|
||||
}
|
||||
encryptor.cleanup()
|
||||
decryptor.cleanup()
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,37 +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.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.util
|
||||
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
import kotlin.coroutines.startCoroutine
|
||||
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 20-Jul-2019
|
||||
*/
|
||||
fun testBlocking(block : suspend () -> Unit) {
|
||||
val continuation = Continuation<Unit>(EmptyCoroutineContext) {
|
||||
//Do nothing
|
||||
if (it.isFailure) {
|
||||
throw it.exceptionOrNull()!!
|
||||
}
|
||||
}
|
||||
block.startCoroutine(continuation)
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
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
|
||||
|
||||
//XChaCha20Poly1305
|
||||
fun crypto_aead_xchacha20poly1305_ietf_encrypt(message: Uint8Array, associatedData: Uint8Array, secretNonce: Uint8Array, nonce: Uint8Array, key: Uint8Array) : Uint8Array
|
||||
fun crypto_aead_xchacha20poly1305_ietf_decrypt(secretNonce: Uint8Array, ciphertext: Uint8Array, associatedData: Uint8Array, nonce: Uint8Array, key: Uint8Array) : Uint8Array
|
||||
|
||||
//XChaCha20Poly1305
|
||||
//encrypt
|
||||
fun crypto_secretstream_xchacha20poly1305_init_push(header: Uint8Array) : dynamic
|
||||
fun crypto_secretstream_xchacha20poly1305_push(state: dynamic, message: Uint8Array, associatedData: Uint8Array, tag: UByte) : Uint8Array
|
||||
|
||||
//decrypt
|
||||
fun crypto_secretstream_xchacha20poly1305_init_pull(header: Uint8Array, key: Uint8Array) : dynamic
|
||||
fun crypto_secretstream_xchacha20poly1305_pull(state: dynamic, ciphertext: Uint8Array, associatedData: Uint8Array) : dynamic
|
||||
|
||||
//util
|
||||
fun memzero(array: Uint8Array)
|
||||
|
||||
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package ext.libsodium.com.ionspin.kotlin.crypto
|
||||
|
||||
import org.khronos.webgl.Uint8Array
|
||||
import org.khronos.webgl.get
|
||||
import org.khronos.webgl.set
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 25-Jun-2020
|
||||
*
|
||||
* TODO investigate using unsafe cast
|
||||
*/
|
||||
fun UByteArray.toUInt8Array() : Uint8Array {
|
||||
val uint8Result = Uint8Array(toByteArray().toTypedArray())
|
||||
return uint8Result
|
||||
}
|
||||
|
||||
|
||||
fun Uint8Array.toUByteArray() : UByteArray {
|
||||
val result = UByteArray(length)
|
||||
for (i in 0 until length) {
|
||||
result[i] = get(i).toUByte()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
@ -1,43 +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.
|
||||
*/
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.authenticated
|
||||
|
||||
import com.ionspin.kotlin.crypto.InvalidTagException
|
||||
import com.ionspin.kotlin.crypto.getSodium
|
||||
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
||||
import ext.libsodium.com.ionspin.kotlin.crypto.toUByteArray
|
||||
import ext.libsodium.com.ionspin.kotlin.crypto.toUInt8Array
|
||||
import org.khronos.webgl.Uint8Array
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 25-Jun-2020
|
||||
*/
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jun-2020
|
||||
*/
|
||||
actual class XChaCha20Poly1305Delegated internal actual constructor() {
|
||||
actual companion object {
|
||||
actual fun encrypt(
|
||||
key: UByteArray,
|
||||
nonce: UByteArray,
|
||||
message: UByteArray,
|
||||
associatedData: UByteArray
|
||||
): UByteArray {
|
||||
val encrypted = getSodium().crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||
message.toUInt8Array(),
|
||||
associatedData.toUInt8Array(),
|
||||
Uint8Array(0),
|
||||
nonce.toUInt8Array(),
|
||||
key.toUInt8Array()
|
||||
)
|
||||
return encrypted.toUByteArray()
|
||||
}
|
||||
|
||||
actual fun decrypt(
|
||||
key: UByteArray,
|
||||
nonce: UByteArray,
|
||||
ciphertext: UByteArray,
|
||||
associatedData: UByteArray
|
||||
): UByteArray {
|
||||
val decrypted = getSodium().crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
Uint8Array(0),
|
||||
ciphertext.toUInt8Array(),
|
||||
associatedData.toUInt8Array(),
|
||||
nonce.toUInt8Array(),
|
||||
key.toUInt8Array()
|
||||
)
|
||||
return decrypted.toUByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
var state : dynamic = null
|
||||
var isInitialized = false
|
||||
var isEncryptor = false
|
||||
|
||||
actual fun initializeForEncryption(key: UByteArray) : UByteArray {
|
||||
println("Initializaing for encryption")
|
||||
val stateAndHeader = getSodium().crypto_secretstream_xchacha20poly1305_init_push(key.toUInt8Array())
|
||||
state = stateAndHeader.state
|
||||
val header = stateAndHeader.header as Uint8Array
|
||||
console.log(state)
|
||||
console.log(header)
|
||||
println("Done initializaing for encryption")
|
||||
isInitialized = true
|
||||
isEncryptor = true
|
||||
return header.toUByteArray()
|
||||
}
|
||||
|
||||
actual fun initializeForDecryption(key: UByteArray, header: UByteArray) {
|
||||
println("Initializing for decryption")
|
||||
header.hexColumsPrint()
|
||||
state = getSodium().crypto_secretstream_xchacha20poly1305_init_pull(header.toUInt8Array(), key.toUInt8Array())
|
||||
console.log(state)
|
||||
isInitialized = true
|
||||
isEncryptor = false
|
||||
}
|
||||
|
||||
internal actual constructor(
|
||||
key: UByteArray,
|
||||
testState: UByteArray,
|
||||
testHeader: UByteArray,
|
||||
isDecryptor: Boolean
|
||||
) : this() {
|
||||
state = getSodium().crypto_secretstream_xchacha20poly1305_init_pull(testHeader.toUInt8Array(), key.toUInt8Array())
|
||||
console.log(state)
|
||||
println("Done initializaing test state")
|
||||
isInitialized = true
|
||||
isEncryptor = !isDecryptor
|
||||
}
|
||||
|
||||
actual fun encrypt(data: UByteArray, associatedData: UByteArray): UByteArray {
|
||||
if (!isInitialized) {
|
||||
throw RuntimeException("Not initalized!")
|
||||
}
|
||||
if (!isEncryptor) {
|
||||
throw RuntimeException("Initialized as decryptor, attempted to use as encryptor")
|
||||
}
|
||||
val encrypted = getSodium().crypto_secretstream_xchacha20poly1305_push(state, data.toUInt8Array(), associatedData.toUInt8Array(), 0U)
|
||||
return encrypted.toUByteArray()
|
||||
}
|
||||
|
||||
actual fun decrypt(data: UByteArray, associatedData: UByteArray): UByteArray {
|
||||
if (!isInitialized) {
|
||||
throw RuntimeException("Not initalized!")
|
||||
}
|
||||
if (isEncryptor) {
|
||||
throw RuntimeException("Initialized as encryptor, attempted to use as decryptor")
|
||||
}
|
||||
val decryptedWithTag = getSodium().crypto_secretstream_xchacha20poly1305_pull(state, data.toUInt8Array(), associatedData.toUInt8Array())
|
||||
val decrypted = decryptedWithTag.message as Uint8Array
|
||||
val validTag = decryptedWithTag.tag
|
||||
|
||||
if (validTag != 0U) {
|
||||
println("Tag validation failed")
|
||||
throw InvalidTagException()
|
||||
}
|
||||
return decrypted.toUByteArray()
|
||||
}
|
||||
|
||||
actual fun cleanup() {
|
||||
//TODO JS cleanup
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.hash.blake2b
|
||||
|
||||
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 14-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
actual class Blake2bDelegated actual constructor(key: UByteArray?, val hashLength: Int) : Blake2bMultipart {
|
||||
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 : Blake2b {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
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 Sha512Delegated : Sha512Multipart {
|
||||
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 : Sha512 {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
@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
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,52 +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.
|
||||
*/
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,28 +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.
|
||||
*/
|
||||
|
||||
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() }
|
@ -1,25 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,34 +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.
|
||||
*/
|
||||
|
||||
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.asUByteArray()
|
||||
}
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.authenticated
|
||||
|
||||
import com.goterl.lazycode.lazysodium.SodiumJava
|
||||
import com.goterl.lazycode.lazysodium.interfaces.SecretStream
|
||||
import com.ionspin.kotlin.crypto.InvalidTagException
|
||||
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jun-2020
|
||||
*/
|
||||
actual class XChaCha20Poly1305Delegated internal actual constructor() {
|
||||
actual companion object {
|
||||
actual fun encrypt(
|
||||
key: UByteArray,
|
||||
nonce: UByteArray,
|
||||
message: UByteArray,
|
||||
associatedData: UByteArray
|
||||
): UByteArray {
|
||||
val ciphertext = ByteArray(message.size + 16)
|
||||
SodiumJava().crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||
ciphertext,
|
||||
longArrayOf(ciphertext.size.toLong()),
|
||||
message.toByteArray(),
|
||||
message.size.toLong(),
|
||||
associatedData.toByteArray(),
|
||||
associatedData.size.toLong(),
|
||||
null,
|
||||
nonce.toByteArray(),
|
||||
key.toByteArray()
|
||||
|
||||
)
|
||||
return ciphertext.asUByteArray()
|
||||
}
|
||||
|
||||
actual fun decrypt(
|
||||
key: UByteArray,
|
||||
nonce: UByteArray,
|
||||
ciphertext: UByteArray,
|
||||
associatedData: UByteArray
|
||||
): UByteArray {
|
||||
val message = ByteArray(ciphertext.size - 16)
|
||||
SodiumJava().crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
message,
|
||||
longArrayOf(ciphertext.size.toLong()),
|
||||
null,
|
||||
ciphertext.toByteArray(),
|
||||
ciphertext.size.toLong(),
|
||||
associatedData.toByteArray(),
|
||||
associatedData.size.toLong(),
|
||||
nonce.toByteArray(),
|
||||
key.toByteArray()
|
||||
|
||||
)
|
||||
return message.asUByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
val state : SecretStream.State = SecretStream.State()
|
||||
val sodium = SodiumJava()
|
||||
|
||||
var isInitialized = false
|
||||
var isEncryptor = false
|
||||
|
||||
internal actual constructor(
|
||||
key: UByteArray,
|
||||
testState: UByteArray,
|
||||
testHeader: UByteArray,
|
||||
isDecryptor: Boolean
|
||||
) : this() {
|
||||
state.k = testState.sliceArray(0 until 32).toByteArray()
|
||||
state.nonce = testState.sliceArray(32 until 44).toByteArray()
|
||||
isInitialized = true
|
||||
isEncryptor = !isDecryptor
|
||||
}
|
||||
|
||||
actual fun initializeForEncryption(key: UByteArray) : UByteArray {
|
||||
val header = UByteArray(24)
|
||||
sodium.crypto_secretstream_xchacha20poly1305_init_push(state, header.asByteArray(), key.asByteArray())
|
||||
isInitialized = true
|
||||
isEncryptor = true
|
||||
return header
|
||||
}
|
||||
|
||||
actual fun initializeForDecryption(key: UByteArray, header: UByteArray) {
|
||||
sodium.crypto_secretstream_xchacha20poly1305_init_pull(state, header.asByteArray(), key.asByteArray())
|
||||
isInitialized = true
|
||||
isEncryptor = false
|
||||
}
|
||||
|
||||
actual fun encrypt(data: UByteArray, associatedData: UByteArray): UByteArray {
|
||||
if (!isInitialized) {
|
||||
throw RuntimeException("Not initalized!")
|
||||
}
|
||||
if (!isEncryptor) {
|
||||
throw RuntimeException("Initialized as decryptor, attempted to use as encryptor")
|
||||
}
|
||||
val ciphertext = ByteArray(1 + data.size + 16)
|
||||
sodium.crypto_secretstream_xchacha20poly1305_push(
|
||||
state, ciphertext, null,
|
||||
data.asByteArray(), data.size.toLong(),
|
||||
associatedData.asByteArray(), associatedData.size.toLong(),
|
||||
0
|
||||
)
|
||||
return ciphertext.asUByteArray()
|
||||
}
|
||||
|
||||
actual fun decrypt(data: UByteArray, associatedData: UByteArray): UByteArray {
|
||||
if (!isInitialized) {
|
||||
throw RuntimeException("Not initalized!")
|
||||
}
|
||||
if (isEncryptor) {
|
||||
throw RuntimeException("Initialized as encryptor, attempted to use as decryptor")
|
||||
}
|
||||
val plaintext = ByteArray(data.size - 17)
|
||||
|
||||
val validTag = sodium.crypto_secretstream_xchacha20poly1305_pull(
|
||||
state, plaintext, null,
|
||||
null,
|
||||
data.asByteArray(),
|
||||
(data.size).toLong(),
|
||||
associatedData.asByteArray(),
|
||||
associatedData.size.toLong()
|
||||
)
|
||||
if (validTag != 0) {
|
||||
println("Tag validation failed")
|
||||
throw InvalidTagException()
|
||||
}
|
||||
return plaintext.asUByteArray()
|
||||
|
||||
}
|
||||
|
||||
actual fun cleanup() {
|
||||
sodium.sodium_memzero(state.k, 32)
|
||||
sodium.sodium_memzero(state.nonce, 12)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
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) : Blake2bMultipart {
|
||||
|
||||
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.asUByteArray()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
actual object Blake2bDelegatedStateless : Blake2b {
|
||||
|
||||
|
||||
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.asUByteArray()
|
||||
}
|
||||
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
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.asUByteArray()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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.asUByteArray()
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
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 : Sha512Multipart {
|
||||
|
||||
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.asUByteArray()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
actual object Sha512StatelessDelegated : Sha512 {
|
||||
|
||||
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.asUByteArray()
|
||||
}
|
||||
}
|
@ -1,34 +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.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.util
|
||||
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
import kotlin.coroutines.startCoroutine
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 20-Jul-2019
|
||||
*/
|
||||
//actual fun testBlocking(block: suspend () -> Unit) {
|
||||
// val continuation = Continuation<Unit>(EmptyCoroutineContext) {
|
||||
// println("Done")
|
||||
// }
|
||||
// block.startCoroutine(continuation)
|
||||
//
|
||||
//}
|
@ -1,9 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 07-Jun-2020
|
||||
*/
|
||||
import platform.posix.*
|
||||
//import cin
|
@ -1,44 +0,0 @@
|
||||
//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() }
|
||||
// }
|
||||
// }
|
||||
//}
|
@ -1,6 +0,0 @@
|
||||
headers = sodium.h
|
||||
headerFilter = sodium.h sodium/**
|
||||
#staticLibraries = libsodium.a
|
||||
#libraryPaths = sodiumWrapper/lib
|
||||
#compilerOpts = -I./sodiumWrapper/include
|
||||
linkerOpts =
|
@ -1,30 +0,0 @@
|
||||
@file:Suppress("VARIABLE_IN_SINGLETON_WITHOUT_THREAD_LOCAL")
|
||||
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
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
|
||||
}
|
||||
}
|
@ -1,40 +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.
|
||||
*/
|
||||
|
||||
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]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,164 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.authenticated
|
||||
|
||||
import com.ionspin.kotlin.bignum.integer.util.hexColumsPrint
|
||||
import com.ionspin.kotlin.crypto.InvalidTagException
|
||||
import com.ionspin.kotlin.crypto.util.toPtr
|
||||
import kotlinx.cinterop.*
|
||||
import libsodium.*
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jun-2020
|
||||
*/
|
||||
actual class XChaCha20Poly1305Delegated internal actual constructor() {
|
||||
actual companion object {
|
||||
actual fun encrypt(
|
||||
key: UByteArray,
|
||||
nonce: UByteArray,
|
||||
message: UByteArray,
|
||||
associatedData: UByteArray
|
||||
): UByteArray {
|
||||
val ciphertextLength = message.size + crypto_aead_xchacha20poly1305_IETF_ABYTES.toInt()
|
||||
val ciphertext = UByteArray(ciphertextLength)
|
||||
val ciphertextPinned = ciphertext.pin()
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||
ciphertextPinned.toPtr(),
|
||||
ulongArrayOf(ciphertextLength.convert()).toCValues(),
|
||||
message.toCValues(),
|
||||
message.size.convert(),
|
||||
associatedData.toCValues(),
|
||||
associatedData.size.convert(),
|
||||
null,
|
||||
nonce.toCValues(),
|
||||
key.toCValues()
|
||||
)
|
||||
ciphertextPinned.unpin()
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
actual fun decrypt(
|
||||
key: UByteArray,
|
||||
nonce: UByteArray,
|
||||
ciphertext: UByteArray,
|
||||
associatedData: UByteArray
|
||||
): UByteArray {
|
||||
val messageLength = ciphertext.size - crypto_aead_xchacha20poly1305_IETF_ABYTES.toInt()
|
||||
val message = UByteArray(messageLength)
|
||||
val messagePinned = message.pin()
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
messagePinned.toPtr(),
|
||||
ulongArrayOf(messageLength.convert()).toCValues(),
|
||||
null,
|
||||
ciphertext.toCValues(),
|
||||
ciphertext.size.convert(),
|
||||
associatedData.toCValues(),
|
||||
associatedData.size.convert(),
|
||||
nonce.toCValues(),
|
||||
key.toCValues()
|
||||
)
|
||||
messagePinned.unpin()
|
||||
return message
|
||||
}
|
||||
}
|
||||
|
||||
var state =
|
||||
sodium_malloc(crypto_secretstream_xchacha20poly1305_state.size.convert())!!
|
||||
.reinterpret<crypto_secretstream_xchacha20poly1305_state>()
|
||||
.pointed
|
||||
val header = UByteArray(crypto_secretstream_xchacha20poly1305_HEADERBYTES.toInt()) { 0U }
|
||||
|
||||
var isInitialized = false
|
||||
var isEncryptor = false
|
||||
|
||||
actual internal constructor(
|
||||
key: UByteArray,
|
||||
testState: UByteArray,
|
||||
testHeader: UByteArray,
|
||||
isDecryptor: Boolean
|
||||
) : this() {
|
||||
val pointer = state.ptr.reinterpret<UByteVar>()
|
||||
for (i in 0 until crypto_secretstream_xchacha20poly1305_state.size.toInt()) {
|
||||
pointer[i] = testState[i]
|
||||
}
|
||||
println("state after setting-----------")
|
||||
state.ptr.readBytes(crypto_secretstream_xchacha20poly1305_state.size.toInt()).asUByteArray().hexColumsPrint()
|
||||
println("state after setting-----------")
|
||||
println("header after setting-----------")
|
||||
testHeader.copyInto(header)
|
||||
header.hexColumsPrint()
|
||||
println("header after setting-----------")
|
||||
isInitialized = true
|
||||
isEncryptor = !isDecryptor
|
||||
}
|
||||
|
||||
|
||||
|
||||
actual fun initializeForEncryption(key: UByteArray) : UByteArray {
|
||||
val pinnedHeader = header.pin()
|
||||
crypto_secretstream_xchacha20poly1305_init_push(state.ptr, pinnedHeader.toPtr(), key.toCValues())
|
||||
println("state-----------")
|
||||
state.ptr.readBytes(crypto_secretstream_xchacha20poly1305_state.size.toInt()).asUByteArray().hexColumsPrint()
|
||||
println("state-----------")
|
||||
println("--------header-----------")
|
||||
header.hexColumsPrint()
|
||||
println("--------header-----------")
|
||||
pinnedHeader.unpin()
|
||||
return header
|
||||
}
|
||||
|
||||
actual fun initializeForDecryption(key: UByteArray, header: UByteArray) {
|
||||
crypto_secretstream_xchacha20poly1305_init_pull(state.ptr, header.toCValues(), key.toCValues())
|
||||
}
|
||||
|
||||
|
||||
actual fun encrypt(data: UByteArray, associatedData: UByteArray): UByteArray {
|
||||
val ciphertextWithTag = UByteArray(data.size + crypto_secretstream_xchacha20poly1305_ABYTES.toInt())
|
||||
val ciphertextWithTagPinned = ciphertextWithTag.pin()
|
||||
crypto_secretstream_xchacha20poly1305_push(
|
||||
state.ptr,
|
||||
ciphertextWithTagPinned.toPtr(),
|
||||
null,
|
||||
data.toCValues(),
|
||||
data.size.convert(),
|
||||
associatedData.toCValues(),
|
||||
associatedData.size.convert(),
|
||||
0U,
|
||||
)
|
||||
println("Encrypt partial")
|
||||
ciphertextWithTag.hexColumsPrint()
|
||||
println("Encrypt partial end")
|
||||
ciphertextWithTagPinned.unpin()
|
||||
return ciphertextWithTag
|
||||
}
|
||||
|
||||
actual fun decrypt(data: UByteArray, associatedData: UByteArray): UByteArray {
|
||||
val plaintext = UByteArray(data.size - crypto_secretstream_xchacha20poly1305_ABYTES.toInt())
|
||||
val plaintextPinned = plaintext.pin()
|
||||
val validTag = crypto_secretstream_xchacha20poly1305_pull(
|
||||
state.ptr,
|
||||
plaintextPinned.toPtr(),
|
||||
null,
|
||||
null,
|
||||
data.toCValues(),
|
||||
data.size.convert(),
|
||||
associatedData.toCValues(),
|
||||
associatedData.size.convert()
|
||||
)
|
||||
plaintextPinned.unpin()
|
||||
println("tag: $validTag")
|
||||
if (validTag != 0) {
|
||||
println("Tag validation failed")
|
||||
throw InvalidTagException()
|
||||
}
|
||||
return plaintext
|
||||
}
|
||||
|
||||
actual fun cleanup() {
|
||||
sodium_free(state.ptr)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.hash.blake2b
|
||||
import com.ionspin.kotlin.crypto.util.toHexString
|
||||
import com.ionspin.kotlin.crypto.util.toPtr
|
||||
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) : Blake2bMultipart {
|
||||
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.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.toPtr(), requestedHashLength.convert())
|
||||
free(state.ptr)
|
||||
return hashResult
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
|
||||
actual object Blake2bDelegatedStateless : Blake2b {
|
||||
|
||||
override fun digest(inputMessage: UByteArray, key: UByteArray, hashLength: Int): UByteArray {
|
||||
val hashResult = UByteArray(MAX_HASH_BYTES)
|
||||
val hashResultPinned = hashResult.pin()
|
||||
crypto_generichash(
|
||||
hashResultPinned.toPtr(),
|
||||
hashLength.convert(),
|
||||
inputMessage.toCValues(),
|
||||
inputMessage.size.convert(),
|
||||
key.toCValues(),
|
||||
key.size.convert()
|
||||
)
|
||||
hashResultPinned.unpin()
|
||||
return hashResult
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bDelegatedStateless
|
||||
import com.ionspin.kotlin.crypto.util.toPtr
|
||||
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 = sodium_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.toPtr())
|
||||
sodium_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.toPtr(), inputMessage.toCValues(), inputMessage.size.convert())
|
||||
hashResultPinned.unpin()
|
||||
return hashResult
|
||||
}
|
||||
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.util.toPtr
|
||||
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 : Sha512Multipart {
|
||||
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.toPtr())
|
||||
free(state.ptr)
|
||||
return hashResult
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
actual object Sha512StatelessDelegated : Sha512 {
|
||||
|
||||
override fun digest(inputMessage: UByteArray): UByteArray {
|
||||
val hashResult = UByteArray(Sha512StatelessDelegated.MAX_HASH_BYTES)
|
||||
val hashResultPinned = hashResult.pin()
|
||||
crypto_hash_sha512(hashResultPinned.toPtr(), inputMessage.toCValues(), inputMessage.size.convert())
|
||||
hashResultPinned.unpin()
|
||||
return hashResult
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.util
|
||||
|
||||
import kotlinx.cinterop.CPointer
|
||||
import kotlinx.cinterop.Pinned
|
||||
import kotlinx.cinterop.UByteVar
|
||||
import kotlinx.cinterop.addressOf
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 27-Aug-2020
|
||||
*/
|
||||
fun Pinned<UByteArray>.toPtr() : CPointer<UByteVar> = addressOf(0)
|
@ -1,77 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||
import com.ionspin.kotlin.crypto.util.toHexString
|
||||
import com.ionspin.kotlin.crypto.util.toPtr
|
||||
import kotlinx.cinterop.*
|
||||
import libsodium.*
|
||||
import platform.posix.free
|
||||
import platform.posix.malloc
|
||||
import kotlin.test.Ignore
|
||||
import kotlin.test.Test
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 13-Jul-2020
|
||||
*/
|
||||
class HelperTest {
|
||||
@Ignore //Just used for debugging pure implementation
|
||||
@Test
|
||||
fun longSha256() {
|
||||
for (target in 0L until 10L) {
|
||||
generateForRounds256(target)
|
||||
}
|
||||
for (target in 0L until 16_777_216L step 1_000_000L) {
|
||||
generateForRounds256(target)
|
||||
}
|
||||
generateForRounds256(16_777_216L)
|
||||
}
|
||||
|
||||
fun generateForRounds256(target: Long) {
|
||||
val updateValue =
|
||||
"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno".encodeToUByteArray().toCValues()
|
||||
val state = malloc(crypto_hash_sha256_state.size.convert())!!
|
||||
.reinterpret<crypto_hash_sha256_state>()
|
||||
|
||||
crypto_hash_sha256_init(state)
|
||||
for (i in 0 until target) {
|
||||
crypto_hash_sha256_update(state, updateValue, updateValue.size.convert())
|
||||
}
|
||||
val result = UByteArray(32)
|
||||
val resultPinned = result.pin()
|
||||
crypto_hash_sha256_final(state, resultPinned.toPtr())
|
||||
println("$target to \"${result.toHexString()}\",")
|
||||
free(state)
|
||||
}
|
||||
@Ignore //Just used for debugging pure implementation
|
||||
@Test
|
||||
fun longSha512() {
|
||||
|
||||
for (target in 0L until 10L) {
|
||||
generateForRounds512(target)
|
||||
}
|
||||
for (target in 0L until 16_777_216L step 1_000_000L) {
|
||||
generateForRounds512(target)
|
||||
}
|
||||
generateForRounds512(16_777_216L)
|
||||
}
|
||||
|
||||
fun generateForRounds512(target: Long) {
|
||||
println("Wut")
|
||||
val updateValue =
|
||||
"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno".encodeToUByteArray().toCValues()
|
||||
val state = malloc(crypto_hash_sha512_state.size.convert())!!
|
||||
.reinterpret<crypto_hash_sha512_state>()
|
||||
|
||||
crypto_hash_sha512_init(state)
|
||||
for (i in 0 until target) {
|
||||
crypto_hash_sha512_update(state, updateValue, updateValue.size.convert())
|
||||
}
|
||||
val result = UByteArray(32)
|
||||
val resultPinned = result.pin()
|
||||
crypto_hash_sha512_final(state, resultPinned.toPtr())
|
||||
println("$target to \"${result.toHexString()}\",")
|
||||
free(state)
|
||||
}
|
||||
}
|
@ -1,27 +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.
|
||||
*/
|
||||
|
||||
//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() }
|
@ -1,522 +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.
|
||||
*
|
||||
*/
|
||||
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest
|
||||
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
|
||||
import org.jetbrains.dokka.Platform
|
||||
|
||||
plugins {
|
||||
kotlin(PluginsDeps.multiplatform)
|
||||
id(PluginsDeps.mavenPublish)
|
||||
id(PluginsDeps.signing)
|
||||
id(PluginsDeps.node) version Versions.nodePlugin
|
||||
id(PluginsDeps.dokka)
|
||||
}
|
||||
|
||||
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 = System.getProperty("idea.active") == "true"
|
||||
|
||||
kotlin {
|
||||
val hostOsName = getHostOsName()
|
||||
val bla =1
|
||||
runningOnLinuxx86_64 {
|
||||
jvm()
|
||||
js {
|
||||
|
||||
browser {
|
||||
|
||||
testTask {
|
||||
// isRunningInTravis {
|
||||
enabled = false //Until I sort out testing on travis, and figure out how to increase karma timeout
|
||||
// }
|
||||
useKarma {
|
||||
useChrome()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
nodejs {
|
||||
testTask {
|
||||
useMocha() {
|
||||
timeout = "10s"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
linuxX64("linux") {
|
||||
binaries {
|
||||
staticLib {
|
||||
optimized = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
linuxArm64() {
|
||||
binaries {
|
||||
staticLib {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
linuxArm32Hfp() {
|
||||
binaries {
|
||||
staticLib {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// No coroutines support for mingwX86
|
||||
// mingwX86() {
|
||||
// binaries {
|
||||
// staticLib {
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
println(targets.names)
|
||||
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Common.stdLib))
|
||||
implementation(kotlin(Deps.Common.test))
|
||||
implementation(Deps.Common.kotlinBigNum)
|
||||
implementation(project(Deps.Common.apiProject))
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin(Deps.Common.test))
|
||||
implementation(kotlin(Deps.Common.testAnnotation))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val nativeMain by creating {
|
||||
dependsOn(commonMain)
|
||||
dependencies {
|
||||
}
|
||||
isRunningInIdea {
|
||||
kotlin.setSrcDirs(emptySet<String>())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val nativeTest by creating {
|
||||
dependsOn(commonTest)
|
||||
dependencies {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
val linuxMain by getting {
|
||||
dependsOn(nativeMain)
|
||||
//Force idea to consider native sourceset
|
||||
if (ideaActive) {
|
||||
kotlin.srcDir("src/nativeMain/kotlin")
|
||||
}
|
||||
}
|
||||
val linuxTest by getting {
|
||||
dependsOn(nativeTest)
|
||||
// Force idea to consider native sourceset
|
||||
if (ideaActive) {
|
||||
kotlin.srcDir("src/nativeTest/kotlin")
|
||||
}
|
||||
}
|
||||
|
||||
val linuxArm64Main by getting {
|
||||
dependsOn(nativeMain)
|
||||
}
|
||||
|
||||
val linuxArm64Test by getting {
|
||||
dependsOn(nativeTest)
|
||||
}
|
||||
|
||||
val linuxArm32HfpMain by getting {
|
||||
dependsOn(nativeMain)
|
||||
}
|
||||
|
||||
val linuxArm32HfpTest by getting {
|
||||
dependsOn(nativeTest)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
runningOnMacos{
|
||||
|
||||
val iosX64Main by getting {
|
||||
dependsOn(nativeMain)
|
||||
}
|
||||
val iosX64Test by getting {
|
||||
dependsOn(nativeTest)
|
||||
}
|
||||
|
||||
val iosArm64Main by getting {
|
||||
dependsOn(nativeMain)
|
||||
}
|
||||
val iosArm64Test by getting {
|
||||
dependsOn(nativeTest)
|
||||
}
|
||||
|
||||
val iosArm32Main by getting {
|
||||
dependsOn(nativeMain)
|
||||
}
|
||||
val iosArm32Test by getting {
|
||||
dependsOn(nativeTest)
|
||||
}
|
||||
|
||||
val macosX64Main by getting {
|
||||
dependsOn(commonMain)
|
||||
if (ideaActive) {
|
||||
kotlin.srcDir("src/nativeMain/kotlin")
|
||||
}
|
||||
}
|
||||
val macosX64Test by getting {
|
||||
dependsOn(commonTest)
|
||||
if (ideaActive) {
|
||||
kotlin.srcDir("src/nativeTest/kotlin")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Coroutines don't support mingwx86 yet
|
||||
// val mingwX86Main by getting {
|
||||
// dependsOn(commonMain)
|
||||
// dependencies {
|
||||
// }
|
||||
// }
|
||||
|
||||
// val mingwX86Test by getting {
|
||||
// dependsOn(commonTest)
|
||||
// }
|
||||
//
|
||||
runningOnWindows {
|
||||
val mingwX64Main by getting {
|
||||
dependsOn(commonMain)
|
||||
dependencies {
|
||||
}
|
||||
}
|
||||
|
||||
val mingwX64Test by getting {
|
||||
dependsOn(commonTest)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
all {
|
||||
languageSettings.enableLanguageFeature("InlineClasses")
|
||||
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
|
||||
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalStdlibApi")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
task<Copy>("copyPackageJson") {
|
||||
dependsOn("compileKotlinJs")
|
||||
println("Copying package.json from $projectDir/core/src/jsMain/npm")
|
||||
from("$projectDir/src/jsMain/npm")
|
||||
println("Node modules dir ${node.nodeModulesDir}")
|
||||
into("${node.nodeModulesDir}")
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
||||
|
||||
create<Jar>("javadocJar") {
|
||||
dependsOn(dokkaJavadoc)
|
||||
archiveClassifier.set("javadoc")
|
||||
from(dokkaJavadoc.get().outputDirectory)
|
||||
}
|
||||
|
||||
dokkaJavadoc {
|
||||
println("Dokka !")
|
||||
dokkaSourceSets {
|
||||
named("commonMain") {
|
||||
displayName.set("common")
|
||||
platform.set(Platform.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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 ?: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,26 +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.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 20-Jul-2019
|
||||
*/
|
||||
object Config {
|
||||
const val DEBUG = false
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import com.ionspin.kotlin.crypto.authenticated.*
|
||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bMultipart
|
||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bPure
|
||||
import com.ionspin.kotlin.crypto.hash.sha.Sha256Pure
|
||||
import com.ionspin.kotlin.crypto.hash.sha.Sha512Pure
|
||||
import com.ionspin.kotlin.crypto.keyderivation.ArgonResult
|
||||
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Pure
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 24-May-2020
|
||||
*/
|
||||
object CryptoInitializerPure : CryptoInitializer {
|
||||
override suspend fun initialize() {
|
||||
//Nothing to do atm.
|
||||
}
|
||||
|
||||
fun initializeWithCallback(done: () -> Unit) {
|
||||
done()
|
||||
}
|
||||
|
||||
override fun isInitialized(): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
object CryptoPrimitives : PrimitivesApi {
|
||||
private fun checkInitialization() {
|
||||
CryptoInitializerPure.isInitialized()
|
||||
}
|
||||
|
||||
override fun hashBlake2bMultipart(key: UByteArray?, hashLength: Int): Blake2bMultipart {
|
||||
checkInitialization()
|
||||
return Blake2bPure(key, hashLength)
|
||||
}
|
||||
|
||||
override fun hashBlake2b(message: UByteArray, key: UByteArray, hashLength: Int): UByteArray {
|
||||
checkInitialization()
|
||||
return Blake2bPure.digest(message, key, hashLength)
|
||||
}
|
||||
|
||||
override fun hashSha256Multipart(): com.ionspin.kotlin.crypto.hash.sha.Sha256 {
|
||||
checkInitialization()
|
||||
return Sha256Pure()
|
||||
}
|
||||
|
||||
override fun hashSha256(message: UByteArray): UByteArray {
|
||||
checkInitialization()
|
||||
return Sha256Pure.digest(inputMessage = message)
|
||||
}
|
||||
|
||||
override fun hashSha512Multipart(): com.ionspin.kotlin.crypto.hash.sha.Sha512Multipart {
|
||||
checkInitialization()
|
||||
return Sha512Pure()
|
||||
}
|
||||
|
||||
override fun hashSha512(message: UByteArray): UByteArray {
|
||||
checkInitialization()
|
||||
return Sha512Pure.digest(inputMessage = message)
|
||||
}
|
||||
|
||||
override fun deriveKey(
|
||||
password: String,
|
||||
salt: String?,
|
||||
key: String,
|
||||
associatedData: String,
|
||||
parallelism: Int,
|
||||
tagLength: Int,
|
||||
memory: Int,
|
||||
numberOfIterations: Int
|
||||
): ArgonResult {
|
||||
return Argon2Pure.derive(
|
||||
password,
|
||||
salt,
|
||||
key,
|
||||
associatedData,
|
||||
parallelism,
|
||||
tagLength,
|
||||
memory,
|
||||
numberOfIterations
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun SymmetricKey.Companion.randomKey() : SymmetricKey {
|
||||
return SymmetricKey(SRNG.getRandomBytes(32))
|
||||
}
|
||||
|
||||
object Crypto {
|
||||
|
||||
object Hash : HashApi {
|
||||
override fun hash(data: UByteArray, key : UByteArray) : HashedData {
|
||||
return HashedData(Blake2bPure.digest(data, key))
|
||||
}
|
||||
|
||||
override fun multipartHash(key: UByteArray?) : com.ionspin.kotlin.crypto.hash.MultipartHash {
|
||||
return Blake2bPure(key)
|
||||
}
|
||||
}
|
||||
|
||||
object Encryption : EncryptionApi {
|
||||
override fun encrypt(key: SymmetricKey, data : Encryptable<*>, associatedData : UByteArray) : EncryptedData {
|
||||
if (key.value.size != 32) {
|
||||
throw RuntimeException("Invalid key size! Required 32, supplied ${key.value.size}")
|
||||
}
|
||||
val nonce = SRNG.getRandomBytes(24)
|
||||
return EncryptedData(XChaCha20Poly1305Pure.encrypt(key.value, nonce, data.toEncryptableForm(), associatedData), nonce)
|
||||
|
||||
}
|
||||
|
||||
override fun <T: Encryptable<T>> decrypt(key: SymmetricKey, encryptedData : EncryptedData, associatedData: UByteArray, byteArrayDeserializer : (UByteArray) -> T) : T {
|
||||
return byteArrayDeserializer(XChaCha20Poly1305Pure.decrypt(key.value, encryptedData.nonce, encryptedData.ciphertext, associatedData))
|
||||
|
||||
}
|
||||
|
||||
override fun createMultipartEncryptor(key: SymmetricKey) : MultipartAuthenticatedEncryption {
|
||||
return MultipartAuthenticatedEncryptor(key)
|
||||
}
|
||||
|
||||
override fun createMultipartDecryptor(key: SymmetricKey, header: MultipartEncryptionHeader) : MultipartAuthenticatedDecryption {
|
||||
val decryptor = XChaCha20Poly1305Pure(key.value, header.nonce)
|
||||
return MultipartAuthenticatedDecryptor(decryptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MultipartAuthenticatedEncryptor internal constructor(val key : SymmetricKey) : MultipartAuthenticatedEncryption {
|
||||
val header = MultipartEncryptionHeader(SRNG.getRandomBytes(24))
|
||||
val primitive = XChaCha20Poly1305Pure(key.value, header.nonce)
|
||||
override fun encryptPartialData(data: UByteArray, associatedData: UByteArray): EncryptedDataPart {
|
||||
return EncryptedDataPart(primitive.streamEncrypt(data, associatedData, 0U))
|
||||
}
|
||||
|
||||
override fun startEncryption(): MultipartEncryptionHeader {
|
||||
return header.copy()
|
||||
}
|
||||
|
||||
override fun cleanup() {
|
||||
primitive.cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
class MultipartAuthenticatedDecryptor internal constructor(val decryptor: XChaCha20Poly1305Pure) : MultipartAuthenticatedDecryption {
|
||||
override fun decryptPartialData(data: EncryptedDataPart, associatedData: UByteArray): DecryptedDataPart {
|
||||
return DecryptedDataPart(decryptor.streamDecrypt(data.data, associatedData, 0U))
|
||||
}
|
||||
|
||||
override fun cleanup() {
|
||||
decryptor.cleanup()
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
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
|
@ -1,26 +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.
|
||||
*/
|
||||
|
||||
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
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.authenticated
|
||||
|
||||
import com.ionspin.kotlin.crypto.mac.Poly1305
|
||||
import com.ionspin.kotlin.crypto.symmetric.ChaCha20Pure
|
||||
import com.ionspin.kotlin.crypto.util.fromLittleEndianArrayToUIntWithPosition
|
||||
import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-Jun-2020
|
||||
*/
|
||||
internal class ChaCha20Poly1305Pure {
|
||||
companion object {
|
||||
|
||||
fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, associatedData: UByteArray) : UByteArray {
|
||||
val state = UIntArray(16) {
|
||||
when (it) {
|
||||
0 -> ChaCha20Pure.sigma0_32
|
||||
1 -> ChaCha20Pure.sigma1_32
|
||||
2 -> ChaCha20Pure.sigma2_32
|
||||
3 -> ChaCha20Pure.sigma3_32
|
||||
4 -> key.fromLittleEndianArrayToUIntWithPosition(0)
|
||||
5 -> key.fromLittleEndianArrayToUIntWithPosition(4)
|
||||
6 -> key.fromLittleEndianArrayToUIntWithPosition(8)
|
||||
7 -> key.fromLittleEndianArrayToUIntWithPosition(12)
|
||||
8 -> key.fromLittleEndianArrayToUIntWithPosition(16)
|
||||
9 -> key.fromLittleEndianArrayToUIntWithPosition(20)
|
||||
10 -> key.fromLittleEndianArrayToUIntWithPosition(24)
|
||||
11 -> key.fromLittleEndianArrayToUIntWithPosition(28)
|
||||
12 -> 0U
|
||||
13 -> nonce.fromLittleEndianArrayToUIntWithPosition(0)
|
||||
14 -> nonce.fromLittleEndianArrayToUIntWithPosition(4)
|
||||
15 -> nonce.fromLittleEndianArrayToUIntWithPosition(8)
|
||||
else -> 0U
|
||||
}
|
||||
}
|
||||
val oneTimeKey = ChaCha20Pure.hash(state).sliceArray(0 until 32)
|
||||
val cipherText = ChaCha20Pure.xorWithKeystream(key, nonce, message, 1U)
|
||||
val associatedDataPad = UByteArray(16 - associatedData.size % 16) { 0U }
|
||||
val cipherTextPad = UByteArray(16 - cipherText.size % 16) { 0U }
|
||||
val macData = associatedData + associatedDataPad +
|
||||
cipherText + cipherTextPad +
|
||||
associatedData.size.toULong().toLittleEndianUByteArray() +
|
||||
cipherText.size.toULong().toLittleEndianUByteArray()
|
||||
val tag = Poly1305.poly1305Authenticate(oneTimeKey, macData)
|
||||
return cipherText + tag
|
||||
}
|
||||
}
|
||||
}
|
@ -1,203 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.authenticated
|
||||
|
||||
import com.ionspin.kotlin.crypto.AuthenticatedEncryption
|
||||
import com.ionspin.kotlin.crypto.InvalidTagException
|
||||
import com.ionspin.kotlin.crypto.mac.Poly1305
|
||||
import com.ionspin.kotlin.crypto.symmetric.ChaCha20Pure
|
||||
import com.ionspin.kotlin.crypto.symmetric.XChaCha20Pure
|
||||
import com.ionspin.kotlin.crypto.util.*
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-Jun-2020
|
||||
*/
|
||||
class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray) {
|
||||
companion object : AuthenticatedEncryption {
|
||||
|
||||
override fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, associatedData: UByteArray) : UByteArray {
|
||||
val subKey = XChaCha20Pure.hChacha(key, nonce)
|
||||
val authKey =
|
||||
ChaCha20Pure.xorWithKeystream(
|
||||
subKey.toLittleEndianUByteArray(),
|
||||
ubyteArrayOf(0U, 0U, 0U, 0U) + nonce.sliceArray(16 until 24),
|
||||
UByteArray(64) { 0U },
|
||||
0U // If this is moved as a default parameter in encrypt, and not here (in 1.4-M2)
|
||||
// js compiler dies with: e: java.lang.NullPointerException
|
||||
// at org.jetbrains.kotlin.ir.backend.js.lower.ConstTransformer$visitConst$1$3.invoke(ConstLowering.kt:28)
|
||||
// at org.jetbrains.kotlin.ir.backend.js.lower.ConstTransformer.lowerConst(ConstLowering.kt:38)
|
||||
)
|
||||
val cipherText = XChaCha20Pure.xorWithKeystream(key, nonce, message, 1U)
|
||||
val associatedDataPad = UByteArray(16 - associatedData.size % 16) { 0U }
|
||||
val cipherTextPad = UByteArray(16 - cipherText.size % 16) { 0U }
|
||||
val macData = associatedData + associatedDataPad +
|
||||
cipherText + cipherTextPad +
|
||||
associatedData.size.toULong().toLittleEndianUByteArray() +
|
||||
cipherText.size.toULong().toLittleEndianUByteArray()
|
||||
val tag = Poly1305.poly1305Authenticate(authKey, macData)
|
||||
return cipherText + tag
|
||||
}
|
||||
|
||||
override fun decrypt(key: UByteArray, nonce: UByteArray, cipherText: UByteArray, associatedData: UByteArray) : UByteArray {
|
||||
val subKey = XChaCha20Pure.hChacha(key, nonce)
|
||||
val authKey =
|
||||
ChaCha20Pure.xorWithKeystream(
|
||||
subKey.toLittleEndianUByteArray(),
|
||||
ubyteArrayOf(0U, 0U, 0U, 0U) + nonce.sliceArray(16 until 24),
|
||||
UByteArray(64) { 0U },
|
||||
0U
|
||||
)
|
||||
//2. Get the tag
|
||||
val tag = cipherText.sliceArray(cipherText.size - 16 until cipherText.size)
|
||||
//3. Verify tag is valid
|
||||
val cipherTextWithoutTag = cipherText.sliceArray(0 until cipherText.size - 16)
|
||||
val associatedDataPad = UByteArray(16 - associatedData.size % 16) { 0U }
|
||||
val cipherTextPad = UByteArray(16 - cipherTextWithoutTag.size % 16) { 0U }
|
||||
val macData = associatedData + associatedDataPad +
|
||||
cipherTextWithoutTag + cipherTextPad +
|
||||
associatedData.size.toULong().toLittleEndianUByteArray() +
|
||||
cipherTextWithoutTag.size.toULong().toLittleEndianUByteArray()
|
||||
val calculatedTag = Poly1305.poly1305Authenticate(authKey, macData)
|
||||
if (!calculatedTag.contentEquals(tag)) {
|
||||
throw InvalidTagException()
|
||||
}
|
||||
//4. Decrypt data
|
||||
return XChaCha20Pure.xorWithKeystream(key, nonce, cipherTextWithoutTag, 1U)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private val polyBuffer = UByteArray(16)
|
||||
private var polyBufferByteCounter = 0
|
||||
|
||||
private var processedBytes = 0
|
||||
|
||||
internal val calcKey : UByteArray = UByteArray(32)
|
||||
internal val calcNonce : UByteArray = UByteArray(12)
|
||||
|
||||
init {
|
||||
val calc = XChaCha20Pure.hChacha(key, nonce).toLittleEndianUByteArray()
|
||||
calc.sliceArray(0 until 32).copyInto(calcKey)
|
||||
nonce.sliceArray(16 until 24).copyInto(calcNonce, 4)
|
||||
calcNonce[0] = 1U
|
||||
calcNonce[1] = 0U
|
||||
calcNonce[2] = 0U
|
||||
calcNonce[3] = 0U
|
||||
}
|
||||
|
||||
fun streamEncrypt(data: UByteArray, associatedData: UByteArray, tag : UByte) : UByteArray {
|
||||
//get encryption state
|
||||
val block = UByteArray(64) { 0U }
|
||||
ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 0U).copyInto(block) // This is equivalent to the first 64 bytes of keystream
|
||||
val poly1305 = Poly1305(block)
|
||||
block.overwriteWithZeroes()
|
||||
if (associatedData.isNotEmpty()) {
|
||||
val associatedDataPadded = associatedData + UByteArray(16 - associatedData.size % 16) { 0U }
|
||||
processPolyBytes(poly1305, associatedDataPadded)
|
||||
}
|
||||
block[0] = tag
|
||||
ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 1U).copyInto(block) // This just xors block[0] with keystream
|
||||
processPolyBytes(poly1305, block) // but updates the mac with the full block!
|
||||
// In libsodium c code, it now sets the first byte to be a tag, we'll just save it for now
|
||||
val encryptedTag = block[0]
|
||||
//And then encrypt the rest of the message
|
||||
val ciphertext = ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, data, 2U) // With appropriate counter
|
||||
// Next we update the poly1305 with ciphertext and padding, BUT the padding in libsodium is not correctly calculated, so it doesn't
|
||||
// pad correctly. https://github.com/jedisct1/libsodium/issues/976
|
||||
// We want to use libsodium in delegated flavour, so we will use the same incorrect padding here.
|
||||
// From security standpoint there are no obvious drawbacks, as padding was initially added to decrease implementation complexity.
|
||||
processPolyBytes(poly1305, ciphertext + UByteArray(((16U + data.size.toUInt() - block.size.toUInt()) % 16U).toInt()) { 0U } ) //TODO this is inefficient as it creates a new array and copies data
|
||||
// Last 16byte block containing actual additional data nad ciphertext sizes
|
||||
val finalMac = associatedData.size.toULong().toLittleEndianUByteArray() + (ciphertext.size + 64).toULong().toLittleEndianUByteArray()
|
||||
processPolyBytes(poly1305, finalMac)
|
||||
val mac = poly1305.finalizeMac(polyBuffer.sliceArray(0 until polyBufferByteCounter))
|
||||
calcNonce.xorWithPositionsAndInsertIntoArray(0, 12, mac, 0, calcNonce, 0)
|
||||
return ubyteArrayOf(encryptedTag) + ciphertext + mac
|
||||
}
|
||||
|
||||
fun streamDecrypt(data: UByteArray, associatedData: UByteArray, tag: UByte) : UByteArray {
|
||||
val block = UByteArray(64) { 0U }
|
||||
ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 0U).copyInto(block) // This is equivalent to the first 64 bytes of keystream
|
||||
val poly1305 = Poly1305(block)
|
||||
block.overwriteWithZeroes()
|
||||
if (associatedData.isNotEmpty()) {
|
||||
val associatedDataPadded = associatedData + UByteArray(16 - associatedData.size % 16) { 0U }
|
||||
processPolyBytes(poly1305, associatedDataPadded)
|
||||
}
|
||||
block[0] = data[0]
|
||||
ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 1U).copyInto(block)// get the keystream xored with zeroes, but also decrypteg tag marker
|
||||
val tag = block[0] //get the decrypted tag
|
||||
block[0] = data[0] // this brings it back to state that is delivered to poly in encryption function
|
||||
processPolyBytes(poly1305, block)
|
||||
// Next we update the poly1305 with ciphertext and padding, BUT the padding in libsodium is not correctly calculated, so it doesn't
|
||||
// pad correctly. https://github.com/jedisct1/libsodium/issues/976
|
||||
// We want to use libsodium in delegated flavour, so we will use the same incorrect padding here.
|
||||
// From security standpoint there are no obvious drawbacks, as padding was initially added to decrease implementation complexity.
|
||||
val ciphertext = data.sliceArray(1 until data.size - 16)
|
||||
processPolyBytes(poly1305, ciphertext + UByteArray(((16U + ciphertext.size.toUInt() - block.size.toUInt()) % 16U).toInt()) { 0U } )
|
||||
val plaintext = ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, ciphertext, 2U)
|
||||
val finalMac = associatedData.size.toULong().toLittleEndianUByteArray() + (ciphertext.size + 64).toULong().toLittleEndianUByteArray()
|
||||
processPolyBytes(poly1305, finalMac)
|
||||
val mac = poly1305.finalizeMac(polyBuffer.sliceArray(0 until polyBufferByteCounter))
|
||||
val expectedMac = data.sliceArray(data.size - 16 until data.size)
|
||||
|
||||
if (expectedMac.contentEquals(mac).not()){
|
||||
throw InvalidTagException()
|
||||
}
|
||||
calcNonce.xorWithPositionsAndInsertIntoArray(0, 12, mac, 0, calcNonce, 0)
|
||||
return plaintext
|
||||
}
|
||||
|
||||
fun cleanup() {
|
||||
key.overwriteWithZeroes()
|
||||
nonce.overwriteWithZeroes()
|
||||
calcKey.overwriteWithZeroes()
|
||||
calcNonce.overwriteWithZeroes()
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun processPolyBytes(updateableMacPrimitive: Poly1305, data: UByteArray) {
|
||||
if (polyBufferByteCounter == 0) {
|
||||
val polyBlocks = data.size / 16
|
||||
val polyRemainder = data.size % 16
|
||||
for (i in 0 until polyBlocks) {
|
||||
updateableMacPrimitive.updateMac(data.sliceArray(i * 16 until i * 16 + 16))
|
||||
}
|
||||
if (polyRemainder != 0) {
|
||||
for (i in 0 until polyRemainder) {
|
||||
polyBuffer[i] = data[data.size - polyRemainder + i]
|
||||
}
|
||||
polyBufferByteCounter = polyRemainder
|
||||
}
|
||||
} else {
|
||||
if (polyBufferByteCounter + data.size < 16) {
|
||||
for (i in 0 until data.size) {
|
||||
polyBuffer[polyBufferByteCounter + i] = data[i]
|
||||
}
|
||||
polyBufferByteCounter += data.size
|
||||
} else {
|
||||
val borrowed = 16 - polyBufferByteCounter
|
||||
for (i in polyBufferByteCounter until 16) {
|
||||
polyBuffer[i] = data[i - polyBufferByteCounter]
|
||||
}
|
||||
updateableMacPrimitive.updateMac(polyBuffer)
|
||||
polyBufferByteCounter = 0
|
||||
val polyBlocks = (data.size - borrowed) / 16
|
||||
val polyRemainder = (data.size - borrowed) % 16
|
||||
for (i in 0 until polyBlocks) {
|
||||
updateableMacPrimitive.updateMac(data.sliceArray(borrowed + i * 16 until borrowed + i * 16 + 16))
|
||||
}
|
||||
if (polyRemainder != 0) {
|
||||
for (i in 0 until polyRemainder) {
|
||||
polyBuffer[i] = data[borrowed + i]
|
||||
}
|
||||
polyBufferByteCounter = polyRemainder
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,351 +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.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.hash.blake2b
|
||||
|
||||
import com.ionspin.kotlin.bignum.integer.BigInteger
|
||||
import com.ionspin.kotlin.bignum.integer.toBigInteger
|
||||
import com.ionspin.kotlin.crypto.*
|
||||
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||
import com.ionspin.kotlin.crypto.util.rotateRight
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
class Blake2bPure(val key: UByteArray? = null, val hashLength: Int = 64) : Blake2bMultipart {
|
||||
|
||||
companion object : Blake2b {
|
||||
//Hack start
|
||||
//If this line is not included konanc 1.4-M1 fails to link because it cant find ByteArray which is
|
||||
//a backing class for UByteArray
|
||||
val byteArray: ByteArray = byteArrayOf(0, 1)
|
||||
//Hack end
|
||||
const val BITS_IN_WORD = 64
|
||||
const val ROUNDS_IN_COMPRESS = 12
|
||||
const val BLOCK_BYTES = 128
|
||||
override val MAX_HASH_BYTES = 64
|
||||
const val MIN_HASH_BYTES = 1
|
||||
const val MAX_KEY_BYTES = 64
|
||||
const val MIN_KEY_BYTES = 0
|
||||
val MAX_INPUT_BYTES = 2.toBigInteger() shl 128
|
||||
|
||||
private val sigma = arrayOf(
|
||||
arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
|
||||
arrayOf(14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3),
|
||||
arrayOf(11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4),
|
||||
arrayOf(7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8),
|
||||
arrayOf(9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13),
|
||||
arrayOf(2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9),
|
||||
arrayOf(12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11),
|
||||
arrayOf(13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10),
|
||||
arrayOf(6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5),
|
||||
arrayOf(10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0)
|
||||
)
|
||||
|
||||
|
||||
private val iv = arrayOf(
|
||||
0X6A09E667F3BCC908UL,
|
||||
0XBB67AE8584CAA73BUL,
|
||||
0X3C6EF372FE94F82BUL,
|
||||
0XA54FF53A5F1D36F1UL,
|
||||
0X510E527FADE682D1UL,
|
||||
0X9B05688C2B3E6C1FUL,
|
||||
0X1F83D9ABFB41BD6BUL,
|
||||
0X5BE0CD19137E2179UL
|
||||
)
|
||||
|
||||
const val R1 = 32
|
||||
const val R2 = 24
|
||||
const val R3 = 16
|
||||
const val R4 = 63
|
||||
|
||||
internal fun mixRound(input: Array<ULong>, message: Array<ULong>, round: Int): Array<ULong> {
|
||||
var v = input
|
||||
val selectedSigma = sigma[round % 10]
|
||||
v = mix(v, 0, 4, 8, 12, message[selectedSigma[0]], message[selectedSigma[1]])
|
||||
v = mix(v, 1, 5, 9, 13, message[selectedSigma[2]], message[selectedSigma[3]])
|
||||
v = mix(v, 2, 6, 10, 14, message[selectedSigma[4]], message[selectedSigma[5]])
|
||||
v = mix(v, 3, 7, 11, 15, message[selectedSigma[6]], message[selectedSigma[7]])
|
||||
v = mix(v, 0, 5, 10, 15, message[selectedSigma[8]], message[selectedSigma[9]])
|
||||
v = mix(v, 1, 6, 11, 12, message[selectedSigma[10]], message[selectedSigma[11]])
|
||||
v = mix(v, 2, 7, 8, 13, message[selectedSigma[12]], message[selectedSigma[13]])
|
||||
v = mix(v, 3, 4, 9, 14, message[selectedSigma[14]], message[selectedSigma[15]])
|
||||
return v
|
||||
|
||||
}
|
||||
|
||||
private fun mix(v: Array<ULong>, a: Int, b: Int, c: Int, d: Int, x: ULong, y: ULong): Array<ULong> {
|
||||
v[a] = (v[a] + v[b] + x)
|
||||
v[d] = (v[d] xor v[a]) rotateRight R1
|
||||
v[c] = (v[c] + v[d])
|
||||
v[b] = (v[b] xor v[c]) rotateRight R2
|
||||
v[a] = (v[a] + v[b] + y)
|
||||
v[d] = (v[d] xor v[a]) rotateRight R3
|
||||
v[c] = (v[c] + v[d])
|
||||
v[b] = (v[b] xor v[c]) rotateRight R4
|
||||
return v
|
||||
}
|
||||
|
||||
fun compress(
|
||||
h: Array<ULong>,
|
||||
input: UByteArray,
|
||||
offsetCounter: BigInteger,
|
||||
finalBlock: Boolean
|
||||
): Array<ULong> {
|
||||
var v = Array(16) {
|
||||
when (it) {
|
||||
in 0..7 -> h[it]
|
||||
else -> iv[it - 8]
|
||||
}
|
||||
}
|
||||
|
||||
val m = input.foldIndexed(Array(16) { 0UL }) { index, acc, byte ->
|
||||
val slot = index / 8
|
||||
val position = index % 8
|
||||
acc[slot] = acc[slot] + (byte.toULong() shl ((position) * 8))
|
||||
acc
|
||||
}
|
||||
if (Config.DEBUG) {
|
||||
val printout = m.map { it.toString(16) }.chunked(4)
|
||||
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
||||
println("Offset ${offsetCounter}")
|
||||
}
|
||||
|
||||
v[12] = v[12] xor offsetCounter.ulongValue()
|
||||
v[13] = v[13] xor (offsetCounter shr BITS_IN_WORD).ulongValue()
|
||||
|
||||
if (finalBlock) {
|
||||
v[14] = v[14].inv()
|
||||
}
|
||||
|
||||
for (i in 0 until ROUNDS_IN_COMPRESS) {
|
||||
mixRound(v, m, i)
|
||||
}
|
||||
|
||||
for (i in 0..7) {
|
||||
h[i] = h[i] xor v[i] xor v[i + 8]
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
|
||||
fun digest(inputString: String, key: String?, hashLength: Int): UByteArray {
|
||||
val array = inputString.encodeToUByteArray()
|
||||
val keyBytes = key?.run {
|
||||
encodeToUByteArray()
|
||||
} ?: ubyteArrayOf()
|
||||
return digest(inputMessage = array, key = keyBytes, hashLength = hashLength)
|
||||
|
||||
}
|
||||
|
||||
override fun digest(
|
||||
inputMessage: UByteArray,
|
||||
key: UByteArray,
|
||||
hashLength: Int
|
||||
): UByteArray {
|
||||
if (hashLength > MAX_HASH_BYTES) {
|
||||
throw RuntimeException("Invalid hash length. Requested length more than maximum length. Requested length $hashLength")
|
||||
}
|
||||
val chunkedMessage = inputMessage.chunked(BLOCK_BYTES).map { it.toUByteArray() }.toTypedArray()
|
||||
|
||||
val h = iv.copyOf()
|
||||
|
||||
h[0] = h[0] xor 0x01010000UL xor (key.size.toULong() shl 8) xor hashLength.toULong()
|
||||
|
||||
|
||||
val message = if (key.isEmpty()) {
|
||||
if (chunkedMessage.isEmpty()) {
|
||||
Array(1) {
|
||||
UByteArray(128) {
|
||||
0U
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chunkedMessage
|
||||
}
|
||||
} else {
|
||||
arrayOf(padToBlock(key), *chunkedMessage)
|
||||
}
|
||||
|
||||
if (message.size > 1) {
|
||||
for (i in 0 until message.size - 1) {
|
||||
compress(h, message[i], ((i + 1) * BLOCK_BYTES).toBigInteger(), false).copyInto(h)
|
||||
}
|
||||
}
|
||||
|
||||
val lastSize = when (message.size) {
|
||||
0 -> 0
|
||||
1 -> message[message.size - 1].size
|
||||
else -> (message.size - 1) * BLOCK_BYTES + message[message.size - 1].size
|
||||
|
||||
}
|
||||
|
||||
val lastBlockPadded = if (message.isNotEmpty()) {
|
||||
padToBlock(message[message.size - 1])
|
||||
} else {
|
||||
UByteArray(16) { 0U }
|
||||
}
|
||||
|
||||
compress(h, lastBlockPadded, lastSize.toBigInteger(), true).copyInto(h)
|
||||
|
||||
|
||||
return formatResult(h).copyOfRange(0, hashLength)
|
||||
}
|
||||
|
||||
private fun formatResult(h: Array<ULong>): UByteArray {
|
||||
return h.map {
|
||||
arrayOf(
|
||||
(it and 0xFFUL).toUByte(),
|
||||
(it shr 8 and 0xFFUL).toUByte(),
|
||||
(it shr 16 and 0xFFUL).toUByte(),
|
||||
(it shr 24 and 0xFFUL).toUByte(),
|
||||
(it shr 32 and 0xFFUL).toUByte(),
|
||||
(it shr 40 and 0xFFUL).toUByte(),
|
||||
(it shr 48 and 0xFFUL).toUByte(),
|
||||
(it shr 56 and 0xFFUL).toUByte()
|
||||
)
|
||||
}.flatMap {
|
||||
it.toList()
|
||||
}.toUByteArray()
|
||||
}
|
||||
|
||||
private fun padToBlock(unpadded: UByteArray): UByteArray {
|
||||
if (unpadded.size == BLOCK_BYTES) {
|
||||
return unpadded
|
||||
}
|
||||
|
||||
if (unpadded.size > BLOCK_BYTES) {
|
||||
throw IllegalStateException("Block larger than 128 bytes")
|
||||
}
|
||||
|
||||
return UByteArray(BLOCK_BYTES) {
|
||||
when (it) {
|
||||
in 0 until unpadded.size -> unpadded[it]
|
||||
else -> 0U
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
key: String?,
|
||||
requestedHashLenght: Int = 64
|
||||
) : this(
|
||||
(key?.encodeToUByteArray() ?: ubyteArrayOf()),
|
||||
requestedHashLenght
|
||||
)
|
||||
|
||||
override val MAX_HASH_BYTES: Int = Blake2bPure.MAX_HASH_BYTES
|
||||
|
||||
var h = iv.copyOf()
|
||||
var counter = BigInteger.ZERO
|
||||
var bufferCounter = 0
|
||||
var buffer = UByteArray(BLOCK_BYTES) { 0U }
|
||||
|
||||
|
||||
init {
|
||||
h[0] = h[0] xor 0x01010000UL xor (key?.run { size.toULong() shl 8 } ?: 0UL) xor hashLength.toULong()
|
||||
|
||||
if (!key.isNullOrEmpty()) {
|
||||
appendToBuffer(padToBlock(key), bufferCounter)
|
||||
}
|
||||
}
|
||||
|
||||
override fun update(data: UByteArray) {
|
||||
if (data.isEmpty()) {
|
||||
throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
|
||||
}
|
||||
|
||||
when {
|
||||
bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter)
|
||||
bufferCounter + data.size >= BLOCK_BYTES -> {
|
||||
val chunked = data.chunked(BLOCK_BYTES).map { it.toUByteArray() }
|
||||
chunked.forEach { chunk ->
|
||||
if (bufferCounter + chunk.size < BLOCK_BYTES) {
|
||||
appendToBuffer(chunk, bufferCounter)
|
||||
} else {
|
||||
chunk.copyInto(
|
||||
destination = buffer,
|
||||
destinationOffset = bufferCounter,
|
||||
startIndex = 0,
|
||||
endIndex = BLOCK_BYTES - bufferCounter
|
||||
)
|
||||
counter += BLOCK_BYTES
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun update(data: String) {
|
||||
update(data.encodeToUByteArray())
|
||||
}
|
||||
|
||||
private fun appendToBuffer(array: UByteArray, start: Int) {
|
||||
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||
bufferCounter += array.size
|
||||
}
|
||||
|
||||
private fun consumeBlock(block: UByteArray) {
|
||||
h = compress(h, block, counter, false)
|
||||
}
|
||||
|
||||
override fun digest(): UByteArray {
|
||||
val lastBlockPadded = padToBlock(buffer)
|
||||
counter += bufferCounter
|
||||
compress(h, lastBlockPadded, counter, true)
|
||||
|
||||
val result = formatResult(h)
|
||||
reset()
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
|
||||
private fun reset() {
|
||||
h = iv.copyOf()
|
||||
counter = BigInteger.ZERO
|
||||
bufferCounter = 0
|
||||
buffer = UByteArray(BLOCK_BYTES) { 0U }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,328 +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.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||
import com.ionspin.kotlin.crypto.util.rotateRight
|
||||
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 17-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
|
||||
class Sha256Pure : Sha256 {
|
||||
|
||||
override val MAX_HASH_BYTES: Int = 32
|
||||
|
||||
|
||||
companion object : StatelessSha256 {
|
||||
const val BLOCK_SIZE = 512
|
||||
const val BLOCK_SIZE_IN_BYTES = 64
|
||||
const val UINT_MASK = 0xFFFFFFFFU
|
||||
const val BYTE_MASK_FROM_ULONG = 0xFFUL
|
||||
const val BYTE_MASK_FROM_UINT = 0xFFU
|
||||
|
||||
override val MAX_HASH_BYTES: Int = 32
|
||||
|
||||
val iv = arrayOf(
|
||||
0x6a09e667U,
|
||||
0xbb67ae85U,
|
||||
0x3c6ef372U,
|
||||
0xa54ff53aU,
|
||||
0x510e527fU,
|
||||
0x9b05688cU,
|
||||
0x1f83d9abU,
|
||||
0x5be0cd19U
|
||||
)
|
||||
|
||||
val k = arrayOf(
|
||||
0x428a2f98U, 0x71374491U, 0xb5c0fbcfU, 0xe9b5dba5U, 0x3956c25bU, 0x59f111f1U, 0x923f82a4U, 0xab1c5ed5U,
|
||||
0xd807aa98U, 0x12835b01U, 0x243185beU, 0x550c7dc3U, 0x72be5d74U, 0x80deb1feU, 0x9bdc06a7U, 0xc19bf174U,
|
||||
0xe49b69c1U, 0xefbe4786U, 0x0fc19dc6U, 0x240ca1ccU, 0x2de92c6fU, 0x4a7484aaU, 0x5cb0a9dcU, 0x76f988daU,
|
||||
0x983e5152U, 0xa831c66dU, 0xb00327c8U, 0xbf597fc7U, 0xc6e00bf3U, 0xd5a79147U, 0x06ca6351U, 0x14292967U,
|
||||
0x27b70a85U, 0x2e1b2138U, 0x4d2c6dfcU, 0x53380d13U, 0x650a7354U, 0x766a0abbU, 0x81c2c92eU, 0x92722c85U,
|
||||
0xa2bfe8a1U, 0xa81a664bU, 0xc24b8b70U, 0xc76c51a3U, 0xd192e819U, 0xd6990624U, 0xf40e3585U, 0x106aa070U,
|
||||
0x19a4c116U, 0x1e376c08U, 0x2748774cU, 0x34b0bcb5U, 0x391c0cb3U, 0x4ed8aa4aU, 0x5b9cca4fU, 0x682e6ff3U,
|
||||
0x748f82eeU, 0x78a5636fU, 0x84c87814U, 0x8cc70208U, 0x90befffaU, 0xa4506cebU, 0xbef9a3f7U, 0xc67178f2U
|
||||
)
|
||||
|
||||
|
||||
override fun digest(inputMessage: UByteArray): UByteArray {
|
||||
|
||||
var h = iv.copyOf()
|
||||
|
||||
val expansionArray = createExpansionArray(inputMessage.size.toLong())
|
||||
|
||||
val chunks = (
|
||||
inputMessage +
|
||||
expansionArray +
|
||||
(inputMessage.size * 8).toULong().toPaddedByteArray()
|
||||
)
|
||||
.chunked(BLOCK_SIZE_IN_BYTES)
|
||||
|
||||
chunks.forEach { chunk ->
|
||||
val w = expandChunk(chunk.toUByteArray())
|
||||
mix(h, w).copyInto(h)
|
||||
|
||||
}
|
||||
|
||||
val digest = h[0].toPaddedByteArray() +
|
||||
h[1].toPaddedByteArray() +
|
||||
h[2].toPaddedByteArray() +
|
||||
h[3].toPaddedByteArray() +
|
||||
h[4].toPaddedByteArray() +
|
||||
h[5].toPaddedByteArray() +
|
||||
h[6].toPaddedByteArray() +
|
||||
h[7].toPaddedByteArray()
|
||||
return digest
|
||||
}
|
||||
|
||||
private fun scheduleSigma0(value: UInt): UInt {
|
||||
return value.rotateRight(7) xor value.rotateRight(18) xor (value shr 3)
|
||||
}
|
||||
|
||||
private fun scheduleSigma1(value: UInt): UInt {
|
||||
return value.rotateRight(17) xor value.rotateRight(19) xor (value shr 10)
|
||||
}
|
||||
|
||||
private fun compressionSigma0(a: UInt): UInt {
|
||||
return (a rotateRight 2) xor (a rotateRight 13) xor (a rotateRight 22)
|
||||
}
|
||||
|
||||
private fun compressionSigma1(e: UInt): UInt {
|
||||
return (e rotateRight 6) xor (e rotateRight 11) xor (e rotateRight 25)
|
||||
}
|
||||
|
||||
private fun ch(x: UInt, y: UInt, z: UInt): UInt {
|
||||
return ((x and y) xor ((x xor UINT_MASK) and z))
|
||||
}
|
||||
|
||||
private fun maj(x: UInt, y: UInt, z: UInt): UInt {
|
||||
return (((x and y) xor (x and z) xor (y and z)))
|
||||
}
|
||||
|
||||
private fun expandChunk(chunk: UByteArray): Array<UInt> {
|
||||
val w = Array<UInt>(BLOCK_SIZE_IN_BYTES) {
|
||||
when (it) {
|
||||
in 0 until 16 -> {
|
||||
var collected = (chunk[(it * 4)].toUInt() shl 24) +
|
||||
(chunk[(it * 4) + 1].toUInt() shl 16) +
|
||||
(chunk[(it * 4) + 2].toUInt() shl 8) +
|
||||
(chunk[(it * 4) + 3].toUInt())
|
||||
collected
|
||||
}
|
||||
else -> 0U
|
||||
}
|
||||
}
|
||||
for (i in 16 until BLOCK_SIZE_IN_BYTES) {
|
||||
val s0 = scheduleSigma0(w[i - 15])
|
||||
val s1 = scheduleSigma1(w[i - 2])
|
||||
w[i] = w[i - 16] + s0 + w[i - 7] + s1
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
private fun mix(h: Array<UInt>, w: Array<UInt>): Array<UInt> {
|
||||
var paramA = h[0]
|
||||
var paramB = h[1]
|
||||
var paramC = h[2]
|
||||
var paramD = h[3]
|
||||
var paramE = h[4]
|
||||
var paramF = h[5]
|
||||
var paramG = h[6]
|
||||
var paramH = h[7]
|
||||
|
||||
for (i in 0 until BLOCK_SIZE_IN_BYTES) {
|
||||
val s1 = compressionSigma1(paramE)
|
||||
val ch = ch(paramE, paramF, paramG)
|
||||
val temp1 = paramH + s1 + ch + k[i] + w[i]
|
||||
val s0 = compressionSigma0(paramA)
|
||||
val maj = maj(paramA, paramB, paramC)
|
||||
val temp2 = s0 + maj
|
||||
paramH = paramG
|
||||
paramG = paramF
|
||||
paramF = paramE
|
||||
paramE = paramD + temp1
|
||||
paramD = paramC
|
||||
paramC = paramB
|
||||
paramB = paramA
|
||||
paramA = temp1 + temp2
|
||||
}
|
||||
|
||||
h[0] += paramA
|
||||
h[1] += paramB
|
||||
h[2] += paramC
|
||||
h[3] += paramD
|
||||
h[4] += paramE
|
||||
h[5] += paramF
|
||||
h[6] += paramG
|
||||
h[7] += paramH
|
||||
return h
|
||||
}
|
||||
|
||||
|
||||
fun createExpansionArray(originalSizeInBytes: Long): UByteArray {
|
||||
val originalMessageSizeInBits = originalSizeInBytes * 8
|
||||
|
||||
|
||||
//K such that L + 1 + K + 64 is a multiple of 512
|
||||
val expandedRemainderOf512 = (originalMessageSizeInBits + BLOCK_SIZE_IN_BYTES + 1) % BLOCK_SIZE
|
||||
val zeroAddAmount = when (expandedRemainderOf512) {
|
||||
0L -> 0
|
||||
else -> ((BLOCK_SIZE - expandedRemainderOf512) / 8).toInt()
|
||||
}
|
||||
val expansionArray = UByteArray(zeroAddAmount + 1) {
|
||||
when (it) {
|
||||
0 -> 0b10000000U
|
||||
else -> 0U
|
||||
}
|
||||
}
|
||||
return expansionArray
|
||||
}
|
||||
|
||||
private fun ULong.toPaddedByteArray(): UByteArray {
|
||||
val byteMask = BYTE_MASK_FROM_ULONG
|
||||
return UByteArray(8) {
|
||||
when (it) {
|
||||
7 -> (this and byteMask).toUByte()
|
||||
6 -> ((this shr 8) and byteMask).toUByte()
|
||||
5 -> ((this shr 16) and byteMask).toUByte()
|
||||
4 -> ((this shr 24) and byteMask).toUByte()
|
||||
3 -> ((this shr 32) and byteMask).toUByte()
|
||||
2 -> ((this shr 40) and byteMask).toUByte()
|
||||
1 -> ((this shr 48) and byteMask).toUByte()
|
||||
0 -> ((this shr 54) and byteMask).toUByte()
|
||||
else -> throw RuntimeException("Invalid conversion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun UInt.toPaddedByteArray(): UByteArray {
|
||||
val byteMask = BYTE_MASK_FROM_UINT
|
||||
return UByteArray(4) {
|
||||
when (it) {
|
||||
3 -> (this and byteMask).toUByte()
|
||||
2 -> ((this shr 8) and byteMask).toUByte()
|
||||
1 -> ((this shr 16) and byteMask).toUByte()
|
||||
0 -> ((this shr 24) and byteMask).toUByte()
|
||||
else -> throw RuntimeException("Invalid conversion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var h = iv.copyOf()
|
||||
var counter = 0L
|
||||
var bufferCounter = 0
|
||||
var buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { 0U }
|
||||
var digested = false
|
||||
|
||||
|
||||
fun update(data: String) {
|
||||
if (digested) {
|
||||
throw RuntimeException("This instance of updateable SHA256 was already finished once. You should use new instance")
|
||||
}
|
||||
return update(data.encodeToUByteArray())
|
||||
}
|
||||
|
||||
override fun update(data: UByteArray) {
|
||||
if (data.isEmpty()) {
|
||||
throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
|
||||
}
|
||||
|
||||
when {
|
||||
bufferCounter + data.size < BLOCK_SIZE_IN_BYTES -> appendToBuffer(data, bufferCounter)
|
||||
bufferCounter + data.size == BLOCK_SIZE_IN_BYTES -> {
|
||||
counter += BLOCK_SIZE_IN_BYTES
|
||||
consumeBlock(data)
|
||||
}
|
||||
bufferCounter + data.size == BLOCK_SIZE_IN_BYTES -> {
|
||||
counter += BLOCK_SIZE_IN_BYTES
|
||||
consumeBlock(data)
|
||||
}
|
||||
bufferCounter + data.size >= BLOCK_SIZE_IN_BYTES -> {
|
||||
val chunked = data.chunked(BLOCK_SIZE_IN_BYTES)
|
||||
chunked.forEach { chunk ->
|
||||
if (bufferCounter + chunk.size < BLOCK_SIZE_IN_BYTES) {
|
||||
appendToBuffer(chunk.toUByteArray(), bufferCounter)
|
||||
} else {
|
||||
chunk.toUByteArray().copyInto(
|
||||
destination = buffer,
|
||||
destinationOffset = bufferCounter,
|
||||
startIndex = 0,
|
||||
endIndex = BLOCK_SIZE_IN_BYTES - bufferCounter
|
||||
)
|
||||
counter += BLOCK_SIZE_IN_BYTES
|
||||
consumeBlock(buffer)
|
||||
buffer = UByteArray(BLOCK_SIZE_IN_BYTES) {
|
||||
when (it) {
|
||||
in (0 until (chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter))) -> {
|
||||
chunk[it + (BLOCK_SIZE_IN_BYTES - bufferCounter)]
|
||||
}
|
||||
else -> {
|
||||
0U
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
bufferCounter = chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun consumeBlock(block: UByteArray) {
|
||||
val w = expandChunk(block)
|
||||
mix(h, w).copyInto(h)
|
||||
}
|
||||
|
||||
override fun digest(): UByteArray {
|
||||
val length = counter + bufferCounter
|
||||
val expansionArray = createExpansionArray(length)
|
||||
val finalBlock =
|
||||
buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPaddedByteArray()
|
||||
finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach {
|
||||
consumeBlock(it.toUByteArray())
|
||||
}
|
||||
|
||||
|
||||
val digest = h[0].toPaddedByteArray() +
|
||||
h[1].toPaddedByteArray() +
|
||||
h[2].toPaddedByteArray() +
|
||||
h[3].toPaddedByteArray() +
|
||||
h[4].toPaddedByteArray() +
|
||||
h[5].toPaddedByteArray() +
|
||||
h[6].toPaddedByteArray() +
|
||||
h[7].toPaddedByteArray()
|
||||
digested = true
|
||||
return digest
|
||||
}
|
||||
|
||||
private fun appendToBuffer(array: UByteArray, start: Int) {
|
||||
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||
bufferCounter += array.size
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,394 +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.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.hash.sha
|
||||
|
||||
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||
import com.ionspin.kotlin.crypto.util.rotateRight
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 18-Jul-2019
|
||||
*/
|
||||
|
||||
|
||||
class Sha512Pure : Sha512Multipart {
|
||||
|
||||
override val MAX_HASH_BYTES: Int = 32
|
||||
|
||||
companion object : Sha512 {
|
||||
const val BLOCK_SIZE = 1024
|
||||
const val BLOCK_SIZE_IN_BYTES = 128
|
||||
const val CHUNK_SIZE = 80
|
||||
const val ULONG_MASK = 0xFFFFFFFFFFFFFFFFUL
|
||||
|
||||
override val MAX_HASH_BYTES: Int = 32
|
||||
|
||||
val k = arrayOf(
|
||||
0x428a2f98d728ae22UL,
|
||||
0x7137449123ef65cdUL,
|
||||
0xb5c0fbcfec4d3b2fUL,
|
||||
0xe9b5dba58189dbbcUL,
|
||||
0x3956c25bf348b538UL,
|
||||
0x59f111f1b605d019UL,
|
||||
0x923f82a4af194f9bUL,
|
||||
0xab1c5ed5da6d8118UL,
|
||||
0xd807aa98a3030242UL,
|
||||
0x12835b0145706fbeUL,
|
||||
0x243185be4ee4b28cUL,
|
||||
0x550c7dc3d5ffb4e2UL,
|
||||
0x72be5d74f27b896fUL,
|
||||
0x80deb1fe3b1696b1UL,
|
||||
0x9bdc06a725c71235UL,
|
||||
0xc19bf174cf692694UL,
|
||||
0xe49b69c19ef14ad2UL,
|
||||
0xefbe4786384f25e3UL,
|
||||
0x0fc19dc68b8cd5b5UL,
|
||||
0x240ca1cc77ac9c65UL,
|
||||
0x2de92c6f592b0275UL,
|
||||
0x4a7484aa6ea6e483UL,
|
||||
0x5cb0a9dcbd41fbd4UL,
|
||||
0x76f988da831153b5UL,
|
||||
0x983e5152ee66dfabUL,
|
||||
0xa831c66d2db43210UL,
|
||||
0xb00327c898fb213fUL,
|
||||
0xbf597fc7beef0ee4UL,
|
||||
0xc6e00bf33da88fc2UL,
|
||||
0xd5a79147930aa725UL,
|
||||
0x06ca6351e003826fUL,
|
||||
0x142929670a0e6e70UL,
|
||||
0x27b70a8546d22ffcUL,
|
||||
0x2e1b21385c26c926UL,
|
||||
0x4d2c6dfc5ac42aedUL,
|
||||
0x53380d139d95b3dfUL,
|
||||
0x650a73548baf63deUL,
|
||||
0x766a0abb3c77b2a8UL,
|
||||
0x81c2c92e47edaee6UL,
|
||||
0x92722c851482353bUL,
|
||||
0xa2bfe8a14cf10364UL,
|
||||
0xa81a664bbc423001UL,
|
||||
0xc24b8b70d0f89791UL,
|
||||
0xc76c51a30654be30UL,
|
||||
0xd192e819d6ef5218UL,
|
||||
0xd69906245565a910UL,
|
||||
0xf40e35855771202aUL,
|
||||
0x106aa07032bbd1b8UL,
|
||||
0x19a4c116b8d2d0c8UL,
|
||||
0x1e376c085141ab53UL,
|
||||
0x2748774cdf8eeb99UL,
|
||||
0x34b0bcb5e19b48a8UL,
|
||||
0x391c0cb3c5c95a63UL,
|
||||
0x4ed8aa4ae3418acbUL,
|
||||
0x5b9cca4f7763e373UL,
|
||||
0x682e6ff3d6b2b8a3UL,
|
||||
0x748f82ee5defb2fcUL,
|
||||
0x78a5636f43172f60UL,
|
||||
0x84c87814a1f0ab72UL,
|
||||
0x8cc702081a6439ecUL,
|
||||
0x90befffa23631e28UL,
|
||||
0xa4506cebde82bde9UL,
|
||||
0xbef9a3f7b2c67915UL,
|
||||
0xc67178f2e372532bUL,
|
||||
0xca273eceea26619cUL,
|
||||
0xd186b8c721c0c207UL,
|
||||
0xeada7dd6cde0eb1eUL,
|
||||
0xf57d4f7fee6ed178UL,
|
||||
0x06f067aa72176fbaUL,
|
||||
0x0a637dc5a2c898a6UL,
|
||||
0x113f9804bef90daeUL,
|
||||
0x1b710b35131c471bUL,
|
||||
0x28db77f523047d84UL,
|
||||
0x32caab7b40c72493UL,
|
||||
0x3c9ebe0a15c9bebcUL,
|
||||
0x431d67c49c100d4cUL,
|
||||
0x4cc5d4becb3e42b6UL,
|
||||
0x597f299cfc657e2aUL,
|
||||
0x5fcb6fab3ad6faecUL,
|
||||
0x6c44198c4a475817UL
|
||||
)
|
||||
|
||||
val iv = arrayOf(
|
||||
0x6a09e667f3bcc908UL,
|
||||
0xbb67ae8584caa73bUL,
|
||||
0x3c6ef372fe94f82bUL,
|
||||
0xa54ff53a5f1d36f1UL,
|
||||
0x510e527fade682d1UL,
|
||||
0x9b05688c2b3e6c1fUL,
|
||||
0x1f83d9abfb41bd6bUL,
|
||||
0x5be0cd19137e2179UL
|
||||
)
|
||||
|
||||
|
||||
override fun digest(inputMessage: UByteArray): UByteArray {
|
||||
|
||||
var h = iv.copyOf()
|
||||
|
||||
val expansionArray = createExpansionArray(inputMessage.size.toLong())
|
||||
|
||||
val chunks =
|
||||
(inputMessage + expansionArray + (inputMessage.size * 8).toULong().toPadded128BitByteArray()).chunked(
|
||||
BLOCK_SIZE_IN_BYTES
|
||||
)
|
||||
|
||||
chunks.forEach { chunk ->
|
||||
val w = expandChunk(chunk.toUByteArray())
|
||||
mix(h, w)
|
||||
|
||||
}
|
||||
|
||||
val digest =
|
||||
h[0].toPaddedByteArray() +
|
||||
h[1].toPaddedByteArray() +
|
||||
h[2].toPaddedByteArray() +
|
||||
h[3].toPaddedByteArray() +
|
||||
h[4].toPaddedByteArray() +
|
||||
h[5].toPaddedByteArray() +
|
||||
h[6].toPaddedByteArray() +
|
||||
h[7].toPaddedByteArray()
|
||||
return digest
|
||||
}
|
||||
|
||||
private fun scheduleSigma0(value: ULong): ULong {
|
||||
return value.rotateRight(1) xor value.rotateRight(8) xor (value shr 7)
|
||||
}
|
||||
|
||||
private fun scheduleSigma1(value: ULong): ULong {
|
||||
return value.rotateRight(19) xor value.rotateRight(61) xor (value shr 6)
|
||||
}
|
||||
|
||||
private fun compressionSigma0(e: ULong): ULong {
|
||||
return (e rotateRight 28) xor (e rotateRight 34) xor (e rotateRight 39)
|
||||
}
|
||||
|
||||
private fun compressionSigma1(a: ULong): ULong {
|
||||
return (a rotateRight 14) xor (a rotateRight 18) xor (a rotateRight 41)
|
||||
}
|
||||
|
||||
private fun ch(x: ULong, y: ULong, z: ULong): ULong {
|
||||
return ((x and y) xor ((x xor ULONG_MASK) and z))
|
||||
}
|
||||
|
||||
private fun maj(x: ULong, y: ULong, z: ULong): ULong {
|
||||
return ((x and y) xor (x and z) xor (y and z))
|
||||
}
|
||||
|
||||
private fun expandChunk(chunk: UByteArray): Array<ULong> {
|
||||
val w = Array<ULong>(CHUNK_SIZE) {
|
||||
when (it) {
|
||||
in 0 until 16 -> {
|
||||
var collected = (chunk[(it * 8)].toULong() shl 56) +
|
||||
(chunk[(it * 8) + 1].toULong() shl 48) +
|
||||
(chunk[(it * 8) + 2].toULong() shl 40) +
|
||||
(chunk[(it * 8) + 3].toULong() shl 32) +
|
||||
(chunk[(it * 8) + 4].toULong() shl 24) +
|
||||
(chunk[(it * 8) + 5].toULong() shl 16) +
|
||||
(chunk[(it * 8) + 6].toULong() shl 8) +
|
||||
(chunk[(it * 8) + 7].toULong())
|
||||
collected
|
||||
}
|
||||
else -> 0UL
|
||||
}
|
||||
}
|
||||
for (i in 16 until CHUNK_SIZE) {
|
||||
val s0 = scheduleSigma0(w[i - 15])
|
||||
val s1 = scheduleSigma1(w[i - 2])
|
||||
w[i] = w[i - 16] + s0 + w[i - 7] + s1
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
private fun mix(h: Array<ULong>, w: Array<ULong>): Array<ULong> {
|
||||
var paramA = h[0]
|
||||
var paramB = h[1]
|
||||
var paramC = h[2]
|
||||
var paramD = h[3]
|
||||
var paramE = h[4]
|
||||
var paramF = h[5]
|
||||
var paramG = h[6]
|
||||
var paramH = h[7]
|
||||
|
||||
for (i in 0 until CHUNK_SIZE) {
|
||||
val s1 = compressionSigma1(paramE)
|
||||
val ch = ch(paramE, paramF, paramG)
|
||||
val temp1 = paramH + s1 + ch + k[i] + w[i]
|
||||
val s0 = compressionSigma0(paramA)
|
||||
val maj = maj(paramA, paramB, paramC)
|
||||
val temp2 = s0 + maj
|
||||
paramH = paramG
|
||||
paramG = paramF
|
||||
paramF = paramE
|
||||
paramE = paramD + temp1
|
||||
paramD = paramC
|
||||
paramC = paramB
|
||||
paramB = paramA
|
||||
paramA = temp1 + temp2
|
||||
}
|
||||
|
||||
h[0] += paramA
|
||||
h[1] += paramB
|
||||
h[2] += paramC
|
||||
h[3] += paramD
|
||||
h[4] += paramE
|
||||
h[5] += paramF
|
||||
h[6] += paramG
|
||||
h[7] += paramH
|
||||
return h
|
||||
}
|
||||
|
||||
fun createExpansionArray(originalSizeInBytes: Long): UByteArray {
|
||||
val originalMessageSizeInBits = originalSizeInBytes * 8
|
||||
|
||||
val expandedRemainderOf1024 = (originalMessageSizeInBits + 129) % BLOCK_SIZE
|
||||
val zeroAddAmount = when (expandedRemainderOf1024) {
|
||||
0L -> 0
|
||||
else -> ((BLOCK_SIZE - expandedRemainderOf1024) / 8).toInt()
|
||||
}
|
||||
val expansionArray = UByteArray(zeroAddAmount + 1) {
|
||||
when (it) {
|
||||
0 -> 0b10000000U
|
||||
else -> 0U
|
||||
}
|
||||
}
|
||||
return expansionArray
|
||||
}
|
||||
|
||||
|
||||
private fun ULong.toPaddedByteArray(): UByteArray {
|
||||
val byteMask = 0xFFUL
|
||||
//Ignore messages longer than 64 bits for now
|
||||
return UByteArray(8) {
|
||||
when (it) {
|
||||
7 -> (this and byteMask).toUByte()
|
||||
6 -> ((this shr 8) and byteMask).toUByte()
|
||||
5 -> ((this shr 16) and byteMask).toUByte()
|
||||
4 -> ((this shr 24) and byteMask).toUByte()
|
||||
3 -> ((this shr 32) and byteMask).toUByte()
|
||||
2 -> ((this shr 40) and byteMask).toUByte()
|
||||
1 -> ((this shr 48) and byteMask).toUByte()
|
||||
0 -> ((this shr 56) and byteMask).toUByte()
|
||||
else -> 0U
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ULong.toPadded128BitByteArray(): UByteArray {
|
||||
val byteMask = 0xFFUL
|
||||
//Ignore messages longer than 64 bits for now
|
||||
return UByteArray(16) {
|
||||
when (it) {
|
||||
15 -> (this and byteMask).toUByte()
|
||||
14 -> ((this shr 8) and byteMask).toUByte()
|
||||
13 -> ((this shr 16) and byteMask).toUByte()
|
||||
12 -> ((this shr 24) and byteMask).toUByte()
|
||||
11 -> ((this shr 32) and byteMask).toUByte()
|
||||
10 -> ((this shr 40) and byteMask).toUByte()
|
||||
9 -> ((this shr 48) and byteMask).toUByte()
|
||||
8 -> ((this shr 54) and byteMask).toUByte()
|
||||
else -> 0U
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var h = iv.copyOf()
|
||||
var counter = 0L
|
||||
var bufferCounter = 0
|
||||
var buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { 0U }
|
||||
var digested = false
|
||||
|
||||
|
||||
fun update(data: String) {
|
||||
return update(data.encodeToUByteArray())
|
||||
}
|
||||
|
||||
override fun update(data: UByteArray) {
|
||||
if (data.isEmpty()) {
|
||||
throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
|
||||
}
|
||||
if (digested) {
|
||||
throw RuntimeException("This instance of updateable SHA256 was already finished once. You should use new instance")
|
||||
}
|
||||
|
||||
when {
|
||||
bufferCounter + data.size < BLOCK_SIZE_IN_BYTES -> appendToBuffer(data, bufferCounter)
|
||||
bufferCounter + data.size >= BLOCK_SIZE_IN_BYTES -> {
|
||||
val chunked = data.chunked(BLOCK_SIZE_IN_BYTES)
|
||||
chunked.forEach { chunk ->
|
||||
if (bufferCounter + chunk.size < BLOCK_SIZE_IN_BYTES) {
|
||||
appendToBuffer(chunk.toUByteArray(), bufferCounter)
|
||||
} else {
|
||||
chunk.toUByteArray().copyInto(
|
||||
destination = buffer,
|
||||
destinationOffset = bufferCounter,
|
||||
startIndex = 0,
|
||||
endIndex = BLOCK_SIZE_IN_BYTES - bufferCounter
|
||||
)
|
||||
counter += BLOCK_SIZE_IN_BYTES
|
||||
consumeBlock(buffer)
|
||||
buffer = UByteArray(BLOCK_SIZE_IN_BYTES) {
|
||||
when (it) {
|
||||
in (0 until (chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter))) -> {
|
||||
chunk[it + (BLOCK_SIZE_IN_BYTES - bufferCounter)]
|
||||
}
|
||||
else -> {
|
||||
0U
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
bufferCounter = chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun consumeBlock(block: UByteArray) {
|
||||
val w = expandChunk(block)
|
||||
mix(h, w).copyInto(h)
|
||||
}
|
||||
|
||||
override fun digest(): UByteArray {
|
||||
val length = counter + bufferCounter
|
||||
val expansionArray = createExpansionArray(length)
|
||||
val finalBlock =
|
||||
buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPadded128BitByteArray()
|
||||
finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach {
|
||||
consumeBlock(it.toUByteArray())
|
||||
}
|
||||
|
||||
|
||||
val digest = h[0].toPaddedByteArray() +
|
||||
h[1].toPaddedByteArray() +
|
||||
h[2].toPaddedByteArray() +
|
||||
h[3].toPaddedByteArray() +
|
||||
h[4].toPaddedByteArray() +
|
||||
h[5].toPaddedByteArray() +
|
||||
h[6].toPaddedByteArray() +
|
||||
h[7].toPaddedByteArray()
|
||||
digested = true
|
||||
return digest
|
||||
}
|
||||
|
||||
private fun appendToBuffer(array: UByteArray, start: Int) {
|
||||
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||
bufferCounter += array.size
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
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
|
@ -1,31 +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.
|
||||
*/
|
||||
|
||||
package com.ionspin.kotlin.crypto.keyderivation.argon2
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 16-May-2020
|
||||
*/
|
||||
class Argon2TagTooShort(tagLength: UInt) : RuntimeException("Too short tag (output) requested. Requested: $tagLength")
|
||||
class Argon2TagTooLong(tagLength: UInt) : RuntimeException("Too long tag (output) requested. Requested: $tagLength")
|
||||
class Argon2TimeTooShort(iterations: Int) : RuntimeException("Too short time parameter (Too few iterations). Requested iterations: $iterations")
|
||||
class Argon2TimeTooLong(iterations: Int) : RuntimeException("Too long time parameter (Too many iterations). Requested iterations: $iterations")
|
||||
class Argon2MemoryTooLitlle(requestedMemorySize: UInt) : RuntimeException("Requested memory size must be larger than 8 * parallelism. Requested size: $requestedMemorySize")
|
||||
class Argon2MemoryTooMuch(requestedMemorySize: UInt) : RuntimeException("Requested memory size too large. Requested size: $requestedMemorySize")
|
||||
class Argon2LanesTooFew(parallelism: Int) : RuntimeException("Too few, or invalid number of threads requested $parallelism")
|
||||
class Argon2LanesTooMany(parallelism: Int) : RuntimeException("Too many threads requested (parallelism). Requested: $parallelism")
|
@ -1,401 +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.
|
||||
*/
|
||||
|
||||
@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.SRNG
|
||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bPure
|
||||
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||
import com.ionspin.kotlin.crypto.keyderivation.ArgonResult
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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.encodeToUByteArray(),
|
||||
salt,
|
||||
parallelism,
|
||||
tagLength.toUInt(),
|
||||
memory.toUInt(),
|
||||
numberOfIterations,
|
||||
key.encodeToUByteArray(),
|
||||
associatedData.encodeToUByteArray(),
|
||||
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.encodeToUByteArray(),
|
||||
salt.encodeToUByteArray(),
|
||||
parallelism,
|
||||
tagLength,
|
||||
requestedMemorySize,
|
||||
numberOfIterations,
|
||||
key.encodeToUByteArray(),
|
||||
associatedData.encodeToUByteArray(),
|
||||
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 = Blake2bPure.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()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,167 +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.
|
||||
*/
|
||||
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
|
||||
|
||||
package com.ionspin.kotlin.crypto.keyderivation.argon2
|
||||
|
||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bPure
|
||||
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.BLOCK_SIZE
|
||||
import com.ionspin.kotlin.crypto.util.plus
|
||||
import com.ionspin.kotlin.crypto.util.rotateRight
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 16-May-2020
|
||||
*/
|
||||
object Argon2Utils {
|
||||
const val BLOCK_SIZE = 1024
|
||||
|
||||
const val R1 = 32
|
||||
const val R2 = 24
|
||||
const val R3 = 16
|
||||
const val R4 = 63
|
||||
|
||||
//Based on Blake2b mix
|
||||
internal fun inplaceMixRound(v : ULongArray) : ULongArray{
|
||||
mix(v, 0, 4, 8, 12)
|
||||
mix(v, 1, 5, 9, 13)
|
||||
mix(v, 2, 6, 10, 14)
|
||||
mix(v, 3, 7, 11, 15)
|
||||
mix(v, 0, 5, 10, 15)
|
||||
mix(v, 1, 6, 11, 12)
|
||||
mix(v, 2, 7, 8, 13)
|
||||
mix(v, 3, 4, 9, 14)
|
||||
return v //Just for chaining, array is mixed in place
|
||||
}
|
||||
|
||||
//Based on Blake2b mix
|
||||
private fun mix(v: ULongArray, a: Int, b: Int, c: Int, d: Int) {
|
||||
v[a] = (v[a] + v[b] + 2U * (v[a] and 0xFFFFFFFFUL) * (v[b] and 0xFFFFFFFFUL))
|
||||
v[d] = (v[d] xor v[a]) rotateRight R1
|
||||
v[c] = (v[c] + v[d] + 2U * (v[c] and 0xFFFFFFFFUL) * (v[d] and 0xFFFFFFFFUL))
|
||||
v[b] = (v[b] xor v[c]) rotateRight R2
|
||||
v[a] = (v[a] + v[b] + 2U * (v[a] and 0xFFFFFFFFUL) * (v[b] and 0xFFFFFFFFUL))
|
||||
v[d] = (v[d] xor v[a]) rotateRight R3
|
||||
v[c] = (v[c] + v[d] + 2U * (v[c] and 0xFFFFFFFFUL) * (v[d] and 0xFFFFFFFFUL))
|
||||
v[b] = (v[b] xor v[c]) rotateRight R4
|
||||
}
|
||||
|
||||
internal fun extractColumnFromGBlock(gBlock: UByteArray, columnPosition: Int): UByteArray {
|
||||
val result = UByteArray(128) { 0U }
|
||||
for (i in 0..7) {
|
||||
gBlock.copyOfRange(i * 128 + (columnPosition * 16), i * 128 + (columnPosition * 16) + 16)
|
||||
.copyInto(result, i * 16)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal fun compressionFunctionG(
|
||||
previousBlock: ArgonBlockPointer,
|
||||
referenceBlock: ArgonBlockPointer,
|
||||
currentBlock: ArgonBlockPointer,
|
||||
xorWithCurrentBlock: Boolean
|
||||
): ArgonBlockPointer {
|
||||
val r = (referenceBlock xorBlocksAndGetPointerToNewBlock previousBlock).getBlockPointer()
|
||||
//Since we are doing inplace xors, we don't need the Q that exists in specification
|
||||
val z = ArgonBlock().getBlockPointer()
|
||||
// Do the argon/blake2b mixing on rows
|
||||
for (i in 0..7) {
|
||||
z.setRowFromMixedULongs(i, inplaceMixRound(r.getRowOfULongsForMixing(i)))
|
||||
}
|
||||
// Do the argon/blake2b mixing on columns
|
||||
for (i in 0..7) {
|
||||
z.setColumnFromMixedULongs(i, inplaceMixRound(z.getColumnOfULongsForMixing(i)))
|
||||
}
|
||||
val final = if (xorWithCurrentBlock) {
|
||||
(z xorInplaceWith r) xorInplaceWith currentBlock
|
||||
} else {
|
||||
z xorInplaceWith r
|
||||
}
|
||||
return final
|
||||
}
|
||||
|
||||
internal fun argonBlake2bArbitraryLenghtHash(input: UByteArray, length: UInt): UByteArray {
|
||||
if (length <= 64U) {
|
||||
return Blake2bPure.digest(inputMessage = length + input, hashLength = length.toInt())
|
||||
}
|
||||
//We can cast to int because UInt even if MAX_VALUE divided by 32 is guaranteed not to overflow
|
||||
val numberOf64ByteBlocks = (1U + ((length - 1U) / 32U) - 2U).toInt() // equivalent to ceil(length/32) - 2
|
||||
val v = Array<UByteArray>(numberOf64ByteBlocks) { ubyteArrayOf() }
|
||||
v[0] = Blake2bPure.digest(length + input)
|
||||
for (i in 1 until numberOf64ByteBlocks) {
|
||||
v[i] = Blake2bPure.digest(v[i - 1])
|
||||
}
|
||||
val remainingPartOfInput = length.toInt() - numberOf64ByteBlocks * 32
|
||||
val vLast = Blake2bPure.digest(v[numberOf64ByteBlocks - 1], hashLength = remainingPartOfInput)
|
||||
val concat =
|
||||
(v.map { it.copyOfRange(0, 32) })
|
||||
.plus(listOf(vLast))
|
||||
.foldRight(ubyteArrayOf()) { arrayOfUBytes, acc -> arrayOfUBytes + acc }
|
||||
|
||||
return concat
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the argon 2 parameters.
|
||||
* Since Kotlin arrays that we are currently using cannot have more than 2^31 bytes, we don't need to check
|
||||
* sizes for password, salt, key and associated data. Also since UInt is 32bit we cant set more than 2^32-1 of
|
||||
* tagLength, requested memory size and number of iterations, so no need to check for upper bound, just lower.
|
||||
*/
|
||||
internal fun validateArgonParameters(
|
||||
password: UByteArray,
|
||||
salt: UByteArray,
|
||||
parallelism: Int ,
|
||||
tagLength: UInt,
|
||||
requestedMemorySize: UInt ,
|
||||
numberOfIterations: Int ,
|
||||
key: UByteArray,
|
||||
associatedData: UByteArray,
|
||||
argonType: ArgonType
|
||||
) {
|
||||
|
||||
//Parallelism
|
||||
if (parallelism > 0xFFFFFF) {
|
||||
throw Argon2LanesTooMany(parallelism)
|
||||
}
|
||||
if (parallelism <= 0) {
|
||||
throw Argon2LanesTooFew(parallelism)
|
||||
}
|
||||
//Tag length
|
||||
if (tagLength <= 0U) {
|
||||
throw Argon2TagTooShort(tagLength)
|
||||
}
|
||||
//Requested memory
|
||||
if (requestedMemorySize < 8U || requestedMemorySize < (8 * parallelism).toUInt()) {
|
||||
throw Argon2MemoryTooLitlle(requestedMemorySize)
|
||||
}
|
||||
//Number of iterations
|
||||
if (numberOfIterations <= 0) {
|
||||
throw Argon2TimeTooShort(numberOfIterations)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ------------ Arithmetic and other utils
|
||||
|
||||
|
||||
fun UByteArray.xorWithBlock(other : ArgonMatrix, rowPosition: Int, columnPosition: Int) : UByteArray {
|
||||
return UByteArray(BLOCK_SIZE) { this[it] xor other[rowPosition, columnPosition, it] }
|
||||
}
|
@ -1,285 +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.
|
||||
*/
|
||||
|
||||
@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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.mac
|
||||
|
||||
import com.ionspin.kotlin.bignum.Endianness
|
||||
import com.ionspin.kotlin.bignum.integer.BigInteger
|
||||
import com.ionspin.kotlin.bignum.integer.Sign
|
||||
import com.ionspin.kotlin.crypto.util.fromLittleEndianUByteArrayToBigEndianUByteArray
|
||||
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 18-Jun-2020
|
||||
*/
|
||||
class Poly1305(key: UByteArray) {
|
||||
companion object {
|
||||
fun clampR(r: UByteArray) : UByteArray {
|
||||
val clamped = UByteArray(16) { r[it] }
|
||||
clamped[3] = r[3] and 0b00001111U
|
||||
clamped[7] = r[7] and 0b00001111U
|
||||
clamped[11] = r[11] and 0b00001111U
|
||||
clamped[15] = r[15] and 0b00001111U
|
||||
|
||||
clamped[4] = r[4] and 0b11111100U
|
||||
clamped[8] = r[8] and 0b11111100U
|
||||
clamped[12] = r[12] and 0b11111100U
|
||||
return clamped
|
||||
|
||||
}
|
||||
|
||||
val P = BigInteger.fromUByteArray(
|
||||
ubyteArrayOf(
|
||||
0x03U, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xfbU
|
||||
),
|
||||
Sign.POSITIVE
|
||||
)
|
||||
val powersOfTwo = Array(129) {
|
||||
BigInteger.ONE shl it
|
||||
}
|
||||
val resultMask = (BigInteger.ONE shl 128) - 1
|
||||
//Doesn't have to be every power, just divisible by 8
|
||||
val twoToThe128 = BigInteger.ONE.shl(128)
|
||||
|
||||
/**
|
||||
* Limit - stop poly calculating tag at desired index, ignored if 0
|
||||
*/
|
||||
fun poly1305Authenticate(key: UByteArray, message: UByteArray) : UByteArray {
|
||||
val r = clampR(UByteArray(16) { key[it] })
|
||||
val s= UByteArray(16) { key[it + 16]}
|
||||
|
||||
var accumulator = BigInteger.ZERO
|
||||
val rAsBigInt = BigInteger.fromUByteArray(r.fromLittleEndianUByteArrayToBigEndianUByteArray(), Sign.POSITIVE) //TODO convert from little endian ubyte array to what BigInteger expects
|
||||
val sAsBigInt = BigInteger.fromUByteArray(s.fromLittleEndianUByteArrayToBigEndianUByteArray(), Sign.POSITIVE)
|
||||
val blocks = message.size / 16
|
||||
val remainder = message.size % 16
|
||||
|
||||
for (i in 0 until blocks) {
|
||||
val slice = message.sliceArray(i * 16 until i * 16 + 16)
|
||||
slice.hexColumsPrint()
|
||||
val blockAsInt = BigInteger.fromUByteArray(slice.fromLittleEndianUByteArrayToBigEndianUByteArray(), Sign.POSITIVE) + powersOfTwo[128]
|
||||
accumulator += blockAsInt
|
||||
accumulator *= rAsBigInt
|
||||
accumulator %= P
|
||||
}
|
||||
if (remainder != 0) {
|
||||
val slice = message.sliceArray(blocks * 16 until blocks * 16 + remainder)
|
||||
val blockAsInt = BigInteger.fromUByteArray(slice.fromLittleEndianUByteArrayToBigEndianUByteArray(), Sign.POSITIVE) + powersOfTwo[remainder * 8]
|
||||
accumulator += blockAsInt
|
||||
accumulator *= rAsBigInt
|
||||
accumulator %= P
|
||||
}
|
||||
|
||||
accumulator += sAsBigInt
|
||||
accumulator = accumulator and resultMask
|
||||
val result = accumulator.toUByteArray()
|
||||
result.reverse()
|
||||
return result
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
var rAsBigInt = BigInteger.fromUByteArray(
|
||||
clampR(key.sliceArray(0 until 16)).fromLittleEndianUByteArrayToBigEndianUByteArray(),
|
||||
Sign.POSITIVE
|
||||
)
|
||||
var sAsBigInt = BigInteger.fromUByteArray(
|
||||
key.sliceArray(16 until 32).fromLittleEndianUByteArrayToBigEndianUByteArray(),
|
||||
Sign.POSITIVE)
|
||||
var accumulator = BigInteger.ZERO
|
||||
|
||||
fun updateMac(data : UByteArray) {
|
||||
if (data.size != 16) {
|
||||
throw RuntimeException("Invalide block size, required 16, got ${data.size}")
|
||||
}
|
||||
val blockAsInt = BigInteger.fromUByteArray(
|
||||
data.fromLittleEndianUByteArrayToBigEndianUByteArray(), Sign.POSITIVE
|
||||
) + powersOfTwo[128]
|
||||
accumulator += blockAsInt
|
||||
accumulator *= rAsBigInt
|
||||
accumulator %= P
|
||||
}
|
||||
|
||||
fun finalizeMac(data: UByteArray = ubyteArrayOf()) : UByteArray{
|
||||
if (data.size != 0) {
|
||||
val blockAsInt = BigInteger.fromUByteArray(
|
||||
data.fromLittleEndianUByteArrayToBigEndianUByteArray(),
|
||||
Sign.POSITIVE
|
||||
) + powersOfTwo[data.size * 8]
|
||||
accumulator += blockAsInt
|
||||
accumulator *= rAsBigInt
|
||||
accumulator %= P
|
||||
}
|
||||
accumulator += sAsBigInt
|
||||
accumulator = accumulator and resultMask
|
||||
val result = accumulator.toUByteArray()
|
||||
result.reverse()
|
||||
return result
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package com.ionspin.kotlin.crypto.symmetric
|
||||
|
||||
import com.ionspin.kotlin.crypto.util.hexStringToUByteArray
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 13-Jun-2020
|
||||
*/
|
||||
internal sealed class InternalAesKey(val key: String, val keyLength: Int) {
|
||||
val keyArray: UByteArray = key.hexStringToUByteArray()
|
||||
val numberOf32BitWords = keyLength / 32
|
||||
|
||||
class Aes128Key(key: String) : InternalAesKey(key, 128)
|
||||
class Aes192Key(key: String) : InternalAesKey(key, 192)
|
||||
class Aes256Key(key: String) : InternalAesKey(key, 256)
|
||||
|
||||
init {
|
||||
checkKeyLength(key, keyLength)
|
||||
}
|
||||
|
||||
fun checkKeyLength(key: String, expectedLength: Int) {
|
||||
if ((key.length / 2) != expectedLength / 8) {
|
||||
throw RuntimeException("Invalid key length")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,243 +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.
|
||||
*/
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
internal class AesCbcPure internal constructor(val aesKey: InternalAesKey, 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: InternalAesKey) : 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 : InternalAesKey) : AesCbcPure {
|
||||
return AesCbcPure(aesKey, Mode.DECRYPT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk encryption, returns encrypted data and a random initialization vector
|
||||
*/
|
||||
fun encrypt(aesKey: InternalAesKey, data: UByteArray): EncryptedDataAndInitializationVector {
|
||||
val aesCbc = AesCbcPure(aesKey, Mode.ENCRYPT)
|
||||
aesCbc.addData(data)
|
||||
return aesCbc.encrypt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk decryption, returns decrypted data
|
||||
*/
|
||||
fun decrypt(aesKey: InternalAesKey, 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.
|
||||
* This also applies correct PKCS#7 padding
|
||||
* @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)
|
||||
}
|
||||
} else {
|
||||
output += consumeBlock(UByteArray(BLOCK_BYTES) { BLOCK_BYTES.toUByte()})
|
||||
}
|
||||
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 {
|
||||
ubyteArrayOf()
|
||||
}.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 initializationVector : 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 (!initializationVector.contentEquals(other.initializationVector)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = encryptedData.contentHashCode()
|
||||
result = 31 * result + initializationVector.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