diff --git a/CHANGELOG.md b/CHANGELOG.md index fe5ce1df..51f150c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,16 @@ # Changelog -## 2.0.0 (Unreleased) -We're marking this as a major version release due to new implementions of `KeyFactory`. -Though these changes are expected to have no visible impact (other than performance) to consumers, -they do correspond to a significant rearchitecture of ACCP. +## EVP (Unreleased) +(Temporarily marking it as 2.0) ### Improvements * Add `KeyFactory` implementations for RSA, EC, DH, and DSA keys. This also includes our own implementations of keys for the same algorithms. [PR #132](https://github.com/corretto/amazon-corretto-crypto-provider/pull/132) ### Patches * Correctly reject non-empty `PSource.PSpecified` values for RSA-OAEP. +## 1.6.1 +### Patches +* Fix an issue where a race condition can cause ACCP's MessageDigest hashing algorithms to return the same value for different inputs [PR #157](https://github.com/corretto/amazon-corretto-crypto-provider/pull/157) ## 1.6.0 ### Breaking Change diff --git a/CMakeLists.txt b/CMakeLists.txt index 414d3bf4..76732968 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ endif() if (NOT DEFINED TEST_JAVA_HOME) set(TEST_JAVA_EXECUTABLE ${Java_JAVA_EXECUTABLE}) + set(TEST_JAVA_MAJOR_VERSION ${Java_VERSION_MAJOR}) else() set(TEST_JAVA_EXECUTABLE ${TEST_JAVA_HOME}/bin/java) endif() @@ -608,8 +609,20 @@ add_custom_command( add_custom_target(tests-jar DEPENDS ${TESTS_JAR}) set_property(TARGET tests-jar PROPERTY JAR_FILE ${TESTS_JAR}) +# These flags are necessary in Java17+ in order for certain unit tests to +# perform deep reflection on nonpublic members +if (TEST_JAVA_MAJOR_VERSION VERSION_GREATER_EQUAL 17) + set(TEST_ADD_OPENS + --add-opens java.base/javax.crypto=ALL-UNNAMED + --add-opens java.base/java.lang.invoke=ALL-UNNAMED + --add-opens java.base/java.security=ALL-UNNAMED + --add-exports jdk.crypto.ec/sun.security.ec=java.base + ) +endif() + set(TEST_RUNNER_ARGUMENTS -javaagent:${JACOCO_AGENT_JAR}=destfile=coverage/jacoco.exec,classdumpdir=coverage/classes + ${TEST_ADD_OPENS} -Djava.library.path=$ -Dcom.amazon.corretto.crypto.provider.inTestSuite=hunter2 -Dtest.data.dir=${TEST_DATA_DIR} diff --git a/README.md b/README.md index af341926..2e40fea4 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ Whether you're using Maven, Gradle, or some other build system that also pulls packages from Maven Central, it's important to specify `linux-x86_64` as the classifier. You'll get an empty package otherwise. -Regardless of how you acquire ACCP (Maven, manual build, etc.) you will still need to follow the guidance in the [Configuration section][#configuration] to enable ACCP in your application. +Regardless of how you acquire ACCP (Maven, manual build, etc.) you will still need to follow the guidance in the [Configuration section](#configuration) to enable ACCP in your application. ### Maven Add the following to your `pom.xml` or wherever you configure your Maven dependencies. @@ -175,6 +175,8 @@ Building this provider requires a 64 bit Linux build system with the following p * release: **Default target** depends on build, test, and coverage * overkill: Run **all** tests (no coverage) * generateEclipseClasspath: Generates a `.classpath` file which is understandable by Eclipse and VS Code to make development easier. (This should ideally be run prior to opening ACCP in your IDE.) +* single_test: Runs a single unit test. The test is selected with the Java system property `SINGLE_TEST`. For example: `./gradlew single_test -DSINGLE_TEST=com.amazon.corretto.crypto.provider.test.EcGenTest` + (You may need to do a clean build when switching between selected tests.) ## Configuration There are several ways to configure the ACCP as the highest priority provider in Java. diff --git a/arm.patch b/arm.patch deleted file mode 100644 index f064c3ee..00000000 --- a/arm.patch +++ /dev/null @@ -1,73 +0,0 @@ -This patch file was written by szelei.t@gmail.com and released by him under an MIT license at -https://github.com/openssl/openssl/issues/10842#issuecomment-608287665 - ---- a/crypto/sha/asm/sha1-armv8.pl 2020-01-10 15:51:40.284641928 +0100 -+++ b/crypto/sha/asm/sha1-armv8.pl 2020-01-10 15:52:46.924618409 +0100 -@@ -329,7 +329,7 @@ - #endif - .asciz "SHA1 block transform for ARMv8, CRYPTOGAMS by " - .align 2 --.comm OPENSSL_armcap_P,4,4 -+.hidden OPENSSL_armcap_P - ___ - }}} - ---- a/crypto/sha/asm/sha512-armv8.pl 2019-09-10 15:13:07.000000000 +0200 -+++ b/crypto/sha/asm/sha512-armv8.pl 2020-01-10 16:03:59.641110272 +0100 -@@ -842,7 +842,7 @@ - - $code.=<<___; - #ifndef __KERNEL__ --.comm OPENSSL_armcap_P,4,4 -+.hidden OPENSSL_armcap_P - #endif - ___ - ---- a/crypto/poly1305/asm/poly1305-armv8.pl 2019-09-10 15:13:07.000000000 +0200 -+++ b/crypto/poly1305/asm/poly1305-armv8.pl 2020-01-13 09:11:50.915315471 +0100 -@@ -93,9 +93,9 @@ - - tst w17,#ARMV7_NEON - -- adr $d0,poly1305_blocks -+ adr $d0,.Lpoly1305_blocks - adr $r0,poly1305_blocks_neon -- adr $d1,poly1305_emit -+ adr $d1,.Lpoly1305_emit - adr $r1,poly1305_emit_neon - - csel $d0,$d0,$r0,eq -@@ -115,6 +115,7 @@ - .type poly1305_blocks,%function - .align 5 - poly1305_blocks: -+.Lpoly1305_blocks: - ands $len,$len,#-16 - b.eq .Lno_data - -@@ -179,6 +180,7 @@ - .type poly1305_emit,%function - .align 5 - poly1305_emit: -+.Lpoly1305_emit: - ldp $h0,$h1,[$ctx] // load hash base 2^64 - ldr $h2,[$ctx,#16] - ldp $t0,$t1,[$nonce] // load nonce -@@ -288,7 +290,7 @@ - ldr $is_base2_26,[$ctx,#24] - cmp $len,#128 - b.hs .Lblocks_neon -- cbz $is_base2_26,poly1305_blocks -+ cbz $is_base2_26,.Lpoly1305_blocks - - .Lblocks_neon: - .inst 0xd503233f // paciasp -@@ -869,7 +871,7 @@ - .align 5 - poly1305_emit_neon: - ldr $is_base2_26,[$ctx,#24] -- cbz $is_base2_26,poly1305_emit -+ cbz $is_base2_26,.Lpoly1305_emit - - ldp w10,w11,[$ctx] // load hash value base 2^26 - ldp w12,w13,[$ctx,#8] diff --git a/build.gradle b/build.gradle index 070ea98a..7bf9bb6c 100644 --- a/build.gradle +++ b/build.gradle @@ -48,15 +48,15 @@ repositories { dependencies { // Separate so we can extract the jar for the agent specifically - jacocoAgent group: 'org.jacoco', name: 'org.jacoco.agent', version: '0.8.3', classifier: 'runtime' + jacocoAgent group: 'org.jacoco', name: 'org.jacoco.agent', version: '0.8.7', classifier: 'runtime' // Separate so we can extract the jar for the runner specifically testRunner group: 'org.junit.platform', name: 'junit-platform-console-standalone', version: '1.6.2' testDep 'org.junit.jupiter:junit-jupiter:5.6.2' testDep 'org.junit.vintage:junit-vintage-engine:5.6.2' - testDep 'org.bouncycastle:bcprov-debug-jdk15on:1.65' - testDep 'org.bouncycastle:bcpkix-jdk15on:1.65' + testDep 'org.bouncycastle:bcprov-debug-jdk15on:1.69' + testDep 'org.bouncycastle:bcpkix-jdk15on:1.69' testDep 'commons-codec:commons-codec:1.12' testDep 'org.hamcrest:hamcrest:2.1' testDep 'org.jacoco:org.jacoco.core:0.8.3' @@ -83,13 +83,6 @@ task getOpensslSrc(type: Copy) { def outputDir = file("${buildDir}/openssl/") from tarTree(tarFile) into outputDir - - doLast { - // Remove this once the patch is incorporated into an official OpenSSL release - ant.patch(patchfile: "${projectDir}/arm.patch", - dir: opensslSrcPath, - strip: 1) - } } task buildOpenSsl { @@ -160,6 +153,9 @@ task executeCmake(type: Exec) { if (System.properties['TEST_JAVA_HOME'] != null) { args '-DTEST_JAVA_HOME=' + System.properties['TEST_JAVA_HOME'] } + if (System.properties['TEST_JAVA_MAJOR_VERSION'] != null) { + args '-DTEST_JAVA_MAJOR_VERSION=' + System.properties['TEST_JAVA_MAJOR_VERSION'] + } if (System.properties['SINGLE_TEST'] != null) { args '-DSINGLE_TEST=' + System.properties['SINGLE_TEST'] @@ -442,7 +438,7 @@ if (project.hasProperty('mavenUser') && project.hasProperty('jcecertAlias')) { } } - task sign(overwrite: true) { + task sign { doFirst { ant.fail('Insufficient configuration for signing') } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf0..7454180f 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 838e6bc8..ffed3a25 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index b0d6d0ab..1b6c7873 100755 --- a/gradlew +++ b/gradlew @@ -1,13 +1,13 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # 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 +# https://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, @@ -17,78 +17,113 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -105,84 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 15e1ee37..ac1b06f9 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -5,7 +5,7 @@ @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem -@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -51,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -61,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/src/com/amazon/corretto/crypto/provider/InputBuffer.java b/src/com/amazon/corretto/crypto/provider/InputBuffer.java index 6f428fbd..026a4a12 100644 --- a/src/com/amazon/corretto/crypto/provider/InputBuffer.java +++ b/src/com/amazon/corretto/crypto/provider/InputBuffer.java @@ -6,9 +6,7 @@ import java.nio.ByteBuffer; import java.util.Optional; import java.util.function.BiConsumer; -import java.util.function.Consumer; import java.util.function.Function; -import java.util.function.Supplier; /** *

@@ -31,9 +29,9 @@ * InitialUpdate handlers default to calling their Update equivalents. * {@link #withSinglePass(ArrayFunction)} defaults to calling the update and doFinal steps. * - * @param T result type - * @param S state type - * @param X exception which can be thrown upon completion + * @param result type + * @param state type + * @param exception which can be thrown upon completion */ // Note: Please consult the "How to Read JML" readme to understand the JML annotations // in this file (contained in //@ or /*@ @*/ comments). @@ -138,12 +136,12 @@ public static interface ByteBufferBiConsumer extends BiConsumer extends Supplier { + public static interface StateSupplier extends Function { //@ also //@ public normal_behavior //@ ensures \result != null ==> \fresh(\result); //@ pure - public /*@ nullable @*/ S get(); + public /*@ nullable @*/ S apply(S state); } //@ private invariant 0 <= buffSize; @@ -163,9 +161,7 @@ public static interface StateSupplier extends Supplier { //@ spec_public private /*@ nullable @*/ FinalHandlerFunction finalHandler; //@ spec_public - private /*@ { Consumer.Local } @*/ Consumer stateResetter = (ignored) -> { }; // NOP - //@ spec_public - private StateSupplier stateSupplier = () -> state; + private StateSupplier stateSupplier = (oldState) -> oldState; //@ spec_public private Optional> stateCloner = Optional.empty(); // If absent, delegates to arrayUpdater @@ -229,7 +225,6 @@ public static interface StateSupplier extends Supplier { public void reset() { buff.reset(); firstData = true; - state = null; /*@ set bytesReceived = 0; @ set bytesProcessed = 0; @ set bufferState = ((bufferState == BufferState.Uninitialized) @@ -311,15 +306,6 @@ public InputBuffer withStateCloner(final /*@ nullable @*/ Function withStateResetter(final /*@ { Consumer.Local } @*/ Consumer resetter) { - stateResetter = resetter; - return this; - } - /*@ normal_behavior @ requires canSetHandler(bufferState); @ assignable stateSupplier; @@ -469,7 +455,7 @@ private void processBuffer(boolean forceInit) { buff.reset(); //@ set bytesProcessed = bytesProcessed + oldSize; } else { - state = stateSupplier.get(); + state = stateSupplier.apply(state); } //@ set bufferState = BufferState.HandlerCalled; firstData = false; @@ -522,7 +508,7 @@ public void update(final ByteBuffer src) { if (initialBufferUpdater.isPresent()) { state = initialBufferUpdater.get().apply(src.slice()); } else { - state = stateSupplier.get(); + state = stateSupplier.apply(state); bufferUpdater.get().accept(state, src.slice()); } } else { @@ -569,7 +555,7 @@ public void update(final byte[] src, final int offset, final int length) { if (initialArrayUpdater.isPresent()) { state = initialArrayUpdater.get().apply(src, offset, length); } else { - state = stateSupplier.get(); + state = stateSupplier.apply(state); arrayUpdater.accept(state, src, offset, length); } } else { diff --git a/template-src/com/amazon/corretto/crypto/provider/TemplateHashSpi.java b/template-src/com/amazon/corretto/crypto/provider/TemplateHashSpi.java index 9b5a3228..e095f318 100644 --- a/template-src/com/amazon/corretto/crypto/provider/TemplateHashSpi.java +++ b/template-src/com/amazon/corretto/crypto/provider/TemplateHashSpi.java @@ -28,7 +28,6 @@ public final class TemplateHashSpi extends MessageDigestSpi implements Cloneable private static final int HASH_SIZE; private static final byte[] INITIAL_CONTEXT; - private byte[] myContext; private InputBuffer buffer; static { @@ -108,33 +107,40 @@ private static void synchronizedFinish(byte[] context, byte[] digest, int offset } } - private byte[] resetContext() { - System.arraycopy(INITIAL_CONTEXT, 0, myContext, 0, INITIAL_CONTEXT.length); - return myContext; + private static byte[] resetContext(byte[] context) { + if (context == null) { + context = INITIAL_CONTEXT.clone(); + } else { + System.arraycopy(INITIAL_CONTEXT, 0, context, 0, INITIAL_CONTEXT.length); + } + return context; + } + + private static byte[] doFinal(byte[] context) { + final byte[] result = new byte[HASH_SIZE]; + synchronizedFinish(context, result, 0); + return result; + } + + private static byte[] singlePass(byte[] src, int offset, int length) { + if (offset != 0 || length != src.length) { + src = Arrays.copyOf(src, length); + offset = 0; + } + final byte[] result = new byte[HASH_SIZE]; + fastDigest(result, src, src.length); + return result; } public TemplateHashSpi() { Loader.checkNativeLibraryAvailability(); - myContext = INITIAL_CONTEXT.clone(); this.buffer = new InputBuffer(1024) - .withInitialStateSupplier(this::resetContext) + .withInitialStateSupplier(TemplateHashSpi::resetContext) .withUpdater(TemplateHashSpi::synchronizedUpdateContextByteArray) .withUpdater(TemplateHashSpi::synchronizedUpdateNativeByteBuffer) - .withDoFinal((context) -> { - final byte[] result = new byte[HASH_SIZE]; - synchronizedFinish(context, result, 0); - return result; - }) - .withSinglePass((src, offset, length) -> { - if (offset != 0 || length != src.length) { - src = Arrays.copyOf(src, length); - offset = 0; - } - final byte[] result = new byte[HASH_SIZE]; - fastDigest(result, src, src.length); - return result; - }) + .withDoFinal(TemplateHashSpi::doFinal) + .withSinglePass(TemplateHashSpi::singlePass) .withStateCloner((context) -> context.clone()); } @@ -165,7 +171,6 @@ public Object clone() { TemplateHashSpi clonedObject = (TemplateHashSpi)super.clone(); clonedObject.buffer = (InputBuffer) buffer.clone(); - clonedObject.myContext = myContext.clone(); return clonedObject; } catch (CloneNotSupportedException e) { diff --git a/tst/com/amazon/corretto/crypto/provider/test/EcGenTest.java b/tst/com/amazon/corretto/crypto/provider/test/EcGenTest.java index 8f5a69d1..0a096d7c 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EcGenTest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EcGenTest.java @@ -10,7 +10,6 @@ import java.math.BigInteger; import java.security.*; -import java.security.interfaces.ECKey; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.spec.ECFieldFp; @@ -22,7 +21,6 @@ import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; -import java.util.Base64; import java.util.List; import org.bouncycastle.asn1.ASN1Encodable; @@ -32,6 +30,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; @@ -47,6 +47,13 @@ @ResourceLock(value = TestUtil.RESOURCE_GLOBAL, mode = ResourceAccessMode.READ) public class EcGenTest { public static final String[][] KNOWN_CURVES = new String[][] { + new String[]{"secp256r1", "NIST P-256", "X9.62 prime256v1", /* "prime256v1", */ "1.2.840.10045.3.1.7"}, + new String[]{"secp384r1", "NIST P-384", "1.3.132.0.34"}, + new String[]{"secp521r1", "NIST P-521", "1.3.132.0.35"}, + }; + + // Not supported in JDK17 + public static final String[][] LEGACY_CURVES = new String[][] { // Prime Curves new String[]{"secp112r1", "1.3.132.0.6"}, new String[]{"secp112r2", "1.3.132.0.7"}, @@ -56,15 +63,10 @@ public class EcGenTest { new String[]{"secp160r1", "1.3.132.0.8"}, new String[]{"secp160r2", "1.3.132.0.30"}, new String[]{"secp192k1", "1.3.132.0.31"}, - // Not supported by Openssl new String[]{"secp192r1", "NIST P-192", "X9.62 prime192v1", /* "prime192v1", */ "1.2.840.10045.3.1.1"}, new String[]{"secp224k1", "1.3.132.0.32"}, new String[]{"secp224r1", "NIST P-224", "1.3.132.0.33"}, new String[]{"secp256k1", "1.3.132.0.10"}, - // Not supported by Openssl - new String[]{"secp256r1", "NIST P-256", "X9.62 prime256v1", /* "prime256v1", */ "1.2.840.10045.3.1.7"}, - new String[]{"secp384r1", "NIST P-384", "1.3.132.0.34"}, - new String[]{"secp521r1", "NIST P-521", "1.3.132.0.35"}, // Binary Curves new String[]{"sect113r1", "1.3.132.0.4"}, new String[]{"sect113r2", "1.3.132.0.5"}, @@ -93,16 +95,17 @@ public class EcGenTest { new String[]{"X9.62 c2tnb359v1", "1.2.840.10045.3.0.18"}, new String[]{"X9.62 c2tnb431r1", "1.2.840.10045.3.0.20"}, }; + public static final ECParameterSpec EXPLICIT_CURVE; private static final KeyFactory KEY_FACTORY; static { - final BigInteger a = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", 16); - final BigInteger b = new BigInteger("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", 16); - final BigInteger p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", 16); - final BigInteger order = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D", 16); - final BigInteger gx = new BigInteger("B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", 16); - final BigInteger gy = new BigInteger("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34", 16); + final BigInteger a = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", 16); + final BigInteger b = new BigInteger("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", 16); + final BigInteger p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", 16); + final BigInteger order = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", 16); + final BigInteger gx = new BigInteger("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", 16); + final BigInteger gy = new BigInteger("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", 16); final ECFieldFp field = new ECFieldFp(p); final EllipticCurve curve = new EllipticCurve(field, a, b); @@ -133,6 +136,19 @@ public void teardown() { jceGen = null; } + private static String[][] legacyCurveParams() { + return LEGACY_CURVES; + } + + @ParameterizedTest + @EnabledForJreRange(min=JRE.JAVA_8, max=JRE.JAVA_14) + @MethodSource("legacyCurveParams") + public void legacyCurves(ArgumentsAccessor arguments) throws GeneralSecurityException { + for (final Object name : arguments.toArray()) { + testCurveByName((String) name); + } + } + private static String[][] knownCurveParams() { return KNOWN_CURVES; } @@ -141,45 +157,60 @@ private static String[][] knownCurveParams() { @MethodSource("knownCurveParams") public void knownCurves(ArgumentsAccessor arguments) throws Exception { for (final Object name : arguments.toArray()) { - ECGenParameterSpec spec = new ECGenParameterSpec((String) name); - nativeGen.initialize(spec); - KeyPair nativePair = nativeGen.generateKeyPair(); - - jceGen.initialize(spec); - KeyPair jcePair = jceGen.generateKeyPair(); - final ECParameterSpec jceParams = ((ECPublicKey) jcePair.getPublic()).getParams(); - AlgorithmParameters p = AlgorithmParameters.getInstance("EC"); - p.init(jceParams); - - final ECParameterSpec nativeParams = ((ECPublicKey) nativePair.getPublic()).getParams(); - assertECEquals((String) name, jceParams, nativeParams); - - // Ensure we can construct the curve using raw numbers rather than the name - nativeGen.initialize(jceParams); - nativePair = nativeGen.generateKeyPair(); - assertECEquals(name + "-explicit", jceParams, nativeParams); - - final SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(nativePair.getPublic().getEncoded()); - ASN1Encodable algorithmParameters = publicKeyInfo.getAlgorithm().getParameters(); - assertTrue(algorithmParameters instanceof ASN1ObjectIdentifier, "Public key uses named curve"); - - // PKCS #8 = SEQ [ Integer, AlgorithmIdentifier, Octet String, ???] - // AlgorithmIdentifier = SEQ [ OID, {OID | SEQ}] - final ASN1Sequence p8 = ASN1Sequence.getInstance(nativePair.getPrivate().getEncoded()); - final ASN1Sequence algIdentifier = (ASN1Sequence) p8.getObjectAt(1); - assertTrue(algIdentifier.getObjectAt(1) instanceof ASN1ObjectIdentifier, "Private key uses named curve"); - - // Check encoding/decoding - Key bouncedKey = KEY_FACTORY.generatePublic(new X509EncodedKeySpec(nativePair.getPublic().getEncoded())); - assertECEquals("Public key survives encoding", (ECPublicKey) nativePair.getPublic(), (ECPublicKey) bouncedKey); - bouncedKey = KEY_FACTORY.generatePrivate(new PKCS8EncodedKeySpec(nativePair.getPrivate().getEncoded())); - assertECEquals("Private key survives encoding", (ECPrivateKey) nativePair.getPrivate(), (ECPrivateKey) bouncedKey); - + testCurveByName((String) name); } } + private void testCurveByName(String name) throws GeneralSecurityException { + ECGenParameterSpec spec = new ECGenParameterSpec(name); + nativeGen.initialize(spec); + KeyPair nativePair = nativeGen.generateKeyPair(); + jceGen.initialize(spec); + KeyPair jcePair = jceGen.generateKeyPair(); + final ECParameterSpec jceParams = ((ECPublicKey) jcePair.getPublic()).getParams(); + final ECParameterSpec nativeParams = ((ECPublicKey) nativePair.getPublic()).getParams(); + assertECEquals(name, jceParams, nativeParams); + + // Ensure we can construct the curve using raw numbers rather than the name + nativeGen.initialize(jceParams); + nativePair = nativeGen.generateKeyPair(); + assertECEquals(name + "-explicit", jceParams, nativeParams); + + final SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(nativePair.getPublic().getEncoded()); + ASN1Encodable algorithmParameters = publicKeyInfo.getAlgorithm().getParameters(); + assertTrue(algorithmParameters instanceof ASN1ObjectIdentifier, "Public key uses named curve"); + + // PKCS #8 = SEQ [ Integer, AlgorithmIdentifier, Octet String, ???] + // AlgorithmIdentifier = SEQ [ OID, {OID | SEQ}] + final ASN1Sequence p8 = ASN1Sequence.getInstance(nativePair.getPrivate().getEncoded()); + final ASN1Sequence algIdentifier = (ASN1Sequence) p8.getObjectAt(1); + assertTrue(algIdentifier.getObjectAt(1) instanceof ASN1ObjectIdentifier, "Private key uses named curve"); + + // Check encoding/decoding + Key bouncedKey = KEY_FACTORY.generatePublic(new X509EncodedKeySpec(nativePair.getPublic().getEncoded())); + assertEquals(nativePair.getPublic(), bouncedKey, "Public key survives encoding"); + bouncedKey = KEY_FACTORY.generatePrivate(new PKCS8EncodedKeySpec(nativePair.getPrivate().getEncoded())); + assertEquals(nativePair.getPrivate(), bouncedKey, "Private key survives encoding"); + } + @ParameterizedTest - @ValueSource(ints = {192, 224, 256, 384, 521}) + @EnabledForJreRange(min=JRE.JAVA_8, max=JRE.JAVA_14) + @ValueSource(ints = {192, 224}) + public void legacyKnownSizes(int keysize) throws GeneralSecurityException { + TestUtil.assumeMinimumVersion("1.2.0", nativeGen.getProvider()); + nativeGen.initialize(keysize); + jceGen.initialize(keysize); + + final KeyPair nativePair = nativeGen.generateKeyPair(); + final KeyPair jcePair = jceGen.generateKeyPair(); + + final ECParameterSpec jceParams = ((ECPublicKey) jcePair.getPublic()).getParams(); + final ECParameterSpec nativeParams = ((ECPublicKey) nativePair.getPublic()).getParams(); + assertECEquals(Integer.toString(keysize), jceParams, nativeParams); + } + + @ParameterizedTest + @ValueSource(ints = {256, 384, 521}) public void knownSizes(int keysize) throws GeneralSecurityException { TestUtil.assumeMinimumVersion("1.2.0", nativeGen.getProvider()); nativeGen.initialize(keysize); @@ -237,6 +268,7 @@ public void unknownCurveValidNid() throws GeneralSecurityException { } @Test + @EnabledForJreRange(min=JRE.JAVA_8, max=JRE.JAVA_14) public void validBinaryCurve() throws GeneralSecurityException { final String name = "sect113r1"; ECGenParameterSpec spec = new ECGenParameterSpec(name); @@ -269,6 +301,28 @@ public void ecdsaValidation() throws GeneralSecurityException { } } + @Test + @EnabledForJreRange(min=JRE.JAVA_8, max=JRE.JAVA_14) + public void LegacyEcdsaValidation() throws GeneralSecurityException { + final byte[] message = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + // We're purposefully using Java's ECDSA logic, since we trust it to be correct + final Signature ecdsa = Signature.getInstance("NONEwithECDSA", "SunEC"); + for (final String[] names : LEGACY_CURVES) { + for (final String name : names) { + nativeGen.initialize(new ECGenParameterSpec(name)); + final KeyPair keyPair = nativeGen.generateKeyPair(); + + ecdsa.initSign(keyPair.getPrivate()); + ecdsa.update(message); + final byte[] signature = ecdsa.sign(); + + ecdsa.initVerify(keyPair.getPublic()); + ecdsa.update(message); + assertTrue(ecdsa.verify(signature), name); + } + } + } + @Test public void defaultParams() throws GeneralSecurityException { nativeGen.generateKeyPair(); diff --git a/tst/com/amazon/corretto/crypto/provider/test/EvpKeyAgreementSpecificTest.java b/tst/com/amazon/corretto/crypto/provider/test/EvpKeyAgreementSpecificTest.java index ee77f866..694355f7 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EvpKeyAgreementSpecificTest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EvpKeyAgreementSpecificTest.java @@ -53,7 +53,7 @@ public class EvpKeyAgreementSpecificTest { KeyAgreement.getInstance("ECDH", NATIVE_PROVIDER); KeyPairGenerator gen = KeyPairGenerator.getInstance("EC"); - gen.initialize(new ECGenParameterSpec("NIST P-224")); + gen.initialize(new ECGenParameterSpec("NIST P-256")); EC_KEYPAIR = gen.generateKeyPair(); gen = KeyPairGenerator.getInstance("DH"); gen.initialize(1024); diff --git a/tst/com/amazon/corretto/crypto/provider/test/EvpKeyAgreementTest.java b/tst/com/amazon/corretto/crypto/provider/test/EvpKeyAgreementTest.java index fa579795..eab3dcf6 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EvpKeyAgreementTest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EvpKeyAgreementTest.java @@ -73,15 +73,9 @@ public class EvpKeyAgreementTest { @BeforeAll public static void setupParams() throws GeneralSecurityException, IOException { MASTER_PARAMS_LIST = new ArrayList<>(); - MASTER_PARAMS_LIST.add(buildEcdhParameters(new ECGenParameterSpec("secp112r1"), "secp112r1")); - MASTER_PARAMS_LIST.add(buildEcdhParameters(new ECGenParameterSpec("NIST P-224"), "NIST P-224")); + MASTER_PARAMS_LIST.add(buildEcdhParameters(new ECGenParameterSpec("NIST P-256"), "NIST P-256")); MASTER_PARAMS_LIST.add(buildEcdhParameters(new ECGenParameterSpec("NIST P-384"), "NIST P-384")); MASTER_PARAMS_LIST.add(buildEcdhParameters(new ECGenParameterSpec("NIST P-521"), "NIST P-521")); - MASTER_PARAMS_LIST.add(buildEcdhParameters(new ECGenParameterSpec("sect113r1"), "sect113r1")); - MASTER_PARAMS_LIST.add(buildEcdhParameters(new ECGenParameterSpec("sect163k1"), "sect163k1")); - MASTER_PARAMS_LIST.add(buildEcdhParameters(new ECGenParameterSpec("sect283k1"), "sect283k1")); - MASTER_PARAMS_LIST.add(buildEcdhParameters(new ECGenParameterSpec("sect571r1"), "sect571r1")); - MASTER_PARAMS_LIST.add(buildEcdhParameters(new ECGenParameterSpec("X9.62 c2tnb239v3"), "X9.62 c2tnb239v3")); MASTER_PARAMS_LIST.add(buildEcdhParameters(EcGenTest.EXPLICIT_CURVE, "Explicit Curve")); MASTER_PARAMS_LIST.add(buildDhParameters(512)); @@ -187,8 +181,7 @@ private static TestParams buildEcdhParameters(final AlgorithmParameterSpec genSp Arrays.asList( buildKeyAtInfinity(pubKey), buildKeyOffCurve(pubKey), - buildKeyOnWrongCurve(pubKey), - buildKeyOnWrongField(pubKey)) + buildKeyOnWrongCurve(pubKey)) ); } @@ -242,7 +235,7 @@ static ECPublicKey buildKeyOnWrongCurve(final ECPublicKey goodKey) throws Genera // This is a prime curve generator.initialize(new ECGenParameterSpec("NIST P-384")); final ECPublicKey pub1 = (ECPublicKey) generator.generateKeyPair().getPublic(); - generator.initialize(new ECGenParameterSpec("NIST P-224")); + generator.initialize(new ECGenParameterSpec("NIST P-256")); final ECPublicKey pub2 = (ECPublicKey) generator.generateKeyPair().getPublic(); if (curve.getField().getFieldSize() == pub1.getParams().getCurve().getField().getFieldSize()) { @@ -264,17 +257,6 @@ static ECPublicKey buildKeyOnWrongCurve(final ECPublicKey goodKey) throws Genera } } - public static ECPublicKey buildKeyOnWrongField(final ECPublicKey goodKey) throws GeneralSecurityException { - final KeyPairGenerator generator = KeyPairGenerator.getInstance("EC"); - final EllipticCurve curve = goodKey.getParams().getCurve(); - if (curve.getField() instanceof ECFieldFp) { - generator.initialize(new ECGenParameterSpec("sect163k1")); - } else { - generator.initialize(new ECGenParameterSpec("NIST P-384")); - } - return (ECPublicKey) generator.generateKeyPair().getPublic(); - } - @ParameterizedTest @MethodSource("params") public void jceCompatability(TestParams params) { diff --git a/tst/com/amazon/corretto/crypto/provider/test/EvpSignatureSpecificTest.java b/tst/com/amazon/corretto/crypto/provider/test/EvpSignatureSpecificTest.java index e2e17bdd..86ef9314 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EvpSignatureSpecificTest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EvpSignatureSpecificTest.java @@ -373,7 +373,7 @@ public void simpleCorrectnessAllAlgorithms() throws Throwable { } if ("ECDSA".equals(base)) { keyGenAlgorithm = "EC"; - if (null == shaLength || "1".equals(shaLength) || "512".equals(shaLength)) { + if (null == shaLength || "1".equals(shaLength) || "224".equals(shaLength) || "512".equals(shaLength)) { keyGenSpec = new ECGenParameterSpec("NIST P-521"); } else { keyGenSpec = new ECGenParameterSpec("NIST P-" + shaLength); diff --git a/tst/com/amazon/corretto/crypto/provider/test/HashFunctionTester.java b/tst/com/amazon/corretto/crypto/provider/test/HashFunctionTester.java index 58374f37..8edcf526 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/HashFunctionTester.java +++ b/tst/com/amazon/corretto/crypto/provider/test/HashFunctionTester.java @@ -3,6 +3,7 @@ package com.amazon.corretto.crypto.provider.test; +import static com.amazon.corretto.crypto.provider.test.TestUtil.assertArraysHexEquals; import static com.amazon.corretto.crypto.provider.test.TestUtil.assertThrows; import static com.amazon.corretto.crypto.provider.test.TestUtil.sneakyInvoke; import static org.junit.jupiter.api.Assertions.assertArrayEquals; @@ -222,6 +223,8 @@ public void testAPI() throws Exception { testBoundsChecks(); testByteBufferReflectionFallback(); testClone(); + testCloneLarge(); + testDraggedState(); testDirectBufferSlices(); testLargeArray(); testLargeDirectBuffer(); @@ -266,6 +269,76 @@ private void testDirectBufferSlices() { assertArrayEquals(expected.digest(), md.digest()); } + private void testDraggedState() throws CloneNotSupportedException { + final byte[] base = new byte[4096]; + final byte[] suffix1 = new byte[4096]; + final byte[] suffix2 = new byte[4096]; + for (int x = 0; x < base.length; x++) { + base[x] = (byte) x; + suffix1[x] = (byte) (x + 1); + suffix2[x] = (byte) (x + 2); + } + MessageDigest defaultInstance = getDefaultInstance(); + defaultInstance.update(base); + final byte[] expected1 = defaultInstance.digest(suffix1); + + defaultInstance.update(base); + final byte[] expected2 = defaultInstance.digest(suffix2); + + final MessageDigest original = getAmazonInstance(); + final MessageDigest duplicate = (MessageDigest) original.clone(); + + // First use uses the explicitly cloned state + original.update(base); + duplicate.update(base); + + assertArraysHexEquals(expected1, original.digest(suffix1)); + assertArraysHexEquals(expected2, duplicate.digest(suffix2)); + + // State has been reset and thus we might no longer be on the explicitly cloned state + original.update(base); + duplicate.update(base); + + assertArraysHexEquals(expected1, original.digest(suffix1)); + assertArraysHexEquals(expected2, duplicate.digest(suffix2)); + } + + private void testCloneLarge() throws CloneNotSupportedException { + MessageDigest md = getAmazonInstance(); + + final byte[] base = new byte[4096]; + final byte[] suffix1 = new byte[4096]; + final byte[] suffix2 = new byte[4096]; + for (int x = 0; x < base.length; x++) { + base[x] = (byte) x; + suffix1[x] = (byte) (x + 1); + suffix2[x] = (byte) (x + 2); + } + + md.update(base); + + MessageDigest md2 = (MessageDigest) md.clone(); + + md2.update(suffix1); + md.update(suffix2); + + MessageDigest defaultInstance = getDefaultInstance(); + defaultInstance.update(base); + final byte[] expected1 = defaultInstance.digest(suffix1); + + defaultInstance.update(base); + final byte[] expected2 = defaultInstance.digest(suffix2); + + assertArraysHexEquals( + expected1, + md2.digest() + ); + assertArraysHexEquals( + expected2, + md.digest() + ); + } + private void testClone() throws CloneNotSupportedException { MessageDigest md = getAmazonInstance(); diff --git a/tst/com/amazon/corretto/crypto/provider/test/HmacTest.java b/tst/com/amazon/corretto/crypto/provider/test/HmacTest.java index 8cbaa577..76c6b68b 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/HmacTest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/HmacTest.java @@ -3,6 +3,7 @@ package com.amazon.corretto.crypto.provider.test; +import static com.amazon.corretto.crypto.provider.test.TestUtil.assertArraysHexEquals; import static com.amazon.corretto.crypto.provider.test.TestUtil.NATIVE_PROVIDER; import static com.amazon.corretto.crypto.provider.test.TestUtil.assertThrows; import static com.amazon.corretto.crypto.provider.test.TestUtil.sneakyInvoke; @@ -427,6 +428,96 @@ public void supportsCloneable() throws Exception { } } + @Test + public void supportsCloneableLarge() throws Exception { + TestUtil.assumeMinimumVersion("1.3.0", NATIVE_PROVIDER); + final byte[] prefix = new byte[4096]; + final byte[] suffix1 = new byte[4096]; + final byte[] suffix2 = new byte[4096]; + + for (int x = 0; x < prefix.length; x++) { + prefix[x] = (byte) x; + suffix1[x] = (byte) (x + 1); + suffix2[x] = (byte) (x + 2); + } + + final SecretKeySpec key = new SecretKeySpec(new byte[4096], "Generic"); + for (final String algorithm : SUPPORTED_HMACS) { + final Mac defaultInstance = Mac.getInstance(algorithm, "SunJCE"); + defaultInstance.init(key); + defaultInstance.update(prefix); + + final byte[] expected1 = defaultInstance.doFinal(suffix1); + + defaultInstance.update(prefix); + final byte[] expected2 = defaultInstance.doFinal(suffix2); + + + final Mac original = Mac.getInstance(algorithm, NATIVE_PROVIDER); + original.init(key); + original.update(prefix); + + final Mac duplicate = (Mac) original.clone(); + + original.update(suffix1); + duplicate.update(suffix2); + + assertArraysHexEquals( + expected1, + original.doFinal() + ); + assertArraysHexEquals( + expected2, + duplicate.doFinal() + ); + } + } + + + @Test + public void testDraggedState() throws Exception { + TestUtil.assumeMinimumVersion("1.3.0", NATIVE_PROVIDER); + final byte[] prefix = new byte[4096]; + final byte[] suffix1 = new byte[4096]; + final byte[] suffix2 = new byte[4096]; + + for (int x = 0; x < prefix.length; x++) { + prefix[x] = (byte) x; + suffix1[x] = (byte) (x + 1); + suffix2[x] = (byte) (x + 2); + } + + final SecretKeySpec key = new SecretKeySpec(new byte[4096], "Generic"); + for (final String algorithm : SUPPORTED_HMACS) { + final Mac defaultInstance = Mac.getInstance(algorithm, "SunJCE"); + defaultInstance.init(key); + defaultInstance.update(prefix); + final byte[] expected1 = defaultInstance.doFinal(suffix1); + + defaultInstance.update(prefix); + final byte[] expected2 = defaultInstance.doFinal(suffix2); + + final Mac original = Mac.getInstance(algorithm, NATIVE_PROVIDER); + final Mac duplicate = (Mac) original.clone(); + original.init(key); + duplicate.init(key); + + // First use uses the explicitly cloned state + original.update(prefix); + duplicate.update(prefix); + + assertArraysHexEquals(expected1, original.doFinal(suffix1)); + assertArraysHexEquals(expected2, duplicate.doFinal(suffix2)); + + // State has been reset and thus we might no longer be on the explicitly cloned state + original.update(prefix); + duplicate.update(prefix); + + assertArraysHexEquals(expected1, original.doFinal(suffix1)); + assertArraysHexEquals(expected2, duplicate.doFinal(suffix2)); + } + } + @Test public void selfTest() { assertEquals(SelfTestStatus.PASSED, HmacSHA512Spi.runSelfTest().getStatus()); diff --git a/tst/com/amazon/corretto/crypto/provider/test/InputBufferTest.java b/tst/com/amazon/corretto/crypto/provider/test/InputBufferTest.java index efda94c8..a835c2e9 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/InputBufferTest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/InputBufferTest.java @@ -7,6 +7,7 @@ import static com.amazon.corretto.crypto.provider.test.TestUtil.assumeMinimumVersion; import static com.amazon.corretto.crypto.provider.test.TestUtil.assertThrows; import static com.amazon.corretto.crypto.provider.test.TestUtil.sneakyConstruct; +import static com.amazon.corretto.crypto.provider.test.TestUtil.NATIVE_PROVIDER; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -17,7 +18,6 @@ import org.junit.jupiter.api.Test; -import com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider; import com.amazon.corretto.crypto.provider.InputBuffer; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.parallel.Execution; @@ -29,8 +29,6 @@ @Execution(ExecutionMode.CONCURRENT) @ResourceLock(value = TestUtil.RESOURCE_GLOBAL, mode = ResourceAccessMode.READ) public class InputBufferTest { - private static final AmazonCorrettoCryptoProvider PROVIDER = AmazonCorrettoCryptoProvider.INSTANCE; // used for version checks - @SuppressWarnings("unchecked") private InputBuffer getBuffer(int capacity) { try { @@ -43,18 +41,19 @@ private InputBuffer getBuffer(int capacity) { @Test public void requiresPositiveCapacity() throws Throwable { assertThrows(IllegalArgumentException.class, () -> sneakyConstruct(InputBuffer.class.getName(), Integer.valueOf(-1))); - assumeMinimumVersion("1.1.1", AmazonCorrettoCryptoProvider.INSTANCE); + assumeMinimumVersion("1.1.1", NATIVE_PROVIDER); assertThrows(IllegalArgumentException.class, () -> sneakyConstruct(InputBuffer.class.getName(), Integer.valueOf(0))); } @Test public void minimalCase() { + assumeMinimumVersion("1.6.1", NATIVE_PROVIDER); // Just tests the bare minimum configuration and ensures things are properly buffered byte[] expected = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; final ByteBuffer result = ByteBuffer.allocate(17); final InputBuffer buffer = getBuffer(4); - buffer.withInitialStateSupplier(() -> { return result; }) + buffer.withInitialStateSupplier((s) -> { return result; }) .withUpdater((ctx, src, offset, length) -> ctx.put(src, offset, length)) .withDoFinal(ByteBuffer::array); @@ -87,13 +86,13 @@ public void minimalCase() { @Test public void singleByteUpdates() { - assumeMinimumVersion("1.1.1", AmazonCorrettoCryptoProvider.INSTANCE); + assumeMinimumVersion("1.6.1", NATIVE_PROVIDER); byte[] expected = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; final ByteBuffer result = ByteBuffer.allocate(2); // In all cases, the byte being processed should be exactly one byte and one byte behind. final InputBuffer buffer = getBuffer(1); - buffer.withInitialStateSupplier(() -> { return result; }) + buffer.withInitialStateSupplier((s) -> { return result; }) .withUpdater((ctx, src, offset, length) -> ctx.put(src, offset, length)) .withDoFinal(ByteBuffer::array); @@ -143,6 +142,7 @@ public void prefersSinglePass() { // Suppress redundant cast warnings; they're redundant in java 9 but not java 8 @SuppressWarnings({"cast", "RedundantCast"}) public void prefersBufferHandlers() { + assumeMinimumVersion("1.6.1", NATIVE_PROVIDER); byte[] expected = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; final ByteBuffer result = ByteBuffer.allocate(17); final ByteBuffer direct = ByteBuffer.allocateDirect(17); @@ -150,7 +150,7 @@ public void prefersBufferHandlers() { // By leaving other handlers null, I'll force an exception if they are used final InputBuffer buffer = getBuffer(1); - buffer.withInitialStateSupplier(() -> { return result;} ) + buffer.withInitialStateSupplier((s) -> { return result;} ) .withUpdater((ctx, src) -> ctx.put(src)) .withDoFinal(ByteBuffer::array); @@ -172,9 +172,10 @@ public void prefersBufferHandlers() { @SuppressWarnings("unchecked") @Test public void cloneDuplicatesBufferAndState() throws Throwable { + assumeMinimumVersion("1.6.1", NATIVE_PROVIDER); byte[] expected = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; final InputBuffer buffer1 = getBuffer(16); - buffer1.withInitialStateSupplier(ByteArrayOutputStream::new) + buffer1.withInitialStateSupplier((s) -> new ByteArrayOutputStream()) .withUpdater((state, src, offset, length) -> { state.write(src, offset, length); }) .withDoFinal(ByteArrayOutputStream::toByteArray) .withStateCloner((state) -> { @@ -215,8 +216,9 @@ public void cloneDuplicatesBufferAndState() throws Throwable { @Test public void cantCloneUncloneable() throws Throwable { + assumeMinimumVersion("1.6.1", NATIVE_PROVIDER); final InputBuffer buffer = getBuffer(8); - buffer.withInitialStateSupplier(() -> { return new byte[128]; } ) + buffer.withInitialStateSupplier((s) -> { return new byte[128]; } ) .withUpdater((state, src, offset, length) -> { System.arraycopy(src, offset, state, 0, length); }) .withDoFinal((state) -> state.clone()); @@ -227,8 +229,9 @@ public void cantCloneUncloneable() throws Throwable { @Test public void nullStateProperlyHandled() throws Throwable { + assumeMinimumVersion("1.6.1", NATIVE_PROVIDER); InputBuffer buffer = getBuffer(4); - buffer.withInitialStateSupplier(() -> { + buffer.withInitialStateSupplier((s) -> { return new byte[4]; }).withUpdater((state, src, offset, length) -> { System.arraycopy(src, offset, state, 0, length); diff --git a/tst/com/amazon/corretto/crypto/provider/test/TestUtil.java b/tst/com/amazon/corretto/crypto/provider/test/TestUtil.java index 97c31538..286cf265 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/TestUtil.java +++ b/tst/com/amazon/corretto/crypto/provider/test/TestUtil.java @@ -4,9 +4,12 @@ package com.amazon.corretto.crypto.provider.test; import com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider; + +import org.apache.commons.codec.binary.Hex; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.Assumptions; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; @@ -58,7 +61,13 @@ public static byte[] getRandomBytes(int length) { MISC_SECURE_RANDOM.get().nextBytes(result); return result; } - + + public static void assertArraysHexEquals(byte[] expected, byte[] actual) { + final String expectedHex = Hex.encodeHexString(expected); + final String actualHex = Hex.encodeHexString(actual); + assertEquals(expectedHex, actualHex); + } + public static void assertThrows(Class expected, ThrowingRunnable callable) { try { callable.run();