Remove pure and delegated, leave api just to trigger fetching konan deps in advance, remove most of it later
This commit is contained in:
		
							parent
							
								
									b0c6b43f2e
								
							
						
					
					
						commit
						85ae96165f
					
				| @ -54,7 +54,6 @@ macPublishToSnapshot: | ||||
|   script: | ||||
|     - ./macBuild.sh | ||||
|     - ./macBuildAndPublishSnapshot-bindings.sh | ||||
|     - ./macBuildAndPublishSnapshot-delegated.sh | ||||
|   only: | ||||
|     - master | ||||
|   tags: | ||||
| @ -69,7 +68,6 @@ buildWindows: | ||||
|   script: | ||||
|     - $env:CHERE_INVOKING = 'yes' | ||||
|     - C:\msys64\usr\bin\bash -lc "./windowsBuild-delegated.sh" | ||||
|     - C:\msys64\usr\bin\bash -lc "./windowsBuild-pure.sh" | ||||
|   tags: | ||||
|     - windowsX64 | ||||
| 
 | ||||
| @ -78,7 +76,6 @@ windowsPublishToSnapshot: | ||||
|   script: | ||||
|     - $env:CHERE_INVOKING = 'yes' | ||||
|     - C:\msys64\usr\bin\bash -lc "./windowsBuildAndPublish-delegated.sh" | ||||
|     - C:\msys64\usr\bin\bash -lc "./windowsBuildAndPublish-pure.sh" | ||||
|   only: | ||||
|     - master | ||||
|   tags: | ||||
|  | ||||
							
								
								
									
										197
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										197
									
								
								.travis.yml
									
									
									
									
									
								
							| @ -1,197 +0,0 @@ | ||||
| matrix: | ||||
|   include: | ||||
|     - os: linux | ||||
|       name: linux | ||||
|       language: java | ||||
|       jdk: openjdk12 | ||||
| 
 | ||||
|       env: | ||||
|         KBUILD=linux | ||||
|         JAVA_OPTS=-Xmx2g | ||||
|       #skip ./gradlew assemble that is normally invoked in installation step | ||||
|       install: | ||||
|         - sudo apt-get update | ||||
|         - sudo apt-get -y install automake | ||||
|       script: | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./linuxBuild.sh; fi' | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./linuxBuildAndPublish.sh; fi' | ||||
|     # OSX macos | ||||
|     - os: osx | ||||
|       name: osx-mac | ||||
|       osx_image: xcode11.4 | ||||
|       language: java | ||||
|       jdk: openjdk12 | ||||
|       install: true | ||||
|       env: | ||||
|         KBUILD=linux | ||||
|         JAVA_OPTS=-Xmx2g | ||||
|       script: | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild-mac.sh; fi' | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-mac.sh; fi' | ||||
|     # OSX ios | ||||
|     - os: osx | ||||
|       name: osx-ios | ||||
|       osx_image: xcode11.4 | ||||
|       language: java | ||||
|       jdk: openjdk12 | ||||
|       install: true | ||||
|       env: | ||||
|         KBUILD=linux | ||||
|         JAVA_OPTS=-Xmx2g | ||||
|       script: | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild-ios.sh; fi' | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-ios.sh; fi' | ||||
|     #   OSX watchos | ||||
|     - os: osx | ||||
|       name: osx-watchos | ||||
|       osx_image: xcode11.4 | ||||
|       language: java | ||||
|       jdk: openjdk12 | ||||
|       install: true | ||||
|       env: | ||||
|         KBUILD=linux | ||||
|         JAVA_OPTS=-Xmx2g | ||||
|       script: | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild-watchos.sh; fi' | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-watchos.sh; fi' | ||||
|     #   OSX tvos | ||||
|     - os: osx | ||||
|       name: osx-tvos | ||||
|       osx_image: xcode11.4 | ||||
|       language: java | ||||
|       jdk: openjdk12 | ||||
|       install: true | ||||
|       env: | ||||
|         KBUILD=linux | ||||
|         JAVA_OPTS=-Xmx2g | ||||
|       script: | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild-tvos.sh; fi' | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-tvos.sh; fi' | ||||
| 
 | ||||
|     # OSX macos PURE | ||||
|     - os: osx | ||||
|       name: osx-mac-pure | ||||
|       osx_image: xcode11.4 | ||||
|       language: java | ||||
|       jdk: openjdk12 | ||||
|       install: true | ||||
|       env: | ||||
|         KBUILD=linux | ||||
|         JAVA_OPTS=-Xmx2g | ||||
|       script: | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild-pure-mac.sh; fi' | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-pure-mac.sh; fi' | ||||
|     # OSX ios PURE | ||||
|     - os: osx | ||||
|       name: osx-ios-pure | ||||
|       osx_image: xcode11.4 | ||||
|       language: java | ||||
|       jdk: openjdk12 | ||||
|       install: true | ||||
|       env: | ||||
|         KBUILD=linux | ||||
|         JAVA_OPTS=-Xmx2g | ||||
|       script: | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild-pure-ios.sh; fi' | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-pure-ios.sh; fi' | ||||
|     #   OSX watchos PURE | ||||
|     - os: osx | ||||
|       name: osx-watchos-pure | ||||
|       osx_image: xcode11.4 | ||||
|       language: java | ||||
|       jdk: openjdk12 | ||||
|       install: true | ||||
|       env: | ||||
|         KBUILD=linux | ||||
|         JAVA_OPTS=-Xmx2g | ||||
|       script: | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild-pure-watchos.sh; fi' | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-pure-watchos.sh; fi' | ||||
|     #   OSX tvos PURE | ||||
|     - os: osx | ||||
|       name: osx-tvos-pure | ||||
|       osx_image: xcode11.4 | ||||
|       language: java | ||||
|       jdk: openjdk12 | ||||
|       install: true | ||||
|       env: | ||||
|         KBUILD=linux | ||||
|         JAVA_OPTS=-Xmx2g | ||||
|       script: | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then bash ./macBuild-pure-tvos.sh; fi' | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then bash ./macBuildAndPublish-pure-tvos.sh; fi' | ||||
| 
 | ||||
|     - os: windows | ||||
|       name: windows-pure | ||||
|       language: shell | ||||
|       jdk: openjdk12 | ||||
|       env: | ||||
|         - GRAVIS="https://raw.githubusercontent.com/DanySK/Gravis-CI/master/" | ||||
|         - JAVA_OPTS=-Xmx2g | ||||
|         - JDK="adopt-openj9@1.11" | ||||
|       before_install: | ||||
|         - curl "${GRAVIS}.install-jdk-travis.sh" --output ~/.install-jdk-travis.sh | ||||
|         - source ~/.install-jdk-travis.sh | ||||
|       install: true | ||||
|       script: | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then $shell ./windowsBuild-pure.sh; fi' | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then ./windowsBuildAndPublish-pure.sh; fi' | ||||
|     - os: windows | ||||
|       name: windows-delegated | ||||
|       language: shell | ||||
|       jdk: openjdk12 | ||||
|       env: | ||||
|         - GRAVIS="https://raw.githubusercontent.com/DanySK/Gravis-CI/master/" | ||||
|         - JAVA_OPTS=-Xmx2g | ||||
|         - JDK="adopt-openj9@1.11" | ||||
|       before_install: | ||||
|         - curl "${GRAVIS}.install-jdk-travis.sh" --output ~/.install-jdk-travis.sh | ||||
|         - source ~/.install-jdk-travis.sh | ||||
|         - |- | ||||
|           case $TRAVIS_OS_NAME in | ||||
|             windows) | ||||
|               [[ ! -f C:/tools/msys64/msys2_shell.cmd ]] && rm -rf C:/tools/msys64 | ||||
|               choco uninstall -y mingw | ||||
|               choco upgrade --no-progress -y msys2 bazel | ||||
|               export msys2='cmd //C RefreshEnv.cmd ' | ||||
|               export msys2+='& set MSYS=winsymlinks:nativestrict ' | ||||
|               export msys2+='& C:\\tools\\msys64\\msys2_shell.cmd -defterm -no-start' | ||||
|               export shell="$msys2 -mingw64 -full-path -here -c \$\* --" | ||||
|               export msys2+=" -msys2 -c \$\* --" | ||||
|               $msys2 pacman --sync --noconfirm --needed \ | ||||
|                       autoconf \ | ||||
|                       automake \ | ||||
|                       mingw-w64-x86_64-libtool \ | ||||
|                       mingw-w64-x86_64-toolchain \ | ||||
|                       perl \ | ||||
|                       unzip | ||||
|               taskkill //IM gpg-agent.exe //F | ||||
|               export CPPFLAGS=-D__USE_MINGW_ANSI_STDIO=1 | ||||
|               export PATH=/C/tools/msys64/mingw64/bin:$PATH | ||||
|               export GNU_MAKE=mingw32-make | ||||
|               export MAKE=mingw32-make | ||||
|               export AR=gcc-ar | ||||
|               export RANLIB=gcc-ranlib | ||||
|               export COVERITY_SCAN_BRANCH_PATTERN=disable_coverity_scan | ||||
|               ;; | ||||
|           esac | ||||
|         - export GIT=git | ||||
|         - g++ --version | ||||
|         - $GNU_MAKE --version | ||||
|         - $GIT --version | ||||
|       install: true | ||||
|       before_cache: | ||||
|         - $msys2 pacman --sync --clean --noconfirm | ||||
|       script: | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then $shell ./windowsBuild-delegated.sh; fi' | ||||
|         - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then $shell ./windowsBuildAndPublish-delegated.sh; fi' | ||||
| cache: | ||||
|   directories: | ||||
|     - $HOME/.m2/ | ||||
|     - $HOME/.gradle | ||||
|     - $HOME/.konan | ||||
|     - $HOME/AppData/Local/Temp/chocolatey | ||||
|     - /C/tools/msys64 | ||||
| branches: | ||||
|   only: | ||||
|     - master | ||||
| @ -15,10 +15,8 @@ fi | ||||
| ./makeLinuxArm64.sh | ||||
| #now we can do the delegated build | ||||
| cd .. | ||||
| ./gradlew multiplatform-crypto-delegated:build | ||||
| 
 | ||||
| #build libsodium bindings | ||||
| ./gradlew multiplatform-crypto-libsodium-bindings:build | ||||
| #and finally pure build | ||||
| ./gradlew multiplatform-crypto:build | ||||
| set +e | ||||
| 
 | ||||
|  | ||||
| @ -15,11 +15,9 @@ fi | ||||
| ./makeLinuxArm64.sh | ||||
| #now we can do the delegated build | ||||
| cd .. | ||||
| ./gradlew multiplatform-crypto-delegated:build | ||||
| #build libsodium bindings | ||||
| ./gradlew multiplatform-crypto-libsodium-bindings:build | ||||
| #and finally pure build | ||||
| ./gradlew multiplatform-crypto:build | ||||
| 
 | ||||
| ./gradlew publishJvmPublicationToSnapshotRepository publishJsPublicationToSnapshotRepository \ | ||||
| publishKotlinMultiplatformPublicationToSnapshotRepository publishLinuxX64PublicationToSnapshotRepository \ | ||||
| publishLinuxArm64PublicationToSnapshotRepository publishMetadataPublicationToSnapshotRepository | ||||
|  | ||||
| @ -8,10 +8,6 @@ cd sodiumWrapper | ||||
| ./makeIos.sh | ||||
| #now we can do the delegated build of ios and macos libraries | ||||
| cd .. | ||||
| ./gradlew multiplatform-crypto-delegated:iosArm32MainKlibrary multiplatform-crypto-delegated:iosArm32TestKlibrary \ | ||||
| multiplatform-crypto-delegated:iosArm64MainKlibrary multiplatform-crypto-delegated:iosArm64TestKlibrary \ | ||||
| multiplatform-crypto-delegated:iosX64MainKlibrary multiplatform-crypto-delegated:iosX64TestKlibrary | ||||
| ./gradlew multiplatform-crypto-delegated:iosX64Test | ||||
| 
 | ||||
| ./gradlew multiplatform-crypto-libsodium-bindings:iosArm32MainKlibrary multiplatform-crypto-libsodium-bindings:iosArm32TestKlibrary \ | ||||
| multiplatform-crypto-libsodium-bindings:iosArm64MainKlibrary multiplatform-crypto-libsodium-bindings:iosArm64TestKlibrary \ | ||||
|  | ||||
| @ -8,9 +8,6 @@ cd sodiumWrapper | ||||
| ./makeIos.sh | ||||
| #now we can do the delegated build of ios and macos libraries | ||||
| cd .. | ||||
| ./gradlew multiplatform-crypto-delegated:macosX64MainKlibrary multiplatform-crypto-delegated:macosX64TestKlibrary | ||||
| ./gradlew multiplatform-crypto-delegated:macosX64Test | ||||
| 
 | ||||
| ./gradlew multiplatform-crypto-libsodium-bindings:macosX64MainKlibrary multiplatform-crypto-libsodium-bindings:macosX64TestKlibrary | ||||
| ./gradlew multiplatform-crypto-libsodium-bindings:macosX64Test | ||||
| set +e | ||||
|  | ||||
| @ -1,9 +0,0 @@ | ||||
| set -e | ||||
| #!/bin/sh | ||||
| ./gradlew multiplatform-crypto-api:build | ||||
| ./gradlew multiplatform-crypto:iosArm32MainKlibrary multiplatform-crypto:iosArm32TestKlibrary \ | ||||
| multiplatform-crypto:iosArm64MainKlibrary multiplatform-crypto:iosArm64TestKlibrary \ | ||||
| multiplatform-crypto:iosX64MainKlibrary multiplatform-crypto:iosX64TestKlibrary | ||||
| ./gradlew multiplatform-crypto:iosX64Test | ||||
| 
 | ||||
| set +e | ||||
| @ -1,7 +0,0 @@ | ||||
| set -e | ||||
| #!/bin/sh | ||||
| ./gradlew multiplatform-crypto-api:build | ||||
| ./gradlew multiplatform-crypto:macosX64MainKlibrary multiplatform-crypto:macosX64TestKlibrary | ||||
| ./gradlew multiplatform-crypto:macosX64Test | ||||
| 
 | ||||
| set +e | ||||
| @ -1,7 +0,0 @@ | ||||
| set -e | ||||
| #!/bin/sh | ||||
| ./gradlew multiplatform-crypto-api:build | ||||
| ./gradlew multiplatform-crypto:tvosArm64MainKlibrary multiplatform-crypto:tvosArm64TestKlibrary \ | ||||
| multiplatform-crypto:tvosX64MainKlibrary multiplatform-crypto:tvosX64TestKlibrary | ||||
| ./gradlew multiplatform-crypto:tvosX64Test | ||||
| set +e | ||||
| @ -1,8 +0,0 @@ | ||||
| set -e | ||||
| #!/bin/sh | ||||
| ./gradlew multiplatform-crypto-api:build | ||||
| ./gradlew multiplatform-crypto:watchosArm32MainKlibrary multiplatform-crypto:watchosArm32TestKlibrary \ | ||||
| multiplatform-crypto:watchosArm64MainKlibrary multiplatform-crypto:watchosArm64TestKlibrary \ | ||||
| multiplatform-crypto:watchosX86MainKlibrary multiplatform-crypto:watchosX86TestKlibrary | ||||
| ./gradlew multiplatform-crypto:watchosX86Test | ||||
| set +e | ||||
| @ -7,9 +7,6 @@ cd sodiumWrapper | ||||
| ./makeTvos.sh | ||||
| #now we can do the delegated build of ios and macos libraries | ||||
| cd .. | ||||
| ./gradlew multiplatform-crypto-delegated:tvosArm64MainKlibrary multiplatform-crypto-delegated:tvosArm64TestKlibrary \ | ||||
| multiplatform-crypto-delegated:tvosX64MainKlibrary multiplatform-crypto-delegated:tvosX64TestKlibrary | ||||
| ./gradlew multiplatform-crypto-delegated:tvosX64Test | ||||
| 
 | ||||
| ./gradlew multiplatform-crypto-libsodium-bindings:tvosArm64MainKlibrary multiplatform-crypto-libsodium-bindings:tvosArm64TestKlibrary \ | ||||
| multiplatform-crypto-libsodium-bindings:tvosX64MainKlibrary multiplatform-crypto-libsodium-bindings:tvosX64TestKlibrary | ||||
|  | ||||
| @ -7,10 +7,6 @@ cd sodiumWrapper | ||||
| ./makeWatchos.sh | ||||
| #now we can do the delegated build of ios and macos libraries | ||||
| cd .. | ||||
| ./gradlew multiplatform-crypto-delegated:watchosArm32MainKlibrary multiplatform-crypto-delegated:watchosArm32TestKlibrary \ | ||||
| multiplatform-crypto-delegated:watchosArm64MainKlibrary multiplatform-crypto-delegated:watchosArm64TestKlibrary \ | ||||
| multiplatform-crypto-delegated:watchosX86MainKlibrary multiplatform-crypto-delegated:watchosX86TestKlibrary | ||||
| ./gradlew multiplatform-crypto-delegated:watchosX86Test | ||||
| 
 | ||||
| ./gradlew multiplatform-crypto-libsodium-bindings:watchosArm32MainKlibrary multiplatform-crypto-libsodium-bindings:watchosArm32TestKlibrary \ | ||||
| multiplatform-crypto-libsodium-bindings:watchosArm64MainKlibrary multiplatform-crypto-libsodium-bindings:watchosArm64TestKlibrary \ | ||||
|  | ||||
| @ -8,9 +8,6 @@ cd sodiumWrapper | ||||
| ./makeIosWatchosTvos.sh | ||||
| #now we can do the delegated build | ||||
| cd .. | ||||
| ./gradlew multiplatform-crypto-delegated:build | ||||
| #pure build | ||||
| ./gradlew multiplatform-crypto:build | ||||
| #libsodium bindings | ||||
| ./gradlew multiplatform-crypto-libsodium-bindings:build | ||||
| set +e | ||||
|  | ||||
| @ -8,9 +8,6 @@ cd sodiumWrapper | ||||
| ./makeIos.sh | ||||
| #now we can do the delegated build of ios and macos libraries | ||||
| cd .. | ||||
| ./gradlew multiplatform-crypto-delegated:publishIosArm32PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto-delegated:publishIosArm64PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto-delegated:publishIosX64PublicationToSnapshotRepository | ||||
| 
 | ||||
| ./gradlew multiplatform-crypto-libsodium-bindings:publishIosArm32PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto-libsodium-bindings:publishIosArm64PublicationToSnapshotRepository \ | ||||
|  | ||||
| @ -7,7 +7,6 @@ cd sodiumWrapper | ||||
| ./makeMacosX86-64.sh | ||||
| #now we can do the delegated build of ios and macos libraries | ||||
| cd .. | ||||
| ./gradlew multiplatform-crypto-delegated:publishMacosX64PublicationToSnapshotRepository | ||||
| 
 | ||||
| ./gradlew multiplatform-crypto-libsodium-bindings:publishMacosX64PublicationToSnapshotRepository | ||||
| set +e | ||||
|  | ||||
| @ -1,8 +0,0 @@ | ||||
| set -e | ||||
| #!/bin/sh | ||||
| #this will hopefully download all konan dependancies that we use in the build scripts | ||||
| ./gradlew multiplatform-crypto-api:build | ||||
| ./gradlew multiplatform-crypto:publishIosArm32PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto:publishIosArm64PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto:publishIosX64PublicationToSnapshotRepository | ||||
| set +e | ||||
| @ -1,6 +0,0 @@ | ||||
| set -e | ||||
| #!/bin/sh | ||||
| #this will hopefully download all konan dependancies that we use in the build scripts | ||||
| ./gradlew multiplatform-crypto-api:build | ||||
| ./gradlew multiplatform-crypto:publishMacosX64PublicationToSnapshotRepository | ||||
| set +e | ||||
| @ -1,8 +0,0 @@ | ||||
| set -e | ||||
| #!/bin/sh | ||||
| #this will hopefully download all konan dependancies that we use in the build scripts | ||||
| ./gradlew multiplatform-crypto-api:build | ||||
| ./gradlew multiplatform-crypto:publishTvosArm64PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto:publishTvosX64PublicationToSnapshotRepository | ||||
| 
 | ||||
| set +e | ||||
| @ -1,8 +0,0 @@ | ||||
| set -e | ||||
| #!/bin/sh | ||||
| #this will hopefully download all konan dependancies that we use in the build scripts | ||||
| ./gradlew multiplatform-crypto-api:build | ||||
| ./gradlew multiplatform-crypto:publishWatchosArm32PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto:publishWatchosArm64PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto:publishWatchosX86PublicationToSnapshotRepository | ||||
| set +e | ||||
| @ -1,8 +0,0 @@ | ||||
| set -e | ||||
| #!/bin/sh | ||||
| #this will hopefully download all konan dependancies that we use in the build scripts | ||||
| ./gradlew multiplatform-crypto-api:build | ||||
| ./gradlew multiplatform-crypto:publishTvosArm64PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto:publishTvosX64PublicationToSnapshotRepository | ||||
| 
 | ||||
| set +e | ||||
| @ -7,9 +7,6 @@ cd sodiumWrapper | ||||
| ./makeWatchos.sh | ||||
| #now we can do the delegated build of ios and macos libraries | ||||
| cd .. | ||||
| ./gradlew multiplatform-crypto-delegated:publishWatchosArm32PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto-delegated:publishWatchosArm64PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto-delegated:publishWatchosX86PublicationToSnapshotRepository | ||||
| 
 | ||||
| ./gradlew multiplatform-crypto-libsodium-bindings:publishWatchosArm32PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto-libsodium-bindings:publishWatchosArm64PublicationToSnapshotRepository \ | ||||
|  | ||||
| @ -1,15 +0,0 @@ | ||||
| set -e | ||||
| #!/bin/sh | ||||
| ./gradlew multiplatform-crypto-delegated:publishMacosX64PublicationToSnapshotRepository | ||||
| 
 | ||||
| ./gradlew multiplatform-crypto-delegated:publishIosArm32PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto-delegated:publishIosArm64PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto-delegated:publishIosX64PublicationToSnapshotRepository | ||||
| 
 | ||||
| ./gradlew multiplatform-crypto-delegated:publishWatchosArm32PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto-delegated:publishWatchosArm64PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto-delegated:publishWatchosX86PublicationToSnapshotRepository | ||||
| 
 | ||||
| ./gradlew multiplatform-crypto-delegated:publishTvosArm64PublicationToSnapshotRepository \ | ||||
| multiplatform-crypto-delegated:publishTvosX64PublicationToSnapshotRepository | ||||
| set +e | ||||
| @ -1,698 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| @file:Suppress("UnstableApiUsage") | ||||
| 
 | ||||
| import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget | ||||
| import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest | ||||
| import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest | ||||
| import org.jetbrains.dokka.Platform | ||||
| 
 | ||||
| plugins { | ||||
|     kotlin(PluginsDeps.multiplatform) | ||||
|     id(PluginsDeps.mavenPublish) | ||||
|     id(PluginsDeps.signing) | ||||
|     id(PluginsDeps.node) version Versions.nodePlugin | ||||
|     id(PluginsDeps.dokka) | ||||
|     id(PluginsDeps.taskTree) version Versions.taskTreePlugin | ||||
| } | ||||
| 
 | ||||
| val sonatypeStaging = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" | ||||
| val sonatypeSnapshots = "https://oss.sonatype.org/content/repositories/snapshots/" | ||||
| 
 | ||||
| val sonatypePassword: String? by project | ||||
| 
 | ||||
| val sonatypeUsername: String? by project | ||||
| 
 | ||||
| val sonatypePasswordEnv: String? = System.getenv()["SONATYPE_PASSWORD"] | ||||
| val sonatypeUsernameEnv: String? = System.getenv()["SONATYPE_USERNAME"] | ||||
| 
 | ||||
| repositories { | ||||
|     mavenCentral() | ||||
|     jcenter() | ||||
| 
 | ||||
| } | ||||
| group = ReleaseInfo.group | ||||
| version = ReleaseInfo.version | ||||
| 
 | ||||
| val ideaActive = isInIdea() | ||||
| println("Idea active: $ideaActive") | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| kotlin { | ||||
|     val hostOsName = getHostOsName() | ||||
|     runningOnLinuxx86_64 { | ||||
|         println("Configuring Linux X86-64 targets") | ||||
|         jvm() | ||||
|         js { | ||||
|             browser { | ||||
|                 testTask { | ||||
|                     isRunningInGitlabCi { | ||||
|                         enabled = false //Until I sort out testing on travis | ||||
|                     } | ||||
|                     useKarma { | ||||
|                         useChrome() | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             nodejs { | ||||
|                 testTask { | ||||
|                     useMocha() { | ||||
|                         timeout = "10s" | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|         linuxX64() { | ||||
|             compilations.getByName("main") { | ||||
|                 val libsodiumCinterop by cinterops.creating { | ||||
|                     defFile(project.file("src/nativeInterop/cinterop/libsodium.def")) | ||||
|                     compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-linux-x86-64/include/") | ||||
|                 } | ||||
|                 kotlinOptions.freeCompilerArgs = listOf( | ||||
|                     "-include-binary", "${project.rootDir}/sodiumWrapper/static-linux-x86-64/lib/libsodium.a" | ||||
|                 ) | ||||
|             } | ||||
|             binaries { | ||||
|                 staticLib { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         linuxArm64() { | ||||
|             binaries { | ||||
|                 staticLib { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         // Linux 32 is using target-sysroot-2-raspberrypi which is missing getrandom and explicit_bzero in stdlib | ||||
|         // so konanc can't build klib because getrandom missing will cause sodium_misuse() | ||||
|         //     ld.lld: error: undefined symbol: explicit_bzero | ||||
|         //     >>> referenced by utils.c | ||||
|         //     >>>               libsodium_la-utils.o:(sodium_memzero) in archive /tmp/included11051337748775083797/libsodium.a | ||||
|         // | ||||
|         //     ld.lld: error: undefined symbol: getrandom | ||||
|         //     >>> referenced by randombytes_sysrandom.c | ||||
|         //     >>>               libsodium_la-randombytes_sysrandom.o:(_randombytes_linux_getrandom) in archive /tmp/included11051337748775083797/libsodium.a | ||||
| 
 | ||||
| //        linuxArm32Hfp() { | ||||
| //            binaries { | ||||
| //                staticLib { | ||||
| //                } | ||||
| //            } | ||||
| //            compilations.getByName("main") { | ||||
| //                val libsodiumCinterop by cinterops.creating { | ||||
| //                    defFile(project.file("src/nativeInterop/cinterop/libsodium.def")) | ||||
| //                    compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-arm32/include/") | ||||
| //                } | ||||
| //                kotlinOptions.freeCompilerArgs = listOf( | ||||
| //                    "-include-binary", "${project.rootDir}/sodiumWrapper/static-arm32/lib/libsodium.a" | ||||
| //                ) | ||||
| //            } | ||||
| //        } | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     runningOnLinuxArm64 { | ||||
|         println("Configuring Linux Arm 64 targets") | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     runningOnLinuxArm32 { | ||||
|         println("Configuring Linux Arm 32 targets") | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     runningOnMacos { | ||||
|         println("Configuring macos targets") | ||||
|         iosX64() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         iosArm64() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         iosArm32() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         macosX64() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|             compilations.getByName("main") { | ||||
|                 val libsodiumCinterop by cinterops.creating { | ||||
|                     defFile(project.file("src/nativeInterop/cinterop/libsodium.def")) | ||||
|                     compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-macos-x86-64/include") | ||||
|                 } | ||||
|                 kotlinOptions.freeCompilerArgs = listOf( | ||||
|                     "-include-binary", "${project.rootDir}/sodiumWrapper/static-macos-x86-64/lib/libsodium.a" | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|         tvosX64() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         tvosArm64() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         watchosArm64() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         watchosArm32() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         watchosX86() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     runningOnWindows { | ||||
|         println("Configuring Mingw targets") | ||||
|         mingwX64() { | ||||
|             binaries { | ||||
|                 staticLib { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|             compilations.getByName("main") { | ||||
|                 val libsodiumCinterop by cinterops.creating { | ||||
|                     defFile(project.file("src/nativeInterop/cinterop/libsodium.def")) | ||||
|                     compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-mingw-x86-64/include") | ||||
|                 } | ||||
|                 kotlinOptions.freeCompilerArgs = listOf( | ||||
|                     "-include-binary", "${project.rootDir}/sodiumWrapper/static-mingw-x86-64/lib/libsodium.a" | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     println(targets.names) | ||||
| 
 | ||||
|     sourceSets { | ||||
|         val commonMain by getting { | ||||
|             dependencies { | ||||
|                 implementation(kotlin(Deps.Common.stdLib)) | ||||
|                 implementation(kotlin(Deps.Common.test)) | ||||
|                 implementation(Deps.Common.kotlinBigNum) | ||||
|                 api(project(Deps.Common.apiProject)) | ||||
|             } | ||||
|         } | ||||
|         val commonTest by getting { | ||||
|             dependencies { | ||||
|                 implementation(kotlin(Deps.Common.test)) | ||||
|                 implementation(kotlin(Deps.Common.testAnnotation)) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         val nativeDependencies = independentDependencyBlock { | ||||
|         } | ||||
| 
 | ||||
|         val nativeMain by creating { | ||||
|             dependsOn(commonMain) | ||||
|             isRunningInIdea { | ||||
|                 kotlin.setSrcDirs(emptySet<String>()) | ||||
|             } | ||||
|             dependencies { | ||||
|                 nativeDependencies(this) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         val nativeTest by creating { | ||||
|             dependsOn(commonTest) | ||||
|             isRunningInIdea { | ||||
|                 kotlin.setSrcDirs(emptySet<String>()) | ||||
|             } | ||||
|             dependencies { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         //Set up shared source sets | ||||
|         //linux, linuxArm32Hfp, linuxArm64 | ||||
|         val linux64Bit = setOf( | ||||
|             "linuxX64" | ||||
|         ) | ||||
|         val linuxArm64Bit = setOf( | ||||
|             "linuxArm64" | ||||
|         ) | ||||
|         val linux32Bit = setOf( | ||||
|             "" // "linuxArm32Hfp" | ||||
|         ) | ||||
| 
 | ||||
|         //iosArm32, iosArm64, iosX64, macosX64, metadata, tvosArm64, tvosX64, watchosArm32, watchosArm64, watchosX86 | ||||
|         val macos64Bit = setOf( | ||||
|             "macosX64" | ||||
|         ) | ||||
|         val iosArm = setOf( | ||||
|             "iosArm64", "iosArm32" | ||||
|         ) | ||||
|         val iosSimulator = setOf( | ||||
|             "iosX64" | ||||
|         ) | ||||
|         val mingw64Bit = setOf( | ||||
|             "mingwX64" | ||||
|         ) | ||||
| 
 | ||||
|         val tvosArm = setOf( | ||||
|             "tvosArm64" | ||||
|         ) | ||||
|         val tvosSimulator = setOf( | ||||
|             "tvosX64" | ||||
|         ) | ||||
| 
 | ||||
|         val watchosArm = setOf( | ||||
|             "watchosArm32", "watchosArm64" | ||||
|         ) | ||||
|         val watchosSimulator = setOf( | ||||
|             "watchosX86" | ||||
|         ) | ||||
| 
 | ||||
|         targets.withType<KotlinNativeTarget> { | ||||
|             println("Target $name") | ||||
| 
 | ||||
|             compilations.getByName("main") { | ||||
|                 if (linux64Bit.contains(this@withType.name)) { | ||||
|                     defaultSourceSet.dependsOn(nativeMain) | ||||
|                 } | ||||
|                 if (linuxArm64Bit.contains(this@withType.name)) { | ||||
|                         defaultSourceSet.dependsOn( | ||||
|                             createWorkaroundNativeMainSourceSet( | ||||
|                                 this@withType.name, | ||||
|                                 nativeDependencies | ||||
|                             ) | ||||
|                         ) | ||||
| 
 | ||||
|                     compilations.getByName("main") { | ||||
|                         val libsodiumCinterop by cinterops.creating { | ||||
|                             defFile(project.file("src/nativeInterop/cinterop/libsodium.def")) | ||||
|                             compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-arm64/include/") | ||||
|                         } | ||||
|                         kotlinOptions.freeCompilerArgs = listOf( | ||||
|                             "-include-binary", "${project.rootDir}/sodiumWrapper/static-arm64/lib/libsodium.a" | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|                 if (linux32Bit.contains(this@withType.name)) { | ||||
|                     defaultSourceSet.dependsOn(createWorkaroundNativeMainSourceSet(this@withType.name, nativeDependencies)) | ||||
|                 } | ||||
|                 if (macos64Bit.contains(this@withType.name)) { | ||||
|                     defaultSourceSet.dependsOn(createWorkaroundNativeMainSourceSet(this@withType.name, nativeDependencies)) | ||||
|                 } | ||||
|                 //All ioses share the same static library | ||||
|                 if (iosArm.contains(this@withType.name)) { | ||||
|                     defaultSourceSet.dependsOn(createWorkaroundNativeMainSourceSet(this@withType.name, nativeDependencies)) | ||||
|                     println("Setting ios cinterop for $this") | ||||
|                     val libsodiumCinterop by cinterops.creating { | ||||
|                         defFile(project.file("src/nativeInterop/cinterop/libsodium.def")) | ||||
|                         compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-ios/include") | ||||
|                     } | ||||
|                     kotlinOptions.freeCompilerArgs = listOf( | ||||
|                         "-include-binary", "${project.rootDir}/sodiumWrapper/static-ios/lib/libsodium.a" | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|                 if (iosSimulator.contains(this@withType.name)) { | ||||
|                     defaultSourceSet.dependsOn(createWorkaroundNativeMainSourceSet(this@withType.name, nativeDependencies)) | ||||
|                     println("Setting ios cinterop for $this") | ||||
|                     val libsodiumCinterop by cinterops.creating { | ||||
|                         defFile(project.file("src/nativeInterop/cinterop/libsodium.def")) | ||||
|                         compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-ios-simulators/include") | ||||
|                     } | ||||
|                     kotlinOptions.freeCompilerArgs = listOf( | ||||
|                         "-include-binary", "${project.rootDir}/sodiumWrapper/static-ios-simulators/lib/libsodium.a" | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|                 if (tvosArm.contains(this@withType.name)) { | ||||
|                     defaultSourceSet.dependsOn(createWorkaroundNativeMainSourceSet(this@withType.name, nativeDependencies)) | ||||
|                     println("Setting ios cinterop for $this") | ||||
|                     val libsodiumCinterop by cinterops.creating { | ||||
|                         defFile(project.file("src/nativeInterop/cinterop/libsodium.def")) | ||||
|                         compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-tvos/include") | ||||
|                     } | ||||
|                     kotlinOptions.freeCompilerArgs = listOf( | ||||
|                         "-include-binary", "${project.rootDir}/sodiumWrapper/static-tvos/lib/libsodium.a" | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|                 if (tvosSimulator.contains(this@withType.name)) { | ||||
|                     defaultSourceSet.dependsOn(createWorkaroundNativeMainSourceSet(this@withType.name, nativeDependencies)) | ||||
|                     println("Setting ios cinterop for $this") | ||||
|                     val libsodiumCinterop by cinterops.creating { | ||||
|                         defFile(project.file("src/nativeInterop/cinterop/libsodium.def")) | ||||
|                         compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-tvos-simulators/include") | ||||
|                     } | ||||
|                     kotlinOptions.freeCompilerArgs = listOf( | ||||
|                         "-include-binary", "${project.rootDir}/sodiumWrapper/static-tvos-simulators/lib/libsodium.a" | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|                 if (watchosArm.contains(this@withType.name)) { | ||||
|                     defaultSourceSet.dependsOn(createWorkaroundNativeMainSourceSet(this@withType.name, nativeDependencies)) | ||||
|                     println("Setting ios cinterop for $this") | ||||
|                     val libsodiumCinterop by cinterops.creating { | ||||
|                         defFile(project.file("src/nativeInterop/cinterop/libsodium.def")) | ||||
|                         compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-watchos/include") | ||||
|                     } | ||||
|                     kotlinOptions.freeCompilerArgs = listOf( | ||||
|                         "-include-binary", "${project.rootDir}/sodiumWrapper/static-watchos/lib/libsodium.a" | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|                 if (watchosSimulator.contains(this@withType.name)) { | ||||
|                     defaultSourceSet.dependsOn(createWorkaroundNativeMainSourceSet(this@withType.name, nativeDependencies)) | ||||
|                     println("Setting ios cinterop for $this") | ||||
|                     val libsodiumCinterop by cinterops.creating { | ||||
|                         defFile(project.file("src/nativeInterop/cinterop/libsodium.def")) | ||||
|                         compilerOpts.add("-I${project.rootDir}/sodiumWrapper/static-watchos-simulators/include") | ||||
|                     } | ||||
|                     kotlinOptions.freeCompilerArgs = listOf( | ||||
|                         "-include-binary", "${project.rootDir}/sodiumWrapper/static-watchos-simulators/lib/libsodium.a" | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|             } | ||||
|             compilations.getByName("test") { | ||||
|                 println("Setting native test dep for $this@withType.name") | ||||
|                 defaultSourceSet.dependsOn(nativeTest) | ||||
| 
 | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         runningOnLinuxx86_64 { | ||||
|             println("Configuring Linux 64 Bit source sets") | ||||
|             val jvmMain by getting { | ||||
|                 dependencies { | ||||
|                     implementation(kotlin(Deps.Jvm.stdLib)) | ||||
|                     implementation(kotlin(Deps.Jvm.test)) | ||||
|                     implementation(kotlin(Deps.Jvm.testJUnit)) | ||||
| 
 | ||||
|                     //lazysodium | ||||
|                     implementation(Deps.Jvm.Delegated.lazysodium) | ||||
|                     implementation(Deps.Jvm.Delegated.jna) | ||||
|                 } | ||||
|             } | ||||
|             val jvmTest by getting { | ||||
|                 dependencies { | ||||
|                     implementation(kotlin(Deps.Jvm.test)) | ||||
|                     implementation(kotlin(Deps.Jvm.testJUnit)) | ||||
|                     implementation(kotlin(Deps.Jvm.reflection)) | ||||
|                 } | ||||
|             } | ||||
|             val jsMain by getting { | ||||
|                 dependencies { | ||||
|                     implementation(kotlin(Deps.Js.stdLib)) | ||||
|                     implementation(npm(Deps.Js.Npm.libsodiumWrappers.first, Deps.Js.Npm.libsodiumWrappers.second)) | ||||
|                 } | ||||
|             } | ||||
|             val jsTest by getting { | ||||
|                 dependencies { | ||||
|                     implementation(kotlin(Deps.Js.test)) | ||||
|                     implementation(npm(Deps.Js.Npm.libsodiumWrappers.first, Deps.Js.Npm.libsodiumWrappers.second)) | ||||
|                 } | ||||
|             } | ||||
|             val linuxX64Main by getting { | ||||
|                 isRunningInIdea { | ||||
|                     kotlin.srcDir("src/nativeMain/kotlin") | ||||
|                 } | ||||
|             } | ||||
|             val linuxX64Test by getting { | ||||
|                 dependsOn(nativeTest) | ||||
|                 isRunningInIdea { | ||||
|                     kotlin.srcDir("src/nativeTest/kotlin") | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         runningOnMacos { | ||||
|             println("Configuring Macos source sets") | ||||
|             val macosX64Main by getting { | ||||
|                 dependsOn(nativeMain) | ||||
|                 if (ideaActive) { | ||||
|                     kotlin.srcDir("src/nativeMain/kotlin") | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|             val macosX64Test by getting { | ||||
|                 dependsOn(nativeTest) | ||||
|                 if (ideaActive) { | ||||
|                     kotlin.srcDir("src/nativeTest/kotlin") | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
| 
 | ||||
|             val tvosX64Main by getting { | ||||
|                 dependsOn(commonMain) | ||||
|             } | ||||
| 
 | ||||
|             val tvosArm64Main by getting { | ||||
|                 dependsOn(commonMain) | ||||
|             } | ||||
| 
 | ||||
|             val watchosX86Main by getting { | ||||
|                 dependsOn(commonMain) | ||||
|             } | ||||
| 
 | ||||
|             val watchosArm64Main by getting { | ||||
|                 dependsOn(commonMain) | ||||
|             } | ||||
| 
 | ||||
|             val watchosArm32Main by getting { | ||||
|                 dependsOn(commonMain) | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         if (hostOsName == "windows") { | ||||
|             val mingwX64Main by getting { | ||||
|                 dependsOn(nativeMain) | ||||
|                 if (ideaActive) { | ||||
|                     kotlin.srcDir("src/nativeMain/kotlin") | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             val mingwX64Test by getting { | ||||
|                 dependsOn(nativeTest) | ||||
|                 if (ideaActive) { | ||||
|                     kotlin.srcDir("src/nativeTest/kotlin") | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         all { | ||||
|             languageSettings.enableLanguageFeature("InlineClasses") | ||||
|             languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes") | ||||
|             languageSettings.useExperimentalAnnotation("kotlin.ExperimentalStdlibApi") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| tasks { | ||||
| 
 | ||||
| 
 | ||||
|     create<Jar>("javadocJar") { | ||||
|         dependsOn(dokkaJavadoc) | ||||
|         archiveClassifier.set("javadoc") | ||||
|         from(dokkaJavadoc.get().outputDirectory) | ||||
|     } | ||||
| 
 | ||||
|     dokkaJavadoc { | ||||
|         println("Dokka !") | ||||
|         dokkaSourceSets { | ||||
|             named("commonMain") { | ||||
|                 displayName.set("common") | ||||
|                 platform.set(Platform.common) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
|     if (getHostOsName() == "linux" && getHostArchitecture() == "x86-64") { | ||||
|         val jvmTest by getting(Test::class) { | ||||
|             testLogging { | ||||
|                 events("PASSED", "FAILED", "SKIPPED") | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         val linuxX64Test by getting(KotlinNativeTest::class) { | ||||
| 
 | ||||
|             testLogging { | ||||
|                 events("PASSED", "FAILED", "SKIPPED") | ||||
|                 showStandardStreams = true | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         val jsNodeTest by getting(KotlinJsTest::class) { | ||||
|             testLogging { | ||||
|                 events("PASSED", "FAILED", "SKIPPED") | ||||
| //                showStandardStreams = true | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| //        val legacyjsNodeTest by getting(KotlinJsTest::class) { | ||||
| // | ||||
| //            testLogging { | ||||
| //                events("PASSED", "FAILED", "SKIPPED") | ||||
| //                showStandardStreams = true | ||||
| //            } | ||||
| //        } | ||||
| 
 | ||||
| //        val jsIrBrowserTest by getting(KotlinJsTest::class) { | ||||
| //            testLogging { | ||||
| //                events("PASSED", "FAILED", "SKIPPED") | ||||
| //                 showStandardStreams = true | ||||
| //            } | ||||
| //        } | ||||
|     } | ||||
| 
 | ||||
|     if (getHostOsName() == "windows") { | ||||
|         val mingwX64Test by getting(KotlinNativeTest::class) { | ||||
| 
 | ||||
|             testLogging { | ||||
|                 events("PASSED", "FAILED", "SKIPPED") | ||||
|                 showStandardStreams = true | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| signing { | ||||
|     isRequired = false | ||||
|     sign(publishing.publications) | ||||
| } | ||||
| 
 | ||||
| publishing { | ||||
|     publications.withType(MavenPublication::class) { | ||||
|         artifact(tasks["javadocJar"]) | ||||
|         pom { | ||||
|             name.set("Kotlin Multiplatform Crypto") | ||||
|             description.set("Kotlin Multiplatform Crypto library") | ||||
|             url.set("https://github.com/ionspin/kotlin-multiplatform-crypto") | ||||
|             licenses { | ||||
|                 license { | ||||
|                     name.set("The Apache License, Version 2.0") | ||||
|                     url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") | ||||
|                 } | ||||
|             } | ||||
|             developers { | ||||
|                 developer { | ||||
|                     id.set("ionspin") | ||||
|                     name.set("Ugljesa Jovanovic") | ||||
|                     email.set("opensource@ionspin.com") | ||||
|                 } | ||||
|             } | ||||
|             scm { | ||||
|                 url.set("https://github.com/ionspin/kotlin-multiplatform-crypto") | ||||
|                 connection.set("scm:git:git://git@github.com:ionspin/kotlin-multiplatform-crypto.git") | ||||
|                 developerConnection.set("scm:git:ssh://git@github.com:ionspin/kotlin-multiplatform-crypto.git") | ||||
| 
 | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
|     repositories { | ||||
|         maven { | ||||
| 
 | ||||
|             url = uri(sonatypeStaging) | ||||
|             credentials { | ||||
|                 username = sonatypeUsername ?: sonatypeUsernameEnv ?: "" | ||||
|                 password = sonatypePassword ?: sonatypePasswordEnv ?: "" | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         maven { | ||||
|             name = "snapshot" | ||||
|             url = uri(sonatypeSnapshots) | ||||
|             credentials { | ||||
|                 username = sonatypeUsername ?: sonatypeUsernameEnv ?: "" | ||||
|                 password = sonatypePassword ?: sonatypePasswordEnv ?: "" | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| //configurations.forEach { | ||||
| // | ||||
| //    if (it.name == "linuxCompileKlibraries") { | ||||
| //        println("Configuration name: ${it.name}") | ||||
| //        it.attributes { | ||||
| //            this.keySet().forEach { key -> | ||||
| //                val attribute = getAttribute(key) | ||||
| //                println(" |-- Attribute $key ${attribute}") | ||||
| //                attribute(org.jetbrains.kotlin.gradle.plugin.ProjectLocalConfigurations.ATTRIBUTE, "publicZ") | ||||
| //            } | ||||
| //        } | ||||
| //    } | ||||
| //} | ||||
| 
 | ||||
| 
 | ||||
										
											Binary file not shown.
										
									
								
							| @ -1,26 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 20-Jul-2019 | ||||
|  */ | ||||
| object Config { | ||||
|     const val DEBUG = false | ||||
| } | ||||
| @ -1,216 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.authenticated.XChaCha20Poly1305Delegated | ||||
| import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bProperties | ||||
| import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bDelegated | ||||
| import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bDelegatedStateless | ||||
| import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bMultipart | ||||
| import com.ionspin.kotlin.crypto.hash.sha.* | ||||
| import com.ionspin.kotlin.crypto.keyderivation.ArgonResult | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 24-May-2020 | ||||
|  */ | ||||
| 
 | ||||
| object CryptoInitializerDelegated : CryptoInitializer { | ||||
|     override suspend fun initialize() { | ||||
|         Initializer.initialize() | ||||
|     } | ||||
| 
 | ||||
|     fun initializeWithCallback(done: () -> Unit) { | ||||
|         Initializer.initializeWithCallback(done) | ||||
|     } | ||||
| 
 | ||||
|     override fun isInitialized(): Boolean { | ||||
|         return Initializer.isInitialized() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| object CryptoPrimitives : PrimitivesApi { | ||||
| 
 | ||||
|     object Blake2b { | ||||
|         fun updateable(key: UByteArray? = null, hashLength: Int = Blake2bProperties.MAX_HASH_BYTES): com.ionspin.kotlin.crypto.hash.blake2b.Blake2bMultipart { | ||||
|             checkInitialization() | ||||
|             return Blake2bDelegated(key, hashLength) | ||||
|         } | ||||
| 
 | ||||
|         fun stateless(message: UByteArray, key: UByteArray = ubyteArrayOf(), hashLength: Int = Blake2bProperties.MAX_HASH_BYTES): UByteArray { | ||||
|             checkInitialization() | ||||
|             return Blake2bDelegatedStateless.digest(message, key, hashLength) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     object Sha256 { | ||||
|         fun updateable(): com.ionspin.kotlin.crypto.hash.sha.Sha256 { | ||||
|             checkInitialization() | ||||
|             return Sha256Delegated() | ||||
|         } | ||||
| 
 | ||||
|         fun stateless(message: UByteArray) : UByteArray{ | ||||
|             checkInitialization() | ||||
|             return Sha256StatelessDelegated.digest(inputMessage =  message) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     object Sha512 { | ||||
|         fun updateable(): Sha512Multipart { | ||||
|             checkInitialization() | ||||
|             return Sha512Delegated() | ||||
|         } | ||||
| 
 | ||||
|         fun stateless(message: UByteArray) : UByteArray { | ||||
|             checkInitialization() | ||||
|             return Sha512StatelessDelegated.digest(inputMessage =  message) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun checkInitialization() { | ||||
|         if (!Initializer.isInitialized()) { | ||||
|             throw RuntimeException("Platform library not initialized, check if you called Initializer.initialize()") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun hashBlake2bMultipart(key: UByteArray?, hashLength: Int): Blake2bMultipart { | ||||
|         checkInitialization() | ||||
|         return Blake2bDelegated(key, hashLength) | ||||
|     } | ||||
| 
 | ||||
|     override fun hashBlake2b(message: UByteArray, key: UByteArray, hashLength: Int): UByteArray { | ||||
|         checkInitialization() | ||||
|         return Blake2bDelegatedStateless.digest(message, key, hashLength) | ||||
|     } | ||||
| 
 | ||||
|     override fun hashSha256Multipart(): com.ionspin.kotlin.crypto.hash.sha.Sha256 { | ||||
|         checkInitialization() | ||||
|         return Sha256Delegated() | ||||
|     } | ||||
| 
 | ||||
|     override fun hashSha256(message: UByteArray): UByteArray { | ||||
|         checkInitialization() | ||||
|         return Sha256StatelessDelegated.digest(inputMessage =  message) | ||||
|     } | ||||
| 
 | ||||
|     override fun hashSha512Multipart(): com.ionspin.kotlin.crypto.hash.sha.Sha512Multipart { | ||||
|         checkInitialization() | ||||
|         return Sha512Delegated() | ||||
|     } | ||||
| 
 | ||||
|     override fun hashSha512(message: UByteArray): UByteArray { | ||||
|         checkInitialization() | ||||
|         return Sha512StatelessDelegated.digest(inputMessage =  message) | ||||
|     } | ||||
| 
 | ||||
|     override fun deriveKey( | ||||
|         password: String, | ||||
|         salt: String?, | ||||
|         key: String, | ||||
|         associatedData: String, | ||||
|         parallelism: Int, | ||||
|         tagLength: Int, | ||||
|         memory: Int, | ||||
|         numberOfIterations: Int | ||||
|     ): ArgonResult { | ||||
| //        return Argon2Delegated.derive( | ||||
| //            password, | ||||
| //            salt, | ||||
| //            key, | ||||
| //            associatedData, | ||||
| //            parallelism | ||||
| //            tagLength, | ||||
| //            memory, | ||||
| //            numberOfIterations | ||||
| //        ) | ||||
|         TODO() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| fun SymmetricKey.Companion.randomKey() : SymmetricKey { | ||||
|     return SymmetricKey(SRNG.getRandomBytes(32)) | ||||
| } | ||||
| 
 | ||||
| object Crypto { | ||||
| 
 | ||||
|     object Hash : HashApi { | ||||
|         override fun hash(data: UByteArray, key : UByteArray) : HashedData { | ||||
|             return HashedData(Blake2bDelegatedStateless.digest(data, key)) | ||||
|         } | ||||
| 
 | ||||
|         override fun multipartHash(key: UByteArray?) : com.ionspin.kotlin.crypto.hash.MultipartHash { | ||||
|             return Blake2bDelegated(key) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     object Encryption : EncryptionApi { | ||||
|         override fun encrypt(key: SymmetricKey, data : Encryptable<*>, associatedData : UByteArray) : EncryptedData { | ||||
|             if (key.value.size != 32) { | ||||
|                 throw RuntimeException("Invalid key size! Required 32, supplied ${key.value.size}") | ||||
|             } | ||||
|             val nonce = SRNG.getRandomBytes(24) | ||||
|             return EncryptedData(XChaCha20Poly1305Delegated.encrypt(key.value, nonce, data.toEncryptableForm(), associatedData), nonce) | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         override fun <T: Encryptable<T>> decrypt(key: SymmetricKey, encryptedData : EncryptedData, associatedData: UByteArray, byteArrayDeserializer : (UByteArray) -> T) : T { | ||||
|             return byteArrayDeserializer(XChaCha20Poly1305Delegated.decrypt(key.value, encryptedData.nonce, encryptedData.ciphertext, associatedData)) | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         override fun createMultipartEncryptor(key: SymmetricKey): MultipartAuthenticatedEncryption { | ||||
|             return MultipartAuthenticatedEncryptor(key) | ||||
|         } | ||||
| 
 | ||||
|         override fun createMultipartDecryptor(key: SymmetricKey, header: MultipartEncryptionHeader) : MultipartAuthenticatedDecryption { | ||||
|             val decryptor = XChaCha20Poly1305Delegated() | ||||
|             decryptor.initializeForDecryption(key.value, header.nonce) | ||||
|             return MultipartAuthenticatedDecryptor(decryptor) | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| class MultipartAuthenticatedEncryptor internal constructor(val key : SymmetricKey) : MultipartAuthenticatedEncryption { | ||||
| 
 | ||||
|     val header : MultipartEncryptionHeader | ||||
|     val primitive = XChaCha20Poly1305Delegated() | ||||
|     init { | ||||
|         header = MultipartEncryptionHeader(primitive.initializeForEncryption(key.value)) | ||||
|     } | ||||
| 
 | ||||
|     override fun startEncryption(): MultipartEncryptionHeader { | ||||
|         return header | ||||
|     } | ||||
| 
 | ||||
|     override fun encryptPartialData(data: UByteArray, associatedData: UByteArray): EncryptedDataPart { | ||||
|         return EncryptedDataPart(primitive.encrypt(data, associatedData)) | ||||
|     } | ||||
| 
 | ||||
|     override fun cleanup() { | ||||
|         primitive.cleanup() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| class MultipartAuthenticatedDecryptor internal constructor(val decryptor: XChaCha20Poly1305Delegated) : MultipartAuthenticatedDecryption { | ||||
|     override fun decryptPartialData(data: EncryptedDataPart, associatedData: UByteArray): DecryptedDataPart { | ||||
|         return DecryptedDataPart(decryptor.decrypt(data.data, associatedData)) | ||||
|     } | ||||
| 
 | ||||
|     override fun cleanup() { | ||||
|         decryptor.cleanup() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| expect object Initializer { | ||||
|     fun isInitialized() : Boolean | ||||
| 
 | ||||
|     suspend fun initialize() | ||||
| 
 | ||||
|     fun initializeWithCallback(done: () -> (Unit)) | ||||
| } | ||||
| 
 | ||||
| @ -1,12 +0,0 @@ | ||||
| package _multiplatform_crypto_delegated | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 24-May-2020 | ||||
|  */ | ||||
| //Workaround for https://youtrack.jetbrains.com/issue/KT-36878 | ||||
| val byteArray = byteArrayOf(0) | ||||
| val byte = 0.toByte() | ||||
| val longArray = longArrayOf(0) | ||||
| val long = 0L | ||||
| @ -1,26 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 21-Sep-2019 | ||||
|  */ | ||||
| expect object SRNG { | ||||
|     fun getRandomBytes(amount : Int) : UByteArray | ||||
| } | ||||
| @ -1,24 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.authenticated | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 14-Jun-2020 | ||||
|  */ | ||||
| expect class XChaCha20Poly1305Delegated internal constructor() { | ||||
|     internal constructor(key: UByteArray, testState : UByteArray, testHeader: UByteArray, isDecryptor: Boolean) | ||||
|     companion object { | ||||
|         fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, associatedData: UByteArray) : UByteArray | ||||
|         fun decrypt(key: UByteArray, nonce: UByteArray, ciphertext: UByteArray, associatedData: UByteArray) : UByteArray | ||||
|     } | ||||
|     fun initializeForEncryption(key: UByteArray) : UByteArray | ||||
|     fun initializeForDecryption(key: UByteArray, header: UByteArray) | ||||
|     fun encrypt(data: UByteArray, associatedData: UByteArray = ubyteArrayOf()) : UByteArray | ||||
|     fun decrypt(data: UByteArray, associatedData: UByteArray = ubyteArrayOf()) : UByteArray | ||||
|     fun cleanup() | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @ -1,31 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.hash.blake2b | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 14-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| expect class Blake2bDelegated(key: UByteArray? = null, hashLength: Int = Blake2bProperties.MAX_HASH_BYTES) : Blake2bMultipart | ||||
| 
 | ||||
| 
 | ||||
| expect object Blake2bDelegatedStateless : Blake2b | ||||
| 
 | ||||
| 
 | ||||
| @ -1,29 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.hash.sha | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 17-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| expect class Sha256Delegated() : Sha256 | ||||
| 
 | ||||
| expect object Sha256StatelessDelegated : StatelessSha256 | ||||
| @ -1,29 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.hash.sha | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 17-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| expect class Sha512Delegated() : Sha512Multipart | ||||
| 
 | ||||
| expect object Sha512StatelessDelegated : Sha512 | ||||
| @ -1,10 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.keyderivation.argon2 | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.keyderivation.KeyDerivationFunction | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 24-May-2020 | ||||
|  */ | ||||
| interface Argon2 : KeyDerivationFunction | ||||
| @ -1,34 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.parallelization | ||||
| 
 | ||||
| import kotlin.time.ExperimentalTime | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 17-May-2020 | ||||
|  */ | ||||
| @ExperimentalTime | ||||
| object Coroutines14 { | ||||
|     fun argonParallel() : Array<UByte> { | ||||
| //        val argon = Argon2() | ||||
| //        argon | ||||
|         println("Placeholder") | ||||
|         return emptyArray() | ||||
|     } | ||||
| } | ||||
| @ -1,216 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| @file:Suppress("EXPERIMENTAL_API_USAGE") | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.util | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 15-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| inline fun <reified T> Array<T>.chunked(sliceSize: Int): Array<Array<T>> { | ||||
|     val last = this.size % sliceSize | ||||
|     val hasLast = last != 0 | ||||
|     val numberOfSlices = this.size / sliceSize | ||||
| 
 | ||||
| 
 | ||||
|     val result : MutableList<List<T>> = MutableList<List<T>>(0) { emptyList() } | ||||
| 
 | ||||
|     for (i in 0 until numberOfSlices) { | ||||
|         result.add(this.slice(i * sliceSize until (i + 1) * sliceSize)) | ||||
|     } | ||||
|     if (hasLast) { | ||||
|         result.add(this.slice(numberOfSlices * sliceSize until this.size)) | ||||
|     } | ||||
| 
 | ||||
|     return result.map { it.toTypedArray() }.toTypedArray() | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| infix fun UInt.rotateRight(places: Int): UInt { | ||||
|     return (this shr places) xor (this shl (32 - places)) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| infix fun ULong.rotateRight(places: Int): ULong { | ||||
|     return (this shr places) xor (this shl (64 - places)) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| infix fun Array<UByte>.xor(other : Array<UByte>) : Array<UByte> { | ||||
|     if (this.size != other.size) { | ||||
|         throw RuntimeException("Operands of different sizes are not supported yet") | ||||
|     } | ||||
|     return Array(this.size) { this[it] xor other[it] } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| infix fun UByteArray.xor(other : UByteArray) : UByteArray { | ||||
|     if (this.size != other.size) { | ||||
|         throw RuntimeException("Operands of different sizes are not supported yet") | ||||
|     } | ||||
|     return UByteArray(this.size) { this[it] xor other[it] } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| // UInt / Array utils | ||||
| 
 | ||||
| fun UInt.toBigEndianUByteArray() : Array<UByte> { | ||||
|     return Array<UByte> (4) { | ||||
|         ((this shr (24 - (it * 8))) and 0xFFU).toUByte() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun UInt.toLittleEndianTypedUByteArray() : Array<UByte> { | ||||
|     return Array<UByte> (4) { | ||||
|         ((this shr (it * 8)) and 0xFFU).toUByte() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| fun UInt.toLittleEndianUByteArray() : UByteArray { | ||||
|     return UByteArray (4) { | ||||
|         ((this shr (it * 8)) and 0xFFU).toUByte() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // UInt / Array utils | ||||
| 
 | ||||
| fun ULong.toBigEndianUByteArray() : Array<UByte> { | ||||
|     return Array<UByte> (8) { | ||||
|         ((this shr (56 - (it * 8))) and 0xFFU).toUByte() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun ULong.toLittleEndianTypedUByteArray() : Array<UByte> { | ||||
|     return Array<UByte> (8) { | ||||
|         ((this shr (it * 8)) and 0xFFU).toUByte() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| fun ULong.toLittleEndianUByteArray() :UByteArray { | ||||
|     return UByteArray (8) { | ||||
|         ((this shr (it * 8)) and 0xFFU).toUByte() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| fun Array<UByte>.fromLittleEndianArrayToULong() : ULong { | ||||
|     if (this.size > 8) { | ||||
|         throw RuntimeException("ore than 8 bytes in input, potential overflow") | ||||
|     } | ||||
|     var ulong = this.foldIndexed(0UL) { index, acc, uByte -> acc or (uByte.toULong() shl (index * 8))} | ||||
|     return ulong | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| fun UByteArray.fromLittleEndianArrayToULong() : ULong { | ||||
|     if (this.size > 8) { | ||||
|         throw RuntimeException("ore than 8 bytes in input, potential overflow") | ||||
|     } | ||||
|     var ulong = this.foldIndexed(0UL) { index, acc, uByte -> acc or (uByte.toULong() shl (index * 8))} | ||||
|     return ulong | ||||
| } | ||||
| 
 | ||||
| fun UByteArray.arrayChunked(sliceSize: Int): List<UByteArray> { | ||||
|     val last = this.size % sliceSize | ||||
|     val hasLast = last != 0 | ||||
|     val numberOfSlices = this.size / sliceSize | ||||
| 
 | ||||
| 
 | ||||
|     val result : MutableList<UByteArray> = MutableList<UByteArray>(0) { ubyteArrayOf() } | ||||
| 
 | ||||
|     for (i in 0 until numberOfSlices) { | ||||
|         result.add(this.sliceArray(i * sliceSize until (i + 1) * sliceSize)) | ||||
|     } | ||||
|     if (hasLast) { | ||||
|         result.add(this.sliceArray(numberOfSlices * sliceSize until this.size)) | ||||
|     } | ||||
| 
 | ||||
|     return result | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| fun Array<UByte>.fromBigEndianArrayToULong() : ULong { | ||||
|     if (this.size > 8) { | ||||
|         throw RuntimeException("ore than 8 bytes in input, potential overflow") | ||||
|     } | ||||
|     var ulong = this.foldIndexed(0UL) { | ||||
|             index, acc, uByte -> | ||||
|         val res = acc or (uByte.toULong() shl (56 - (index * 8))) | ||||
|         res | ||||
| 
 | ||||
|     } | ||||
|     return ulong | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| fun Array<UByte>.fromLittleEndianArrayToUInt() : UInt { | ||||
|     if (this.size > 4) { | ||||
|         throw RuntimeException("ore than 8 bytes in input, potential overflow") | ||||
|     } | ||||
|     var uint = this.foldIndexed(0U) { index, acc, uByte -> acc or (uByte.toUInt() shl (index * 8))} | ||||
|     return uint | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| fun UByteArray.fromLittleEndianArrayToUInt() : UInt { | ||||
|     if (this.size > 4) { | ||||
|         throw RuntimeException("ore than 8 bytes in input, potential overflow") | ||||
|     } | ||||
|     var uint = this.foldIndexed(0U) { index, acc, uByte -> acc or (uByte.toUInt() shl (index * 8))} | ||||
|     return uint | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| fun Array<UByte>.fromBigEndianArrayToUInt() : UInt { | ||||
|     if (this.size > 4) { | ||||
|         throw RuntimeException("ore than 8 bytes in input, potential overflow") | ||||
|     } | ||||
|     var uint = this.foldIndexed(0U) { index, acc, uByte -> acc or (uByte.toUInt() shl (24 - (index * 8))) } | ||||
|     return uint | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| operator fun UInt.plus(other : UByteArray) : UByteArray { | ||||
|     return this.toLittleEndianUByteArray() + other | ||||
| } | ||||
| 
 | ||||
| //AES Flatten | ||||
| fun Collection<UByteArray>.flattenToUByteArray(): UByteArray { | ||||
|     val result = UByteArray(sumBy { it.size }) | ||||
|     var position = 0 | ||||
|     for (element in this) { | ||||
|         element.forEach { uByte -> | ||||
|             result[position] = uByte | ||||
|             position++ | ||||
|         } | ||||
|     } | ||||
|     return result | ||||
| } | ||||
| @ -1,13 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| import kotlin.test.Test | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 17-Jul-2020 | ||||
|  */ | ||||
| class DebugTest { | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,37 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.util.testBlocking | ||||
| import kotlin.test.Test | ||||
| import kotlin.test.assertTrue | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 21-Sep-2019 | ||||
|  */ | ||||
| class SRNGTest { | ||||
|     @Test | ||||
|     fun testSrng() = testBlocking { | ||||
|         CryptoInitializerDelegated.initialize() | ||||
|         //Just a sanity test, need to add better srng tests. | ||||
|         val randomBytes1 = SRNG.getRandomBytes(10) | ||||
|         val randomBytes2 = SRNG.getRandomBytes(10) | ||||
|         assertTrue { !randomBytes1.contentEquals(randomBytes2) } | ||||
|     } | ||||
| } | ||||
| @ -1,240 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.authenticated | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.CryptoInitializerDelegated | ||||
| import com.ionspin.kotlin.crypto.Initializer | ||||
| import com.ionspin.kotlin.crypto.hash.encodeToUByteArray | ||||
| import com.ionspin.kotlin.crypto.util.hexColumsPrint | ||||
| import com.ionspin.kotlin.crypto.util.testBlocking | ||||
| import kotlin.test.Ignore | ||||
| import kotlin.test.Test | ||||
| import kotlin.test.assertFails | ||||
| import kotlin.test.assertTrue | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 17-Jun-2020 | ||||
|  */ | ||||
| class XChaCha20Poly1305Test { | ||||
| 
 | ||||
| 
 | ||||
|     @Test | ||||
|     fun xChaCha20Poly1305() = testBlocking { | ||||
|         CryptoInitializerDelegated.initialize() | ||||
| 
 | ||||
|         assertTrue { | ||||
|             val message = ("Ladies and Gentlemen of the class of '99: If I could offer you " + | ||||
|                     "only one tip for the future, sunscreen would be it.").encodeToUByteArray() | ||||
| 
 | ||||
|             val associatedData = ubyteArrayOf( | ||||
|                 0x50U, 0x51U, 0x52U, 0x53U, 0xc0U, 0xc1U, 0xc2U, 0xc3U, 0xc4U, 0xc5U, 0xc6U, 0xc7U | ||||
|             ) | ||||
|             val key = ubyteArrayOf( | ||||
|                 0x80U, 0x81U, 0x82U, 0x83U, 0x84U, 0x85U, 0x86U, 0x87U, | ||||
|                 0x88U, 0x89U, 0x8aU, 0x8bU, 0x8cU, 0x8dU, 0x8eU, 0x8fU, | ||||
|                 0x90U, 0x91U, 0x92U, 0x93U, 0x94U, 0x95U, 0x96U, 0x97U, | ||||
|                 0x98U, 0x99U, 0x9aU, 0x9bU, 0x9cU, 0x9dU, 0x9eU, 0x9fU, | ||||
|             ) | ||||
| 
 | ||||
|             val nonce = ubyteArrayOf( | ||||
|                 0x40U, 0x41U, 0x42U, 0x43U, 0x44U, 0x45U, 0x46U, 0x47U, | ||||
|                 0x48U, 0x49U, 0x4aU, 0x4bU, 0x4cU, 0x4dU, 0x4eU, 0x4fU, | ||||
|                 0x50U, 0x51U, 0x52U, 0x53U, 0x54U, 0x55U, 0x56U, 0x57U, | ||||
|             ) | ||||
| 
 | ||||
|             val expected = ubyteArrayOf( | ||||
|                 0xbdU, 0x6dU, 0x17U, 0x9dU, 0x3eU, 0x83U, 0xd4U, 0x3bU, | ||||
|                 0x95U, 0x76U, 0x57U, 0x94U, 0x93U, 0xc0U, 0xe9U, 0x39U, | ||||
|                 0x57U, 0x2aU, 0x17U, 0x00U, 0x25U, 0x2bU, 0xfaU, 0xccU, | ||||
|                 0xbeU, 0xd2U, 0x90U, 0x2cU, 0x21U, 0x39U, 0x6cU, 0xbbU, | ||||
|                 0x73U, 0x1cU, 0x7fU, 0x1bU, 0x0bU, 0x4aU, 0xa6U, 0x44U, | ||||
|                 0x0bU, 0xf3U, 0xa8U, 0x2fU, 0x4eU, 0xdaU, 0x7eU, 0x39U, | ||||
|                 0xaeU, 0x64U, 0xc6U, 0x70U, 0x8cU, 0x54U, 0xc2U, 0x16U, | ||||
|                 0xcbU, 0x96U, 0xb7U, 0x2eU, 0x12U, 0x13U, 0xb4U, 0x52U, | ||||
|                 0x2fU, 0x8cU, 0x9bU, 0xa4U, 0x0dU, 0xb5U, 0xd9U, 0x45U, | ||||
|                 0xb1U, 0x1bU, 0x69U, 0xb9U, 0x82U, 0xc1U, 0xbbU, 0x9eU, | ||||
|                 0x3fU, 0x3fU, 0xacU, 0x2bU, 0xc3U, 0x69U, 0x48U, 0x8fU, | ||||
|                 0x76U, 0xb2U, 0x38U, 0x35U, 0x65U, 0xd3U, 0xffU, 0xf9U, | ||||
|                 0x21U, 0xf9U, 0x66U, 0x4cU, 0x97U, 0x63U, 0x7dU, 0xa9U, | ||||
|                 0x76U, 0x88U, 0x12U, 0xf6U, 0x15U, 0xc6U, 0x8bU, 0x13U, | ||||
|                 0xb5U, 0x2eU, 0xc0U, 0x87U, 0x59U, 0x24U, 0xc1U, 0xc7U, | ||||
|                 0x98U, 0x79U, 0x47U, 0xdeU, 0xafU, 0xd8U, 0x78U, 0x0aU, | ||||
|                 0xcfU, 0x49U | ||||
|             ) | ||||
|             val encrypted = XChaCha20Poly1305Delegated.encrypt(key, nonce, message, associatedData) | ||||
|             encrypted.hexColumsPrint() | ||||
|             val decrypted = XChaCha20Poly1305Delegated.decrypt(key, nonce, encrypted, associatedData) | ||||
|             println("Decrypted") | ||||
|             decrypted.hexColumsPrint() | ||||
|             println("----------") | ||||
|             encrypted.contentEquals(expected) && decrypted.contentEquals(message) | ||||
|         } | ||||
| 
 | ||||
|         assertTrue { | ||||
|             val message = ubyteArrayOf( | ||||
|                 0x00U | ||||
|             ) | ||||
|             val associatedData = ubyteArrayOf( | ||||
|                 0x00U | ||||
|             ) | ||||
|             val key = ubyteArrayOf( | ||||
|                 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, | ||||
|                 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, | ||||
|                 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, | ||||
|                 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, | ||||
|             ) | ||||
| 
 | ||||
|             val nonce = ubyteArrayOf( | ||||
|                 0x00U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U, 0x08U, 0x09U, 0x0aU, 0x0bU, | ||||
|                 0x0cU, 0x0dU, 0x0eU, 0x0fU, 0x10U, 0x11U, 0x12U, 0x13U, 0x14U, 0x15U, 0x16U, 0x17U, | ||||
|             ) | ||||
| 
 | ||||
|             val expected = ubyteArrayOf( | ||||
|                 0xbdU, 0x3bU, 0x8aU, 0xd7U, 0xa1U, 0x9dU, 0xe8U, 0xc4U, 0x55U, | ||||
|                 0x84U, 0x6fU, 0xfcU, 0x75U, 0x31U, 0xbfU, 0x0cU, 0x2dU | ||||
|             ) | ||||
|             val encrypted = XChaCha20Poly1305Delegated.encrypt(key, nonce, message, associatedData) | ||||
|             val decrypted = XChaCha20Poly1305Delegated.decrypt(key, nonce, encrypted, associatedData) | ||||
| 
 | ||||
|             encrypted.contentEquals(expected)  && decrypted.contentEquals(message) | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Ignore() //"Will fail because nonce is not a parameter any more" | ||||
|     @Test | ||||
|     fun updateableXChaCha20Poly1305() { | ||||
|         assertTrue { | ||||
|             val message = ("Ladies and Gentlemen of the class of '99: If I could offer you " + | ||||
|                     "only one tip for the future, sunscreen would be it.").encodeToUByteArray() | ||||
| 
 | ||||
|             val associatedData = ubyteArrayOf( | ||||
|                 0x50U, 0x51U, 0x52U, 0x53U, 0xc0U, 0xc1U, 0xc2U, 0xc3U, 0xc4U, 0xc5U, 0xc6U, 0xc7U | ||||
|             ) | ||||
|             val key = ubyteArrayOf( | ||||
|                 0x80U, 0x81U, 0x82U, 0x83U, 0x84U, 0x85U, 0x86U, 0x87U, | ||||
|                 0x88U, 0x89U, 0x8aU, 0x8bU, 0x8cU, 0x8dU, 0x8eU, 0x8fU, | ||||
|                 0x90U, 0x91U, 0x92U, 0x93U, 0x94U, 0x95U, 0x96U, 0x97U, | ||||
|                 0x98U, 0x99U, 0x9aU, 0x9bU, 0x9cU, 0x9dU, 0x9eU, 0x9fU, | ||||
|             ) | ||||
| 
 | ||||
|             val nonce = ubyteArrayOf( | ||||
|                 0x40U, 0x41U, 0x42U, 0x43U, 0x44U, 0x45U, 0x46U, 0x47U, | ||||
|                 0x48U, 0x49U, 0x4aU, 0x4bU, 0x4cU, 0x4dU, 0x4eU, 0x4fU, | ||||
|                 0x50U, 0x51U, 0x52U, 0x53U, 0x54U, 0x55U, 0x56U, 0x57U, | ||||
|             ) | ||||
| 
 | ||||
|             val expected = ubyteArrayOf( | ||||
|                 0xbdU, 0x6dU, 0x17U, 0x9dU, 0x3eU, 0x83U, 0xd4U, 0x3bU, | ||||
|                 0x95U, 0x76U, 0x57U, 0x94U, 0x93U, 0xc0U, 0xe9U, 0x39U, | ||||
|                 0x57U, 0x2aU, 0x17U, 0x00U, 0x25U, 0x2bU, 0xfaU, 0xccU, | ||||
|                 0xbeU, 0xd2U, 0x90U, 0x2cU, 0x21U, 0x39U, 0x6cU, 0xbbU, | ||||
|                 0x73U, 0x1cU, 0x7fU, 0x1bU, 0x0bU, 0x4aU, 0xa6U, 0x44U, | ||||
|                 0x0bU, 0xf3U, 0xa8U, 0x2fU, 0x4eU, 0xdaU, 0x7eU, 0x39U, | ||||
|                 0xaeU, 0x64U, 0xc6U, 0x70U, 0x8cU, 0x54U, 0xc2U, 0x16U, | ||||
|                 0xcbU, 0x96U, 0xb7U, 0x2eU, 0x12U, 0x13U, 0xb4U, 0x52U, | ||||
|                 0x2fU, 0x8cU, 0x9bU, 0xa4U, 0x0dU, 0xb5U, 0xd9U, 0x45U, | ||||
|                 0xb1U, 0x1bU, 0x69U, 0xb9U, 0x82U, 0xc1U, 0xbbU, 0x9eU, | ||||
|                 0x3fU, 0x3fU, 0xacU, 0x2bU, 0xc3U, 0x69U, 0x48U, 0x8fU, | ||||
|                 0x76U, 0xb2U, 0x38U, 0x35U, 0x65U, 0xd3U, 0xffU, 0xf9U, | ||||
|                 0x21U, 0xf9U, 0x66U, 0x4cU, 0x97U, 0x63U, 0x7dU, 0xa9U, | ||||
|                 0x76U, 0x88U, 0x12U, 0xf6U, 0x15U, 0xc6U, 0x8bU, 0x13U, | ||||
|                 0xb5U, 0x2eU, 0xc0U, 0x87U, 0x59U, 0x24U, 0xc1U, 0xc7U, | ||||
|                 0x98U, 0x79U, 0x47U, 0xdeU, 0xafU, 0xd8U, 0x78U, 0x0aU, | ||||
|                 0xcfU, 0x49U | ||||
|             ) | ||||
| //            val xChaChaPoly = XChaCha20Poly1305Delegated(key, associatedData) | ||||
| //            val firstChunk = xChaChaPoly.encrypt(message) | ||||
| //            val finalChunk = xChaChaPoly.finishEncryption().first | ||||
| //            val result = firstChunk + finalChunk | ||||
| 
 | ||||
| //            result.contentEquals(expected) | ||||
|             1 == 1 | ||||
|         } | ||||
| 
 | ||||
|         assertTrue { | ||||
|             val message = ubyteArrayOf( | ||||
|                 0x00U | ||||
|             ) | ||||
|             val associatedData = ubyteArrayOf( | ||||
|                 0x00U | ||||
|             ) | ||||
|             val key = ubyteArrayOf( | ||||
|                 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, | ||||
|                 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, | ||||
|                 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, | ||||
|                 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, | ||||
|             ) | ||||
| 
 | ||||
|             val nonce = ubyteArrayOf( | ||||
|                 0x00U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U, 0x08U, 0x09U, 0x0aU, 0x0bU, | ||||
|                 0x0cU, 0x0dU, 0x0eU, 0x0fU, 0x10U, 0x11U, 0x12U, 0x13U, 0x14U, 0x15U, 0x16U, 0x17U, | ||||
|             ) | ||||
| 
 | ||||
|             val expected = ubyteArrayOf( | ||||
|                 0xbdU, 0x3bU, 0x8aU, 0xd7U, 0xa1U, 0x9dU, 0xe8U, 0xc4U, 0x55U, | ||||
|                 0x84U, 0x6fU, 0xfcU, 0x75U, 0x31U, 0xbfU, 0x0cU, 0x2dU | ||||
|             ) | ||||
| //            val xChaChaPoly = XChaCha20Poly1305Delegated(key, associatedData) | ||||
| //            val firstChunk = xChaChaPoly.encrypt(message) | ||||
| //            val finalChunk = xChaChaPoly.finishEncryption().first | ||||
| //            val result = firstChunk + finalChunk | ||||
| //            result.contentEquals(expected) | ||||
|             1 == 1 | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun testStreamingImpl() = testBlocking { | ||||
|         Initializer.initialize() | ||||
|         val key = UByteArray(32) { 0U } | ||||
|         val state = ubyteArrayOf( | ||||
|             0x2DU, 0xDBU, 0xC7U, 0xB2U, 0x03U, 0xBCU, 0xC3U, 0x22U, 0xBDU, 0x0CU, 0xBAU, 0x82U, 0xADU, 0x77U, 0x79U, 0x44U, | ||||
|             0xE6U, 0x8FU, 0xA9U, 0x94U, 0x89U, 0xB1U, 0xDFU, 0xBEU, 0x00U, 0x9FU, 0x69U, 0xECU, 0x21U, 0x88U, 0x47U, 0x55U, | ||||
|             0x01U, 0x00U, 0x00U, 0x00U, 0xC5U, 0x55U, 0x06U, 0x38U, 0xEBU, 0xA3U, 0x12U, 0x7BU, 0x00U, 0x00U, 0x00U, 0x00U, | ||||
|             0x00U, 0x00U, 0x00U, 0x00U, | ||||
|         ) | ||||
|         val header = ubyteArrayOf( | ||||
|             0x49U, 0x62U, 0x22U, 0x03U, 0xB7U, 0x46U, 0x11U, 0x97U, 0x8FU, 0x46U, 0x4AU, 0x3BU, 0x2FU, 0x2AU, 0x81U, 0x03U, | ||||
|             0xC5U, 0x55U, 0x06U, 0x38U, 0xEBU, 0xA3U, 0x12U, 0x7BU, | ||||
|         ) | ||||
|         val expected = ubyteArrayOf( | ||||
|             0xAFU, 0xD3U, 0x2DU, 0x59U, 0xB8U, 0xC4U, 0x66U, 0x2EU, 0x47U, 0x29U, 0xC6U, 0xF9U, 0x93U, 0x4BU, 0x09U, 0x27U, | ||||
|             0x24U, 0xDDU, 0xF3U, 0x05U, 0x48U, 0x94U, 0x67U, 0x10U, 0x00U, 0x21U, 0x85U, 0x22U, 0x96U, 0x3CU, 0xCEU, 0x8EU, | ||||
|             0xB7U, 0x53U, 0x9DU, 0x46U, 0xF5U, 0x3CU, 0x5EU, 0x48U, 0x9BU, 0x8CU, 0x13U, 0xB7U, 0x28U, 0x6BU, 0xB3U, 0x6CU, | ||||
|             0x3AU, 0x04U, 0xB7U, 0x25U, 0xB9U, 0x50U, 0x45U, 0x08U, 0x0BU, 0x89U, 0xA2U, 0x0FU, 0x70U, 0xCCU, 0x60U, 0x1BU, | ||||
|             0xC3U, 0x17U, 0x35U, 0x9FU, 0xAEU, 0x82U, 0x51U, 0x43U, 0x1BU, 0x9DU, 0x53U, 0x9EU, 0xE2U, 0xAFU, 0x20U, 0x1FU, | ||||
|             0xFDU, 0x03U, 0x59U, 0x11U, 0x51U, 0x9EU, 0xACU, 0x83U, 0xCDU, 0x78U, 0xD1U, 0xD0U, 0xE5U, 0xD7U, 0x0EU, 0x41U, | ||||
|             0xDEU, 0xFBU, 0x5CU, 0x7FU, 0x1CU, 0x26U, 0x32U, 0x2CU, 0x51U, 0xF6U, 0xEFU, 0xC6U, 0x34U, 0xC4U, 0xACU, 0x6CU, | ||||
|             0xE8U, 0xF9U, 0x4BU, 0xABU, 0xA3U, | ||||
|         ) | ||||
|         val encryptor = XChaCha20Poly1305Delegated(key, state, header, false) | ||||
|         val decryptor = XChaCha20Poly1305Delegated(key, state, header, true) | ||||
|         val data = UByteArray(100) { 0U } | ||||
|         val result = encryptor.encrypt(data) | ||||
|         val decrypted = decryptor.decrypt(result) | ||||
|         println("Encrypted -----------") | ||||
|         result.hexColumsPrint() | ||||
|         println("Encrypted end -----------") | ||||
|         println("Decrypted -----------") | ||||
|         decrypted.hexColumsPrint() | ||||
|         println("Decrypted end -----------") | ||||
|         assertTrue { | ||||
|             expected.contentEquals(result) && decrypted.contentEquals(data) | ||||
|         } | ||||
|         val messedUpTag = result.copyOf() | ||||
|         messedUpTag[messedUpTag.size - 3] = 0U | ||||
|         messedUpTag[messedUpTag.size - 2] = 0U | ||||
|         messedUpTag[messedUpTag.size - 1] = 0U | ||||
|         assertFails { | ||||
|             val decryptorForWrongTag = XChaCha20Poly1305Delegated(key, state, header, true) | ||||
|             val plaintext = decryptorForWrongTag.decrypt(messedUpTag) | ||||
|             println("Decrypted with wrong tag -----------") | ||||
|             plaintext.hexColumsPrint() | ||||
|             println("Decrypted with wrong tag end -----------") | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,46 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.hash.blake2b | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.Crypto | ||||
| import com.ionspin.kotlin.crypto.CryptoPrimitives | ||||
| import com.ionspin.kotlin.crypto.Initializer | ||||
| import com.ionspin.kotlin.crypto.hash.encodeToUByteArray | ||||
| import com.ionspin.kotlin.crypto.util.testBlocking | ||||
| import com.ionspin.kotlin.crypto.util.toHexString | ||||
| import kotlin.test.BeforeTest | ||||
| import kotlin.test.Test | ||||
| import kotlin.test.assertTrue | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 09-Jun-2020 | ||||
|  */ | ||||
| class Blake2bTest { | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     @Test | ||||
|     fun statelessSimpleTest() = testBlocking { | ||||
|         Initializer.initialize() | ||||
|         val expected = "a71079d42853dea26e453004338670a53814b78137ffbed07603a41d76a483aa9bc33b582f77d30a65e6f29a89" + | ||||
|                 "6c0411f38312e1d66e0bf16386c86a89bea572" | ||||
|         val result = CryptoPrimitives.Blake2b.stateless("test".encodeToUByteArray()).toHexString() | ||||
| //        println("Result: $result") | ||||
|         assertTrue { result == expected } | ||||
|     } | ||||
| 
 | ||||
|     //This is a bad test since it's not larger than one block | ||||
|     //but for now I'm testing that the platform library is being correctly called | ||||
|     @Test | ||||
|     fun updateableSimpleTest() = testBlocking { | ||||
|         Initializer.initialize() | ||||
|         val expected = "a71079d42853dea26e453004338670a53814b78137ffbed07603a41d76a483aa9bc33b582f77d30a65e6f29a89" + | ||||
|                 "6c0411f38312e1d66e0bf16386c86a89bea572" | ||||
|         val blake2b = CryptoPrimitives.Blake2b.updateable() | ||||
|         blake2b.update("t".encodeToUByteArray()) | ||||
|         blake2b.update(("est".encodeToUByteArray())) | ||||
|         val result = blake2b.digest().toHexString() | ||||
| //        println("Result: $result") | ||||
|         assertTrue { result == expected } | ||||
|     } | ||||
| } | ||||
| @ -1,46 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.hash.sha | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.Crypto | ||||
| import com.ionspin.kotlin.crypto.CryptoPrimitives | ||||
| import com.ionspin.kotlin.crypto.Initializer | ||||
| import com.ionspin.kotlin.crypto.hash.encodeToUByteArray | ||||
| import com.ionspin.kotlin.crypto.util.testBlocking | ||||
| import com.ionspin.kotlin.crypto.util.toHexString | ||||
| import kotlin.test.BeforeTest | ||||
| import kotlin.test.Test | ||||
| import kotlin.test.assertTrue | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 07-Jun-2020 | ||||
|  */ | ||||
| class Sha256Test { | ||||
|     @BeforeTest | ||||
|     fun beforeTest() = testBlocking { | ||||
|         Initializer.initialize() | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun statelessSimpleTest() = testBlocking { | ||||
|         Initializer.initialize() | ||||
|         val expected = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" | ||||
|         val result = CryptoPrimitives.Sha256.stateless("test".encodeToUByteArray()).toHexString() | ||||
| //        println("Result: $result") | ||||
|         assertTrue { result == expected } | ||||
|     } | ||||
| 
 | ||||
|     //This is a bad test since it's not larger than one block | ||||
|     //but for now I'm testing that the platform library is being correctly called | ||||
|     @Test | ||||
|     fun updateableSimpleTest() = testBlocking { | ||||
|         Initializer.initialize() | ||||
|         val expected = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" | ||||
|         val sha256 = CryptoPrimitives.Sha256.updateable() | ||||
|         sha256.update("t".encodeToUByteArray()) | ||||
|         sha256.update(("est".encodeToUByteArray())) | ||||
|         val result = sha256.digest().toHexString() | ||||
| //        println("Result: $result") | ||||
|         assertTrue { result == expected } | ||||
|     } | ||||
| } | ||||
| @ -1,48 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.hash.sha | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.Crypto | ||||
| import com.ionspin.kotlin.crypto.CryptoPrimitives | ||||
| import com.ionspin.kotlin.crypto.Initializer | ||||
| import com.ionspin.kotlin.crypto.hash.encodeToUByteArray | ||||
| import com.ionspin.kotlin.crypto.util.testBlocking | ||||
| import com.ionspin.kotlin.crypto.util.toHexString | ||||
| import kotlin.test.BeforeTest | ||||
| import kotlin.test.Test | ||||
| import kotlin.test.assertTrue | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 07-Jun-2020 | ||||
|  */ | ||||
| class Sha512Test { | ||||
|     @BeforeTest | ||||
|     fun beforeTest() = testBlocking { | ||||
|         Initializer.initialize() | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun statelessSimpleTest() = testBlocking { | ||||
|         Initializer.initialize() | ||||
|         val expected = "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67" + | ||||
|                 "b143732c304cc5fa9ad8e6f57f50028a8ff" | ||||
|         val result = CryptoPrimitives.Sha512.stateless("test".encodeToUByteArray()).toHexString() | ||||
| //        println("Result: $result") | ||||
|         assertTrue { result == expected } | ||||
|     } | ||||
| 
 | ||||
|     //This is a bad test since it's not larger than one block | ||||
|     //but for now I'm testing that the platform library is being correctly called | ||||
|     @Test | ||||
|     fun updateableSimpleTest() = testBlocking { | ||||
|         Initializer.initialize() | ||||
|         val expected = "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67" + | ||||
|                 "b143732c304cc5fa9ad8e6f57f50028a8ff" | ||||
|         val sha512 = CryptoPrimitives.Sha512.updateable() | ||||
|         sha512.update("t".encodeToUByteArray()) | ||||
|         sha512.update(("est".encodeToUByteArray())) | ||||
|         val result = sha512.digest().toHexString() | ||||
| //        println("Result: $result") | ||||
|         assertTrue { result == expected } | ||||
|     } | ||||
| } | ||||
| @ -1,50 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.highlevel | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.Crypto | ||||
| import com.ionspin.kotlin.crypto.Initializer | ||||
| import com.ionspin.kotlin.crypto.SymmetricKey | ||||
| import com.ionspin.kotlin.crypto.hash.decodeToString | ||||
| import com.ionspin.kotlin.crypto.hash.encodeToUByteArray | ||||
| import com.ionspin.kotlin.crypto.util.hexColumsPrint | ||||
| import com.ionspin.kotlin.crypto.util.testBlocking | ||||
| import kotlin.test.Test | ||||
| import kotlin.test.assertTrue | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 09-Jul-2020 | ||||
|  */ | ||||
| class EncryptionTest { | ||||
|     @Test | ||||
|     fun testMultipartEncryption() = testBlocking { | ||||
|         Initializer.initialize() | ||||
|         val plaintext = ("pUoR4JVXJUeMKNkt6ZGGzEdTo33ajNGXwXpivBKA0XKs8toGRYI9Eul4bELRDkaQDNhd4vZseEFU" + | ||||
|                 "ojsAn3c9zIifIrMnydSivHVZ2pBtpAQwYoJhYmEsfE0tROGnOwFWyB9K6LRSv1gB3YqKR9VyM8mpRoUM3UCRRjyiX7bnKdCE1" + | ||||
|                 "EiX0myiwcY1nUKTgB3keERWtMU07hX7bCtao5nRvDofSj3o3IInHRQh6opltr5asQwn4m1qn029QF").encodeToUByteArray() | ||||
|         val associatedData = "Additional data 1".encodeToUByteArray() | ||||
|         val keyValue = UByteArray(32) { it.toUByte() } | ||||
|         val key = SymmetricKey(keyValue) | ||||
|         val encryptor = Crypto.Encryption.createMultipartEncryptor(key) | ||||
|         val header = encryptor.startEncryption() | ||||
|         val ciphertext1 = encryptor.encryptPartialData(plaintext.sliceArray(0 until 100), associatedData) | ||||
|         val ciphertext2 = encryptor.encryptPartialData(plaintext.sliceArray(100 until 200)) | ||||
|         val ciphertext3 = encryptor.encryptPartialData(plaintext.sliceArray(200 until 250)) | ||||
|         //decrypt | ||||
|         val decryptor = Crypto.Encryption.createMultipartDecryptor(key, header) | ||||
|         val plaintext1 = decryptor.decryptPartialData(ciphertext1, associatedData) | ||||
|         val plaintext2 = decryptor.decryptPartialData(ciphertext2) | ||||
|         val plaintext3 = decryptor.decryptPartialData(ciphertext3) | ||||
| 
 | ||||
|         val combinedPlaintext = plaintext1.data + plaintext2.data + plaintext3.data | ||||
|         assertTrue { | ||||
|             plaintext.contentEquals(combinedPlaintext) | ||||
|         } | ||||
|         encryptor.cleanup() | ||||
|         decryptor.cleanup() | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @ -1,37 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.util | ||||
| 
 | ||||
| import kotlin.coroutines.Continuation | ||||
| import kotlin.coroutines.EmptyCoroutineContext | ||||
| import kotlin.coroutines.startCoroutine | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 20-Jul-2019 | ||||
|  */ | ||||
| fun testBlocking(block : suspend () -> Unit) { | ||||
|     val continuation = Continuation<Unit>(EmptyCoroutineContext) { | ||||
|         //Do nothing | ||||
|         if (it.isFailure) { | ||||
|             throw it.exceptionOrNull()!! | ||||
|         } | ||||
|     } | ||||
|     block.startCoroutine(continuation) | ||||
| } | ||||
| @ -1,43 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| import ext.libsodium.com.ionspin.kotlin.crypto.JsSodiumInterface | ||||
| import ext.libsodium.com.ionspin.kotlin.crypto.JsSodiumLoader | ||||
| /* 1.4-M1 has some weirdness with static/objects, or I'm misusing something, not sure */ | ||||
| lateinit var sodiumPointer : JsSodiumInterface | ||||
| var sodiumLoaded: Boolean = false | ||||
| 
 | ||||
| fun getSodium() : JsSodiumInterface = sodiumPointer | ||||
| 
 | ||||
| //fun getSodiumAdvanced() : JsSodiumAdvancedInterface = js("sodiumPointer.libsodium") | ||||
| 
 | ||||
| fun setSodiumPointer(jsSodiumInterface: JsSodiumInterface) { | ||||
|     js("sodiumPointer = jsSodiumInterface") | ||||
| } | ||||
| 
 | ||||
| fun getSodiumLoaded() : Boolean = sodiumLoaded | ||||
| 
 | ||||
| fun setSodiumLoaded(loaded: Boolean) { | ||||
|     js("sodiumLoaded = loaded") | ||||
| } | ||||
| 
 | ||||
| actual object Initializer { | ||||
|     private var isPlatformInitialized = false | ||||
| 
 | ||||
|     actual suspend fun initialize() { | ||||
|         JsSodiumLoader.load() | ||||
|         isPlatformInitialized = true | ||||
|     } | ||||
| 
 | ||||
|     actual fun initializeWithCallback(done: () -> Unit) { | ||||
|         JsSodiumLoader.loadWithCallback { | ||||
|             isPlatformInitialized = true | ||||
|             done() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     actual fun isInitialized(): Boolean { | ||||
|         return isPlatformInitialized | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,60 +0,0 @@ | ||||
| package ext.libsodium.com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| 
 | ||||
| import org.khronos.webgl.Uint8Array | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 27-May-2020 | ||||
|  */ | ||||
| interface JsSodiumInterface { | ||||
| 
 | ||||
|     fun randombytes_buf(numberOfBytes: Int): Uint8Array | ||||
| 
 | ||||
|     fun crypto_generichash(hashLength: Int, inputMessage: Uint8Array, key: Uint8Array,): Uint8Array | ||||
| 
 | ||||
|     fun crypto_hash_sha256(message: Uint8Array): Uint8Array | ||||
| 
 | ||||
|     fun crypto_hash_sha512(message: Uint8Array): Uint8Array | ||||
| 
 | ||||
|     //Updateable | ||||
| 
 | ||||
|     fun crypto_generichash_init(key : Uint8Array, hashLength: Int) : dynamic | ||||
| 
 | ||||
|     fun crypto_generichash_update(state: dynamic, inputMessage: Uint8Array) | ||||
| 
 | ||||
|     fun crypto_generichash_final(state: dynamic, hashLength: Int) : Uint8Array | ||||
| 
 | ||||
| 
 | ||||
|     fun crypto_hash_sha256_init() : dynamic | ||||
| 
 | ||||
|     fun crypto_hash_sha256_update(state: dynamic, message: Uint8Array) | ||||
| 
 | ||||
|     fun crypto_hash_sha256_final(state: dynamic): Uint8Array | ||||
| 
 | ||||
|     fun crypto_hash_sha512_init() : dynamic | ||||
| 
 | ||||
|     fun crypto_hash_sha512_update(state: dynamic, message: Uint8Array) | ||||
| 
 | ||||
|     fun crypto_hash_sha512_final(state: dynamic): Uint8Array | ||||
| 
 | ||||
|     //XChaCha20Poly1305 | ||||
|     fun crypto_aead_xchacha20poly1305_ietf_encrypt(message: Uint8Array, associatedData: Uint8Array, secretNonce: Uint8Array, nonce: Uint8Array, key: Uint8Array) : Uint8Array | ||||
|     fun crypto_aead_xchacha20poly1305_ietf_decrypt(secretNonce: Uint8Array, ciphertext: Uint8Array, associatedData: Uint8Array, nonce: Uint8Array, key: Uint8Array) : Uint8Array | ||||
| 
 | ||||
|     //XChaCha20Poly1305 | ||||
|     //encrypt | ||||
|     fun crypto_secretstream_xchacha20poly1305_init_push(header: Uint8Array) : dynamic | ||||
|     fun crypto_secretstream_xchacha20poly1305_push(state: dynamic, message: Uint8Array, associatedData: Uint8Array, tag: UByte) : Uint8Array | ||||
| 
 | ||||
|     //decrypt | ||||
|     fun crypto_secretstream_xchacha20poly1305_init_pull(header: Uint8Array, key: Uint8Array) : dynamic | ||||
|     fun crypto_secretstream_xchacha20poly1305_pull(state: dynamic, ciphertext: Uint8Array, associatedData: Uint8Array) : dynamic | ||||
| 
 | ||||
|     //util | ||||
|     fun memzero(array: Uint8Array) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,56 +0,0 @@ | ||||
| package ext.libsodium.com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.getSodiumLoaded | ||||
| import com.ionspin.kotlin.crypto.setSodiumPointer | ||||
| import com.ionspin.kotlin.crypto.sodiumLoaded | ||||
| import ext.libsodium.* | ||||
| import kotlin.coroutines.Continuation | ||||
| import kotlin.coroutines.suspendCoroutine | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 27-May-2020 | ||||
|  */ | ||||
| object JsSodiumLoader { | ||||
| 
 | ||||
|     class _EmitJsSodiumFunction { | ||||
|         init { | ||||
|             println(::crypto_generichash) | ||||
|             println(::crypto_hash_sha256) | ||||
|             println(::crypto_hash_sha512) | ||||
|             println(::crypto_hash_sha256_init) | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     fun storeSodium(promisedSodium: dynamic, continuation: Continuation<Unit>) { | ||||
|         setSodiumPointer(promisedSodium) | ||||
|         sodiumLoaded = true | ||||
|         continuation.resumeWith(Result.success(Unit)) | ||||
|     } | ||||
| 
 | ||||
|     suspend fun load() = suspendCoroutine<Unit> { continuation -> | ||||
|         console.log(getSodiumLoaded()) | ||||
|         if (!getSodiumLoaded()) { | ||||
|             val libsodiumModule = js("\$module\$libsodium_wrappers_sumo") | ||||
|             _libsodiumPromise.then<dynamic> { storeSodium(libsodiumModule, continuation) } | ||||
|         } else { | ||||
|             continuation.resumeWith(Result.success(Unit)) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fun loadWithCallback(doneCallback: () -> (Unit)) { | ||||
|         console.log(getSodiumLoaded()) | ||||
|         if (!getSodiumLoaded()) { | ||||
|             val libsodiumModule = js("\$module\$libsodium_wrappers_sumo") | ||||
|             _libsodiumPromise.then<dynamic> { | ||||
|                 setSodiumPointer(libsodiumModule) | ||||
|                 sodiumLoaded = true | ||||
|                 doneCallback.invoke() | ||||
|             } | ||||
|         } else { | ||||
|             doneCallback.invoke() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,27 +0,0 @@ | ||||
| package ext.libsodium.com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| import org.khronos.webgl.Uint8Array | ||||
| import org.khronos.webgl.get | ||||
| import org.khronos.webgl.set | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 25-Jun-2020 | ||||
|  * | ||||
|  * TODO investigate using unsafe cast | ||||
|  */ | ||||
| fun UByteArray.toUInt8Array() : Uint8Array { | ||||
|     val uint8Result = Uint8Array(toByteArray().toTypedArray()) | ||||
|     return uint8Result | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| fun Uint8Array.toUByteArray() : UByteArray { | ||||
|     val result = UByteArray(length) | ||||
|     for (i in 0 until length) { | ||||
|         result[i] = get(i).toUByte() | ||||
|     } | ||||
| 
 | ||||
|     return result | ||||
| } | ||||
| @ -1,43 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| import org.khronos.webgl.get | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 21-Sep-2019 | ||||
|  */ | ||||
| actual object SRNG { | ||||
|     var counter = 0 | ||||
| 
 | ||||
|     actual fun getRandomBytes(amount: Int): UByteArray { | ||||
|         val randomBytes = getSodium().randombytes_buf(amount) | ||||
|         val randomBytesUByteArray = UByteArray(amount) { | ||||
|             0U | ||||
|         } | ||||
|         for (i in 0 until amount) { | ||||
|             js(""" | ||||
|                randomBytesUByteArray[i] = randomBytes[i]   | ||||
|             """) | ||||
|         } | ||||
|         return randomBytesUByteArray | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,130 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.authenticated | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.InvalidTagException | ||||
| import com.ionspin.kotlin.crypto.getSodium | ||||
| import com.ionspin.kotlin.crypto.util.hexColumsPrint | ||||
| import ext.libsodium.com.ionspin.kotlin.crypto.toUByteArray | ||||
| import ext.libsodium.com.ionspin.kotlin.crypto.toUInt8Array | ||||
| import org.khronos.webgl.Uint8Array | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 25-Jun-2020 | ||||
|  */ | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 14-Jun-2020 | ||||
|  */ | ||||
| actual class XChaCha20Poly1305Delegated internal actual constructor() { | ||||
|     actual companion object { | ||||
|         actual fun encrypt( | ||||
|             key: UByteArray, | ||||
|             nonce: UByteArray, | ||||
|             message: UByteArray, | ||||
|             associatedData: UByteArray | ||||
|         ): UByteArray { | ||||
|             val encrypted = getSodium().crypto_aead_xchacha20poly1305_ietf_encrypt( | ||||
|                 message.toUInt8Array(), | ||||
|                 associatedData.toUInt8Array(), | ||||
|                 Uint8Array(0), | ||||
|                 nonce.toUInt8Array(), | ||||
|                 key.toUInt8Array() | ||||
|             ) | ||||
|             return encrypted.toUByteArray() | ||||
|         } | ||||
| 
 | ||||
|         actual fun decrypt( | ||||
|             key: UByteArray, | ||||
|             nonce: UByteArray, | ||||
|             ciphertext: UByteArray, | ||||
|             associatedData: UByteArray | ||||
|         ): UByteArray { | ||||
|             val decrypted = getSodium().crypto_aead_xchacha20poly1305_ietf_decrypt( | ||||
|                 Uint8Array(0), | ||||
|                 ciphertext.toUInt8Array(), | ||||
|                 associatedData.toUInt8Array(), | ||||
|                 nonce.toUInt8Array(), | ||||
|                 key.toUInt8Array() | ||||
|             ) | ||||
|             return decrypted.toUByteArray() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     var state : dynamic = null | ||||
|     var isInitialized = false | ||||
|     var isEncryptor = false | ||||
| 
 | ||||
|     actual fun initializeForEncryption(key: UByteArray) : UByteArray { | ||||
|         println("Initializaing for encryption") | ||||
|         val stateAndHeader = getSodium().crypto_secretstream_xchacha20poly1305_init_push(key.toUInt8Array()) | ||||
|         state = stateAndHeader.state | ||||
|         val header = stateAndHeader.header as Uint8Array | ||||
|         console.log(state) | ||||
|         console.log(header) | ||||
|         println("Done initializaing for encryption") | ||||
|         isInitialized = true | ||||
|         isEncryptor = true | ||||
|         return header.toUByteArray() | ||||
|     } | ||||
| 
 | ||||
|     actual fun initializeForDecryption(key: UByteArray, header: UByteArray) { | ||||
|         println("Initializing for decryption") | ||||
|         header.hexColumsPrint() | ||||
|         state = getSodium().crypto_secretstream_xchacha20poly1305_init_pull(header.toUInt8Array(), key.toUInt8Array()) | ||||
|         console.log(state) | ||||
|         isInitialized = true | ||||
|         isEncryptor = false | ||||
|     } | ||||
| 
 | ||||
|     internal actual constructor( | ||||
|         key: UByteArray, | ||||
|         testState: UByteArray, | ||||
|         testHeader: UByteArray, | ||||
|         isDecryptor: Boolean | ||||
|     ) : this() { | ||||
|         state = getSodium().crypto_secretstream_xchacha20poly1305_init_pull(testHeader.toUInt8Array(), key.toUInt8Array()) | ||||
|         console.log(state) | ||||
|         println("Done initializaing test state") | ||||
|         isInitialized = true | ||||
|         isEncryptor = !isDecryptor | ||||
|     } | ||||
| 
 | ||||
|     actual fun encrypt(data: UByteArray, associatedData: UByteArray): UByteArray { | ||||
|         if (!isInitialized) { | ||||
|             throw RuntimeException("Not initalized!") | ||||
|         } | ||||
|         if (!isEncryptor) { | ||||
|             throw RuntimeException("Initialized as decryptor, attempted to use as encryptor") | ||||
|         } | ||||
|         val encrypted = getSodium().crypto_secretstream_xchacha20poly1305_push(state, data.toUInt8Array(), associatedData.toUInt8Array(), 0U) | ||||
|         return encrypted.toUByteArray() | ||||
|     } | ||||
| 
 | ||||
|     actual fun decrypt(data: UByteArray, associatedData: UByteArray): UByteArray { | ||||
|         if (!isInitialized) { | ||||
|             throw RuntimeException("Not initalized!") | ||||
|         } | ||||
|         if (isEncryptor) { | ||||
|             throw RuntimeException("Initialized as encryptor, attempted to use as decryptor") | ||||
|         } | ||||
|         val decryptedWithTag = getSodium().crypto_secretstream_xchacha20poly1305_pull(state, data.toUInt8Array(), associatedData.toUInt8Array()) | ||||
|         val decrypted = decryptedWithTag.message as Uint8Array | ||||
|         val validTag = decryptedWithTag.tag | ||||
| 
 | ||||
|         if (validTag != 0U) { | ||||
|             println("Tag validation failed") | ||||
|             throw InvalidTagException() | ||||
|         } | ||||
|         return decrypted.toUByteArray() | ||||
|     } | ||||
| 
 | ||||
|     actual fun cleanup() { | ||||
|         //TODO JS cleanup | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,60 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.hash.blake2b | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.getSodium | ||||
| import org.khronos.webgl.Uint8Array | ||||
| import org.khronos.webgl.get | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 14-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| actual class Blake2bDelegated actual constructor(key: UByteArray?, val hashLength: Int) : Blake2bMultipart { | ||||
|     override val MAX_HASH_BYTES: Int = 64 | ||||
| 
 | ||||
| 
 | ||||
|     val state : dynamic | ||||
| 
 | ||||
|     init { | ||||
|         state = getSodium().crypto_generichash_init( | ||||
|             Uint8Array(key?.toByteArray()?.toTypedArray() ?: arrayOf()), | ||||
|             hashLength | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     override fun update(data: UByteArray) { | ||||
|         getSodium().crypto_generichash_update(state, Uint8Array(data.toByteArray().toTypedArray())) | ||||
|     } | ||||
| 
 | ||||
|     override fun digest(): UByteArray { | ||||
|         val hashed = getSodium().crypto_generichash_final(state, hashLength) | ||||
|         val hash = UByteArray(hashLength) | ||||
|         for (i in 0 until hashLength) { | ||||
|             hash[i] = hashed[i].toUByte() | ||||
|         } | ||||
|         return hash | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| actual object Blake2bDelegatedStateless : Blake2b { | ||||
|     override val MAX_HASH_BYTES: Int = 64 | ||||
| 
 | ||||
|     override fun digest(inputMessage: UByteArray, key: UByteArray, hashLength: Int): UByteArray { | ||||
|         val hashed =  getSodium().crypto_generichash(hashLength, | ||||
|             Uint8Array(inputMessage.toByteArray().toTypedArray()), | ||||
|             Uint8Array(key.toByteArray().toTypedArray()) | ||||
|         ) | ||||
|         val hash = UByteArray(hashLength) | ||||
|         for (i in 0 until hashLength) { | ||||
|             hash[i] = hashed[i].toUByte() | ||||
|         } | ||||
|         return hash | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,49 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.hash.sha | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.getSodium | ||||
| 
 | ||||
| import org.khronos.webgl.Uint8Array | ||||
| import org.khronos.webgl.get | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 17-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| actual class Sha256Delegated : Sha256 { | ||||
| 
 | ||||
|     val state : dynamic | ||||
| 
 | ||||
|     init { | ||||
|         state = getSodium().crypto_hash_sha256_init() | ||||
|     } | ||||
| 
 | ||||
|     override fun update(data: UByteArray) { | ||||
|         getSodium().crypto_hash_sha256_update(state, Uint8Array(data.toByteArray().toTypedArray())) | ||||
|     } | ||||
| 
 | ||||
|     override fun digest(): UByteArray { | ||||
|         val hashed = getSodium().crypto_hash_sha256_final(state) | ||||
|         val hash = UByteArray(Sha256StatelessDelegated.MAX_HASH_BYTES) | ||||
|         console.log(hashed) | ||||
|         for (i in 0 until Sha256StatelessDelegated.MAX_HASH_BYTES) { | ||||
|             hash[i] = hashed[i].toUByte() | ||||
|         } | ||||
|         return hash | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| actual object Sha256StatelessDelegated : StatelessSha256 { | ||||
| 
 | ||||
|     override fun digest(inputMessage: UByteArray): UByteArray { | ||||
|         val hashed = getSodium().crypto_hash_sha256(Uint8Array(inputMessage.toByteArray().toTypedArray())) | ||||
|         val hash = UByteArray(MAX_HASH_BYTES) | ||||
|         for (i in 0 until MAX_HASH_BYTES) { | ||||
|             hash[i] = hashed[i].toUByte() | ||||
|         } | ||||
|         return hash | ||||
|     } | ||||
| } | ||||
| @ -1,47 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.hash.sha | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.getSodium | ||||
| 
 | ||||
| import org.khronos.webgl.Uint8Array | ||||
| import org.khronos.webgl.get | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 17-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| actual class Sha512Delegated : Sha512Multipart { | ||||
|     val state : dynamic | ||||
| 
 | ||||
|     init { | ||||
|         state = getSodium().crypto_hash_sha512_init() | ||||
|     } | ||||
| 
 | ||||
|     override fun update(data: UByteArray) { | ||||
|         getSodium().crypto_hash_sha512_update(state, Uint8Array(data.toByteArray().toTypedArray())) | ||||
|     } | ||||
| 
 | ||||
|     override fun digest(): UByteArray { | ||||
|         val hashed = getSodium().crypto_hash_sha512_final(state) | ||||
|         val hash = UByteArray(Sha512StatelessDelegated.MAX_HASH_BYTES) | ||||
|         for (i in 0 until Sha512StatelessDelegated.MAX_HASH_BYTES) { | ||||
|             hash[i] = hashed[i].toUByte() | ||||
|         } | ||||
|         return hash | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| actual object Sha512StatelessDelegated : Sha512 { | ||||
| 
 | ||||
|     override fun digest(inputMessage: UByteArray): UByteArray { | ||||
|         val hashed = getSodium().crypto_hash_sha512(Uint8Array(inputMessage.toByteArray().toTypedArray())) | ||||
|         val hash = UByteArray(Sha512StatelessDelegated.MAX_HASH_BYTES) | ||||
|         for (i in 0 until Sha512StatelessDelegated.MAX_HASH_BYTES) { | ||||
|             hash[i] = hashed[i].toUByte() | ||||
|         } | ||||
|         return hash | ||||
|     } | ||||
| } | ||||
| @ -1,28 +0,0 @@ | ||||
| @file:JsModule("libsodium-wrappers-sumo") | ||||
| @file:JsNonModule | ||||
| package ext.libsodium | ||||
| 
 | ||||
| import org.khronos.webgl.Uint8Array | ||||
| import kotlin.js.Promise | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 25-May-2020 | ||||
|  */ | ||||
| 
 | ||||
| @JsName("ready") | ||||
| external val _libsodiumPromise : Promise<dynamic> | ||||
| 
 | ||||
| external fun crypto_generichash(hashLength: Int, inputMessage: Uint8Array) : Uint8Array | ||||
| 
 | ||||
| external fun crypto_hash_sha256(message: Uint8Array) : Uint8Array | ||||
| external fun crypto_hash_sha512(message: Uint8Array) : Uint8Array | ||||
| 
 | ||||
| external fun crypto_hash_sha256_init(): dynamic | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @ -1,52 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.util.testBlocking | ||||
| import ext.libsodium.com.ionspin.kotlin.crypto.JsSodiumLoader | ||||
| import kotlin.test.Test | ||||
| import kotlin.test.assertTrue | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 05-Jan-2020 | ||||
|  */ | ||||
| 
 | ||||
| class SRNGJsTest { | ||||
| 
 | ||||
|     @Test | ||||
|     fun testJsSrng() = testBlocking { | ||||
|         JsSodiumLoader.load() | ||||
|         val bytes1 = SRNG.getRandomBytes(10) | ||||
|         val bytes2 = SRNG.getRandomBytes(10) | ||||
| //        println("BYTES1\n") | ||||
| //        bytes1.forEach { | ||||
| //            print(it.toString(16).padStart(2, '0')) | ||||
| //        } | ||||
| //        println("BYTES2\n") | ||||
| //        bytes2.forEach { | ||||
| //            print(it.toString(16).padStart(2, '0')) | ||||
| //        } | ||||
|         assertTrue { | ||||
|             !bytes1.contentEquals(bytes2) && | ||||
|                     bytes1.size == 10 && | ||||
|                     bytes2.size == 10 | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @ -1,28 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.util | ||||
| // | ||||
| //import kotlinx.coroutines.GlobalScope | ||||
| //import kotlinx.coroutines.promise | ||||
| // | ||||
| // | ||||
| ///** | ||||
| // * Created by Ugljesa Jovanovic | ||||
| // * ugljesa.jovanovic@ionspin.com | ||||
| // * on 20-Jul-2019 | ||||
| // */ | ||||
| //actual fun testBlocking(block: suspend ()-> Unit) : dynamic = GlobalScope.promise { block() } | ||||
| @ -1,25 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| import com.goterl.lazycode.lazysodium.SodiumJava | ||||
| 
 | ||||
| actual object Initializer { | ||||
|     private var isPlatformInitialized = false | ||||
| 
 | ||||
|     lateinit var sodium : SodiumJava | ||||
|     actual suspend fun initialize() { | ||||
|         sodium = SodiumJava() | ||||
|         isPlatformInitialized = true | ||||
|     } | ||||
| 
 | ||||
|     actual fun initializeWithCallback(done: () -> Unit) { | ||||
|         sodium = SodiumJava() | ||||
|         isPlatformInitialized = true | ||||
|         done() | ||||
|     } | ||||
| 
 | ||||
|     actual fun isInitialized(): Boolean { | ||||
|         return isPlatformInitialized | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,34 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| import java.security.SecureRandom | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 21-Sep-2019 | ||||
|  */ | ||||
| 
 | ||||
| actual object SRNG { | ||||
|     val secureRandom = SecureRandom() | ||||
|     actual fun getRandomBytes(amount: Int): UByteArray { | ||||
|         val byteArray = ByteArray(amount) | ||||
|         secureRandom.nextBytes(byteArray) | ||||
|         return byteArray.asUByteArray() | ||||
|     } | ||||
| } | ||||
| @ -1,142 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.authenticated | ||||
| 
 | ||||
| import com.goterl.lazycode.lazysodium.SodiumJava | ||||
| import com.goterl.lazycode.lazysodium.interfaces.SecretStream | ||||
| import com.ionspin.kotlin.crypto.InvalidTagException | ||||
| import com.ionspin.kotlin.crypto.util.hexColumsPrint | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 14-Jun-2020 | ||||
|  */ | ||||
| actual class XChaCha20Poly1305Delegated internal actual constructor() { | ||||
|     actual companion object { | ||||
|         actual fun encrypt( | ||||
|             key: UByteArray, | ||||
|             nonce: UByteArray, | ||||
|             message: UByteArray, | ||||
|             associatedData: UByteArray | ||||
|         ): UByteArray { | ||||
|             val ciphertext = ByteArray(message.size + 16) | ||||
|             SodiumJava().crypto_aead_xchacha20poly1305_ietf_encrypt( | ||||
|                 ciphertext, | ||||
|                 longArrayOf(ciphertext.size.toLong()), | ||||
|                 message.toByteArray(), | ||||
|                 message.size.toLong(), | ||||
|                 associatedData.toByteArray(), | ||||
|                 associatedData.size.toLong(), | ||||
|                 null, | ||||
|                 nonce.toByteArray(), | ||||
|                 key.toByteArray() | ||||
| 
 | ||||
|             ) | ||||
|             return ciphertext.asUByteArray() | ||||
|         } | ||||
| 
 | ||||
|         actual fun decrypt( | ||||
|             key: UByteArray, | ||||
|             nonce: UByteArray, | ||||
|             ciphertext: UByteArray, | ||||
|             associatedData: UByteArray | ||||
|         ): UByteArray { | ||||
|             val message = ByteArray(ciphertext.size - 16) | ||||
|             SodiumJava().crypto_aead_xchacha20poly1305_ietf_decrypt( | ||||
|                 message, | ||||
|                 longArrayOf(ciphertext.size.toLong()), | ||||
|                 null, | ||||
|                 ciphertext.toByteArray(), | ||||
|                 ciphertext.size.toLong(), | ||||
|                 associatedData.toByteArray(), | ||||
|                 associatedData.size.toLong(), | ||||
|                 nonce.toByteArray(), | ||||
|                 key.toByteArray() | ||||
| 
 | ||||
|             ) | ||||
|             return message.asUByteArray() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     val state : SecretStream.State = SecretStream.State() | ||||
|     val sodium = SodiumJava() | ||||
| 
 | ||||
|     var isInitialized = false | ||||
|     var isEncryptor = false | ||||
| 
 | ||||
|     internal actual constructor( | ||||
|         key: UByteArray, | ||||
|         testState: UByteArray, | ||||
|         testHeader: UByteArray, | ||||
|         isDecryptor: Boolean | ||||
|     ) : this() { | ||||
|         state.k = testState.sliceArray(0 until 32).toByteArray() | ||||
|         state.nonce = testState.sliceArray(32 until 44).toByteArray() | ||||
|         isInitialized = true | ||||
|         isEncryptor = !isDecryptor | ||||
|     } | ||||
| 
 | ||||
|     actual fun initializeForEncryption(key: UByteArray) : UByteArray { | ||||
|         val header = UByteArray(24) | ||||
|         sodium.crypto_secretstream_xchacha20poly1305_init_push(state, header.asByteArray(), key.asByteArray()) | ||||
|         isInitialized = true | ||||
|         isEncryptor = true | ||||
|         return header | ||||
|     } | ||||
| 
 | ||||
|     actual fun initializeForDecryption(key: UByteArray, header: UByteArray) { | ||||
|         sodium.crypto_secretstream_xchacha20poly1305_init_pull(state, header.asByteArray(), key.asByteArray()) | ||||
|         isInitialized = true | ||||
|         isEncryptor = false | ||||
|     } | ||||
| 
 | ||||
|     actual fun encrypt(data: UByteArray, associatedData: UByteArray): UByteArray { | ||||
|         if (!isInitialized) { | ||||
|             throw RuntimeException("Not initalized!") | ||||
|         } | ||||
|         if (!isEncryptor) { | ||||
|             throw RuntimeException("Initialized as decryptor, attempted to use as encryptor") | ||||
|         } | ||||
|         val ciphertext = ByteArray(1 + data.size + 16) | ||||
|         sodium.crypto_secretstream_xchacha20poly1305_push( | ||||
|             state, ciphertext, null, | ||||
|             data.asByteArray(), data.size.toLong(), | ||||
|             associatedData.asByteArray(), associatedData.size.toLong(), | ||||
|             0 | ||||
|         ) | ||||
|         return ciphertext.asUByteArray() | ||||
|     } | ||||
| 
 | ||||
|     actual fun decrypt(data: UByteArray, associatedData: UByteArray): UByteArray { | ||||
|         if (!isInitialized) { | ||||
|             throw RuntimeException("Not initalized!") | ||||
|         } | ||||
|         if (isEncryptor) { | ||||
|             throw RuntimeException("Initialized as encryptor, attempted to use as decryptor") | ||||
|         } | ||||
|         val plaintext = ByteArray(data.size - 17) | ||||
| 
 | ||||
|         val validTag = sodium.crypto_secretstream_xchacha20poly1305_pull( | ||||
|             state, plaintext, null, | ||||
|             null, | ||||
|             data.asByteArray(), | ||||
|             (data.size).toLong(), | ||||
|             associatedData.asByteArray(), | ||||
|             associatedData.size.toLong() | ||||
|         ) | ||||
|         if (validTag != 0) { | ||||
|             println("Tag validation failed") | ||||
|             throw InvalidTagException() | ||||
|         } | ||||
|         return plaintext.asUByteArray() | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     actual fun cleanup() { | ||||
|         sodium.sodium_memzero(state.k, 32) | ||||
|         sodium.sodium_memzero(state.nonce, 12) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,40 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.hash.blake2b | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.Initializer.sodium | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 14-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| actual class Blake2bDelegated actual constructor(key: UByteArray?, val hashLength: Int) : Blake2bMultipart { | ||||
| 
 | ||||
|     val state = ByteArray(sodium.crypto_generichash_statebytes()) | ||||
| 
 | ||||
|     init { | ||||
|         sodium.crypto_generichash_init(state,key?.toByteArray() ?: byteArrayOf(), key?.size ?: 0, hashLength) | ||||
|     } | ||||
| 
 | ||||
|     override fun update(data: UByteArray) { | ||||
|         sodium.crypto_generichash_update(state, data.toByteArray(), data.size.toLong()) | ||||
|     } | ||||
| 
 | ||||
|     override fun digest(): UByteArray { | ||||
|         val hashed = ByteArray(hashLength) | ||||
|         sodium.crypto_generichash_final(state, hashed, hashLength) | ||||
|         return hashed.asUByteArray() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| actual object Blake2bDelegatedStateless : Blake2b { | ||||
| 
 | ||||
| 
 | ||||
|     override fun digest(inputMessage: UByteArray, key: UByteArray, hashLength: Int): UByteArray { | ||||
|         val hashed = ByteArray(hashLength) | ||||
|         sodium.crypto_generichash(hashed, hashed.size, inputMessage.toByteArray(), inputMessage.size.toLong(), key.toByteArray(), key.size) | ||||
|         return hashed.asUByteArray() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,41 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.hash.sha | ||||
| 
 | ||||
| import com.goterl.lazycode.lazysodium.interfaces.Hash | ||||
| import com.ionspin.kotlin.crypto.Initializer.sodium | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 17-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| actual class Sha256Delegated actual constructor() : Sha256 { | ||||
| 
 | ||||
|     val state = Hash.State256() | ||||
| 
 | ||||
|     init { | ||||
|         sodium.crypto_hash_sha256_init(state) | ||||
|     } | ||||
| 
 | ||||
|     override fun update(data: UByteArray) { | ||||
|         sodium.crypto_hash_sha256_update(state, data.toByteArray(), data.size.toLong()) | ||||
|     } | ||||
| 
 | ||||
|     override fun digest(): UByteArray { | ||||
|         val hashed = ByteArray(Sha256Properties.MAX_HASH_BYTES) | ||||
|         sodium.crypto_hash_sha256_final(state, hashed) | ||||
|         return hashed.asUByteArray() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| actual object Sha256StatelessDelegated : StatelessSha256 { | ||||
| 
 | ||||
|     override fun digest(inputMessage: UByteArray): UByteArray { | ||||
|         val hashed = ByteArray(Sha256Properties.MAX_HASH_BYTES) | ||||
|         sodium.crypto_hash_sha256(hashed, inputMessage.toByteArray(), inputMessage.size.toLong()) | ||||
|         return hashed.asUByteArray() | ||||
|     } | ||||
| } | ||||
| @ -1,40 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.hash.sha | ||||
| 
 | ||||
| import com.goterl.lazycode.lazysodium.interfaces.Hash | ||||
| import com.ionspin.kotlin.crypto.Initializer | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 17-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| actual class Sha512Delegated : Sha512Multipart { | ||||
| 
 | ||||
|     val state = Hash.State512() | ||||
| 
 | ||||
|     init { | ||||
|         Initializer.sodium.crypto_hash_sha512_init(state) | ||||
|     } | ||||
| 
 | ||||
|     override fun update(data: UByteArray) { | ||||
|         Initializer.sodium.crypto_hash_sha512_update(state, data.toByteArray(), data.size.toLong()) | ||||
|     } | ||||
| 
 | ||||
|     override fun digest(): UByteArray { | ||||
|         val hashed = ByteArray(Sha512Properties.MAX_HASH_BYTES) | ||||
|         Initializer.sodium.crypto_hash_sha512_final(state, hashed) | ||||
|         return hashed.asUByteArray() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| actual object Sha512StatelessDelegated : Sha512 { | ||||
| 
 | ||||
|     override fun digest(inputMessage: UByteArray): UByteArray { | ||||
|         val hashed = ByteArray(Sha512Properties.MAX_HASH_BYTES) | ||||
|         Initializer.sodium.crypto_hash_sha512(hashed, inputMessage.toByteArray(), inputMessage.size.toLong()) | ||||
|         return hashed.asUByteArray() | ||||
|     } | ||||
| } | ||||
| @ -1,34 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.util | ||||
| 
 | ||||
| import kotlin.coroutines.Continuation | ||||
| import kotlin.coroutines.EmptyCoroutineContext | ||||
| import kotlin.coroutines.startCoroutine | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 20-Jul-2019 | ||||
|  */ | ||||
| //actual fun testBlocking(block: suspend () -> Unit) { | ||||
| //    val continuation = Continuation<Unit>(EmptyCoroutineContext) { | ||||
| //        println("Done") | ||||
| //    } | ||||
| //    block.startCoroutine(continuation) | ||||
| // | ||||
| //} | ||||
| @ -1,9 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.hash.sha | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 07-Jun-2020 | ||||
|  */ | ||||
| import platform.posix.* | ||||
| //import cin | ||||
| @ -1,44 +0,0 @@ | ||||
| //We'll handle SRNG through libsodium | ||||
| ///* | ||||
| // *    Copyright 2019 Ugljesa Jovanovic | ||||
| // * | ||||
| // *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // *    you may not use this file except in compliance with the License. | ||||
| // *    You may obtain a copy of the License at | ||||
| // * | ||||
| // *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // * | ||||
| // *    Unless required by applicable law or agreed to in writing, software | ||||
| // *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // *    See the License for the specific language governing permissions and | ||||
| // *    limitations under the License. | ||||
| // */ | ||||
| // | ||||
| //package com.ionspin.kotlin.crypto | ||||
| // | ||||
| //import kotlinx.cinterop.* | ||||
| //import platform.windows.* | ||||
| // | ||||
| ///** | ||||
| // * Created by Ugljesa Jovanovic | ||||
| // * ugljesa.jovanovic@ionspin.com | ||||
| // * on 21-Sep-2019 | ||||
| // */ | ||||
| //actual object SRNG { | ||||
| //    private val advapi by lazy { LoadLibraryA("ADVAPI32.DLL")} | ||||
| // | ||||
| //    private val advapiRandom by lazy { | ||||
| //        GetProcAddress(advapi, "SystemFunction036")?.reinterpret<CFunction<Function2<CPointer<ByteVar>, ULong, Int>>>() ?: error("Failed getting advapi random") | ||||
| //    } | ||||
| // | ||||
| //    @Suppress("EXPERIMENTAL_UNSIGNED_LITERALS") | ||||
| //    actual fun getRandomBytes(amount: Int): UByteArray { | ||||
| //        memScoped { | ||||
| //            val randArray = allocArray<ByteVar>(amount) | ||||
| //            val pointer = randArray.getPointer(this) | ||||
| //            val status = advapiRandom(pointer.reinterpret(), amount.convert()) | ||||
| //            return UByteArray(amount) { pointer[it].toUByte() } | ||||
| //        } | ||||
| //    } | ||||
| //} | ||||
| @ -1,6 +0,0 @@ | ||||
| headers = sodium.h | ||||
| headerFilter = sodium.h sodium/** | ||||
| #staticLibraries = libsodium.a | ||||
| #libraryPaths = sodiumWrapper/lib | ||||
| #compilerOpts = -I./sodiumWrapper/include | ||||
| linkerOpts = | ||||
| @ -1,30 +0,0 @@ | ||||
| @file:Suppress("VARIABLE_IN_SINGLETON_WITHOUT_THREAD_LOCAL") | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| import libsodium.sodium_init | ||||
| import kotlin.native.concurrent.AtomicInt | ||||
| 
 | ||||
| actual object Initializer { | ||||
| 
 | ||||
|     private var isPlatformInitialized : AtomicInt = AtomicInt(0) | ||||
| 
 | ||||
|     actual suspend fun initialize() { | ||||
|         if (isPlatformInitialized.compareAndSet(0, 1)) { | ||||
|             sodium_init() | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     actual fun initializeWithCallback(done: () -> Unit) { | ||||
|         if (isPlatformInitialized.compareAndSet(0, 1)) { | ||||
|             sodium_init() | ||||
|         } | ||||
|         done() | ||||
|     } | ||||
| 
 | ||||
|     actual fun isInitialized(): Boolean { | ||||
|         return isPlatformInitialized.value != 0 | ||||
|     } | ||||
| } | ||||
| @ -1,40 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| import kotlinx.cinterop.* | ||||
| import libsodium.randombytes_buf | ||||
| import platform.posix.* | ||||
| //import libsod | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 21-Sep-2019 | ||||
|  */ | ||||
| actual object SRNG { | ||||
|     @Suppress("EXPERIMENTAL_UNSIGNED_LITERALS") | ||||
|     actual fun getRandomBytes(amount: Int): UByteArray { | ||||
|         memScoped { | ||||
|             val array = allocArray<UByteVar>(amount) | ||||
|             randombytes_buf(array, amount.convert()) | ||||
|             return UByteArray(amount) { | ||||
|                 array[it] | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,164 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.authenticated | ||||
| 
 | ||||
| import com.ionspin.kotlin.bignum.integer.util.hexColumsPrint | ||||
| import com.ionspin.kotlin.crypto.InvalidTagException | ||||
| import com.ionspin.kotlin.crypto.util.toPtr | ||||
| import kotlinx.cinterop.* | ||||
| import libsodium.* | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 14-Jun-2020 | ||||
|  */ | ||||
| actual class XChaCha20Poly1305Delegated internal actual constructor() { | ||||
|     actual companion object { | ||||
|         actual fun encrypt( | ||||
|             key: UByteArray, | ||||
|             nonce: UByteArray, | ||||
|             message: UByteArray, | ||||
|             associatedData: UByteArray | ||||
|         ): UByteArray { | ||||
|             val ciphertextLength = message.size + crypto_aead_xchacha20poly1305_IETF_ABYTES.toInt() | ||||
|             val ciphertext = UByteArray(ciphertextLength) | ||||
|             val ciphertextPinned = ciphertext.pin() | ||||
|             crypto_aead_xchacha20poly1305_ietf_encrypt( | ||||
|                 ciphertextPinned.toPtr(), | ||||
|                 ulongArrayOf(ciphertextLength.convert()).toCValues(), | ||||
|                 message.toCValues(), | ||||
|                 message.size.convert(), | ||||
|                 associatedData.toCValues(), | ||||
|                 associatedData.size.convert(), | ||||
|                 null, | ||||
|                 nonce.toCValues(), | ||||
|                 key.toCValues() | ||||
|             ) | ||||
|             ciphertextPinned.unpin() | ||||
|             return ciphertext | ||||
|         } | ||||
| 
 | ||||
|         actual fun decrypt( | ||||
|             key: UByteArray, | ||||
|             nonce: UByteArray, | ||||
|             ciphertext: UByteArray, | ||||
|             associatedData: UByteArray | ||||
|         ): UByteArray { | ||||
|             val messageLength = ciphertext.size - crypto_aead_xchacha20poly1305_IETF_ABYTES.toInt() | ||||
|             val message = UByteArray(messageLength) | ||||
|             val messagePinned = message.pin() | ||||
|             crypto_aead_xchacha20poly1305_ietf_decrypt( | ||||
|                 messagePinned.toPtr(), | ||||
|                 ulongArrayOf(messageLength.convert()).toCValues(), | ||||
|                 null, | ||||
|                 ciphertext.toCValues(), | ||||
|                 ciphertext.size.convert(), | ||||
|                 associatedData.toCValues(), | ||||
|                 associatedData.size.convert(), | ||||
|                 nonce.toCValues(), | ||||
|                 key.toCValues() | ||||
|             ) | ||||
|             messagePinned.unpin() | ||||
|             return message | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     var state = | ||||
|         sodium_malloc(crypto_secretstream_xchacha20poly1305_state.size.convert())!! | ||||
|             .reinterpret<crypto_secretstream_xchacha20poly1305_state>() | ||||
|             .pointed | ||||
|     val header = UByteArray(crypto_secretstream_xchacha20poly1305_HEADERBYTES.toInt()) { 0U } | ||||
| 
 | ||||
|     var isInitialized = false | ||||
|     var isEncryptor = false | ||||
| 
 | ||||
|     actual internal constructor( | ||||
|         key: UByteArray, | ||||
|         testState: UByteArray, | ||||
|         testHeader: UByteArray, | ||||
|         isDecryptor: Boolean | ||||
|     ) : this() { | ||||
|         val pointer = state.ptr.reinterpret<UByteVar>() | ||||
|         for (i in 0 until crypto_secretstream_xchacha20poly1305_state.size.toInt()) { | ||||
|             pointer[i] = testState[i] | ||||
|         } | ||||
|         println("state after setting-----------") | ||||
|         state.ptr.readBytes(crypto_secretstream_xchacha20poly1305_state.size.toInt()).asUByteArray().hexColumsPrint() | ||||
|         println("state after setting-----------") | ||||
|         println("header after setting-----------") | ||||
|         testHeader.copyInto(header) | ||||
|         header.hexColumsPrint() | ||||
|         println("header after setting-----------") | ||||
|         isInitialized = true | ||||
|         isEncryptor = !isDecryptor | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     actual fun initializeForEncryption(key: UByteArray) : UByteArray { | ||||
|         val pinnedHeader = header.pin() | ||||
|         crypto_secretstream_xchacha20poly1305_init_push(state.ptr, pinnedHeader.toPtr(), key.toCValues()) | ||||
|         println("state-----------") | ||||
|         state.ptr.readBytes(crypto_secretstream_xchacha20poly1305_state.size.toInt()).asUByteArray().hexColumsPrint() | ||||
|         println("state-----------") | ||||
|         println("--------header-----------") | ||||
|         header.hexColumsPrint() | ||||
|         println("--------header-----------") | ||||
|         pinnedHeader.unpin() | ||||
|         return header | ||||
|     } | ||||
| 
 | ||||
|     actual fun initializeForDecryption(key: UByteArray, header: UByteArray) { | ||||
|         crypto_secretstream_xchacha20poly1305_init_pull(state.ptr, header.toCValues(), key.toCValues()) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     actual fun encrypt(data: UByteArray, associatedData: UByteArray): UByteArray { | ||||
|         val ciphertextWithTag = UByteArray(data.size + crypto_secretstream_xchacha20poly1305_ABYTES.toInt()) | ||||
|         val ciphertextWithTagPinned = ciphertextWithTag.pin() | ||||
|         crypto_secretstream_xchacha20poly1305_push( | ||||
|             state.ptr, | ||||
|             ciphertextWithTagPinned.toPtr(), | ||||
|             null, | ||||
|             data.toCValues(), | ||||
|             data.size.convert(), | ||||
|             associatedData.toCValues(), | ||||
|             associatedData.size.convert(), | ||||
|             0U, | ||||
|         ) | ||||
|         println("Encrypt partial") | ||||
|         ciphertextWithTag.hexColumsPrint() | ||||
|         println("Encrypt partial end") | ||||
|         ciphertextWithTagPinned.unpin() | ||||
|         return ciphertextWithTag | ||||
|     } | ||||
| 
 | ||||
|     actual fun decrypt(data: UByteArray, associatedData: UByteArray): UByteArray { | ||||
|         val plaintext = UByteArray(data.size - crypto_secretstream_xchacha20poly1305_ABYTES.toInt()) | ||||
|         val plaintextPinned = plaintext.pin() | ||||
|         val validTag = crypto_secretstream_xchacha20poly1305_pull( | ||||
|             state.ptr, | ||||
|             plaintextPinned.toPtr(), | ||||
|             null, | ||||
|             null, | ||||
|             data.toCValues(), | ||||
|             data.size.convert(), | ||||
|             associatedData.toCValues(), | ||||
|             associatedData.size.convert() | ||||
|         ) | ||||
|         plaintextPinned.unpin() | ||||
|         println("tag: $validTag") | ||||
|         if (validTag != 0) { | ||||
|             println("Tag validation failed") | ||||
|             throw InvalidTagException() | ||||
|         } | ||||
|         return plaintext | ||||
|     } | ||||
| 
 | ||||
|     actual fun cleanup() { | ||||
|         sodium_free(state.ptr) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,61 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.hash.blake2b | ||||
| import com.ionspin.kotlin.crypto.util.toHexString | ||||
| import com.ionspin.kotlin.crypto.util.toPtr | ||||
| import kotlinx.cinterop.* | ||||
| import libsodium.* | ||||
| import platform.posix.free | ||||
| import platform.posix.malloc | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 14-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| actual class Blake2bDelegated actual constructor(key: UByteArray?, hashLength: Int) : Blake2bMultipart { | ||||
|     override val MAX_HASH_BYTES: Int = 64 | ||||
| 
 | ||||
|     val requestedHashLength : Int | ||||
|     val state : crypto_generichash_state | ||||
|     init { | ||||
|         requestedHashLength = hashLength | ||||
|         val allocated = malloc(crypto_generichash_state.size.convert())!! | ||||
|         state = allocated.reinterpret<crypto_generichash_state>().pointed | ||||
|         crypto_generichash_init(state.ptr, key?.run { this.toCValues() }, key?.size?.convert() ?: 0UL.convert(), hashLength.convert()) | ||||
|     } | ||||
| 
 | ||||
|     override fun update(data: UByteArray) { | ||||
|         crypto_generichash_update(state.ptr, data.toCValues(), data.size.convert()) | ||||
|     } | ||||
| 
 | ||||
|     override fun digest(): UByteArray { | ||||
|         val hashResult = UByteArray(requestedHashLength) | ||||
|         val hashResultPinned = hashResult.pin() | ||||
|         crypto_generichash_final(state.ptr, hashResultPinned.toPtr(), requestedHashLength.convert()) | ||||
|         free(state.ptr) | ||||
|         return hashResult | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| @Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") | ||||
| actual object Blake2bDelegatedStateless : Blake2b { | ||||
| 
 | ||||
|     override fun digest(inputMessage: UByteArray, key: UByteArray, hashLength: Int): UByteArray { | ||||
|         val hashResult = UByteArray(MAX_HASH_BYTES) | ||||
|         val hashResultPinned = hashResult.pin() | ||||
|         crypto_generichash( | ||||
|             hashResultPinned.toPtr(), | ||||
|             hashLength.convert(), | ||||
|             inputMessage.toCValues(), | ||||
|             inputMessage.size.convert(), | ||||
|             key.toCValues(), | ||||
|             key.size.convert() | ||||
|         ) | ||||
|         hashResultPinned.unpin() | ||||
|         return hashResult | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,55 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.hash.sha | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bDelegatedStateless | ||||
| import com.ionspin.kotlin.crypto.util.toPtr | ||||
| import kotlinx.cinterop.* | ||||
| import libsodium.* | ||||
| import platform.posix.free | ||||
| import platform.posix.malloc | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 17-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| actual class Sha256Delegated : Sha256 { | ||||
| 
 | ||||
|     val state : crypto_hash_sha256_state | ||||
| 
 | ||||
|     init { | ||||
|         val allocated = sodium_malloc(crypto_hash_sha256_state.size.convert())!! | ||||
|         state = allocated.reinterpret<crypto_hash_sha256_state>().pointed | ||||
|         crypto_hash_sha256_init(state.ptr) | ||||
|     } | ||||
| 
 | ||||
|     override fun update(data: UByteArray) { | ||||
|         crypto_hash_sha256_update(state.ptr, data.toCValues(), data.size.convert()) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     override fun digest(): UByteArray { | ||||
|         val hashResult = UByteArray(Sha256Properties.MAX_HASH_BYTES) | ||||
|         val hashResultPinned = hashResult.pin() | ||||
|         crypto_hash_sha256_final(state.ptr, hashResultPinned.toPtr()) | ||||
|         sodium_free(state.ptr) | ||||
|         return hashResult | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| actual object Sha256StatelessDelegated : StatelessSha256 { | ||||
| 
 | ||||
|     override fun digest(inputMessage: UByteArray): UByteArray { | ||||
|         val hashResult = UByteArray(MAX_HASH_BYTES) | ||||
|         val hashResultPinned = hashResult.pin() | ||||
|         crypto_hash_sha256(hashResultPinned.toPtr(), inputMessage.toCValues(), inputMessage.size.convert()) | ||||
|         hashResultPinned.unpin() | ||||
|         return hashResult | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,50 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.hash.sha | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.util.toPtr | ||||
| import kotlinx.cinterop.* | ||||
| import libsodium.* | ||||
| import platform.posix.free | ||||
| import platform.posix.malloc | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 17-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| actual class Sha512Delegated : Sha512Multipart { | ||||
|     val state : crypto_hash_sha512_state | ||||
| 
 | ||||
|     init { | ||||
|         val allocated = malloc(crypto_hash_sha512_state.size.convert())!! | ||||
|         state = allocated.reinterpret<crypto_hash_sha512_state>().pointed | ||||
|         crypto_hash_sha512_init(state.ptr) | ||||
|     } | ||||
| 
 | ||||
|     override fun update(data: UByteArray) { | ||||
|         crypto_hash_sha512_update(state.ptr, data.toCValues(), data.size.convert()) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     override fun digest(): UByteArray { | ||||
|         val hashResult = UByteArray(Sha512Properties.MAX_HASH_BYTES) | ||||
|         val hashResultPinned = hashResult.pin() | ||||
|         crypto_hash_sha512_final(state.ptr, hashResultPinned.toPtr()) | ||||
|         free(state.ptr) | ||||
|         return hashResult | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| actual object Sha512StatelessDelegated : Sha512  { | ||||
| 
 | ||||
|     override fun digest(inputMessage: UByteArray): UByteArray { | ||||
|         val hashResult = UByteArray(Sha512StatelessDelegated.MAX_HASH_BYTES) | ||||
|         val hashResultPinned = hashResult.pin() | ||||
|         crypto_hash_sha512(hashResultPinned.toPtr(), inputMessage.toCValues(), inputMessage.size.convert()) | ||||
|         hashResultPinned.unpin() | ||||
|         return hashResult | ||||
|     } | ||||
| } | ||||
| @ -1,13 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.util | ||||
| 
 | ||||
| import kotlinx.cinterop.CPointer | ||||
| import kotlinx.cinterop.Pinned | ||||
| import kotlinx.cinterop.UByteVar | ||||
| import kotlinx.cinterop.addressOf | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 27-Aug-2020 | ||||
|  */ | ||||
| fun Pinned<UByteArray>.toPtr() : CPointer<UByteVar> = addressOf(0) | ||||
| @ -1,77 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.hash.encodeToUByteArray | ||||
| import com.ionspin.kotlin.crypto.util.toHexString | ||||
| import com.ionspin.kotlin.crypto.util.toPtr | ||||
| import kotlinx.cinterop.* | ||||
| import libsodium.* | ||||
| import platform.posix.free | ||||
| import platform.posix.malloc | ||||
| import kotlin.test.Ignore | ||||
| import kotlin.test.Test | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 13-Jul-2020 | ||||
|  */ | ||||
| class HelperTest { | ||||
|     @Ignore //Just used for debugging pure implementation | ||||
|     @Test | ||||
|     fun longSha256() { | ||||
|         for (target in 0L until 10L) { | ||||
|             generateForRounds256(target) | ||||
|         } | ||||
|         for (target in 0L until 16_777_216L step 1_000_000L) { | ||||
|             generateForRounds256(target) | ||||
|         } | ||||
|         generateForRounds256(16_777_216L) | ||||
|     } | ||||
| 
 | ||||
|     fun generateForRounds256(target: Long) { | ||||
|         val updateValue = | ||||
|             "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno".encodeToUByteArray().toCValues() | ||||
|         val state = malloc(crypto_hash_sha256_state.size.convert())!! | ||||
|             .reinterpret<crypto_hash_sha256_state>() | ||||
| 
 | ||||
|         crypto_hash_sha256_init(state) | ||||
|         for (i in 0 until target) { | ||||
|             crypto_hash_sha256_update(state, updateValue, updateValue.size.convert()) | ||||
|         } | ||||
|         val result = UByteArray(32) | ||||
|         val resultPinned = result.pin() | ||||
|         crypto_hash_sha256_final(state, resultPinned.toPtr()) | ||||
|         println("$target to \"${result.toHexString()}\",") | ||||
|         free(state) | ||||
|     } | ||||
|     @Ignore //Just used for debugging pure implementation | ||||
|     @Test | ||||
|     fun longSha512() { | ||||
| 
 | ||||
|         for (target in 0L until 10L) { | ||||
|             generateForRounds512(target) | ||||
|         } | ||||
|         for (target in 0L until 16_777_216L step 1_000_000L) { | ||||
|             generateForRounds512(target) | ||||
|         } | ||||
|         generateForRounds512(16_777_216L) | ||||
|     } | ||||
| 
 | ||||
|     fun generateForRounds512(target: Long) { | ||||
|         println("Wut") | ||||
|         val updateValue = | ||||
|             "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno".encodeToUByteArray().toCValues() | ||||
|         val state = malloc(crypto_hash_sha512_state.size.convert())!! | ||||
|             .reinterpret<crypto_hash_sha512_state>() | ||||
| 
 | ||||
|         crypto_hash_sha512_init(state) | ||||
|         for (i in 0 until target) { | ||||
|             crypto_hash_sha512_update(state, updateValue, updateValue.size.convert()) | ||||
|         } | ||||
|         val result = UByteArray(32) | ||||
|         val resultPinned = result.pin() | ||||
|         crypto_hash_sha512_final(state, resultPinned.toPtr()) | ||||
|         println("$target to \"${result.toHexString()}\",") | ||||
|         free(state) | ||||
|     } | ||||
| } | ||||
| @ -1,27 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| //package com.ionspin.kotlin.crypto.util | ||||
| // | ||||
| //import kotlinx.coroutines.CoroutineScope | ||||
| //import kotlinx.coroutines.runBlocking | ||||
| // | ||||
| ///** | ||||
| // * Created by Ugljesa Jovanovic | ||||
| // * ugljesa.jovanovic@ionspin.com | ||||
| // * on 20-Jul-2019 | ||||
| // */ | ||||
| //actual fun testBlocking(block: suspend () -> Unit) = runBlocking { block() } | ||||
| @ -1,522 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| @file:Suppress("UnstableApiUsage") | ||||
| import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest | ||||
| import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest | ||||
| import org.jetbrains.dokka.Platform | ||||
| 
 | ||||
| plugins { | ||||
|     kotlin(PluginsDeps.multiplatform) | ||||
|     id(PluginsDeps.mavenPublish) | ||||
|     id(PluginsDeps.signing) | ||||
|     id(PluginsDeps.node) version Versions.nodePlugin | ||||
|     id(PluginsDeps.dokka) | ||||
| } | ||||
| 
 | ||||
| val sonatypeStaging = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" | ||||
| val sonatypeSnapshots = "https://oss.sonatype.org/content/repositories/snapshots/" | ||||
| 
 | ||||
| val sonatypePassword: String? by project | ||||
| 
 | ||||
| val sonatypeUsername: String? by project | ||||
| 
 | ||||
| val sonatypePasswordEnv: String? = System.getenv()["SONATYPE_PASSWORD"] | ||||
| val sonatypeUsernameEnv: String? = System.getenv()["SONATYPE_USERNAME"] | ||||
| 
 | ||||
| repositories { | ||||
|     mavenCentral() | ||||
|     jcenter() | ||||
| 
 | ||||
| } | ||||
| group = ReleaseInfo.group | ||||
| version = ReleaseInfo.version | ||||
| 
 | ||||
| val ideaActive = System.getProperty("idea.active") == "true" | ||||
| 
 | ||||
| kotlin { | ||||
|     val hostOsName = getHostOsName() | ||||
|     val bla =1 | ||||
|     runningOnLinuxx86_64 { | ||||
|         jvm() | ||||
|         js { | ||||
| 
 | ||||
|         browser { | ||||
| 
 | ||||
|             testTask { | ||||
| //                isRunningInTravis { | ||||
|                     enabled = false //Until I sort out testing on travis, and figure out how to increase karma timeout | ||||
| //                } | ||||
|                 useKarma { | ||||
|                     useChrome() | ||||
| 
 | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|             nodejs { | ||||
|                 testTask { | ||||
|                     useMocha() { | ||||
|                         timeout = "10s" | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|         linuxX64("linux") { | ||||
|             binaries { | ||||
|                 staticLib { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         linuxArm64() { | ||||
|             binaries { | ||||
|                 staticLib { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         linuxArm32Hfp() { | ||||
|             binaries { | ||||
|                 staticLib { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     runningOnMacos { | ||||
|         iosX64() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         iosArm64() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         iosArm32() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         macosX64() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         tvosX64() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         tvosArm64() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         watchosArm64() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         watchosArm32() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         watchosX86() { | ||||
|             binaries { | ||||
|                 framework { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     runningOnWindows { | ||||
| 
 | ||||
|         mingwX64() { | ||||
|             binaries { | ||||
|                 staticLib { | ||||
|                     optimized = true | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| // No coroutines support for mingwX86 | ||||
| //    mingwX86() { | ||||
| //        binaries { | ||||
| //            staticLib { | ||||
| // | ||||
| //            } | ||||
| //        } | ||||
| //    } | ||||
| 
 | ||||
| 
 | ||||
|     println(targets.names) | ||||
| 
 | ||||
|     sourceSets { | ||||
|         val commonMain by getting { | ||||
|             dependencies { | ||||
|                 implementation(kotlin(Deps.Common.stdLib)) | ||||
|                 implementation(kotlin(Deps.Common.test)) | ||||
|                 implementation(Deps.Common.kotlinBigNum) | ||||
|                 implementation(project(Deps.Common.apiProject)) | ||||
|             } | ||||
|         } | ||||
|         val commonTest by getting { | ||||
|             dependencies { | ||||
|                 implementation(kotlin(Deps.Common.test)) | ||||
|                 implementation(kotlin(Deps.Common.testAnnotation)) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         val nativeMain by creating { | ||||
|             dependsOn(commonMain) | ||||
|             dependencies { | ||||
|             } | ||||
|             isRunningInIdea { | ||||
|                 kotlin.setSrcDirs(emptySet<String>()) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         val nativeTest by creating { | ||||
|             dependsOn(commonTest) | ||||
|             dependencies { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> { | ||||
|             compilations.getByName("main") { | ||||
|                 println("Setting native sourceset dependancy for $name") | ||||
|                 if ((this@withType.name.contains("ios") || | ||||
|                             this@withType.name.contains("mingw")).not() | ||||
|                 ) { | ||||
|                     println("Setting native sourceset dependancy for $this@withType.name") | ||||
|                     defaultSourceSet.dependsOn(nativeMain) | ||||
|                 } | ||||
|             } | ||||
|             compilations.getByName("test") { | ||||
|                 println("Setting native sourceset dependancy for $name") | ||||
|                 if ((this@withType.name.contains("ios") || | ||||
|                             this@withType.name.contains("mingw")).not() | ||||
|                 ) { | ||||
|                     println("Setting native sourceset dependancy for $this@withType.name") | ||||
|                     defaultSourceSet.dependsOn(nativeTest) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         runningOnLinuxx86_64 { | ||||
|             val jvmMain by getting { | ||||
|                 dependencies { | ||||
|                     implementation(kotlin(Deps.Jvm.stdLib)) | ||||
|                     implementation(kotlin(Deps.Jvm.test)) | ||||
|                     implementation(kotlin(Deps.Jvm.testJUnit)) | ||||
|                 } | ||||
|             } | ||||
|             val jvmTest by getting { | ||||
|                 dependencies { | ||||
|                     implementation(kotlin(Deps.Jvm.test)) | ||||
|                     implementation(kotlin(Deps.Jvm.testJUnit)) | ||||
|                     implementation(kotlin(Deps.Jvm.reflection)) | ||||
|                 } | ||||
|             } | ||||
|             val jsMain by getting { | ||||
|                 dependencies { | ||||
|                     implementation(kotlin(Deps.Js.stdLib)) | ||||
|                 } | ||||
|             } | ||||
|             val jsTest by getting { | ||||
|                 dependencies { | ||||
|                     implementation(kotlin(Deps.Js.test)) | ||||
|                 } | ||||
|             } | ||||
|             val linuxMain by getting { | ||||
|                 dependsOn(nativeMain) | ||||
|                 //Force idea to consider native sourceset | ||||
|                 if (ideaActive) { | ||||
|                     kotlin.srcDir("src/nativeMain/kotlin") | ||||
|                 } | ||||
|             } | ||||
|             val linuxTest by getting { | ||||
|                 dependsOn(nativeTest) | ||||
| //                Force idea to consider native sourceset | ||||
|                 if (ideaActive) { | ||||
|                     kotlin.srcDir("src/nativeTest/kotlin") | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             val linuxArm64Main by getting { | ||||
|                 dependsOn(nativeMain) | ||||
|             } | ||||
| 
 | ||||
|             val linuxArm64Test by getting { | ||||
|                 dependsOn(nativeTest) | ||||
|             } | ||||
| 
 | ||||
|             val linuxArm32HfpMain by getting { | ||||
|                 dependsOn(nativeMain) | ||||
|             } | ||||
| 
 | ||||
|             val linuxArm32HfpTest by getting { | ||||
|                 dependsOn(nativeTest) | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         runningOnMacos{ | ||||
| 
 | ||||
|             val iosX64Main by getting { | ||||
|                 dependsOn(nativeMain) | ||||
|             } | ||||
|             val iosX64Test by getting { | ||||
|                 dependsOn(nativeTest) | ||||
|             } | ||||
| 
 | ||||
|             val iosArm64Main by getting { | ||||
|                 dependsOn(nativeMain) | ||||
|             } | ||||
|             val iosArm64Test by getting { | ||||
|                 dependsOn(nativeTest) | ||||
|             } | ||||
| 
 | ||||
|             val iosArm32Main by getting { | ||||
|                 dependsOn(nativeMain) | ||||
|             } | ||||
|             val iosArm32Test by getting { | ||||
|                 dependsOn(nativeTest) | ||||
|             } | ||||
| 
 | ||||
|             val macosX64Main by getting { | ||||
|                 dependsOn(commonMain) | ||||
|                 if (ideaActive) { | ||||
|                     kotlin.srcDir("src/nativeMain/kotlin") | ||||
|                 } | ||||
|             } | ||||
|             val macosX64Test by getting { | ||||
|                 dependsOn(commonTest) | ||||
|                 if (ideaActive) { | ||||
|                     kotlin.srcDir("src/nativeTest/kotlin") | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| //      Coroutines don't support mingwx86 yet | ||||
| //        val mingwX86Main by getting { | ||||
| //            dependsOn(commonMain) | ||||
| //            dependencies { | ||||
| //            } | ||||
| //        } | ||||
| 
 | ||||
| //        val mingwX86Test by getting { | ||||
| //            dependsOn(commonTest) | ||||
| //        } | ||||
| // | ||||
|         runningOnWindows { | ||||
|             val mingwX64Main by getting { | ||||
|                 dependsOn(commonMain) | ||||
|                 dependencies { | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             val mingwX64Test by getting { | ||||
|                 dependsOn(commonTest) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         all { | ||||
|             languageSettings.enableLanguageFeature("InlineClasses") | ||||
|             languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes") | ||||
|             languageSettings.useExperimentalAnnotation("kotlin.ExperimentalStdlibApi") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| task<Copy>("copyPackageJson") { | ||||
|     dependsOn("compileKotlinJs") | ||||
|     println("Copying package.json from $projectDir/core/src/jsMain/npm") | ||||
|     from("$projectDir/src/jsMain/npm") | ||||
|     println("Node modules dir ${node.nodeModulesDir}") | ||||
|     into("${node.nodeModulesDir}") | ||||
| } | ||||
| 
 | ||||
| tasks { | ||||
| 
 | ||||
| 
 | ||||
|     create<Jar>("javadocJar") { | ||||
|         dependsOn(dokkaJavadoc) | ||||
|         archiveClassifier.set("javadoc") | ||||
|         from(dokkaJavadoc.get().outputDirectory) | ||||
|     } | ||||
| 
 | ||||
|     dokkaJavadoc { | ||||
|         println("Dokka !") | ||||
|         dokkaSourceSets { | ||||
|             named("commonMain") { | ||||
|                 displayName.set("common") | ||||
|                 platform.set(Platform.common) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
|     if (getHostOsName() == "linux" && getHostArchitecture() == "x86-64") { | ||||
| 
 | ||||
|         val jvmTest by getting(Test::class) { | ||||
|             testLogging { | ||||
|                 events("PASSED", "FAILED", "SKIPPED") | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         val linuxTest by getting(KotlinNativeTest::class) { | ||||
| 
 | ||||
|             testLogging { | ||||
|                 events("PASSED", "FAILED", "SKIPPED") | ||||
| // showStandardStreams = true | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         val jsNodeTest by getting(KotlinJsTest::class) { | ||||
|             testLogging { | ||||
|                 events("PASSED", "FAILED", "SKIPPED") | ||||
|                 showStandardStreams = true | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| //        val legacyjsNodeTest by getting(KotlinJsTest::class) { | ||||
| // | ||||
| //            testLogging { | ||||
| //                events("PASSED", "FAILED", "SKIPPED") | ||||
| //                showStandardStreams = true | ||||
| //            } | ||||
| //        } | ||||
| 
 | ||||
| //        val jsIrBrowserTest by getting(KotlinJsTest::class) { | ||||
| //            testLogging { | ||||
| //                events("PASSED", "FAILED", "SKIPPED") | ||||
| //                 showStandardStreams = true | ||||
| //            } | ||||
| //        } | ||||
|     } | ||||
| 
 | ||||
|     if (getHostOsName() == "windows") { | ||||
|         val mingwX64Test by getting(KotlinNativeTest::class) { | ||||
| 
 | ||||
|             testLogging { | ||||
|                 events("PASSED", "FAILED", "SKIPPED") | ||||
|                 showStandardStreams = true | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| signing { | ||||
|     isRequired = false | ||||
|     sign(publishing.publications) | ||||
| } | ||||
| 
 | ||||
| publishing { | ||||
|     publications.withType(MavenPublication::class) { | ||||
|         artifact(tasks["javadocJar"]) | ||||
|         pom { | ||||
|             name.set("Kotlin Multiplatform Crypto") | ||||
|             description.set("Kotlin Multiplatform Crypto library") | ||||
|             url.set("https://github.com/ionspin/kotlin-multiplatform-crypto") | ||||
|             licenses { | ||||
|                 license { | ||||
|                     name.set("The Apache License, Version 2.0") | ||||
|                     url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") | ||||
|                 } | ||||
|             } | ||||
|             developers { | ||||
|                 developer { | ||||
|                     id.set("ionspin") | ||||
|                     name.set("Ugljesa Jovanovic") | ||||
|                     email.set("opensource@ionspin.com") | ||||
|                 } | ||||
|             } | ||||
|             scm { | ||||
|                 url.set("https://github.com/ionspin/kotlin-multiplatform-crypto") | ||||
|                 connection.set("scm:git:git://git@github.com:ionspin/kotlin-multiplatform-crypto.git") | ||||
|                 developerConnection.set("scm:git:ssh://git@github.com:ionspin/kotlin-multiplatform-crypto.git") | ||||
| 
 | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
|     repositories { | ||||
|         maven { | ||||
| 
 | ||||
|             url = uri(sonatypeStaging) | ||||
|             credentials { | ||||
|                 username = sonatypeUsername ?: sonatypeUsernameEnv ?: "" | ||||
|                 password = sonatypePassword ?: sonatypePasswordEnv ?: "" | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         maven { | ||||
|             name = "snapshot" | ||||
|             url = uri(sonatypeSnapshots) | ||||
|             credentials { | ||||
|                 username = sonatypeUsername ?: sonatypeUsernameEnv ?: "" | ||||
|                 password = sonatypePassword ?: sonatypePasswordEnv ?: "" | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @ -1,26 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 20-Jul-2019 | ||||
|  */ | ||||
| object Config { | ||||
|     const val DEBUG = false | ||||
| } | ||||
| @ -1,155 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.authenticated.* | ||||
| import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bMultipart | ||||
| import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bPure | ||||
| import com.ionspin.kotlin.crypto.hash.sha.Sha256Pure | ||||
| import com.ionspin.kotlin.crypto.hash.sha.Sha512Pure | ||||
| import com.ionspin.kotlin.crypto.keyderivation.ArgonResult | ||||
| import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Pure | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 24-May-2020 | ||||
|  */ | ||||
| object CryptoInitializerPure : CryptoInitializer { | ||||
|     override suspend fun initialize() { | ||||
|         //Nothing to do atm. | ||||
|     } | ||||
| 
 | ||||
|     fun initializeWithCallback(done: () -> Unit) { | ||||
|         done() | ||||
|     } | ||||
| 
 | ||||
|     override fun isInitialized(): Boolean { | ||||
|         return true | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| object CryptoPrimitives : PrimitivesApi { | ||||
|     private fun checkInitialization() { | ||||
|         CryptoInitializerPure.isInitialized() | ||||
|     } | ||||
| 
 | ||||
|     override fun hashBlake2bMultipart(key: UByteArray?, hashLength: Int): Blake2bMultipart { | ||||
|         checkInitialization() | ||||
|         return Blake2bPure(key, hashLength) | ||||
|     } | ||||
| 
 | ||||
|     override fun hashBlake2b(message: UByteArray, key: UByteArray, hashLength: Int): UByteArray { | ||||
|         checkInitialization() | ||||
|         return Blake2bPure.digest(message, key, hashLength) | ||||
|     } | ||||
| 
 | ||||
|     override fun hashSha256Multipart(): com.ionspin.kotlin.crypto.hash.sha.Sha256 { | ||||
|         checkInitialization() | ||||
|         return Sha256Pure() | ||||
|     } | ||||
| 
 | ||||
|     override fun hashSha256(message: UByteArray): UByteArray { | ||||
|         checkInitialization() | ||||
|         return Sha256Pure.digest(inputMessage =  message) | ||||
|     } | ||||
| 
 | ||||
|     override fun hashSha512Multipart(): com.ionspin.kotlin.crypto.hash.sha.Sha512Multipart { | ||||
|         checkInitialization() | ||||
|         return Sha512Pure() | ||||
|     } | ||||
| 
 | ||||
|     override fun hashSha512(message: UByteArray): UByteArray { | ||||
|         checkInitialization() | ||||
|         return Sha512Pure.digest(inputMessage =  message) | ||||
|     } | ||||
| 
 | ||||
|     override fun deriveKey( | ||||
|         password: String, | ||||
|         salt: String?, | ||||
|         key: String, | ||||
|         associatedData: String, | ||||
|         parallelism: Int, | ||||
|         tagLength: Int, | ||||
|         memory: Int, | ||||
|         numberOfIterations: Int | ||||
|     ): ArgonResult { | ||||
|         return Argon2Pure.derive( | ||||
|             password, | ||||
|             salt, | ||||
|             key, | ||||
|             associatedData, | ||||
|             parallelism, | ||||
|             tagLength, | ||||
|             memory, | ||||
|             numberOfIterations | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun SymmetricKey.Companion.randomKey() : SymmetricKey { | ||||
|     return SymmetricKey(SRNG.getRandomBytes(32)) | ||||
| } | ||||
| 
 | ||||
| object Crypto { | ||||
| 
 | ||||
|     object Hash : HashApi { | ||||
|         override fun hash(data: UByteArray, key : UByteArray) : HashedData { | ||||
|             return HashedData(Blake2bPure.digest(data, key)) | ||||
|         } | ||||
| 
 | ||||
|         override fun multipartHash(key: UByteArray?) : com.ionspin.kotlin.crypto.hash.MultipartHash { | ||||
|             return Blake2bPure(key) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     object Encryption : EncryptionApi { | ||||
|         override fun encrypt(key: SymmetricKey, data : Encryptable<*>, associatedData : UByteArray) : EncryptedData { | ||||
|             if (key.value.size != 32) { | ||||
|                 throw RuntimeException("Invalid key size! Required 32, supplied ${key.value.size}") | ||||
|             } | ||||
|             val nonce = SRNG.getRandomBytes(24) | ||||
|             return EncryptedData(XChaCha20Poly1305Pure.encrypt(key.value, nonce, data.toEncryptableForm(), associatedData), nonce) | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         override fun <T: Encryptable<T>> decrypt(key: SymmetricKey, encryptedData : EncryptedData, associatedData: UByteArray, byteArrayDeserializer : (UByteArray) -> T) : T { | ||||
|             return byteArrayDeserializer(XChaCha20Poly1305Pure.decrypt(key.value, encryptedData.nonce, encryptedData.ciphertext, associatedData)) | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         override fun createMultipartEncryptor(key: SymmetricKey) : MultipartAuthenticatedEncryption { | ||||
|             return MultipartAuthenticatedEncryptor(key) | ||||
|         } | ||||
| 
 | ||||
|         override fun createMultipartDecryptor(key: SymmetricKey, header: MultipartEncryptionHeader) : MultipartAuthenticatedDecryption { | ||||
|             val decryptor = XChaCha20Poly1305Pure(key.value, header.nonce) | ||||
|             return MultipartAuthenticatedDecryptor(decryptor) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| class MultipartAuthenticatedEncryptor internal constructor(val key : SymmetricKey) : MultipartAuthenticatedEncryption { | ||||
|     val header = MultipartEncryptionHeader(SRNG.getRandomBytes(24)) | ||||
|     val primitive = XChaCha20Poly1305Pure(key.value, header.nonce) | ||||
|     override fun encryptPartialData(data: UByteArray, associatedData: UByteArray): EncryptedDataPart { | ||||
|         return EncryptedDataPart(primitive.streamEncrypt(data, associatedData, 0U)) | ||||
|     } | ||||
| 
 | ||||
|     override fun startEncryption(): MultipartEncryptionHeader { | ||||
|         return header.copy() | ||||
|     } | ||||
| 
 | ||||
|     override fun cleanup() { | ||||
|         primitive.cleanup() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| class MultipartAuthenticatedDecryptor internal constructor(val decryptor: XChaCha20Poly1305Pure) : MultipartAuthenticatedDecryption { | ||||
|     override fun decryptPartialData(data: EncryptedDataPart, associatedData: UByteArray): DecryptedDataPart { | ||||
|         return DecryptedDataPart(decryptor.streamDecrypt(data.data, associatedData, 0U)) | ||||
|     } | ||||
| 
 | ||||
|     override fun cleanup() { | ||||
|         decryptor.cleanup() | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,12 +0,0 @@ | ||||
| package _multiplatform_crypto | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 24-May-2020 | ||||
|  */ | ||||
| //Workaround for https://youtrack.jetbrains.com/issue/KT-36878 | ||||
| val byteArray = byteArrayOf(0) | ||||
| val byte = 0.toByte() | ||||
| val longArray = longArrayOf(0) | ||||
| val long = 0L | ||||
| @ -1,26 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 21-Sep-2019 | ||||
|  */ | ||||
| expect object SRNG { | ||||
|     fun getRandomBytes(amount : Int) : UByteArray | ||||
| } | ||||
| @ -1,50 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.authenticated | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.mac.Poly1305 | ||||
| import com.ionspin.kotlin.crypto.symmetric.ChaCha20Pure | ||||
| import com.ionspin.kotlin.crypto.util.fromLittleEndianArrayToUIntWithPosition | ||||
| import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 17-Jun-2020 | ||||
|  */ | ||||
| internal class ChaCha20Poly1305Pure { | ||||
|     companion object { | ||||
| 
 | ||||
|         fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, associatedData: UByteArray) : UByteArray { | ||||
|             val state = UIntArray(16) { | ||||
|                 when (it) { | ||||
|                     0 -> ChaCha20Pure.sigma0_32 | ||||
|                     1 -> ChaCha20Pure.sigma1_32 | ||||
|                     2 -> ChaCha20Pure.sigma2_32 | ||||
|                     3 -> ChaCha20Pure.sigma3_32 | ||||
|                     4 -> key.fromLittleEndianArrayToUIntWithPosition(0) | ||||
|                     5 -> key.fromLittleEndianArrayToUIntWithPosition(4) | ||||
|                     6 -> key.fromLittleEndianArrayToUIntWithPosition(8) | ||||
|                     7 -> key.fromLittleEndianArrayToUIntWithPosition(12) | ||||
|                     8 -> key.fromLittleEndianArrayToUIntWithPosition(16) | ||||
|                     9 -> key.fromLittleEndianArrayToUIntWithPosition(20) | ||||
|                     10 -> key.fromLittleEndianArrayToUIntWithPosition(24) | ||||
|                     11 -> key.fromLittleEndianArrayToUIntWithPosition(28) | ||||
|                     12 -> 0U | ||||
|                     13 -> nonce.fromLittleEndianArrayToUIntWithPosition(0) | ||||
|                     14 -> nonce.fromLittleEndianArrayToUIntWithPosition(4) | ||||
|                     15 -> nonce.fromLittleEndianArrayToUIntWithPosition(8) | ||||
|                     else -> 0U | ||||
|                 } | ||||
|             } | ||||
|             val oneTimeKey = ChaCha20Pure.hash(state).sliceArray(0 until 32) | ||||
|             val cipherText = ChaCha20Pure.xorWithKeystream(key, nonce, message, 1U) | ||||
|             val associatedDataPad = UByteArray(16 - associatedData.size % 16) { 0U } | ||||
|             val cipherTextPad = UByteArray(16 - cipherText.size % 16) { 0U } | ||||
|             val macData = associatedData + associatedDataPad + | ||||
|                     cipherText + cipherTextPad + | ||||
|                     associatedData.size.toULong().toLittleEndianUByteArray() + | ||||
|                     cipherText.size.toULong().toLittleEndianUByteArray() | ||||
|             val tag = Poly1305.poly1305Authenticate(oneTimeKey, macData) | ||||
|             return  cipherText + tag | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,203 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.authenticated | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.AuthenticatedEncryption | ||||
| import com.ionspin.kotlin.crypto.InvalidTagException | ||||
| import com.ionspin.kotlin.crypto.mac.Poly1305 | ||||
| import com.ionspin.kotlin.crypto.symmetric.ChaCha20Pure | ||||
| import com.ionspin.kotlin.crypto.symmetric.XChaCha20Pure | ||||
| import com.ionspin.kotlin.crypto.util.* | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 17-Jun-2020 | ||||
|  */ | ||||
| class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray) { | ||||
|     companion object : AuthenticatedEncryption { | ||||
| 
 | ||||
|         override fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, associatedData: UByteArray) : UByteArray { | ||||
|             val subKey = XChaCha20Pure.hChacha(key, nonce) | ||||
|             val authKey = | ||||
|                 ChaCha20Pure.xorWithKeystream( | ||||
|                     subKey.toLittleEndianUByteArray(), | ||||
|                     ubyteArrayOf(0U, 0U, 0U, 0U) + nonce.sliceArray(16 until 24), | ||||
|                     UByteArray(64) { 0U }, | ||||
|                     0U // If this is moved as a default parameter in encrypt, and not here (in 1.4-M2) | ||||
|                                     // js compiler dies with: e: java.lang.NullPointerException | ||||
|                     //	at org.jetbrains.kotlin.ir.backend.js.lower.ConstTransformer$visitConst$1$3.invoke(ConstLowering.kt:28) | ||||
|                     //	at org.jetbrains.kotlin.ir.backend.js.lower.ConstTransformer.lowerConst(ConstLowering.kt:38) | ||||
|                 ) | ||||
|             val cipherText = XChaCha20Pure.xorWithKeystream(key, nonce, message, 1U) | ||||
|             val associatedDataPad = UByteArray(16 - associatedData.size % 16) { 0U } | ||||
|             val cipherTextPad = UByteArray(16 - cipherText.size % 16) { 0U } | ||||
|             val macData = associatedData + associatedDataPad + | ||||
|                     cipherText + cipherTextPad + | ||||
|                     associatedData.size.toULong().toLittleEndianUByteArray() + | ||||
|                     cipherText.size.toULong().toLittleEndianUByteArray() | ||||
|             val tag = Poly1305.poly1305Authenticate(authKey, macData) | ||||
|             return cipherText + tag | ||||
|         } | ||||
| 
 | ||||
|         override fun decrypt(key: UByteArray, nonce: UByteArray, cipherText: UByteArray, associatedData: UByteArray) : UByteArray { | ||||
|             val subKey = XChaCha20Pure.hChacha(key, nonce) | ||||
|             val authKey = | ||||
|                 ChaCha20Pure.xorWithKeystream( | ||||
|                     subKey.toLittleEndianUByteArray(), | ||||
|                     ubyteArrayOf(0U, 0U, 0U, 0U) + nonce.sliceArray(16 until 24), | ||||
|                     UByteArray(64) { 0U }, | ||||
|                     0U | ||||
|                 ) | ||||
|             //2. Get the tag | ||||
|             val tag = cipherText.sliceArray(cipherText.size - 16 until cipherText.size) | ||||
|             //3. Verify tag is valid | ||||
|             val cipherTextWithoutTag = cipherText.sliceArray(0 until cipherText.size - 16) | ||||
|             val associatedDataPad = UByteArray(16 - associatedData.size % 16) { 0U } | ||||
|             val cipherTextPad = UByteArray(16 - cipherTextWithoutTag.size % 16) { 0U } | ||||
|             val macData = associatedData + associatedDataPad + | ||||
|                     cipherTextWithoutTag + cipherTextPad + | ||||
|                     associatedData.size.toULong().toLittleEndianUByteArray() + | ||||
|                     cipherTextWithoutTag.size.toULong().toLittleEndianUByteArray() | ||||
|             val calculatedTag = Poly1305.poly1305Authenticate(authKey, macData) | ||||
|             if (!calculatedTag.contentEquals(tag)) { | ||||
|                 throw InvalidTagException() | ||||
|             } | ||||
|             //4. Decrypt data | ||||
|             return XChaCha20Pure.xorWithKeystream(key, nonce, cipherTextWithoutTag, 1U) | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private val polyBuffer = UByteArray(16) | ||||
|     private var polyBufferByteCounter = 0 | ||||
| 
 | ||||
|     private var processedBytes = 0 | ||||
| 
 | ||||
|     internal val calcKey : UByteArray = UByteArray(32) | ||||
|     internal val calcNonce : UByteArray = UByteArray(12) | ||||
| 
 | ||||
|     init { | ||||
|         val calc = XChaCha20Pure.hChacha(key, nonce).toLittleEndianUByteArray() | ||||
|         calc.sliceArray(0 until 32).copyInto(calcKey) | ||||
|         nonce.sliceArray(16 until 24).copyInto(calcNonce, 4) | ||||
|         calcNonce[0] = 1U | ||||
|         calcNonce[1] = 0U | ||||
|         calcNonce[2] = 0U | ||||
|         calcNonce[3] = 0U | ||||
|     } | ||||
| 
 | ||||
|     fun streamEncrypt(data: UByteArray, associatedData: UByteArray, tag : UByte) : UByteArray { | ||||
|         //get encryption state | ||||
|         val block = UByteArray(64) { 0U } | ||||
|         ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 0U).copyInto(block) // This is equivalent to the first 64 bytes of keystream | ||||
|         val poly1305 = Poly1305(block) | ||||
|         block.overwriteWithZeroes() | ||||
|         if (associatedData.isNotEmpty()) { | ||||
|             val associatedDataPadded = associatedData + UByteArray(16 - associatedData.size % 16) { 0U } | ||||
|             processPolyBytes(poly1305, associatedDataPadded) | ||||
|         } | ||||
|         block[0] = tag | ||||
|         ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 1U).copyInto(block) // This just xors block[0] with keystream | ||||
|         processPolyBytes(poly1305, block) // but updates the mac with the full block! | ||||
|         // In libsodium c code, it now sets the first byte to be a tag, we'll just save it for now | ||||
|         val encryptedTag = block[0] | ||||
|         //And then encrypt the rest of the message | ||||
|         val ciphertext = ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, data, 2U) // With appropriate counter | ||||
|         // Next we update the poly1305 with ciphertext and padding, BUT the padding in libsodium is not correctly calculated, so it doesn't | ||||
|         // pad correctly. https://github.com/jedisct1/libsodium/issues/976 | ||||
|         // We want to use libsodium in delegated flavour, so we will use the same incorrect padding here. | ||||
|         // From security standpoint there are no obvious drawbacks, as padding was initially added to decrease implementation complexity. | ||||
|         processPolyBytes(poly1305, ciphertext + UByteArray(((16U + data.size.toUInt() - block.size.toUInt()) % 16U).toInt()) { 0U } ) //TODO this is inefficient as it creates a new array and copies data | ||||
|         // Last 16byte block containing actual additional data nad ciphertext sizes | ||||
|         val finalMac = associatedData.size.toULong().toLittleEndianUByteArray() + (ciphertext.size + 64).toULong().toLittleEndianUByteArray() | ||||
|         processPolyBytes(poly1305, finalMac) | ||||
|         val mac = poly1305.finalizeMac(polyBuffer.sliceArray(0 until polyBufferByteCounter)) | ||||
|         calcNonce.xorWithPositionsAndInsertIntoArray(0, 12, mac, 0, calcNonce, 0) | ||||
|         return ubyteArrayOf(encryptedTag) + ciphertext + mac | ||||
|     } | ||||
| 
 | ||||
|     fun streamDecrypt(data: UByteArray, associatedData: UByteArray, tag: UByte) : UByteArray { | ||||
|         val block = UByteArray(64) { 0U } | ||||
|         ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 0U).copyInto(block) // This is equivalent to the first 64 bytes of keystream | ||||
|         val poly1305 = Poly1305(block) | ||||
|         block.overwriteWithZeroes() | ||||
|         if (associatedData.isNotEmpty()) { | ||||
|             val associatedDataPadded = associatedData + UByteArray(16 - associatedData.size % 16) { 0U } | ||||
|             processPolyBytes(poly1305, associatedDataPadded) | ||||
|         } | ||||
|         block[0] = data[0] | ||||
|          ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 1U).copyInto(block)// get the keystream xored with zeroes, but also decrypteg tag marker | ||||
|         val tag = block[0] //get the decrypted tag | ||||
|         block[0] = data[0] // this brings it back to state that is delivered to poly in encryption function | ||||
|         processPolyBytes(poly1305, block) | ||||
|         // Next we update the poly1305 with ciphertext and padding, BUT the padding in libsodium is not correctly calculated, so it doesn't | ||||
|         // pad correctly. https://github.com/jedisct1/libsodium/issues/976 | ||||
|         // We want to use libsodium in delegated flavour, so we will use the same incorrect padding here. | ||||
|         // From security standpoint there are no obvious drawbacks, as padding was initially added to decrease implementation complexity. | ||||
|         val ciphertext = data.sliceArray(1 until data.size - 16) | ||||
|         processPolyBytes(poly1305, ciphertext + UByteArray(((16U + ciphertext.size.toUInt() - block.size.toUInt()) % 16U).toInt()) { 0U } ) | ||||
|         val plaintext = ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, ciphertext, 2U) | ||||
|         val finalMac = associatedData.size.toULong().toLittleEndianUByteArray() + (ciphertext.size + 64).toULong().toLittleEndianUByteArray() | ||||
|         processPolyBytes(poly1305, finalMac) | ||||
|         val mac = poly1305.finalizeMac(polyBuffer.sliceArray(0 until polyBufferByteCounter)) | ||||
|         val expectedMac = data.sliceArray(data.size - 16 until data.size) | ||||
| 
 | ||||
|         if (expectedMac.contentEquals(mac).not()){ | ||||
|             throw InvalidTagException() | ||||
|         } | ||||
|         calcNonce.xorWithPositionsAndInsertIntoArray(0, 12, mac, 0, calcNonce, 0) | ||||
|         return plaintext | ||||
|     } | ||||
| 
 | ||||
|     fun cleanup() { | ||||
|         key.overwriteWithZeroes() | ||||
|         nonce.overwriteWithZeroes() | ||||
|         calcKey.overwriteWithZeroes() | ||||
|         calcNonce.overwriteWithZeroes() | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     private fun processPolyBytes(updateableMacPrimitive: Poly1305, data: UByteArray) { | ||||
|         if (polyBufferByteCounter == 0) { | ||||
|             val polyBlocks = data.size / 16 | ||||
|             val polyRemainder = data.size % 16 | ||||
|             for (i in 0 until polyBlocks) { | ||||
|                 updateableMacPrimitive.updateMac(data.sliceArray(i * 16 until i * 16 + 16)) | ||||
|             } | ||||
|             if (polyRemainder != 0) { | ||||
|                 for (i in 0 until polyRemainder) { | ||||
|                     polyBuffer[i] = data[data.size - polyRemainder + i] | ||||
|                 } | ||||
|                 polyBufferByteCounter = polyRemainder | ||||
|             } | ||||
|         } else { | ||||
|             if (polyBufferByteCounter + data.size < 16) { | ||||
|                 for (i in 0 until data.size) { | ||||
|                     polyBuffer[polyBufferByteCounter + i] = data[i] | ||||
|                 } | ||||
|                 polyBufferByteCounter += data.size | ||||
|             } else { | ||||
|                 val borrowed = 16 - polyBufferByteCounter | ||||
|                 for (i in polyBufferByteCounter until 16) { | ||||
|                     polyBuffer[i] = data[i - polyBufferByteCounter] | ||||
|                 } | ||||
|                 updateableMacPrimitive.updateMac(polyBuffer) | ||||
|                 polyBufferByteCounter = 0 | ||||
|                 val polyBlocks = (data.size - borrowed) / 16 | ||||
|                 val polyRemainder = (data.size - borrowed) % 16 | ||||
|                 for (i in 0 until polyBlocks) { | ||||
|                     updateableMacPrimitive.updateMac(data.sliceArray(borrowed + i * 16 until borrowed + i * 16 + 16)) | ||||
|                 } | ||||
|                 if (polyRemainder != 0) { | ||||
|                     for (i in 0 until polyRemainder) { | ||||
|                         polyBuffer[i] = data[borrowed + i] | ||||
|                     } | ||||
|                     polyBufferByteCounter = polyRemainder | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,351 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.hash.blake2b | ||||
| 
 | ||||
| import com.ionspin.kotlin.bignum.integer.BigInteger | ||||
| import com.ionspin.kotlin.bignum.integer.toBigInteger | ||||
| import com.ionspin.kotlin.crypto.* | ||||
| import com.ionspin.kotlin.crypto.hash.encodeToUByteArray | ||||
| import com.ionspin.kotlin.crypto.util.rotateRight | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 14-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| class Blake2bPure(val key: UByteArray? = null, val hashLength: Int = 64) : Blake2bMultipart { | ||||
| 
 | ||||
|     companion object : Blake2b { | ||||
|         //Hack start | ||||
|         //If this line is not included konanc 1.4-M1 fails to link because it cant find ByteArray which is | ||||
|         //a backing class for UByteArray | ||||
|         val byteArray: ByteArray = byteArrayOf(0, 1) | ||||
|         //Hack end | ||||
|         const val BITS_IN_WORD = 64 | ||||
|         const val ROUNDS_IN_COMPRESS = 12 | ||||
|         const val BLOCK_BYTES = 128 | ||||
|         override val MAX_HASH_BYTES = 64 | ||||
|         const val MIN_HASH_BYTES = 1 | ||||
|         const val MAX_KEY_BYTES = 64 | ||||
|         const val MIN_KEY_BYTES = 0 | ||||
|         val MAX_INPUT_BYTES = 2.toBigInteger() shl 128 | ||||
| 
 | ||||
|         private val sigma = arrayOf( | ||||
|             arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), | ||||
|             arrayOf(14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3), | ||||
|             arrayOf(11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4), | ||||
|             arrayOf(7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8), | ||||
|             arrayOf(9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13), | ||||
|             arrayOf(2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9), | ||||
|             arrayOf(12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11), | ||||
|             arrayOf(13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10), | ||||
|             arrayOf(6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5), | ||||
|             arrayOf(10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0) | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
|         private val iv = arrayOf( | ||||
|             0X6A09E667F3BCC908UL, | ||||
|             0XBB67AE8584CAA73BUL, | ||||
|             0X3C6EF372FE94F82BUL, | ||||
|             0XA54FF53A5F1D36F1UL, | ||||
|             0X510E527FADE682D1UL, | ||||
|             0X9B05688C2B3E6C1FUL, | ||||
|             0X1F83D9ABFB41BD6BUL, | ||||
|             0X5BE0CD19137E2179UL | ||||
|         ) | ||||
| 
 | ||||
|         const val R1 = 32 | ||||
|         const val R2 = 24 | ||||
|         const val R3 = 16 | ||||
|         const val R4 = 63 | ||||
| 
 | ||||
|         internal fun mixRound(input: Array<ULong>, message: Array<ULong>, round: Int): Array<ULong> { | ||||
|             var v = input | ||||
|             val selectedSigma = sigma[round % 10] | ||||
|             v = mix(v, 0, 4, 8, 12, message[selectedSigma[0]], message[selectedSigma[1]]) | ||||
|             v = mix(v, 1, 5, 9, 13, message[selectedSigma[2]], message[selectedSigma[3]]) | ||||
|             v = mix(v, 2, 6, 10, 14, message[selectedSigma[4]], message[selectedSigma[5]]) | ||||
|             v = mix(v, 3, 7, 11, 15, message[selectedSigma[6]], message[selectedSigma[7]]) | ||||
|             v = mix(v, 0, 5, 10, 15, message[selectedSigma[8]], message[selectedSigma[9]]) | ||||
|             v = mix(v, 1, 6, 11, 12, message[selectedSigma[10]], message[selectedSigma[11]]) | ||||
|             v = mix(v, 2, 7, 8, 13, message[selectedSigma[12]], message[selectedSigma[13]]) | ||||
|             v = mix(v, 3, 4, 9, 14, message[selectedSigma[14]], message[selectedSigma[15]]) | ||||
|             return v | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         private fun mix(v: Array<ULong>, a: Int, b: Int, c: Int, d: Int, x: ULong, y: ULong): Array<ULong> { | ||||
|             v[a] = (v[a] + v[b] + x) | ||||
|             v[d] = (v[d] xor v[a]) rotateRight R1 | ||||
|             v[c] = (v[c] + v[d]) | ||||
|             v[b] = (v[b] xor v[c]) rotateRight R2 | ||||
|             v[a] = (v[a] + v[b] + y) | ||||
|             v[d] = (v[d] xor v[a]) rotateRight R3 | ||||
|             v[c] = (v[c] + v[d]) | ||||
|             v[b] = (v[b] xor v[c]) rotateRight R4 | ||||
|             return v | ||||
|         } | ||||
| 
 | ||||
|         fun compress( | ||||
|             h: Array<ULong>, | ||||
|             input: UByteArray, | ||||
|             offsetCounter: BigInteger, | ||||
|             finalBlock: Boolean | ||||
|         ): Array<ULong> { | ||||
|             var v = Array(16) { | ||||
|                 when (it) { | ||||
|                     in 0..7 -> h[it] | ||||
|                     else -> iv[it - 8] | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             val m = input.foldIndexed(Array(16) { 0UL }) { index, acc, byte -> | ||||
|                 val slot = index / 8 | ||||
|                 val position = index % 8 | ||||
|                 acc[slot] = acc[slot] + (byte.toULong() shl ((position) * 8)) | ||||
|                 acc | ||||
|             } | ||||
|             if (Config.DEBUG) { | ||||
|                 val printout = m.map { it.toString(16) }.chunked(4) | ||||
|                 printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) } | ||||
|                 println("Offset ${offsetCounter}") | ||||
|             } | ||||
| 
 | ||||
|             v[12] = v[12] xor offsetCounter.ulongValue() | ||||
|             v[13] = v[13] xor (offsetCounter shr BITS_IN_WORD).ulongValue() | ||||
| 
 | ||||
|             if (finalBlock) { | ||||
|                 v[14] = v[14].inv() | ||||
|             } | ||||
| 
 | ||||
|             for (i in 0 until ROUNDS_IN_COMPRESS) { | ||||
|                 mixRound(v, m, i) | ||||
|             } | ||||
| 
 | ||||
|             for (i in 0..7) { | ||||
|                 h[i] = h[i] xor v[i] xor v[i + 8] | ||||
|             } | ||||
|             return h | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         fun digest(inputString: String, key: String?, hashLength: Int): UByteArray { | ||||
|             val array = inputString.encodeToUByteArray() | ||||
|             val keyBytes = key?.run { | ||||
|                 encodeToUByteArray() | ||||
|             } ?: ubyteArrayOf() | ||||
|             return digest(inputMessage = array, key = keyBytes, hashLength = hashLength) | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         override fun digest( | ||||
|             inputMessage: UByteArray, | ||||
|             key: UByteArray, | ||||
|             hashLength: Int | ||||
|         ): UByteArray { | ||||
|             if (hashLength > MAX_HASH_BYTES) { | ||||
|                 throw RuntimeException("Invalid hash length. Requested length more than maximum length. Requested length $hashLength") | ||||
|             } | ||||
|             val chunkedMessage = inputMessage.chunked(BLOCK_BYTES).map { it.toUByteArray() }.toTypedArray() | ||||
| 
 | ||||
|             val h = iv.copyOf() | ||||
| 
 | ||||
|             h[0] = h[0] xor 0x01010000UL xor (key.size.toULong() shl 8) xor hashLength.toULong() | ||||
| 
 | ||||
| 
 | ||||
|             val message = if (key.isEmpty()) { | ||||
|                 if (chunkedMessage.isEmpty()) { | ||||
|                     Array(1) { | ||||
|                         UByteArray(128) { | ||||
|                             0U | ||||
|                         } | ||||
|                     } | ||||
|                 } else { | ||||
|                     chunkedMessage | ||||
|                 } | ||||
|             } else { | ||||
|                 arrayOf(padToBlock(key), *chunkedMessage) | ||||
|             } | ||||
| 
 | ||||
|             if (message.size > 1) { | ||||
|                 for (i in 0 until message.size - 1) { | ||||
|                     compress(h, message[i], ((i + 1) * BLOCK_BYTES).toBigInteger(), false).copyInto(h) | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             val lastSize = when (message.size) { | ||||
|                 0 -> 0 | ||||
|                 1 -> message[message.size - 1].size | ||||
|                 else -> (message.size - 1) * BLOCK_BYTES + message[message.size - 1].size | ||||
| 
 | ||||
|             } | ||||
| 
 | ||||
|             val lastBlockPadded = if (message.isNotEmpty()) { | ||||
|                 padToBlock(message[message.size - 1]) | ||||
|             } else { | ||||
|                 UByteArray(16) { 0U } | ||||
|             } | ||||
| 
 | ||||
|             compress(h, lastBlockPadded, lastSize.toBigInteger(), true).copyInto(h) | ||||
| 
 | ||||
| 
 | ||||
|             return formatResult(h).copyOfRange(0, hashLength) | ||||
|         } | ||||
| 
 | ||||
|         private fun formatResult(h: Array<ULong>): UByteArray { | ||||
|             return h.map { | ||||
|                 arrayOf( | ||||
|                     (it and 0xFFUL).toUByte(), | ||||
|                     (it shr 8 and 0xFFUL).toUByte(), | ||||
|                     (it shr 16 and 0xFFUL).toUByte(), | ||||
|                     (it shr 24 and 0xFFUL).toUByte(), | ||||
|                     (it shr 32 and 0xFFUL).toUByte(), | ||||
|                     (it shr 40 and 0xFFUL).toUByte(), | ||||
|                     (it shr 48 and 0xFFUL).toUByte(), | ||||
|                     (it shr 56 and 0xFFUL).toUByte() | ||||
|                 ) | ||||
|             }.flatMap { | ||||
|                 it.toList() | ||||
|             }.toUByteArray() | ||||
|         } | ||||
| 
 | ||||
|         private fun padToBlock(unpadded: UByteArray): UByteArray { | ||||
|             if (unpadded.size == BLOCK_BYTES) { | ||||
|                 return unpadded | ||||
|             } | ||||
| 
 | ||||
|             if (unpadded.size > BLOCK_BYTES) { | ||||
|                 throw IllegalStateException("Block larger than 128 bytes") | ||||
|             } | ||||
| 
 | ||||
|             return UByteArray(BLOCK_BYTES) { | ||||
|                 when (it) { | ||||
|                     in 0 until unpadded.size -> unpadded[it] | ||||
|                     else -> 0U | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     constructor( | ||||
|         key: String?, | ||||
|         requestedHashLenght: Int = 64 | ||||
|     ) : this( | ||||
|         (key?.encodeToUByteArray() ?: ubyteArrayOf()), | ||||
|         requestedHashLenght | ||||
|     ) | ||||
| 
 | ||||
|     override val MAX_HASH_BYTES: Int = Blake2bPure.MAX_HASH_BYTES | ||||
| 
 | ||||
|     var h = iv.copyOf() | ||||
|     var counter = BigInteger.ZERO | ||||
|     var bufferCounter = 0 | ||||
|     var buffer = UByteArray(BLOCK_BYTES) { 0U } | ||||
| 
 | ||||
| 
 | ||||
|     init { | ||||
|         h[0] = h[0] xor 0x01010000UL xor (key?.run { size.toULong() shl 8 } ?: 0UL) xor hashLength.toULong() | ||||
| 
 | ||||
|         if (!key.isNullOrEmpty()) { | ||||
|             appendToBuffer(padToBlock(key), bufferCounter) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun update(data: UByteArray) { | ||||
|         if (data.isEmpty()) { | ||||
|             throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating") | ||||
|         } | ||||
| 
 | ||||
|         when { | ||||
|             bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter) | ||||
|             bufferCounter + data.size >= BLOCK_BYTES -> { | ||||
|                 val chunked = data.chunked(BLOCK_BYTES).map { it.toUByteArray() } | ||||
|                 chunked.forEach { chunk -> | ||||
|                     if (bufferCounter + chunk.size < BLOCK_BYTES) { | ||||
|                         appendToBuffer(chunk, bufferCounter) | ||||
|                     } else { | ||||
|                         chunk.copyInto( | ||||
|                             destination = buffer, | ||||
|                             destinationOffset = bufferCounter, | ||||
|                             startIndex = 0, | ||||
|                             endIndex = BLOCK_BYTES - bufferCounter | ||||
|                         ) | ||||
|                         counter += BLOCK_BYTES | ||||
|                         consumeBlock(buffer) | ||||
|                         buffer = UByteArray(BLOCK_BYTES) { | ||||
|                             when (it) { | ||||
|                                 in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> { | ||||
|                                     chunk[it + (BLOCK_BYTES - bufferCounter)] | ||||
|                                 } | ||||
|                                 else -> { | ||||
|                                     0U | ||||
|                                 } | ||||
|                             } | ||||
| 
 | ||||
|                         } | ||||
|                         bufferCounter = chunk.size - (BLOCK_BYTES - bufferCounter) | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     fun update(data: String) { | ||||
|         update(data.encodeToUByteArray()) | ||||
|     } | ||||
| 
 | ||||
|     private fun appendToBuffer(array: UByteArray, start: Int) { | ||||
|         array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size) | ||||
|         bufferCounter += array.size | ||||
|     } | ||||
| 
 | ||||
|     private fun consumeBlock(block: UByteArray) { | ||||
|         h = compress(h, block, counter, false) | ||||
|     } | ||||
| 
 | ||||
|     override fun digest(): UByteArray { | ||||
|         val lastBlockPadded = padToBlock(buffer) | ||||
|         counter += bufferCounter | ||||
|         compress(h, lastBlockPadded, counter, true) | ||||
| 
 | ||||
|         val result = formatResult(h) | ||||
|         reset() | ||||
|         return result | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private fun reset() { | ||||
|         h = iv.copyOf() | ||||
|         counter = BigInteger.ZERO | ||||
|         bufferCounter = 0 | ||||
|         buffer = UByteArray(BLOCK_BYTES) { 0U } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @ -1,328 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.hash.sha | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.hash.encodeToUByteArray | ||||
| import com.ionspin.kotlin.crypto.util.rotateRight | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 17-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class Sha256Pure : Sha256 { | ||||
| 
 | ||||
|     override val MAX_HASH_BYTES: Int = 32 | ||||
| 
 | ||||
| 
 | ||||
|     companion object : StatelessSha256 { | ||||
|         const val BLOCK_SIZE = 512 | ||||
|         const val BLOCK_SIZE_IN_BYTES = 64 | ||||
|         const val UINT_MASK = 0xFFFFFFFFU | ||||
|         const val BYTE_MASK_FROM_ULONG = 0xFFUL | ||||
|         const val BYTE_MASK_FROM_UINT = 0xFFU | ||||
| 
 | ||||
|         override val MAX_HASH_BYTES: Int = 32 | ||||
| 
 | ||||
|         val iv = arrayOf( | ||||
|             0x6a09e667U, | ||||
|             0xbb67ae85U, | ||||
|             0x3c6ef372U, | ||||
|             0xa54ff53aU, | ||||
|             0x510e527fU, | ||||
|             0x9b05688cU, | ||||
|             0x1f83d9abU, | ||||
|             0x5be0cd19U | ||||
|         ) | ||||
| 
 | ||||
|         val k = arrayOf( | ||||
|             0x428a2f98U, 0x71374491U, 0xb5c0fbcfU, 0xe9b5dba5U, 0x3956c25bU, 0x59f111f1U, 0x923f82a4U, 0xab1c5ed5U, | ||||
|             0xd807aa98U, 0x12835b01U, 0x243185beU, 0x550c7dc3U, 0x72be5d74U, 0x80deb1feU, 0x9bdc06a7U, 0xc19bf174U, | ||||
|             0xe49b69c1U, 0xefbe4786U, 0x0fc19dc6U, 0x240ca1ccU, 0x2de92c6fU, 0x4a7484aaU, 0x5cb0a9dcU, 0x76f988daU, | ||||
|             0x983e5152U, 0xa831c66dU, 0xb00327c8U, 0xbf597fc7U, 0xc6e00bf3U, 0xd5a79147U, 0x06ca6351U, 0x14292967U, | ||||
|             0x27b70a85U, 0x2e1b2138U, 0x4d2c6dfcU, 0x53380d13U, 0x650a7354U, 0x766a0abbU, 0x81c2c92eU, 0x92722c85U, | ||||
|             0xa2bfe8a1U, 0xa81a664bU, 0xc24b8b70U, 0xc76c51a3U, 0xd192e819U, 0xd6990624U, 0xf40e3585U, 0x106aa070U, | ||||
|             0x19a4c116U, 0x1e376c08U, 0x2748774cU, 0x34b0bcb5U, 0x391c0cb3U, 0x4ed8aa4aU, 0x5b9cca4fU, 0x682e6ff3U, | ||||
|             0x748f82eeU, 0x78a5636fU, 0x84c87814U, 0x8cc70208U, 0x90befffaU, 0xa4506cebU, 0xbef9a3f7U, 0xc67178f2U | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
|         override fun digest(inputMessage: UByteArray): UByteArray { | ||||
| 
 | ||||
|             var h = iv.copyOf() | ||||
| 
 | ||||
|             val expansionArray = createExpansionArray(inputMessage.size.toLong()) | ||||
| 
 | ||||
|             val chunks = ( | ||||
|                     inputMessage + | ||||
|                             expansionArray + | ||||
|                             (inputMessage.size * 8).toULong().toPaddedByteArray() | ||||
|                     ) | ||||
|                 .chunked(BLOCK_SIZE_IN_BYTES) | ||||
| 
 | ||||
|             chunks.forEach { chunk -> | ||||
|                 val w = expandChunk(chunk.toUByteArray()) | ||||
|                 mix(h, w).copyInto(h) | ||||
| 
 | ||||
|             } | ||||
| 
 | ||||
|             val digest = h[0].toPaddedByteArray() + | ||||
|                     h[1].toPaddedByteArray() + | ||||
|                     h[2].toPaddedByteArray() + | ||||
|                     h[3].toPaddedByteArray() + | ||||
|                     h[4].toPaddedByteArray() + | ||||
|                     h[5].toPaddedByteArray() + | ||||
|                     h[6].toPaddedByteArray() + | ||||
|                     h[7].toPaddedByteArray() | ||||
|             return digest | ||||
|         } | ||||
| 
 | ||||
|         private fun scheduleSigma0(value: UInt): UInt { | ||||
|             return value.rotateRight(7) xor value.rotateRight(18) xor (value shr 3) | ||||
|         } | ||||
| 
 | ||||
|         private fun scheduleSigma1(value: UInt): UInt { | ||||
|             return value.rotateRight(17) xor value.rotateRight(19) xor (value shr 10) | ||||
|         } | ||||
| 
 | ||||
|         private fun compressionSigma0(a: UInt): UInt { | ||||
|             return (a rotateRight 2) xor (a rotateRight 13) xor (a rotateRight 22) | ||||
|         } | ||||
| 
 | ||||
|         private fun compressionSigma1(e: UInt): UInt { | ||||
|             return (e rotateRight 6) xor (e rotateRight 11) xor (e rotateRight 25) | ||||
|         } | ||||
| 
 | ||||
|         private fun ch(x: UInt, y: UInt, z: UInt): UInt { | ||||
|             return ((x and y) xor ((x xor UINT_MASK) and z)) | ||||
|         } | ||||
| 
 | ||||
|         private fun maj(x: UInt, y: UInt, z: UInt): UInt { | ||||
|             return (((x and y) xor (x and z) xor (y and z))) | ||||
|         } | ||||
| 
 | ||||
|         private fun expandChunk(chunk: UByteArray): Array<UInt> { | ||||
|             val w = Array<UInt>(BLOCK_SIZE_IN_BYTES) { | ||||
|                 when (it) { | ||||
|                     in 0 until 16 -> { | ||||
|                         var collected = (chunk[(it * 4)].toUInt() shl 24) + | ||||
|                                 (chunk[(it * 4) + 1].toUInt() shl 16) + | ||||
|                                 (chunk[(it * 4) + 2].toUInt() shl 8) + | ||||
|                                 (chunk[(it * 4) + 3].toUInt()) | ||||
|                         collected | ||||
|                     } | ||||
|                     else -> 0U | ||||
|                 } | ||||
|             } | ||||
|             for (i in 16 until BLOCK_SIZE_IN_BYTES) { | ||||
|                 val s0 = scheduleSigma0(w[i - 15]) | ||||
|                 val s1 = scheduleSigma1(w[i - 2]) | ||||
|                 w[i] = w[i - 16] + s0 + w[i - 7] + s1 | ||||
|             } | ||||
|             return w | ||||
|         } | ||||
| 
 | ||||
|         private fun mix(h: Array<UInt>, w: Array<UInt>): Array<UInt> { | ||||
|             var paramA = h[0] | ||||
|             var paramB = h[1] | ||||
|             var paramC = h[2] | ||||
|             var paramD = h[3] | ||||
|             var paramE = h[4] | ||||
|             var paramF = h[5] | ||||
|             var paramG = h[6] | ||||
|             var paramH = h[7] | ||||
| 
 | ||||
|             for (i in 0 until BLOCK_SIZE_IN_BYTES) { | ||||
|                 val s1 = compressionSigma1(paramE) | ||||
|                 val ch = ch(paramE, paramF, paramG) | ||||
|                 val temp1 = paramH + s1 + ch + k[i] + w[i] | ||||
|                 val s0 = compressionSigma0(paramA) | ||||
|                 val maj = maj(paramA, paramB, paramC) | ||||
|                 val temp2 = s0 + maj | ||||
|                 paramH = paramG | ||||
|                 paramG = paramF | ||||
|                 paramF = paramE | ||||
|                 paramE = paramD + temp1 | ||||
|                 paramD = paramC | ||||
|                 paramC = paramB | ||||
|                 paramB = paramA | ||||
|                 paramA = temp1 + temp2 | ||||
|             } | ||||
| 
 | ||||
|             h[0] += paramA | ||||
|             h[1] += paramB | ||||
|             h[2] += paramC | ||||
|             h[3] += paramD | ||||
|             h[4] += paramE | ||||
|             h[5] += paramF | ||||
|             h[6] += paramG | ||||
|             h[7] += paramH | ||||
|             return h | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         fun createExpansionArray(originalSizeInBytes: Long): UByteArray { | ||||
|             val originalMessageSizeInBits = originalSizeInBytes * 8 | ||||
| 
 | ||||
| 
 | ||||
|             //K such that L + 1 + K + 64 is a multiple of 512 | ||||
|             val expandedRemainderOf512 = (originalMessageSizeInBits + BLOCK_SIZE_IN_BYTES + 1) % BLOCK_SIZE | ||||
|             val zeroAddAmount = when (expandedRemainderOf512) { | ||||
|                 0L -> 0 | ||||
|                 else -> ((BLOCK_SIZE - expandedRemainderOf512) / 8).toInt() | ||||
|             } | ||||
|             val expansionArray = UByteArray(zeroAddAmount + 1) { | ||||
|                 when (it) { | ||||
|                     0 -> 0b10000000U | ||||
|                     else -> 0U | ||||
|                 } | ||||
|             } | ||||
|             return expansionArray | ||||
|         } | ||||
| 
 | ||||
|         private fun ULong.toPaddedByteArray(): UByteArray { | ||||
|             val byteMask = BYTE_MASK_FROM_ULONG | ||||
|             return UByteArray(8) { | ||||
|                 when (it) { | ||||
|                     7 -> (this and byteMask).toUByte() | ||||
|                     6 -> ((this shr 8) and byteMask).toUByte() | ||||
|                     5 -> ((this shr 16) and byteMask).toUByte() | ||||
|                     4 -> ((this shr 24) and byteMask).toUByte() | ||||
|                     3 -> ((this shr 32) and byteMask).toUByte() | ||||
|                     2 -> ((this shr 40) and byteMask).toUByte() | ||||
|                     1 -> ((this shr 48) and byteMask).toUByte() | ||||
|                     0 -> ((this shr 54) and byteMask).toUByte() | ||||
|                     else -> throw RuntimeException("Invalid conversion") | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private fun UInt.toPaddedByteArray(): UByteArray { | ||||
|             val byteMask = BYTE_MASK_FROM_UINT | ||||
|             return UByteArray(4) { | ||||
|                 when (it) { | ||||
|                     3 -> (this and byteMask).toUByte() | ||||
|                     2 -> ((this shr 8) and byteMask).toUByte() | ||||
|                     1 -> ((this shr 16) and byteMask).toUByte() | ||||
|                     0 -> ((this shr 24) and byteMask).toUByte() | ||||
|                     else -> throw RuntimeException("Invalid conversion") | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     var h = iv.copyOf() | ||||
|     var counter = 0L | ||||
|     var bufferCounter = 0 | ||||
|     var buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { 0U } | ||||
|     var digested = false | ||||
| 
 | ||||
| 
 | ||||
|     fun update(data: String) { | ||||
|         if (digested) { | ||||
|             throw RuntimeException("This instance of updateable SHA256 was already finished once. You should use new instance") | ||||
|         } | ||||
|         return update(data.encodeToUByteArray()) | ||||
|     } | ||||
| 
 | ||||
|     override fun update(data: UByteArray) { | ||||
|         if (data.isEmpty()) { | ||||
|             throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating") | ||||
|         } | ||||
| 
 | ||||
|         when { | ||||
|             bufferCounter + data.size < BLOCK_SIZE_IN_BYTES -> appendToBuffer(data, bufferCounter) | ||||
|             bufferCounter + data.size == BLOCK_SIZE_IN_BYTES -> { | ||||
|                 counter += BLOCK_SIZE_IN_BYTES | ||||
|                 consumeBlock(data) | ||||
|             } | ||||
|                 bufferCounter + data.size == BLOCK_SIZE_IN_BYTES -> { | ||||
|                 counter += BLOCK_SIZE_IN_BYTES | ||||
|                 consumeBlock(data) | ||||
|             } | ||||
|             bufferCounter + data.size >= BLOCK_SIZE_IN_BYTES -> { | ||||
|                 val chunked = data.chunked(BLOCK_SIZE_IN_BYTES) | ||||
|                 chunked.forEach { chunk -> | ||||
|                     if (bufferCounter + chunk.size < BLOCK_SIZE_IN_BYTES) { | ||||
|                         appendToBuffer(chunk.toUByteArray(), bufferCounter) | ||||
|                     } else { | ||||
|                         chunk.toUByteArray().copyInto( | ||||
|                             destination = buffer, | ||||
|                             destinationOffset = bufferCounter, | ||||
|                             startIndex = 0, | ||||
|                             endIndex = BLOCK_SIZE_IN_BYTES - bufferCounter | ||||
|                         ) | ||||
|                         counter += BLOCK_SIZE_IN_BYTES | ||||
|                         consumeBlock(buffer) | ||||
|                         buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { | ||||
|                             when (it) { | ||||
|                                 in (0 until (chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter))) -> { | ||||
|                                     chunk[it + (BLOCK_SIZE_IN_BYTES - bufferCounter)] | ||||
|                                 } | ||||
|                                 else -> { | ||||
|                                     0U | ||||
|                                 } | ||||
|                             } | ||||
| 
 | ||||
|                         } | ||||
|                         bufferCounter = chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter) | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun consumeBlock(block: UByteArray) { | ||||
|         val w = expandChunk(block) | ||||
|         mix(h, w).copyInto(h) | ||||
|     } | ||||
| 
 | ||||
|     override fun digest(): UByteArray { | ||||
|         val length = counter + bufferCounter | ||||
|         val expansionArray = createExpansionArray(length) | ||||
|         val finalBlock = | ||||
|             buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPaddedByteArray() | ||||
|         finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach { | ||||
|             consumeBlock(it.toUByteArray()) | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         val digest = h[0].toPaddedByteArray() + | ||||
|                 h[1].toPaddedByteArray() + | ||||
|                 h[2].toPaddedByteArray() + | ||||
|                 h[3].toPaddedByteArray() + | ||||
|                 h[4].toPaddedByteArray() + | ||||
|                 h[5].toPaddedByteArray() + | ||||
|                 h[6].toPaddedByteArray() + | ||||
|                 h[7].toPaddedByteArray() | ||||
|         digested = true | ||||
|         return digest | ||||
|     } | ||||
| 
 | ||||
|     private fun appendToBuffer(array: UByteArray, start: Int) { | ||||
|         array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size) | ||||
|         bufferCounter += array.size | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,394 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.hash.sha | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.hash.encodeToUByteArray | ||||
| import com.ionspin.kotlin.crypto.util.rotateRight | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 18-Jul-2019 | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| class Sha512Pure : Sha512Multipart { | ||||
| 
 | ||||
|     override val MAX_HASH_BYTES: Int = 32 | ||||
| 
 | ||||
|     companion object : Sha512 { | ||||
|         const val BLOCK_SIZE = 1024 | ||||
|         const val BLOCK_SIZE_IN_BYTES = 128 | ||||
|         const val CHUNK_SIZE = 80 | ||||
|         const val ULONG_MASK = 0xFFFFFFFFFFFFFFFFUL | ||||
| 
 | ||||
|         override val MAX_HASH_BYTES: Int = 32 | ||||
| 
 | ||||
|         val k = arrayOf( | ||||
|             0x428a2f98d728ae22UL, | ||||
|             0x7137449123ef65cdUL, | ||||
|             0xb5c0fbcfec4d3b2fUL, | ||||
|             0xe9b5dba58189dbbcUL, | ||||
|             0x3956c25bf348b538UL, | ||||
|             0x59f111f1b605d019UL, | ||||
|             0x923f82a4af194f9bUL, | ||||
|             0xab1c5ed5da6d8118UL, | ||||
|             0xd807aa98a3030242UL, | ||||
|             0x12835b0145706fbeUL, | ||||
|             0x243185be4ee4b28cUL, | ||||
|             0x550c7dc3d5ffb4e2UL, | ||||
|             0x72be5d74f27b896fUL, | ||||
|             0x80deb1fe3b1696b1UL, | ||||
|             0x9bdc06a725c71235UL, | ||||
|             0xc19bf174cf692694UL, | ||||
|             0xe49b69c19ef14ad2UL, | ||||
|             0xefbe4786384f25e3UL, | ||||
|             0x0fc19dc68b8cd5b5UL, | ||||
|             0x240ca1cc77ac9c65UL, | ||||
|             0x2de92c6f592b0275UL, | ||||
|             0x4a7484aa6ea6e483UL, | ||||
|             0x5cb0a9dcbd41fbd4UL, | ||||
|             0x76f988da831153b5UL, | ||||
|             0x983e5152ee66dfabUL, | ||||
|             0xa831c66d2db43210UL, | ||||
|             0xb00327c898fb213fUL, | ||||
|             0xbf597fc7beef0ee4UL, | ||||
|             0xc6e00bf33da88fc2UL, | ||||
|             0xd5a79147930aa725UL, | ||||
|             0x06ca6351e003826fUL, | ||||
|             0x142929670a0e6e70UL, | ||||
|             0x27b70a8546d22ffcUL, | ||||
|             0x2e1b21385c26c926UL, | ||||
|             0x4d2c6dfc5ac42aedUL, | ||||
|             0x53380d139d95b3dfUL, | ||||
|             0x650a73548baf63deUL, | ||||
|             0x766a0abb3c77b2a8UL, | ||||
|             0x81c2c92e47edaee6UL, | ||||
|             0x92722c851482353bUL, | ||||
|             0xa2bfe8a14cf10364UL, | ||||
|             0xa81a664bbc423001UL, | ||||
|             0xc24b8b70d0f89791UL, | ||||
|             0xc76c51a30654be30UL, | ||||
|             0xd192e819d6ef5218UL, | ||||
|             0xd69906245565a910UL, | ||||
|             0xf40e35855771202aUL, | ||||
|             0x106aa07032bbd1b8UL, | ||||
|             0x19a4c116b8d2d0c8UL, | ||||
|             0x1e376c085141ab53UL, | ||||
|             0x2748774cdf8eeb99UL, | ||||
|             0x34b0bcb5e19b48a8UL, | ||||
|             0x391c0cb3c5c95a63UL, | ||||
|             0x4ed8aa4ae3418acbUL, | ||||
|             0x5b9cca4f7763e373UL, | ||||
|             0x682e6ff3d6b2b8a3UL, | ||||
|             0x748f82ee5defb2fcUL, | ||||
|             0x78a5636f43172f60UL, | ||||
|             0x84c87814a1f0ab72UL, | ||||
|             0x8cc702081a6439ecUL, | ||||
|             0x90befffa23631e28UL, | ||||
|             0xa4506cebde82bde9UL, | ||||
|             0xbef9a3f7b2c67915UL, | ||||
|             0xc67178f2e372532bUL, | ||||
|             0xca273eceea26619cUL, | ||||
|             0xd186b8c721c0c207UL, | ||||
|             0xeada7dd6cde0eb1eUL, | ||||
|             0xf57d4f7fee6ed178UL, | ||||
|             0x06f067aa72176fbaUL, | ||||
|             0x0a637dc5a2c898a6UL, | ||||
|             0x113f9804bef90daeUL, | ||||
|             0x1b710b35131c471bUL, | ||||
|             0x28db77f523047d84UL, | ||||
|             0x32caab7b40c72493UL, | ||||
|             0x3c9ebe0a15c9bebcUL, | ||||
|             0x431d67c49c100d4cUL, | ||||
|             0x4cc5d4becb3e42b6UL, | ||||
|             0x597f299cfc657e2aUL, | ||||
|             0x5fcb6fab3ad6faecUL, | ||||
|             0x6c44198c4a475817UL | ||||
|         ) | ||||
| 
 | ||||
|         val iv = arrayOf( | ||||
|             0x6a09e667f3bcc908UL, | ||||
|             0xbb67ae8584caa73bUL, | ||||
|             0x3c6ef372fe94f82bUL, | ||||
|             0xa54ff53a5f1d36f1UL, | ||||
|             0x510e527fade682d1UL, | ||||
|             0x9b05688c2b3e6c1fUL, | ||||
|             0x1f83d9abfb41bd6bUL, | ||||
|             0x5be0cd19137e2179UL | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
|         override fun digest(inputMessage: UByteArray): UByteArray { | ||||
| 
 | ||||
|             var h = iv.copyOf() | ||||
| 
 | ||||
|             val expansionArray = createExpansionArray(inputMessage.size.toLong()) | ||||
| 
 | ||||
|             val chunks = | ||||
|                 (inputMessage + expansionArray + (inputMessage.size * 8).toULong().toPadded128BitByteArray()).chunked( | ||||
|                     BLOCK_SIZE_IN_BYTES | ||||
|                 ) | ||||
| 
 | ||||
|             chunks.forEach { chunk -> | ||||
|                 val w = expandChunk(chunk.toUByteArray()) | ||||
|                 mix(h, w) | ||||
| 
 | ||||
|             } | ||||
| 
 | ||||
|             val digest = | ||||
|                 h[0].toPaddedByteArray() + | ||||
|                         h[1].toPaddedByteArray() + | ||||
|                         h[2].toPaddedByteArray() + | ||||
|                         h[3].toPaddedByteArray() + | ||||
|                         h[4].toPaddedByteArray() + | ||||
|                         h[5].toPaddedByteArray() + | ||||
|                         h[6].toPaddedByteArray() + | ||||
|                         h[7].toPaddedByteArray() | ||||
|             return digest | ||||
|         } | ||||
| 
 | ||||
|         private fun scheduleSigma0(value: ULong): ULong { | ||||
|             return value.rotateRight(1) xor value.rotateRight(8) xor (value shr 7) | ||||
|         } | ||||
| 
 | ||||
|         private fun scheduleSigma1(value: ULong): ULong { | ||||
|             return value.rotateRight(19) xor value.rotateRight(61) xor (value shr 6) | ||||
|         } | ||||
| 
 | ||||
|         private fun compressionSigma0(e: ULong): ULong { | ||||
|             return (e rotateRight 28) xor (e rotateRight 34) xor (e rotateRight 39) | ||||
|         } | ||||
| 
 | ||||
|         private fun compressionSigma1(a: ULong): ULong { | ||||
|             return (a rotateRight 14) xor (a rotateRight 18) xor (a rotateRight 41) | ||||
|         } | ||||
| 
 | ||||
|         private fun ch(x: ULong, y: ULong, z: ULong): ULong { | ||||
|             return ((x and y) xor ((x xor ULONG_MASK) and z)) | ||||
|         } | ||||
| 
 | ||||
|         private fun maj(x: ULong, y: ULong, z: ULong): ULong { | ||||
|             return ((x and y) xor (x and z) xor (y and z)) | ||||
|         } | ||||
| 
 | ||||
|         private fun expandChunk(chunk: UByteArray): Array<ULong> { | ||||
|             val w = Array<ULong>(CHUNK_SIZE) { | ||||
|                 when (it) { | ||||
|                     in 0 until 16 -> { | ||||
|                         var collected = (chunk[(it * 8)].toULong() shl 56) + | ||||
|                                 (chunk[(it * 8) + 1].toULong() shl 48) + | ||||
|                                 (chunk[(it * 8) + 2].toULong() shl 40) + | ||||
|                                 (chunk[(it * 8) + 3].toULong() shl 32) + | ||||
|                                 (chunk[(it * 8) + 4].toULong() shl 24) + | ||||
|                                 (chunk[(it * 8) + 5].toULong() shl 16) + | ||||
|                                 (chunk[(it * 8) + 6].toULong() shl 8) + | ||||
|                                 (chunk[(it * 8) + 7].toULong()) | ||||
|                         collected | ||||
|                     } | ||||
|                     else -> 0UL | ||||
|                 } | ||||
|             } | ||||
|             for (i in 16 until CHUNK_SIZE) { | ||||
|                 val s0 = scheduleSigma0(w[i - 15]) | ||||
|                 val s1 = scheduleSigma1(w[i - 2]) | ||||
|                 w[i] = w[i - 16] + s0 + w[i - 7] + s1 | ||||
|             } | ||||
|             return w | ||||
|         } | ||||
| 
 | ||||
|         private fun mix(h: Array<ULong>, w: Array<ULong>): Array<ULong> { | ||||
|             var paramA = h[0] | ||||
|             var paramB = h[1] | ||||
|             var paramC = h[2] | ||||
|             var paramD = h[3] | ||||
|             var paramE = h[4] | ||||
|             var paramF = h[5] | ||||
|             var paramG = h[6] | ||||
|             var paramH = h[7] | ||||
| 
 | ||||
|             for (i in 0 until CHUNK_SIZE) { | ||||
|                 val s1 = compressionSigma1(paramE) | ||||
|                 val ch = ch(paramE, paramF, paramG) | ||||
|                 val temp1 = paramH + s1 + ch + k[i] + w[i] | ||||
|                 val s0 = compressionSigma0(paramA) | ||||
|                 val maj = maj(paramA, paramB, paramC) | ||||
|                 val temp2 = s0 + maj | ||||
|                 paramH = paramG | ||||
|                 paramG = paramF | ||||
|                 paramF = paramE | ||||
|                 paramE = paramD + temp1 | ||||
|                 paramD = paramC | ||||
|                 paramC = paramB | ||||
|                 paramB = paramA | ||||
|                 paramA = temp1 + temp2 | ||||
|             } | ||||
| 
 | ||||
|             h[0] += paramA | ||||
|             h[1] += paramB | ||||
|             h[2] += paramC | ||||
|             h[3] += paramD | ||||
|             h[4] += paramE | ||||
|             h[5] += paramF | ||||
|             h[6] += paramG | ||||
|             h[7] += paramH | ||||
|             return h | ||||
|         } | ||||
| 
 | ||||
|         fun createExpansionArray(originalSizeInBytes: Long): UByteArray { | ||||
|             val originalMessageSizeInBits = originalSizeInBytes * 8 | ||||
| 
 | ||||
|             val expandedRemainderOf1024 = (originalMessageSizeInBits + 129) % BLOCK_SIZE | ||||
|             val zeroAddAmount = when (expandedRemainderOf1024) { | ||||
|                 0L -> 0 | ||||
|                 else -> ((BLOCK_SIZE - expandedRemainderOf1024) / 8).toInt() | ||||
|             } | ||||
|             val expansionArray = UByteArray(zeroAddAmount + 1) { | ||||
|                 when (it) { | ||||
|                     0 -> 0b10000000U | ||||
|                     else -> 0U | ||||
|                 } | ||||
|             } | ||||
|             return expansionArray | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         private fun ULong.toPaddedByteArray(): UByteArray { | ||||
|             val byteMask = 0xFFUL | ||||
|             //Ignore messages longer than 64 bits for now | ||||
|             return UByteArray(8) { | ||||
|                 when (it) { | ||||
|                     7 -> (this and byteMask).toUByte() | ||||
|                     6 -> ((this shr 8) and byteMask).toUByte() | ||||
|                     5 -> ((this shr 16) and byteMask).toUByte() | ||||
|                     4 -> ((this shr 24) and byteMask).toUByte() | ||||
|                     3 -> ((this shr 32) and byteMask).toUByte() | ||||
|                     2 -> ((this shr 40) and byteMask).toUByte() | ||||
|                     1 -> ((this shr 48) and byteMask).toUByte() | ||||
|                     0 -> ((this shr 56) and byteMask).toUByte() | ||||
|                     else -> 0U | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private fun ULong.toPadded128BitByteArray(): UByteArray { | ||||
|             val byteMask = 0xFFUL | ||||
|             //Ignore messages longer than 64 bits for now | ||||
|             return UByteArray(16) { | ||||
|                 when (it) { | ||||
|                     15 -> (this and byteMask).toUByte() | ||||
|                     14 -> ((this shr 8) and byteMask).toUByte() | ||||
|                     13 -> ((this shr 16) and byteMask).toUByte() | ||||
|                     12 -> ((this shr 24) and byteMask).toUByte() | ||||
|                     11 -> ((this shr 32) and byteMask).toUByte() | ||||
|                     10 -> ((this shr 40) and byteMask).toUByte() | ||||
|                     9 -> ((this shr 48) and byteMask).toUByte() | ||||
|                     8 -> ((this shr 54) and byteMask).toUByte() | ||||
|                     else -> 0U | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     var h = iv.copyOf() | ||||
|     var counter = 0L | ||||
|     var bufferCounter = 0 | ||||
|     var buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { 0U } | ||||
|     var digested = false | ||||
| 
 | ||||
| 
 | ||||
|     fun update(data: String) { | ||||
|         return update(data.encodeToUByteArray()) | ||||
|     } | ||||
| 
 | ||||
|     override fun update(data: UByteArray) { | ||||
|         if (data.isEmpty()) { | ||||
|             throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating") | ||||
|         } | ||||
|         if (digested) { | ||||
|             throw RuntimeException("This instance of updateable SHA256 was already finished once. You should use new instance") | ||||
|         } | ||||
| 
 | ||||
|         when { | ||||
|             bufferCounter + data.size < BLOCK_SIZE_IN_BYTES -> appendToBuffer(data, bufferCounter) | ||||
|             bufferCounter + data.size >= BLOCK_SIZE_IN_BYTES -> { | ||||
|                 val chunked = data.chunked(BLOCK_SIZE_IN_BYTES) | ||||
|                 chunked.forEach { chunk -> | ||||
|                     if (bufferCounter + chunk.size < BLOCK_SIZE_IN_BYTES) { | ||||
|                         appendToBuffer(chunk.toUByteArray(), bufferCounter) | ||||
|                     } else { | ||||
|                         chunk.toUByteArray().copyInto( | ||||
|                             destination = buffer, | ||||
|                             destinationOffset = bufferCounter, | ||||
|                             startIndex = 0, | ||||
|                             endIndex = BLOCK_SIZE_IN_BYTES - bufferCounter | ||||
|                         ) | ||||
|                         counter += BLOCK_SIZE_IN_BYTES | ||||
|                         consumeBlock(buffer) | ||||
|                         buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { | ||||
|                             when (it) { | ||||
|                                 in (0 until (chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter))) -> { | ||||
|                                     chunk[it + (BLOCK_SIZE_IN_BYTES - bufferCounter)] | ||||
|                                 } | ||||
|                                 else -> { | ||||
|                                     0U | ||||
|                                 } | ||||
|                             } | ||||
| 
 | ||||
|                         } | ||||
|                         bufferCounter = chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter) | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun consumeBlock(block: UByteArray) { | ||||
|         val w = expandChunk(block) | ||||
|         mix(h, w).copyInto(h) | ||||
|     } | ||||
| 
 | ||||
|     override fun digest(): UByteArray { | ||||
|         val length = counter + bufferCounter | ||||
|         val expansionArray = createExpansionArray(length) | ||||
|         val finalBlock = | ||||
|             buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPadded128BitByteArray() | ||||
|         finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach { | ||||
|             consumeBlock(it.toUByteArray()) | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         val digest = h[0].toPaddedByteArray() + | ||||
|                 h[1].toPaddedByteArray() + | ||||
|                 h[2].toPaddedByteArray() + | ||||
|                 h[3].toPaddedByteArray() + | ||||
|                 h[4].toPaddedByteArray() + | ||||
|                 h[5].toPaddedByteArray() + | ||||
|                 h[6].toPaddedByteArray() + | ||||
|                 h[7].toPaddedByteArray() | ||||
|         digested = true | ||||
|         return digest | ||||
|     } | ||||
| 
 | ||||
|     private fun appendToBuffer(array: UByteArray, start: Int) { | ||||
|         array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size) | ||||
|         bufferCounter += array.size | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,10 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.keyderivation.argon2 | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.keyderivation.KeyDerivationFunction | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 24-May-2020 | ||||
|  */ | ||||
| interface Argon2 : KeyDerivationFunction | ||||
| @ -1,31 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.keyderivation.argon2 | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 16-May-2020 | ||||
|  */ | ||||
| class Argon2TagTooShort(tagLength: UInt) : RuntimeException("Too short tag (output) requested. Requested: $tagLength") | ||||
| class Argon2TagTooLong(tagLength: UInt) : RuntimeException("Too long tag (output) requested. Requested: $tagLength") | ||||
| class Argon2TimeTooShort(iterations: Int) : RuntimeException("Too short time parameter (Too few iterations). Requested iterations: $iterations") | ||||
| class Argon2TimeTooLong(iterations: Int) : RuntimeException("Too long time parameter (Too many iterations). Requested iterations: $iterations") | ||||
| class Argon2MemoryTooLitlle(requestedMemorySize: UInt) : RuntimeException("Requested memory size must be larger than 8 * parallelism. Requested size: $requestedMemorySize") | ||||
| class Argon2MemoryTooMuch(requestedMemorySize: UInt) : RuntimeException("Requested memory size too large. Requested size: $requestedMemorySize") | ||||
| class Argon2LanesTooFew(parallelism: Int) : RuntimeException("Too few, or invalid number of threads requested $parallelism") | ||||
| class Argon2LanesTooMany(parallelism: Int) : RuntimeException("Too many threads requested (parallelism). Requested: $parallelism") | ||||
| @ -1,401 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| @file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.keyderivation.argon2 | ||||
| 
 | ||||
| import com.ionspin.kotlin.bignum.integer.toBigInteger | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.SRNG | ||||
| import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bPure | ||||
| import com.ionspin.kotlin.crypto.hash.encodeToUByteArray | ||||
| import com.ionspin.kotlin.crypto.keyderivation.ArgonResult | ||||
| import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.argonBlake2bArbitraryLenghtHash | ||||
| import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.compressionFunctionG | ||||
| import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.validateArgonParameters | ||||
| import com.ionspin.kotlin.crypto.util.* | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 16-May-2020 | ||||
|  */ | ||||
| 
 | ||||
| enum class ArgonType(val typeId: Int) { | ||||
|     Argon2d(0), Argon2i(1), Argon2id(2) | ||||
| } | ||||
| 
 | ||||
| data class SegmentPosition( | ||||
|     val iteration: Int, | ||||
|     val lane: Int, | ||||
|     val slice: Int | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class Argon2Pure( | ||||
|     private val password: UByteArray, | ||||
|     private val salt: UByteArray = ubyteArrayOf(), | ||||
|     private val parallelism: Int = 1, | ||||
|     private val tagLength: UInt = 64U, | ||||
|     requestedMemorySize: UInt = 0U, | ||||
|     private val numberOfIterations: Int = 1, | ||||
|     private val key: UByteArray = ubyteArrayOf(), | ||||
|     private val associatedData: UByteArray = ubyteArrayOf(), | ||||
|     private val argonType: ArgonType = ArgonType.Argon2id | ||||
| ) : Argon2 { | ||||
| 
 | ||||
|     companion object { | ||||
|         fun derive( | ||||
|             password: String, | ||||
|             salt: String? = null, | ||||
|             key: String, | ||||
|             associatedData: String, | ||||
|             parallelism: Int = 16, | ||||
|             tagLength: Int = 64, | ||||
|             memory: Int = 4096, | ||||
|             numberOfIterations: Int = 10, | ||||
|         ): ArgonResult { | ||||
|             val salt = SRNG.getRandomBytes(64) | ||||
|             val argon = Argon2Pure( | ||||
|                 password.encodeToUByteArray(), | ||||
|                 salt, | ||||
|                 parallelism, | ||||
|                 tagLength.toUInt(), | ||||
|                 memory.toUInt(), | ||||
|                 numberOfIterations, | ||||
|                 key.encodeToUByteArray(), | ||||
|                 associatedData.encodeToUByteArray(), | ||||
|                 ArgonType.Argon2id | ||||
|             ) | ||||
|             val resultArray = argon.derive() | ||||
|             return ArgonResult(resultArray, salt) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     constructor( | ||||
|         password: String, | ||||
|         salt: String = "", | ||||
|         parallelism: Int = 1, | ||||
|         tagLength: UInt = 64U, | ||||
|         requestedMemorySize: UInt = 0U, | ||||
|         numberOfIterations: Int = 10, | ||||
|         key: String = "", | ||||
|         associatedData: String = "", | ||||
|         argonType: ArgonType = ArgonType.Argon2id | ||||
|     ) : this( | ||||
|         password.encodeToUByteArray(), | ||||
|         salt.encodeToUByteArray(), | ||||
|         parallelism, | ||||
|         tagLength, | ||||
|         requestedMemorySize, | ||||
|         numberOfIterations, | ||||
|         key.encodeToUByteArray(), | ||||
|         associatedData.encodeToUByteArray(), | ||||
|         argonType | ||||
|     ) | ||||
| 
 | ||||
|     //We support only the latest version | ||||
|     private val versionNumber: UInt = 0x13U | ||||
| 
 | ||||
|     //Use either requested memory size, or default, or throw exception if the requested amount is less than 8*parallelism | ||||
|     private val memorySize = if (requestedMemorySize == 0U) { | ||||
|         ((8 * parallelism) * 2).toUInt() | ||||
|     } else { | ||||
|         requestedMemorySize | ||||
|     } | ||||
|     private val blockCount = (memorySize / (4U * parallelism.toUInt())) * (4U * parallelism.toUInt()) | ||||
|     private val columnCount = (blockCount / parallelism.toUInt()).toInt() | ||||
|     private val segmentLength = columnCount / 4 | ||||
| 
 | ||||
|     private val useIndependentAddressing = argonType == ArgonType.Argon2id || argonType == ArgonType.Argon2i | ||||
| 
 | ||||
|     // State | ||||
|     private val matrix: ArgonMatrix | ||||
| 
 | ||||
|     init { | ||||
|         matrix = ArgonMatrix(columnCount, parallelism) | ||||
|         validateArgonParameters( | ||||
|             password, | ||||
|             salt, | ||||
|             parallelism, | ||||
|             tagLength, | ||||
|             requestedMemorySize, | ||||
|             numberOfIterations, | ||||
|             key, | ||||
|             associatedData, | ||||
|             argonType | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     private fun populateAddressBlock( | ||||
|         iteration: Int, | ||||
|         slice: Int, | ||||
|         lane: Int, | ||||
|         addressBlock: ArgonBlockPointer, | ||||
|         addressCounter: ULong | ||||
|     ): ArgonBlockPointer { | ||||
|         //Calculate first pass | ||||
|         val zeroesBlock = ArgonBlock() | ||||
|         val firstPass = compressionFunctionG( | ||||
|             zeroesBlock.getBlockPointer(), | ||||
|             ArgonBlock(iteration.toULong().toLittleEndianUByteArray() + | ||||
|                     lane.toULong().toLittleEndianUByteArray() + | ||||
|                     slice.toULong().toLittleEndianUByteArray() + | ||||
|                     blockCount.toULong().toLittleEndianUByteArray() + | ||||
|                     numberOfIterations.toULong().toLittleEndianUByteArray() + | ||||
|                     argonType.typeId.toULong().toLittleEndianUByteArray() + | ||||
|                     addressCounter.toLittleEndianUByteArray() + | ||||
|                     UByteArray(968) { 0U } | ||||
|             ).getBlockPointer(), | ||||
|             addressBlock, | ||||
|             false | ||||
|         ) | ||||
|         val secondPass = compressionFunctionG( | ||||
|             zeroesBlock.getBlockPointer(), | ||||
|             firstPass, | ||||
|             firstPass, | ||||
|             false | ||||
|         ) | ||||
|         return secondPass | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private fun computeReferenceBlockIndexes( | ||||
|         iteration: Int, | ||||
|         slice: Int, | ||||
|         lane: Int, | ||||
|         column: Int, | ||||
|         addressBlockPointer: ArgonBlockPointer? | ||||
|     ): Pair<Int, Int> { | ||||
| 
 | ||||
|         val segmentIndex = (column % segmentLength) | ||||
|         val independentIndex = segmentIndex % 128 // 128 is the number of addresses in address block | ||||
|         val (j1, j2) = when (argonType) { | ||||
|             ArgonType.Argon2d -> { | ||||
|                 val previousBlockStart = if (column == 0) { | ||||
|                     matrix.getBlockPointer(lane, columnCount - 1) //Get last block in the SAME lane | ||||
|                 } else { | ||||
|                     matrix.getBlockPointer(lane, column - 1) | ||||
|                 } | ||||
|                 val first32Bit = matrix.sliceArray(previousBlockStart.asInt() until previousBlockStart.asInt() + 4).fromLittleEndianArrayToUInt() | ||||
|                 val second32Bit = matrix.sliceArray(previousBlockStart.asInt() + 4 until previousBlockStart.asInt() + 8).fromLittleEndianArrayToUInt() | ||||
| 
 | ||||
|                 Pair(first32Bit, second32Bit) | ||||
|             } | ||||
|             ArgonType.Argon2i -> { | ||||
|                 val first32Bit = addressBlockPointer!!.getUIntFromPosition(independentIndex * 8) | ||||
|                 val second32Bit = addressBlockPointer!!.getUIntFromPosition(independentIndex * 8 + 4) | ||||
|                 Pair(first32Bit, second32Bit) | ||||
|             } | ||||
|             ArgonType.Argon2id -> { | ||||
|                 if (iteration == 0 && (slice == 0 || slice == 1)) { | ||||
|                     val first32Bit = addressBlockPointer!!.getUIntFromPosition(independentIndex * 8) | ||||
|                     val second32Bit = addressBlockPointer!!.getUIntFromPosition(independentIndex * 8 + 4) | ||||
|                     Pair(first32Bit, second32Bit) | ||||
|                 } else { | ||||
|                     val previousBlockStart = if (column == 0) { | ||||
|                         matrix.getBlockPointer(lane, columnCount - 1) //Get last block in the SAME lane | ||||
|                     } else { | ||||
|                         matrix.getBlockPointer(lane, column - 1) | ||||
|                     } | ||||
|                     val first32Bit = matrix.sliceArray(previousBlockStart.asInt() until previousBlockStart.asInt() + 4).fromLittleEndianArrayToUInt() | ||||
|                     val second32Bit = matrix.sliceArray(previousBlockStart.asInt() + 4 until previousBlockStart.asInt() + 8).fromLittleEndianArrayToUInt() | ||||
|                     Pair(first32Bit, second32Bit) | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         //If this is first iteration and first slice, block is taken from the current lane | ||||
|         val l = if (iteration == 0 && slice == 0) { | ||||
|             lane | ||||
|         } else { | ||||
|             (j2.toBigInteger() % parallelism).intValue() | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         val referenceAreaSize = if (iteration == 0) { | ||||
|             if (slice == 0) { | ||||
|                 //All indices except the previous | ||||
|                 segmentIndex - 1 | ||||
|             } else { | ||||
|                 if (lane == l) { | ||||
|                     //Same lane | ||||
|                     column - 1 | ||||
|                 } else { | ||||
|                     slice * (columnCount / 4) + if (segmentIndex == 0) { // Check if column is first block of the SEGMENT | ||||
|                         -1 | ||||
|                     } else { | ||||
|                         0 | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             if (lane == l) { | ||||
|                 columnCount - (columnCount / 4) + (segmentIndex - 1) | ||||
|             } else { | ||||
|                 columnCount - (columnCount / 4) + if (segmentIndex == 0) { | ||||
|                     -1 | ||||
|                 } else { | ||||
|                     0 | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         val x = (j1.toULong() * j1) shr 32 | ||||
|         val y = (referenceAreaSize.toULong() * x) shr 32 | ||||
|         val z = referenceAreaSize.toULong() - 1U - y | ||||
| 
 | ||||
|         val startPosition = if (iteration == 0) { | ||||
|             0 | ||||
|         } else { | ||||
|             if (slice == 3) { | ||||
|                 0 | ||||
|             } else { | ||||
|                 (slice + 1) * segmentLength | ||||
|             } | ||||
|         } | ||||
|         val absolutePosition = (startPosition + z.toInt()) % columnCount | ||||
| 
 | ||||
|         return Pair(l, absolutePosition) | ||||
|     } | ||||
| 
 | ||||
|     override fun derive(): UByteArray { | ||||
|         val blakeInput = parallelism.toUInt().toLittleEndianUByteArray() + | ||||
|                 tagLength.toLittleEndianUByteArray() + | ||||
|                 memorySize.toLittleEndianUByteArray() + | ||||
|                 numberOfIterations.toUInt().toLittleEndianUByteArray() + | ||||
|                 versionNumber.toLittleEndianUByteArray() + | ||||
|                 argonType.typeId.toUInt().toLittleEndianUByteArray() + | ||||
|                 password.size.toUInt().toLittleEndianUByteArray() + password + | ||||
|                 salt.size.toUInt().toLittleEndianUByteArray() + salt + | ||||
|                 key.size.toUInt().toLittleEndianUByteArray() + key + | ||||
|                 associatedData.size.toUInt().toLittleEndianUByteArray() + associatedData | ||||
|         val h0 = Blake2bPure.digest( | ||||
|             blakeInput | ||||
|         ) | ||||
| 
 | ||||
|         //Compute B[i][0] | ||||
|         for (i in 0 until parallelism) { | ||||
|             matrix.setBlockAt(i, 0, | ||||
|                 argonBlake2bArbitraryLenghtHash( | ||||
|                     (h0 + 0.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray()).toUByteArray(), | ||||
|                     1024U | ||||
|                 ) | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|         //Compute B[i][1] | ||||
|         for (i in 0 until parallelism) { | ||||
|             matrix.setBlockAt(i, 1, | ||||
|                 argonBlake2bArbitraryLenghtHash( | ||||
|                     (h0 + 1.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray()).toUByteArray(), | ||||
|                     1024U | ||||
|                 ) | ||||
|             ) | ||||
|         } | ||||
|         //Run all iterations over all lanes and all segments | ||||
|         executeArgonWithSingleThread() | ||||
| 
 | ||||
| 
 | ||||
|         val acc = ArgonBlock(matrix.getBlockAt(0, columnCount - 1)) | ||||
|         val accPointer = acc.getBlockPointer() | ||||
|         for (i in 1 until parallelism) { | ||||
|             accPointer.xorInplaceWith(matrix.getBlockPointer(i, columnCount - 1)) | ||||
|         } | ||||
|         //Hash the xored last blocks | ||||
|         val hash = argonBlake2bArbitraryLenghtHash(acc.storage, tagLength) | ||||
|         matrix.clearMatrix() | ||||
|         return hash | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private fun executeArgonWithSingleThread() { | ||||
|         for (iteration in 0 until numberOfIterations) { | ||||
|             for (slice in 0 until 4) { | ||||
|                 for (lane in 0 until parallelism) { | ||||
|                     val segmentPosition = SegmentPosition(iteration, lane, slice) | ||||
|                     processSegment(segmentPosition) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun processSegment(segmentPosition: SegmentPosition) { | ||||
|         val iteration = segmentPosition.iteration | ||||
|         val slice = segmentPosition.slice | ||||
|         val lane = segmentPosition.lane | ||||
| 
 | ||||
|         var addressBlock: ArgonBlockPointer? = null | ||||
|         var addressCounter = 1UL //Starts from 1 in each segment as defined by the spec | ||||
| 
 | ||||
|         //Generate initial segment address block | ||||
|         if (useIndependentAddressing) { | ||||
|             addressBlock = ArgonBlock().getBlockPointer() | ||||
|             addressBlock = populateAddressBlock(iteration, slice, lane, addressBlock, addressCounter) | ||||
|             addressCounter++ | ||||
|         } | ||||
|         val startColumn = if (iteration == 0 && slice == 0) { | ||||
|             2 | ||||
|         } else { | ||||
|             slice * segmentLength | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         for (column in startColumn until (slice + 1) * segmentLength) { | ||||
|             val segmentIndex = column - (slice * segmentLength) | ||||
|             //Each address block contains 128 addresses, and we use one per iteration, | ||||
|             //so once we do 128 iterations we need to calculate a new address block | ||||
|             if (useIndependentAddressing && segmentIndex != 0 && segmentIndex % 128 == 0) { | ||||
|                 addressBlock = populateAddressBlock(iteration, slice, lane, addressBlock!!, addressCounter) | ||||
|                 addressCounter++ | ||||
|             } | ||||
|             val previousColumn = if (column == 0) { | ||||
|                 columnCount - 1 | ||||
|             } else { | ||||
|                 column - 1 | ||||
|             } | ||||
|             val (l, z) = computeReferenceBlockIndexes( | ||||
|                 iteration, | ||||
|                 slice, | ||||
|                 lane, | ||||
|                 column, | ||||
|                 addressBlock | ||||
|             ) | ||||
| 
 | ||||
|             matrix.setBlockAt(lane, column, | ||||
|                 compressionFunctionG( | ||||
|                     matrix.getBlockPointer(lane, previousColumn), | ||||
|                     matrix.getBlockPointer(l,z), | ||||
|                     matrix.getBlockPointer(lane,column), | ||||
|                     true | ||||
|                 ).getAsUByteArray() | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -1,167 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| @file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.keyderivation.argon2 | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bPure | ||||
| import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.BLOCK_SIZE | ||||
| import com.ionspin.kotlin.crypto.util.plus | ||||
| import com.ionspin.kotlin.crypto.util.rotateRight | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 16-May-2020 | ||||
|  */ | ||||
| object Argon2Utils { | ||||
|     const val BLOCK_SIZE = 1024 | ||||
| 
 | ||||
|     const val R1 = 32 | ||||
|     const val R2 = 24 | ||||
|     const val R3 = 16 | ||||
|     const val R4 = 63 | ||||
| 
 | ||||
|     //Based on Blake2b mix | ||||
|     internal fun inplaceMixRound(v : ULongArray) : ULongArray{ | ||||
|         mix(v, 0, 4, 8, 12) | ||||
|         mix(v, 1, 5, 9, 13) | ||||
|         mix(v, 2, 6, 10, 14) | ||||
|         mix(v, 3, 7, 11, 15) | ||||
|         mix(v, 0, 5, 10, 15) | ||||
|         mix(v, 1, 6, 11, 12) | ||||
|         mix(v, 2, 7, 8, 13) | ||||
|         mix(v, 3, 4, 9, 14) | ||||
|         return v //Just for chaining, array is mixed in place | ||||
|     } | ||||
| 
 | ||||
|     //Based on Blake2b mix | ||||
|     private fun mix(v: ULongArray, a: Int, b: Int, c: Int, d: Int) { | ||||
|         v[a] = (v[a] + v[b] + 2U * (v[a] and 0xFFFFFFFFUL) * (v[b] and 0xFFFFFFFFUL)) | ||||
|         v[d] = (v[d] xor v[a]) rotateRight R1 | ||||
|         v[c] = (v[c] + v[d] + 2U * (v[c] and 0xFFFFFFFFUL) * (v[d] and 0xFFFFFFFFUL)) | ||||
|         v[b] = (v[b] xor v[c]) rotateRight R2 | ||||
|         v[a] = (v[a] + v[b] + 2U * (v[a] and 0xFFFFFFFFUL) * (v[b] and 0xFFFFFFFFUL)) | ||||
|         v[d] = (v[d] xor v[a]) rotateRight R3 | ||||
|         v[c] = (v[c] + v[d] + 2U * (v[c] and 0xFFFFFFFFUL) * (v[d] and 0xFFFFFFFFUL)) | ||||
|         v[b] = (v[b] xor v[c]) rotateRight R4 | ||||
|     } | ||||
| 
 | ||||
|     internal fun extractColumnFromGBlock(gBlock: UByteArray, columnPosition: Int): UByteArray { | ||||
|         val result = UByteArray(128) { 0U } | ||||
|         for (i in 0..7) { | ||||
|             gBlock.copyOfRange(i * 128 + (columnPosition * 16), i * 128 + (columnPosition * 16) + 16) | ||||
|                 .copyInto(result, i * 16) | ||||
|         } | ||||
|         return result | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     internal fun compressionFunctionG( | ||||
|         previousBlock: ArgonBlockPointer, | ||||
|         referenceBlock: ArgonBlockPointer, | ||||
|         currentBlock: ArgonBlockPointer, | ||||
|         xorWithCurrentBlock: Boolean | ||||
|     ): ArgonBlockPointer { | ||||
|         val r = (referenceBlock xorBlocksAndGetPointerToNewBlock previousBlock).getBlockPointer() | ||||
|         //Since we are doing inplace xors, we don't need the Q that exists in specification | ||||
|         val z = ArgonBlock().getBlockPointer() | ||||
|         // Do the argon/blake2b mixing on rows | ||||
|         for (i in 0..7) { | ||||
|             z.setRowFromMixedULongs(i, inplaceMixRound(r.getRowOfULongsForMixing(i))) | ||||
|         } | ||||
|         // Do the argon/blake2b mixing on columns | ||||
|         for (i in 0..7) { | ||||
|             z.setColumnFromMixedULongs(i, inplaceMixRound(z.getColumnOfULongsForMixing(i))) | ||||
|         } | ||||
|         val final = if (xorWithCurrentBlock) { | ||||
|             (z xorInplaceWith r) xorInplaceWith  currentBlock | ||||
|         } else { | ||||
|             z xorInplaceWith r | ||||
|         } | ||||
|         return final | ||||
|     } | ||||
| 
 | ||||
|     internal fun argonBlake2bArbitraryLenghtHash(input: UByteArray, length: UInt): UByteArray { | ||||
|         if (length <= 64U) { | ||||
|             return Blake2bPure.digest(inputMessage = length + input, hashLength = length.toInt()) | ||||
|         } | ||||
|         //We can cast to int because UInt even if MAX_VALUE divided by 32 is guaranteed not to overflow | ||||
|         val numberOf64ByteBlocks = (1U + ((length - 1U) / 32U) - 2U).toInt() // equivalent  to ceil(length/32) - 2 | ||||
|         val v = Array<UByteArray>(numberOf64ByteBlocks) { ubyteArrayOf() } | ||||
|         v[0] = Blake2bPure.digest(length + input) | ||||
|         for (i in 1 until numberOf64ByteBlocks) { | ||||
|             v[i] = Blake2bPure.digest(v[i - 1]) | ||||
|         } | ||||
|         val remainingPartOfInput = length.toInt() - numberOf64ByteBlocks * 32 | ||||
|         val vLast = Blake2bPure.digest(v[numberOf64ByteBlocks - 1], hashLength = remainingPartOfInput) | ||||
|         val concat = | ||||
|             (v.map { it.copyOfRange(0, 32) }) | ||||
|                 .plus(listOf(vLast)) | ||||
|                 .foldRight(ubyteArrayOf()) { arrayOfUBytes, acc -> arrayOfUBytes + acc } | ||||
| 
 | ||||
|         return concat | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Validates the argon 2 parameters. | ||||
|      * Since Kotlin arrays that we are currently using cannot have more than 2^31 bytes, we don't need to check | ||||
|      * sizes for password, salt, key and associated data. Also since UInt is 32bit we cant set more than 2^32-1 of | ||||
|      * tagLength, requested memory size and number of iterations, so no need to check for upper bound, just lower. | ||||
|      */ | ||||
|     internal fun validateArgonParameters( | ||||
|         password: UByteArray, | ||||
|         salt: UByteArray, | ||||
|         parallelism: Int , | ||||
|         tagLength: UInt, | ||||
|         requestedMemorySize: UInt , | ||||
|         numberOfIterations: Int , | ||||
|         key: UByteArray, | ||||
|         associatedData: UByteArray, | ||||
|         argonType: ArgonType | ||||
|     ) { | ||||
| 
 | ||||
|         //Parallelism | ||||
|         if (parallelism > 0xFFFFFF) { | ||||
|             throw Argon2LanesTooMany(parallelism) | ||||
|         } | ||||
|         if (parallelism <= 0) { | ||||
|             throw Argon2LanesTooFew(parallelism) | ||||
|         } | ||||
|         //Tag length | ||||
|         if (tagLength <= 0U) { | ||||
|             throw Argon2TagTooShort(tagLength) | ||||
|         } | ||||
|         //Requested memory | ||||
|         if (requestedMemorySize < 8U || requestedMemorySize < (8 * parallelism).toUInt()) { | ||||
|             throw Argon2MemoryTooLitlle(requestedMemorySize) | ||||
|         } | ||||
|         //Number of iterations | ||||
|         if (numberOfIterations <= 0) { | ||||
|             throw Argon2TimeTooShort(numberOfIterations) | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // ------------ Arithmetic and other utils | ||||
| 
 | ||||
| 
 | ||||
| fun UByteArray.xorWithBlock(other : ArgonMatrix, rowPosition: Int, columnPosition: Int) : UByteArray { | ||||
|     return UByteArray(BLOCK_SIZE) { this[it] xor other[rowPosition, columnPosition, it] } | ||||
| } | ||||
| @ -1,285 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| @file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.keyderivation.argon2 | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.util.xor | ||||
| 
 | ||||
| /** | ||||
|  * Represents a pointer to a Argon2 Block, this abstracts what the backing structure is, a Argon 2 Matrix or | ||||
|  * or a UByteArray block | ||||
|  */ | ||||
| interface ArgonBlockPointer { | ||||
|     companion object { | ||||
|         private val _emitLongArray : LongArray = longArrayOf(1L,2L) | ||||
|     } | ||||
|     val blockStartPosition: Int | ||||
| 
 | ||||
|     fun pointerArithmetic(block : (Int, Int) -> Int) : ArgonBlockPointer | ||||
| 
 | ||||
|     infix fun xorBlocksAndGetPointerToNewBlock(other: ArgonBlockPointer) : ArgonBlock | ||||
| 
 | ||||
|     operator fun get(blockPosition: Int) : UByte | ||||
| 
 | ||||
|     operator fun set(blockPosition: Int, value: UByte) | ||||
| 
 | ||||
|     infix fun xorInplaceWith(other: ArgonBlockPointer) : ArgonBlockPointer { | ||||
|         for (it in 0 until 1024) { | ||||
|             this[it] = this[it] xor other[it] | ||||
|         } | ||||
|         return this //For chaining | ||||
|     } | ||||
| 
 | ||||
|     fun asInt() : Int | ||||
| 
 | ||||
|     fun getAsUByteArray() : UByteArray | ||||
| } | ||||
| 
 | ||||
| fun ArgonBlockPointer._emitLongArray() : LongArray = longArrayOf(0,1) | ||||
| 
 | ||||
| fun ArgonBlockPointer.getRowOfULongsForMixing(rowIndex: Int) : ULongArray { | ||||
|     // Each row has 16 unsigned longs (16 ulongs * 8 bytes = 128 bytes) -- Argon2 considers this as 2 word unsigned | ||||
|     // numbers, so strictly speaking argon representation is 8 * 8 matrix of 2 word unsigned numbers (registers | ||||
|     val ulongArray = ULongArray(16) | ||||
|     for (columnIndex in 0 until 16) { | ||||
|         var ulong = 0UL | ||||
|         //Now we create the ulong | ||||
|         for (bytePosition in 0 until 8) { | ||||
|             ulong = ulong or (this[rowIndex * 128 + columnIndex * 8 + bytePosition].toULong() shl (bytePosition * 8)) | ||||
|         } | ||||
|         ulongArray[columnIndex] = ulong | ||||
|     } | ||||
|     return ulongArray | ||||
| } | ||||
| 
 | ||||
| fun ArgonBlockPointer.setRowFromMixedULongs(rowIndex: Int, ulongs: ULongArray) { | ||||
|     // Each row has 16 unsigned longs (16 ulongs * 8 bytes = 128 bytes) -- Argon2 considers this as 2 word unsigned | ||||
|     // numbers, so strictly speaking argon representation is 8 * 8 matrix of 2 word unsigned numbers (registers | ||||
|     for (columnIndex in 0 until 16) { | ||||
|         val ulongToConvert = ulongs[columnIndex] | ||||
|         for (bytePosition in 0 until 8) { | ||||
|             this[rowIndex * 128 + columnIndex * 8 + bytePosition] = ((ulongToConvert shr (bytePosition * 8)) and 0xFFU).toUByte() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun ArgonBlockPointer.getColumnOfULongsForMixing(columnIndex: Int) : ULongArray { | ||||
|     //In Argon2 representation there are 8 double word registers (numbers, but we work with 16 single word ulongs | ||||
|     val ulongArray = ULongArray(16) | ||||
|     //There are 8 rows that consist of 2 words (registers, in our case ulongs) each | ||||
|     for (rowIndex in 0 until 8) { | ||||
|         var ulong = 0UL | ||||
|         //Now we create the ulong | ||||
|         for (bytePosition in 0 until 8) { | ||||
|             ulong = ulong or (this[rowIndex * 128 + columnIndex * 16 + bytePosition].toULong() shl (bytePosition * 8)) | ||||
|         } | ||||
|         ulongArray[rowIndex * 2] = ulong | ||||
|         ulong = 0UL | ||||
|         // But unlike in columns where we can directly iterate and get all TWO WORD registers, here we also need to grab | ||||
|         // the next word | ||||
|         for (bytePosition in 8 until 16) { | ||||
|             ulong = ulong or (this[rowIndex * 128 + columnIndex * 16 + bytePosition].toULong() shl (bytePosition * 8)) | ||||
|         } | ||||
|         ulongArray[rowIndex * 2 + 1] = ulong | ||||
|     } | ||||
|     return ulongArray | ||||
| } | ||||
| 
 | ||||
| fun ArgonBlockPointer.setColumnFromMixedULongs(columnIndex: Int, ulongs: ULongArray)  { | ||||
|     //In Argon2 representation there are 8 double word registers (numbers, but we work with 16 single word ulongs | ||||
|     //There are 8 rows that consist of 2 words (registers, in our case ulongs) each | ||||
|     var ulongToConvert = 0UL | ||||
|     for (rowIndex in 0 until 8) { | ||||
|         ulongToConvert = ulongs[rowIndex * 2] | ||||
|         //Now we create the ulong | ||||
|         for (bytePosition in 0 until 8) { | ||||
|             this[rowIndex * 128 + columnIndex * 16 + bytePosition] = ((ulongToConvert shr (bytePosition * 8)) and 0xFFU).toUByte() | ||||
|         } | ||||
|         // But unlike in columns where we can directly iterate and get all TWO WORD registers, here we also need to set | ||||
|         // the next word | ||||
|         ulongToConvert = ulongs[rowIndex * 2 + 1] | ||||
|         for (bytePosition in 8 until 16) { | ||||
|             this[rowIndex * 128 + columnIndex * 16 + bytePosition] = ((ulongToConvert shr (bytePosition * 8)) and 0xFFU).toUByte() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun ArgonBlockPointer.getUIntFromPosition(positionInBlock: Int) : UInt { | ||||
|     var uint = 0U | ||||
|     for (i in 0 until 4) { | ||||
|         uint = uint or (this[positionInBlock + i].toUInt() shl (i * 8)) | ||||
|     } | ||||
|     return uint | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 21-May-2020 | ||||
|  */ | ||||
|  class ArgonMatrix(val columnCount: Int, val rowCount: Int) { | ||||
| 
 | ||||
|     internal val storage: UByteArray = UByteArray(columnCount * rowCount * 1024) | ||||
| 
 | ||||
|     operator fun get(rowPosition: Int, columnPosition: Int, inBlockPosition: Int) : UByte { | ||||
|         if (rowPosition > rowCount - 1) { | ||||
|             throw RuntimeException("Invalid row (lane) requested: $rowPosition, rowCount: $rowCount") | ||||
|         } | ||||
|         if (columnPosition > columnCount - 1) { | ||||
|             throw RuntimeException("Invalid column requested: $columnPosition, columnCount: $columnCount") | ||||
|         } | ||||
|         return storage[getBlockStartPositionPointer(rowPosition, columnPosition) + inBlockPosition] | ||||
|     } | ||||
| 
 | ||||
|     val size = storage.size | ||||
| 
 | ||||
|     operator fun get(absolutePosition: Int) : UByte { | ||||
|         return storage[absolutePosition] | ||||
|     } | ||||
| 
 | ||||
|     operator fun set(rowPosition: Int, columnPosition: Int, inBlockPosition: Int, value: UByte) { | ||||
|         storage[getBlockStartPositionPointer(rowPosition, columnPosition) + inBlockPosition] = value | ||||
|     } | ||||
| 
 | ||||
|     operator fun set(absolutePosition: Int, value: UByte) { | ||||
|         storage[absolutePosition] = value | ||||
|     } | ||||
| 
 | ||||
|     fun getBlockPointer(rowPosition: Int, columnPosition: Int) : ArgonBlockPointer { | ||||
|         return ArgonBlockPointerWithMatrix(getBlockStartPositionPointer(rowPosition, columnPosition), this) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     fun sliceArray(indices: IntRange): UByteArray { | ||||
|         return storage.sliceArray(indices) | ||||
|     } | ||||
| 
 | ||||
|     fun getBlockAt(rowPosition: Int, columnPosition: Int) : UByteArray { | ||||
|                 println("Expensive get") | ||||
|         return storage.copyOfRange( | ||||
|             getBlockStartPositionPointer(rowPosition, columnPosition), | ||||
|             getBlockStartPositionPointer(rowPosition, columnPosition) + 1024 | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     fun setBlockAt(rowPosition: Int, columnPosition: Int, blockValue: UByteArray) { | ||||
|         blockValue.copyInto( | ||||
|             storage, | ||||
|             getBlockStartPositionPointer(rowPosition, columnPosition) | ||||
|             ) | ||||
|     } | ||||
| 
 | ||||
|     private inline fun getBlockStartPositionPointer(rowPosition: Int, columnPosition: Int) : Int { | ||||
|         return rowPosition * columnCount * 1024 + columnPosition * 1024 | ||||
|     } | ||||
| 
 | ||||
|     internal fun clearMatrix() { | ||||
|         for( index in storage.indices) { storage[index] = 0U } | ||||
|     } | ||||
| 
 | ||||
|     private class ArgonBlockPointerWithMatrix constructor(override val blockStartPosition: Int, val matrix: ArgonMatrix) : ArgonBlockPointer { | ||||
| 
 | ||||
|         override fun pointerArithmetic(block: (Int, Int) -> Int): ArgonBlockPointer { | ||||
|             return ArgonBlockPointerWithMatrix(block(blockStartPosition, matrix.size), matrix) | ||||
|         } | ||||
| 
 | ||||
|         override fun asInt(): Int { | ||||
|             return blockStartPosition | ||||
|         } | ||||
| 
 | ||||
|         override operator fun get(blockPosition: Int) : UByte { | ||||
|             return matrix[blockStartPosition + blockPosition] | ||||
|         } | ||||
| 
 | ||||
|         override fun set(blockPosition: Int, value: UByte) { | ||||
|             matrix[blockStartPosition + blockPosition] = value | ||||
|         } | ||||
| 
 | ||||
|         override infix fun xorBlocksAndGetPointerToNewBlock(other: ArgonBlockPointer) : ArgonBlock { | ||||
|             return ArgonBlock(UByteArray(1024){ | ||||
|                 matrix[blockStartPosition + it] xor other[it] | ||||
|             }) | ||||
|         } | ||||
| 
 | ||||
|         override fun getAsUByteArray(): UByteArray { | ||||
|             return matrix.storage.slice(blockStartPosition until blockStartPosition + 1024).toUByteArray() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS") | ||||
| inline class ArgonBlock internal constructor(internal val storage: UByteArray) { | ||||
|     constructor() : this(UByteArray(1024)) | ||||
|     operator fun get(index: Int) : UByte { | ||||
|         return storage.get(index) | ||||
|     } | ||||
|     operator fun set(index: Int, value: UByte) { | ||||
|         storage.set(index, value) | ||||
|     } | ||||
| 
 | ||||
|     val size: Int get() = storage.size | ||||
| 
 | ||||
|     internal fun getAsUByteArray() : UByteArray = storage | ||||
| 
 | ||||
|     fun getBlockPointer() : ArgonBlockPointer{ | ||||
|         return ArgonBlockPointerWithBlock( this) | ||||
|     } | ||||
| 
 | ||||
|     infix fun xorInplaceWith(other: ArgonBlock) : ArgonBlock { | ||||
|         storage.indices.forEach { | ||||
|             this[it] = this[it] xor other[it] | ||||
|         } | ||||
|         return this //For chaining | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     private class ArgonBlockPointerWithBlock constructor(val storageBlock: ArgonBlock) : ArgonBlockPointer { | ||||
|         override val blockStartPosition: Int = 0 | ||||
| 
 | ||||
|         override fun pointerArithmetic(block: (Int, Int) -> Int): ArgonBlockPointer { | ||||
|             throw RuntimeException("Haven't really tought out pointer arithmetic with blocks") | ||||
|         } | ||||
| 
 | ||||
|         override fun asInt(): Int { | ||||
|             return blockStartPosition | ||||
|         } | ||||
|         override operator fun get(blockPosition: Int) : UByte { | ||||
|             return storageBlock[blockPosition] | ||||
|         } | ||||
| 
 | ||||
|         override fun set(blockPosition: Int, value: UByte) { | ||||
|             storageBlock[blockPosition] = value | ||||
|         } | ||||
| 
 | ||||
|         override infix fun xorBlocksAndGetPointerToNewBlock(other: ArgonBlockPointer) : ArgonBlock { | ||||
|             return ArgonBlock(UByteArray(1024){ | ||||
|                 storageBlock[it] xor other[it] | ||||
|             }) | ||||
|         } | ||||
| 
 | ||||
|         override fun getAsUByteArray(): UByteArray { | ||||
|             return storageBlock.storage | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,118 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.mac | ||||
| 
 | ||||
| import com.ionspin.kotlin.bignum.Endianness | ||||
| import com.ionspin.kotlin.bignum.integer.BigInteger | ||||
| import com.ionspin.kotlin.bignum.integer.Sign | ||||
| import com.ionspin.kotlin.crypto.util.fromLittleEndianUByteArrayToBigEndianUByteArray | ||||
| import com.ionspin.kotlin.crypto.util.hexColumsPrint | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 18-Jun-2020 | ||||
|  */ | ||||
| class Poly1305(key: UByteArray) { | ||||
|     companion object { | ||||
|         fun clampR(r: UByteArray) : UByteArray { | ||||
|             val clamped = UByteArray(16) { r[it] } | ||||
|             clamped[3] = r[3] and 0b00001111U | ||||
|             clamped[7] = r[7] and 0b00001111U | ||||
|             clamped[11] = r[11] and 0b00001111U | ||||
|             clamped[15] = r[15] and 0b00001111U | ||||
| 
 | ||||
|             clamped[4] = r[4] and 0b11111100U | ||||
|             clamped[8] = r[8] and 0b11111100U | ||||
|             clamped[12] = r[12] and 0b11111100U | ||||
|             return clamped | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         val P = BigInteger.fromUByteArray( | ||||
|             ubyteArrayOf( | ||||
|                 0x03U, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xffU, 0xfbU | ||||
|             ), | ||||
|             Sign.POSITIVE | ||||
|         ) | ||||
|         val powersOfTwo = Array(129) { | ||||
|             BigInteger.ONE shl it | ||||
|         } | ||||
|         val resultMask = (BigInteger.ONE shl 128) - 1 | ||||
|         //Doesn't have to be every power, just divisible by 8 | ||||
|         val twoToThe128 = BigInteger.ONE.shl(128) | ||||
| 
 | ||||
|         /** | ||||
|          * Limit - stop poly calculating tag at desired index, ignored if 0 | ||||
|          */ | ||||
|         fun poly1305Authenticate(key: UByteArray, message: UByteArray) : UByteArray { | ||||
|             val r = clampR(UByteArray(16) { key[it] }) | ||||
|             val s= UByteArray(16) { key[it + 16]} | ||||
| 
 | ||||
|             var accumulator = BigInteger.ZERO | ||||
|             val rAsBigInt = BigInteger.fromUByteArray(r.fromLittleEndianUByteArrayToBigEndianUByteArray(), Sign.POSITIVE) //TODO convert from little endian ubyte array to what BigInteger expects | ||||
|             val sAsBigInt = BigInteger.fromUByteArray(s.fromLittleEndianUByteArrayToBigEndianUByteArray(), Sign.POSITIVE) | ||||
|             val blocks = message.size / 16 | ||||
|             val remainder = message.size % 16 | ||||
| 
 | ||||
|             for (i in 0 until blocks) { | ||||
|                 val slice = message.sliceArray(i * 16 until i * 16 + 16) | ||||
|                 slice.hexColumsPrint() | ||||
|                 val blockAsInt = BigInteger.fromUByteArray(slice.fromLittleEndianUByteArrayToBigEndianUByteArray(), Sign.POSITIVE) + powersOfTwo[128] | ||||
|                 accumulator += blockAsInt | ||||
|                 accumulator *= rAsBigInt | ||||
|                 accumulator %= P | ||||
|             } | ||||
|             if (remainder != 0) { | ||||
|                 val slice = message.sliceArray(blocks * 16 until blocks * 16 + remainder) | ||||
|                 val blockAsInt = BigInteger.fromUByteArray(slice.fromLittleEndianUByteArrayToBigEndianUByteArray(), Sign.POSITIVE) + powersOfTwo[remainder * 8] | ||||
|                 accumulator += blockAsInt | ||||
|                 accumulator *= rAsBigInt | ||||
|                 accumulator %= P | ||||
|             } | ||||
| 
 | ||||
|             accumulator += sAsBigInt | ||||
|             accumulator = accumulator and resultMask | ||||
|             val result = accumulator.toUByteArray() | ||||
|             result.reverse() | ||||
|             return result | ||||
| 
 | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
|     var rAsBigInt = BigInteger.fromUByteArray( | ||||
|         clampR(key.sliceArray(0 until 16)).fromLittleEndianUByteArrayToBigEndianUByteArray(), | ||||
|         Sign.POSITIVE | ||||
|     ) | ||||
|     var sAsBigInt = BigInteger.fromUByteArray( | ||||
|         key.sliceArray(16 until 32).fromLittleEndianUByteArrayToBigEndianUByteArray(), | ||||
|         Sign.POSITIVE) | ||||
|     var accumulator = BigInteger.ZERO | ||||
| 
 | ||||
|     fun updateMac(data : UByteArray) { | ||||
|         if (data.size != 16) { | ||||
|             throw RuntimeException("Invalide block size, required 16, got ${data.size}") | ||||
|         } | ||||
|         val blockAsInt = BigInteger.fromUByteArray( | ||||
|             data.fromLittleEndianUByteArrayToBigEndianUByteArray(), Sign.POSITIVE | ||||
|         ) + powersOfTwo[128] | ||||
|         accumulator += blockAsInt | ||||
|         accumulator *= rAsBigInt | ||||
|         accumulator %= P | ||||
|     } | ||||
| 
 | ||||
|     fun finalizeMac(data: UByteArray = ubyteArrayOf()) : UByteArray{ | ||||
|         if (data.size != 0) { | ||||
|             val blockAsInt = BigInteger.fromUByteArray( | ||||
|                 data.fromLittleEndianUByteArrayToBigEndianUByteArray(), | ||||
|                 Sign.POSITIVE | ||||
|             ) + powersOfTwo[data.size * 8] | ||||
|             accumulator += blockAsInt | ||||
|             accumulator *= rAsBigInt | ||||
|             accumulator %= P | ||||
|         } | ||||
|         accumulator += sAsBigInt | ||||
|         accumulator = accumulator and resultMask | ||||
|         val result = accumulator.toUByteArray() | ||||
|         result.reverse() | ||||
|         return result | ||||
|     } | ||||
| } | ||||
| @ -1,28 +0,0 @@ | ||||
| package com.ionspin.kotlin.crypto.symmetric | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.util.hexStringToUByteArray | ||||
| 
 | ||||
| /** | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 13-Jun-2020 | ||||
|  */ | ||||
| internal sealed class InternalAesKey(val key: String, val keyLength: Int) { | ||||
|     val keyArray: UByteArray = key.hexStringToUByteArray() | ||||
|     val numberOf32BitWords = keyLength / 32 | ||||
| 
 | ||||
|     class Aes128Key(key: String) : InternalAesKey(key, 128) | ||||
|     class Aes192Key(key: String) : InternalAesKey(key, 192) | ||||
|     class Aes256Key(key: String) : InternalAesKey(key, 256) | ||||
| 
 | ||||
|     init { | ||||
|         checkKeyLength(key, keyLength) | ||||
|     } | ||||
| 
 | ||||
|     fun checkKeyLength(key: String, expectedLength: Int) { | ||||
|         if ((key.length / 2) != expectedLength / 8) { | ||||
|             throw RuntimeException("Invalid key length") | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -1,243 +0,0 @@ | ||||
| /* | ||||
|  *    Copyright 2019 Ugljesa Jovanovic | ||||
|  * | ||||
|  *    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *    you may not use this file except in compliance with the License. | ||||
|  *    You may obtain a copy of the License at | ||||
|  * | ||||
|  *        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  *    Unless required by applicable law or agreed to in writing, software | ||||
|  *    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *    See the License for the specific language governing permissions and | ||||
|  *    limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package com.ionspin.kotlin.crypto.symmetric | ||||
| 
 | ||||
| import com.ionspin.kotlin.crypto.SRNG | ||||
| import com.ionspin.kotlin.crypto.util.xor | ||||
| 
 | ||||
| /** | ||||
|  * Advanced encryption standard with cipher block chaining and PKCS #5 | ||||
|  * | ||||
|  * For bulk encryption/decryption use [AesCbcPure.encrypt] and [AesCbcPure.decrypt] | ||||
|  * | ||||
|  * To get an instance of AesCbc and then feed it data sequentially with [addData] use [createEncryptor] and [createDecryptor] | ||||
|  * | ||||
|  * Created by Ugljesa Jovanovic | ||||
|  * ugljesa.jovanovic@ionspin.com | ||||
|  * on 21-Sep-2019 | ||||
|  */ | ||||
| 
 | ||||
| internal class AesCbcPure internal constructor(val aesKey: InternalAesKey, val mode: Mode, initializationVector: UByteArray? = null) { | ||||
| 
 | ||||
|     companion object { | ||||
|         const val BLOCK_BYTES = 16 | ||||
|         /** | ||||
|          * Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all | ||||
|          * data call [encrypt] | ||||
|          */ | ||||
|         fun createEncryptor(aesKey: InternalAesKey) : AesCbcPure { | ||||
|             return AesCbcPure(aesKey, Mode.ENCRYPT) | ||||
|         } | ||||
|         /** | ||||
|          * Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all | ||||
|          * data call [decrypt] | ||||
|          */ | ||||
|         fun createDecryptor(aesKey : InternalAesKey) : AesCbcPure { | ||||
|             return AesCbcPure(aesKey, Mode.DECRYPT) | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Bulk encryption, returns encrypted data and a random initialization vector | ||||
|          */ | ||||
|         fun encrypt(aesKey: InternalAesKey, data: UByteArray): EncryptedDataAndInitializationVector { | ||||
|             val aesCbc = AesCbcPure(aesKey, Mode.ENCRYPT) | ||||
|             aesCbc.addData(data) | ||||
|             return aesCbc.encrypt() | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Bulk decryption, returns decrypted data | ||||
|          */ | ||||
|         fun decrypt(aesKey: InternalAesKey, data: UByteArray, initialCounter: UByteArray? = null): UByteArray { | ||||
|             val aesCbc = AesCbcPure(aesKey, Mode.DECRYPT, initialCounter) | ||||
|             aesCbc.addData(data) | ||||
|             return aesCbc.decrypt() | ||||
|         } | ||||
| 
 | ||||
|         private fun padToBlock(unpadded: UByteArray): UByteArray { | ||||
|             val paddingSize = 16 - unpadded.size | ||||
|             if (unpadded.size == BLOCK_BYTES) { | ||||
|                 return unpadded | ||||
|             } | ||||
| 
 | ||||
|             if (unpadded.size == BLOCK_BYTES) { | ||||
|                 return UByteArray(BLOCK_BYTES) { | ||||
|                     BLOCK_BYTES.toUByte() | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (unpadded.size > BLOCK_BYTES) { | ||||
|                 throw IllegalStateException("Block larger than 128 bytes") | ||||
|             } | ||||
| 
 | ||||
|             return UByteArray(BLOCK_BYTES) { | ||||
|                 when (it) { | ||||
|                     in unpadded.indices -> unpadded[it] | ||||
|                     else -> paddingSize.toUByte() | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     var currentOutput: UByteArray = ubyteArrayOf() | ||||
|     var previousEncrypted: UByteArray = ubyteArrayOf() | ||||
|     val initVector = if (initializationVector.isNullOrEmpty()) { | ||||
|         SRNG.getRandomBytes(16) | ||||
|     } else { | ||||
|         initializationVector | ||||
|     } | ||||
| 
 | ||||
|     val output = MutableList<UByteArray>(0) { ubyteArrayOf() } | ||||
| 
 | ||||
|     var buffer: UByteArray = UByteArray(16) { 0U } | ||||
|     var bufferCounter = 0 | ||||
| 
 | ||||
|     fun addData(data: UByteArray) { | ||||
|         //Padding | ||||
|         when { | ||||
|             bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter) | ||||
|             bufferCounter + data.size >= BLOCK_BYTES -> { | ||||
|                 val chunked = data.chunked(BLOCK_BYTES) | ||||
|                 chunked.forEach { chunk -> | ||||
|                     if (bufferCounter + chunk.size < BLOCK_BYTES) { | ||||
|                         appendToBuffer(chunk.toUByteArray(), bufferCounter) | ||||
|                     } else { | ||||
|                         chunk.toUByteArray().copyInto( | ||||
|                             destination = buffer, | ||||
|                             destinationOffset = bufferCounter, | ||||
|                             startIndex = 0, | ||||
|                             endIndex = BLOCK_BYTES - bufferCounter | ||||
|                         ) | ||||
|                         output += consumeBlock(buffer) | ||||
|                         buffer = UByteArray(BLOCK_BYTES) { | ||||
|                             when (it) { | ||||
|                                 in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> { | ||||
|                                     chunk[it + (BLOCK_BYTES - bufferCounter)] | ||||
|                                 } | ||||
|                                 else -> { | ||||
|                                     0U | ||||
|                                 } | ||||
|                             } | ||||
| 
 | ||||
|                         } | ||||
|                         bufferCounter = chunk.size - (BLOCK_BYTES - bufferCounter) | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Encrypt fed data and return it alongside the randomly chosen initialization vector. | ||||
|      * This also applies correct PKCS#7 padding | ||||
|      * @return Encrypted data and initialization vector | ||||
|      */ | ||||
|     fun encrypt(): EncryptedDataAndInitializationVector { | ||||
|         if (bufferCounter > 0) { | ||||
|             val lastBlockPadded = padToBlock(buffer) | ||||
|             if (lastBlockPadded.size > BLOCK_BYTES) { | ||||
|                 val chunks = lastBlockPadded.chunked(BLOCK_BYTES).map { it.toUByteArray() } | ||||
|                 output += consumeBlock(chunks[0]) | ||||
|                 output += consumeBlock(chunks[1]) | ||||
|             } else { | ||||
|                 output += consumeBlock(lastBlockPadded) | ||||
|             } | ||||
|         } else { | ||||
|             output += consumeBlock(UByteArray(BLOCK_BYTES) { BLOCK_BYTES.toUByte()}) | ||||
|         } | ||||
|         return EncryptedDataAndInitializationVector( | ||||
|             output.reversed().foldRight(UByteArray(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes }, | ||||
|             initVector | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Decrypt data | ||||
|      * @return Decrypted data | ||||
|      */ | ||||
|     fun decrypt(): UByteArray { | ||||
|         val removePaddingCount = output.last().last() | ||||
| 
 | ||||
|         val removedPadding = if (removePaddingCount > 0U && removePaddingCount < 16U) { | ||||
|             output.last().dropLast(removePaddingCount.toInt() and 0x7F) | ||||
|         } else { | ||||
|             ubyteArrayOf() | ||||
|         }.toUByteArray() | ||||
|         val preparedOutput = (output.dropLast(1) + listOf(removedPadding)) | ||||
|         //JS compiler freaks out here if we don't supply exact type | ||||
|         val reversed : List<UByteArray> = preparedOutput.reversed() as List<UByteArray> | ||||
|         val folded : UByteArray = reversed.foldRight(UByteArray(0) { 0U }) { uByteArray, acc -> | ||||
|             acc + uByteArray | ||||
|         } | ||||
|         return folded | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private fun appendToBuffer(array: UByteArray, start: Int) { | ||||
|         array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size) | ||||
|         bufferCounter += array.size | ||||
|     } | ||||
| 
 | ||||
|     private fun consumeBlock(data: UByteArray): UByteArray { | ||||
|         return when (mode) { | ||||
|             Mode.ENCRYPT -> { | ||||
|                 currentOutput = if (currentOutput.isEmpty()) { | ||||
|                     println("IV: $initVector") | ||||
|                     AesPure.encrypt(aesKey, data xor initVector) | ||||
|                 } else { | ||||
|                     AesPure.encrypt(aesKey, data xor currentOutput) | ||||
|                 } | ||||
|                 currentOutput | ||||
|             } | ||||
|             Mode.DECRYPT -> { | ||||
|                 if (currentOutput.isEmpty()) { | ||||
|                     currentOutput = AesPure.decrypt(aesKey, data) xor initVector | ||||
|                 } else { | ||||
|                     currentOutput = AesPure.decrypt(aesKey, data) xor previousEncrypted | ||||
|                 } | ||||
|                 previousEncrypted = data | ||||
|                 currentOutput | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| data class EncryptedDataAndInitializationVector(val encryptedData : UByteArray, val initializationVector : UByteArray) { | ||||
|     override fun equals(other: Any?): Boolean { | ||||
|         if (this === other) return true | ||||
|         if (other == null || this::class != other::class) return false | ||||
| 
 | ||||
|         other as EncryptedDataAndInitializationVector | ||||
| 
 | ||||
|         if (!encryptedData.contentEquals(other.encryptedData)) return false | ||||
|         if (!initializationVector.contentEquals(other.initializationVector)) return false | ||||
| 
 | ||||
|         return true | ||||
|     } | ||||
| 
 | ||||
|     override fun hashCode(): Int { | ||||
|         var result = encryptedData.contentHashCode() | ||||
|         result = 31 * result + initializationVector.contentHashCode() | ||||
|         return result | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user