Remove pure and delegated, leave api just to trigger fetching konan deps in advance, remove most of it later

This commit is contained in:
Ugljesa Jovanovic 2021-01-09 13:36:37 +01:00
parent b0c6b43f2e
commit 85ae96165f
No known key found for this signature in database
GPG Key ID: 178E6DFCECCB0E0F
150 changed files with 2 additions and 14089 deletions

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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

View File

@ -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 \

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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

View File

@ -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")
// }
// }
// }
//}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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

View File

@ -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
}

View File

@ -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()
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()
}
}

View File

@ -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
}

View File

@ -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 {
}

View File

@ -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) }
}
}

View File

@ -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 -----------")
}
}
}

View File

@ -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 }
}
}

View File

@ -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 }
}
}

View File

@ -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 }
}
}

View File

@ -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()
}
}

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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()
}
}
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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
}
}
}

View File

@ -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() }

View File

@ -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
}
}

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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()
}
}

View File

@ -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()
}
}

View File

@ -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)
//
//}

View File

@ -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

View File

@ -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() }
// }
// }
//}

View File

@ -1,6 +0,0 @@
headers = sodium.h
headerFilter = sodium.h sodium/**
#staticLibraries = libsodium.a
#libraryPaths = sodiumWrapper/lib
#compilerOpts = -I./sodiumWrapper/include
linkerOpts =

View File

@ -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
}
}

View File

@ -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]
}
}
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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() }

View File

@ -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 ?: ""
}
}
}
}

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}
}
}

View File

@ -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 }
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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")

View File

@ -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()
)
}
}
}

View File

@ -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] }
}

View File

@ -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
}
}
}

View File

@ -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
}
}

View File

@ -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")
}
}
}

View File

@ -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