From 948d10ef08cd38710b2213ae22ea7af02c12fb31 Mon Sep 17 00:00:00 2001 From: JohnE Date: Mon, 25 Feb 2019 18:14:14 -0800 Subject: [PATCH] MOD: added new crypto wrapper files, builds, time to test --- docs/proj_agile.rst | 35 +- docs/proj_nc | 6 - docs/proj_nc.rst | 14 + gradle/wrapper/gradle-wrapper.jar | Bin 53324 -> 55190 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 28 +- gradlew.bat | 84 + settings.gradle | 2 +- wolfssl-jni/build.gradle | 2 +- .../main/java/com/wolfssl/wolfcrypt/Asn.java | 43 + .../java/com/wolfssl/wolfcrypt/Chacha.java | 101 + .../com/wolfssl/wolfcrypt/Curve25519.java | 172 ++ .../main/java/com/wolfssl/wolfcrypt/Des3.java | 59 + .../main/java/com/wolfssl/wolfcrypt/Dh.java | 150 ++ .../main/java/com/wolfssl/wolfcrypt/Ecc.java | 296 +++ .../java/com/wolfssl/wolfcrypt/Ed25519.java | 183 ++ .../main/java/com/wolfssl/wolfcrypt/Fips.java | 1690 +++++++++++++++++ .../main/java/com/wolfssl/wolfcrypt/Hmac.java | 214 +++ .../main/java/com/wolfssl/wolfcrypt/Md5.java | 61 + .../com/wolfssl/wolfcrypt/MessageDigest.java | 142 ++ .../main/java/com/wolfssl/wolfcrypt/Rng.java | 93 + .../main/java/com/wolfssl/wolfcrypt/Rsa.java | 229 +++ .../main/java/com/wolfssl/wolfcrypt/Sha.java | 61 + .../java/com/wolfssl/wolfcrypt/Sha256.java | 62 + .../java/com/wolfssl/wolfcrypt/Sha384.java | 62 + .../java/com/wolfssl/wolfcrypt/Sha512.java | 62 + .../com/wolfssl/wolfcrypt/test/AesTest.java | 2 +- 27 files changed, 3816 insertions(+), 40 deletions(-) delete mode 100644 docs/proj_nc create mode 100644 docs/proj_nc.rst create mode 100644 gradlew.bat create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Asn.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Chacha.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Curve25519.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Des3.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Dh.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Ecc.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Ed25519.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Fips.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Hmac.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Md5.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/MessageDigest.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Rng.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Rsa.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha256.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha384.java create mode 100644 wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha512.java diff --git a/docs/proj_agile.rst b/docs/proj_agile.rst index d18ace5..860cc48 100644 --- a/docs/proj_agile.rst +++ b/docs/proj_agile.rst @@ -3,27 +3,16 @@ CCC Agile ========= -FEATURES --------- -* ndk cmake builds (C++, LLVM support, ABI) -* .aar library packaging, includes java .class files (and other resources) - - - - WORKING -------- +======= +* NC: add more JNI crypto features: SHA, RSA, CHACHA +* UI: adding encrypt screen * l@@k at Android Weekly emails -Milestone -^^^^^^^^^ -* WolfSSL validate with JNI - - BACKLOG -------- +======= Tasks ^^^^^ @@ -50,9 +39,6 @@ Tasks Milestones ^^^^^^^^^^ -* JNI wrapper tests - - RSA Public Key Generation - - AES crypt * normalizedcrypto - maybe wrap nextcrypto with a *VERY* simple API @@ -64,7 +50,8 @@ Milestones COMPLETED ---------- +========= + * TEST: add test code for JNI calls - tested AES, everything else should work accordingly * combine wolfssl-lib + wolfssl-jni == ccc-ndk-jni (not ccc-jni which would be for java) @@ -76,5 +63,15 @@ COMPLETED - discover which gradle to target --> wolfssl was main gradle app target +Milestones +^^^^^^^^^^ +* JNI wrapper tests + - RSA Public Key Generation + - AES crypt +* X Android application skeleton +* X WolfSSL validate with JNI + + + diff --git a/docs/proj_nc b/docs/proj_nc deleted file mode 100644 index 9cc9fac..0000000 --- a/docs/proj_nc +++ /dev/null @@ -1,6 +0,0 @@ -============= -Project Notes -============= - - - diff --git a/docs/proj_nc.rst b/docs/proj_nc.rst new file mode 100644 index 0000000..368a6dc --- /dev/null +++ b/docs/proj_nc.rst @@ -0,0 +1,14 @@ +========================= +Next Crypto Project Notes +========================= + + +FEATURES +-------- +* ndk cmake builds (C++, LLVM support, ABI) +* .aar library packaging, includes java .class files (and other resources) + + + + + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 3baa851b28c65f87dd36a6748e1a85cf360c1301..87b738cbd051603d91cc39de6cb000dd98fe6b02 100644 GIT binary patch literal 55190 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 z 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%0DGb5d&h)KC8(}$^u@02hW?*4w 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 cCI|qNUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +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% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle index 69ce2c6..53852da 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ // include this at the top, for shared libraries -include ':wolfssl-lib' +// include ':wolfssl-lib' // include ':wolfssl-jni' include ':ccc-jni' diff --git a/wolfssl-jni/build.gradle b/wolfssl-jni/build.gradle index 5d07fb2..8d99335 100644 --- a/wolfssl-jni/build.gradle +++ b/wolfssl-jni/build.gradle @@ -46,5 +46,5 @@ task copyLibs(type: Copy) { dependencies { - // testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.12' } diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Asn.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Asn.java new file mode 100644 index 0000000..92d0a7f --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Asn.java @@ -0,0 +1,43 @@ +/* Asn.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.nio.ByteBuffer; + +/** + * Wrapper for the native WolfCrypt Asn implementation. + * + * @author Moisés Guimarães + * @version 1.0, March 2015 + */ +public class Asn extends WolfObject { + + public static final int MAX_ENCODED_SIG_SIZE = 512; + + public static native void encodeSignature(ByteBuffer encoded, + ByteBuffer hash, long hashSize, int hashOID); + + public static native long encodeSignature(byte[] encoded, + byte[] hash, long hashSize, int hashOID); + + public static native int getCTC_HashOID(int type); +} diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Chacha.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Chacha.java new file mode 100644 index 0000000..e300e81 --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Chacha.java @@ -0,0 +1,101 @@ +/* Chacha.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.security.InvalidAlgorithmParameterException; + +/** + * Wrapper for the native WolfCrypt Chacha implementation. + * + * @author Daniele Lacamera + * @version 1.0, March 2018 + */ +public class Chacha extends NativeStruct { + + private WolfCryptState state = WolfCryptState.UNINITIALIZED; + + public Chacha() { + init(); + } + + @Override + public void releaseNativeStruct() { + free(); + + super.releaseNativeStruct(); + } + + protected native long mallocNativeStruct() throws OutOfMemoryError; + + private native void wc_Chacha_init(); + + private native void wc_Chacha_free(); + + private native byte[] wc_Chacha_process(byte in[]); + + private native void wc_Chacha_setKey(byte[] Key); + + private native void wc_Chacha_setIV(byte[] IV); + + + + + protected void init() { + if (state == WolfCryptState.UNINITIALIZED) { + wc_Chacha_init(); + state = WolfCryptState.INITIALIZED; + } else { + throw new IllegalStateException( + "Native resources already initialized."); + } + } + + protected void free() { + if (state != WolfCryptState.UNINITIALIZED) { + wc_Chacha_free(); + state = WolfCryptState.UNINITIALIZED; + } + } + + public void setKey(byte[] Key) { + if (state == WolfCryptState.INITIALIZED) { + wc_Chacha_setKey(Key); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public void setIV(byte[] IV) { + wc_Chacha_setIV(IV); + } + + public byte[] process(byte[] in) { + if (state == WolfCryptState.READY) { + return wc_Chacha_process(in); + } else { + throw new IllegalStateException( + "No available key to perform the operation."); + } + } +} + diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Curve25519.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Curve25519.java new file mode 100644 index 0000000..0e766ca --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Curve25519.java @@ -0,0 +1,172 @@ +/* Curve25519.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.security.InvalidAlgorithmParameterException; +import java.security.spec.EllipticCurve; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECFieldFp; + +/** + * Wrapper for the native WolfCrypt curve25519 implementation. + * + * @author Daniele Lacamera + * @version 1.0, March 2018 + */ +public class Curve25519 extends NativeStruct { + + private WolfCryptState state = WolfCryptState.UNINITIALIZED; + + public Curve25519() { + init(); + } + + @Override + public void releaseNativeStruct() { + free(); + + super.releaseNativeStruct(); + } + + protected native long mallocNativeStruct() throws OutOfMemoryError; + + private native void wc_curve25519_init(); + + private native void wc_curve25519_free(); + + private native void wc_curve25519_make_key(Rng rng, int size); + + private native void wc_curve25519_make_key_ex(Rng rng, int size, int endian); + + private native void wc_curve25519_check_key(); + + private native byte[] wc_curve25519_make_shared_secret(Curve25519 pubKey); + + private native void wc_curve25519_import_private(byte[] privKey, byte[] key); + private native void wc_curve25519_import_private_only(byte[] privKey); + private native void wc_curve25519_import_public(byte[] pubKey); + + + private native byte[] wc_curve25519_export_private(); + private native byte[] wc_curve25519_export_public(); + + + protected void init() { + if (state == WolfCryptState.UNINITIALIZED) { + wc_curve25519_init(); + state = WolfCryptState.INITIALIZED; + } else { + throw new IllegalStateException( + "Native resources already initialized."); + } + } + + protected void free() { + if (state != WolfCryptState.UNINITIALIZED) { + wc_curve25519_free(); + state = WolfCryptState.UNINITIALIZED; + } + } + + public void makeKey(Rng rng, int size) { + if (state == WolfCryptState.INITIALIZED) { + wc_curve25519_make_key(rng, size); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public void makeKeyWithEndian(Rng rng, int size, int endian) { + if (state == WolfCryptState.INITIALIZED) { + wc_curve25519_make_key_ex(rng, size, endian); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public void checkKey() { + if (state == WolfCryptState.READY) { + wc_curve25519_check_key(); + } else { + throw new IllegalStateException( + "No available key to perform the operation."); + } + } + + public void importPrivate(byte[] privKey, byte[] xKey) { + if (state == WolfCryptState.INITIALIZED) { + wc_curve25519_import_private(privKey, xKey); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public void importPrivateOnly(byte[] privKey) { + if (state == WolfCryptState.INITIALIZED) { + wc_curve25519_import_private_only(privKey); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public void importPublic(byte[] pubKey) { + if (state == WolfCryptState.INITIALIZED) { + wc_curve25519_import_public(pubKey); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public byte[] exportPrivate() { + if (state == WolfCryptState.READY) { + return wc_curve25519_export_private(); + } else { + throw new IllegalStateException( + "No available key to perform the operation."); + } + } + + public byte[] exportPublic() { + if (state == WolfCryptState.READY) { + return wc_curve25519_export_public(); + } else { + throw new IllegalStateException( + "No available key to perform the operation."); + } + } + + public byte[] makeSharedSecret(Curve25519 pubKey) { + if (state == WolfCryptState.READY) { + return wc_curve25519_make_shared_secret(pubKey); + } else { + throw new IllegalStateException( + "No available key to perform the operation."); + } + } + +} + diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Des3.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Des3.java new file mode 100644 index 0000000..f720e1d --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Des3.java @@ -0,0 +1,59 @@ +/* Des3.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.nio.ByteBuffer; + +/** + * Wrapper for the native WolfCrypt Des3 implementation. + * + * @author Moisés Guimarães + * @version 1.0, February 2015 + */ +public class Des3 extends BlockCipher { + + public static final int KEY_SIZE = 24; + public static final int BLOCK_SIZE = 8; + public static final int ENCRYPT_MODE = 0; + public static final int DECRYPT_MODE = 1; + + private WolfCryptState state = WolfCryptState.UNINITIALIZED; + + private int opmode; + + protected native long mallocNativeStruct() throws OutOfMemoryError; + + protected native void native_set_key(byte[] key, byte[] iv, int opmode); + + protected native int native_update(int opmode, byte[] input, int offset, + int length, byte[] output, int outputOffset); + + protected native int native_update(int opmode, ByteBuffer input, + int offset, int length, ByteBuffer output, int outputOffset); + + public Des3() { + } + + public Des3(byte[] key, byte[] iv, int opmode) { + setKey(key, iv, opmode); + } +} \ No newline at end of file diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Dh.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Dh.java new file mode 100644 index 0000000..5e430e0 --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Dh.java @@ -0,0 +1,150 @@ +/* Dh.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +/** + * Wrapper for the native WolfCrypt DH implementation. + * + * @author Moisés Guimarães + * @version 2.0, March 2017 + */ +public class Dh extends NativeStruct { + + private WolfCryptState state = WolfCryptState.UNINITIALIZED; + private byte[] privateKey = null; + private byte[] publicKey = null; + private int pSize = 0; + + public Dh() { + init(); + } + + public Dh(byte[] p, byte[] g) { + init(); + setParams(p, g); + } + + @Override + public void releaseNativeStruct() { + free(); + + super.releaseNativeStruct(); + } + + protected native long mallocNativeStruct() throws OutOfMemoryError; + + private native void wc_InitDhKey(); + + private native void wc_FreeDhKey(); + + private native void wc_DhSetKey(byte[] p, byte[] g); + + private native void wc_DhGenerateKeyPair(Rng rng, int pSize); + + private native byte[] wc_DhAgree(byte[] priv, byte[] pub); + + protected void init() { + if (state == WolfCryptState.UNINITIALIZED) { + wc_InitDhKey(); + state = WolfCryptState.INITIALIZED; + } else { + throw new IllegalStateException( + "Native resources already initialized."); + } + } + + protected void free() { + if (state != WolfCryptState.UNINITIALIZED) { + wc_FreeDhKey(); + + setPrivateKey(new byte[0]); + setPublicKey(new byte[0]); + + state = WolfCryptState.UNINITIALIZED; + } + } + + public void setPrivateKey(byte[] priv) { + if (state != WolfCryptState.UNINITIALIZED) { + if (privateKey != null) + for (int i = 0; i < privateKey.length; i++) + privateKey[i] = 0; + + privateKey = priv.clone(); + } else { + throw new IllegalStateException( + "No available parameters to perform opetarion."); + } + } + + public void setPublicKey(byte[] pub) { + if (state != WolfCryptState.UNINITIALIZED) { + if (publicKey != null) + for (int i = 0; i < publicKey.length; i++) + publicKey[i] = 0; + + publicKey = pub.clone(); + } else { + throw new IllegalStateException( + "No available parameters to perform opetarion."); + } + } + + public byte[] getPublicKey() { + return publicKey; + } + + public byte[] getPrivateKey() { + return privateKey; + } + + public void setParams(byte[] p, byte[] g) { + if (state == WolfCryptState.INITIALIZED) { + wc_DhSetKey(p, g); + this.pSize = p.length; + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has parameters."); + } + } + + public void makeKey(Rng rng) { + if (privateKey == null) { + /* use size of P to allocate key buffer size */ + wc_DhGenerateKeyPair(rng, this.pSize); + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public byte[] makeSharedSecret(Dh pubKey) { + byte[] publicKey = pubKey.getPublicKey(); + + if (privateKey != null || publicKey != null) { + return wc_DhAgree(privateKey, publicKey); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } +} + diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Ecc.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Ecc.java new file mode 100644 index 0000000..718516b --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Ecc.java @@ -0,0 +1,296 @@ +/* Ecc.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.security.InvalidAlgorithmParameterException; +import java.security.spec.EllipticCurve; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECFieldFp; + +/** + * Wrapper for the native WolfCrypt ecc implementation. + * + * @author Moisés Guimarães + * @version 2.0, February 2017 + */ +public class Ecc extends NativeStruct { + + private WolfCryptState state = WolfCryptState.UNINITIALIZED; + + public Ecc() { + init(); + } + + @Override + public void releaseNativeStruct() { + free(); + + super.releaseNativeStruct(); + } + + protected native long mallocNativeStruct() throws OutOfMemoryError; + + private native void wc_ecc_init(); + + private native void wc_ecc_free(); + + private native void wc_ecc_make_key(Rng rng, int size); + + private native void wc_ecc_make_key_ex(Rng rng, int size, String curveName); + + private native void wc_ecc_check_key(); + + private native byte[] wc_ecc_shared_secret(Ecc pubKey); + + private native void wc_ecc_import_private(byte[] privKey, byte[] x963Key, + String curveName); + + private native byte[] wc_ecc_export_private(); + + private native void wc_ecc_import_x963(byte[] key); + + private native byte[] wc_ecc_export_x963(); + + private native void wc_EccPrivateKeyDecode(byte[] key); + + private native byte[] wc_EccKeyToDer(); + + private native void wc_EccPublicKeyDecode(byte[] key); + + private native byte[] wc_EccPublicKeyToDer(); + + private native byte[] wc_ecc_sign_hash(byte[] hash, Rng rng); + + private native boolean wc_ecc_verify_hash(byte[] hash, byte[] signature); + + private static native int wc_ecc_get_curve_size_from_name(String name); + + private native byte[] wc_ecc_private_key_to_pkcs8(); + + private static native String wc_ecc_get_curve_name_from_id(int curve_id); + + private static native int wc_ecc_get_curve_id_from_params(int fieldSize, + byte[] prime, byte[] Af, byte[] Bf, byte[] order, + byte[] Gx, byte[] Gy, int cofactor); + + protected void init() { + if (state == WolfCryptState.UNINITIALIZED) { + wc_ecc_init(); + state = WolfCryptState.INITIALIZED; + } else { + throw new IllegalStateException( + "Native resources already initialized."); + } + } + + protected void free() { + if (state != WolfCryptState.UNINITIALIZED) { + wc_ecc_free(); + state = WolfCryptState.UNINITIALIZED; + } + } + + public void makeKey(Rng rng, int size) { + if (state == WolfCryptState.INITIALIZED) { + wc_ecc_make_key(rng, size); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public void makeKeyOnCurve(Rng rng, int size, String curveName) { + if (state == WolfCryptState.INITIALIZED) { + wc_ecc_make_key_ex(rng, size, curveName.toUpperCase()); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public void checkKey() { + if (state == WolfCryptState.READY) { + wc_ecc_check_key(); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + public void importPrivate(byte[] privKey, byte[] x963Key) { + if (state == WolfCryptState.INITIALIZED) { + wc_ecc_import_private(privKey, x963Key, null); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public void importPrivateOnCurve(byte[] privKey, byte[] x963Key, + String curveName) { + if (state == WolfCryptState.INITIALIZED) { + wc_ecc_import_private(privKey, x963Key, curveName); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public byte[] exportPrivate() { + if (state == WolfCryptState.READY) { + return wc_ecc_export_private(); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + public void importX963(byte[] key) { + if (state == WolfCryptState.INITIALIZED) { + wc_ecc_import_x963(key); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public byte[] exportX963() { + if (state == WolfCryptState.READY) { + return wc_ecc_export_x963(); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + public void privateKeyDecode(byte[] key) { + if (state == WolfCryptState.INITIALIZED) { + wc_EccPrivateKeyDecode(key); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public byte[] privateKeyEncode() { + if (state == WolfCryptState.READY) { + return wc_EccKeyToDer(); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + public void publicKeyDecode(byte[] key) { + if (state == WolfCryptState.INITIALIZED) { + wc_EccPublicKeyDecode(key); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public byte[] publicKeyEncode() { + if (state == WolfCryptState.READY) { + return wc_EccPublicKeyToDer(); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + public byte[] makeSharedSecret(Ecc pubKey) { + if (state == WolfCryptState.READY) { + return wc_ecc_shared_secret(pubKey); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + public byte[] sign(byte[] hash, Rng rng) { + byte[] signature = new byte[0]; + + if (state == WolfCryptState.READY) { + signature = wc_ecc_sign_hash(hash, rng); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + + return signature; + } + + public boolean verify(byte[] hash, byte[] signature) { + boolean result = false; + + if (state == WolfCryptState.READY) { + result = wc_ecc_verify_hash(hash, signature); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + + return result; + } + + public static int getCurveSizeFromName(String curveName) { + /* Ecc object doesn't need to be initialied before call */ + return wc_ecc_get_curve_size_from_name(curveName); + } + + public byte[] privateKeyEncodePKCS8() { + if (state == WolfCryptState.READY) { + return wc_ecc_private_key_to_pkcs8(); + } else { + throw new IllegalStateException( + "No available key to perform the operation."); + } + } + + public static String getCurveName(ECParameterSpec spec) + throws InvalidAlgorithmParameterException + { + int curve_id; + + /* Ecc object doesn't need to be initialied before call */ + if (!(spec.getCurve().getField() instanceof ECFieldFp)) { + throw new InvalidAlgorithmParameterException( + "Currently only ECFieldFp fields supported"); + } + ECFieldFp field = (ECFieldFp)spec.getCurve().getField(); + EllipticCurve curve = spec.getCurve(); + + curve_id = wc_ecc_get_curve_id_from_params( + field.getFieldSize(), + field.getP().toByteArray(), + curve.getA().toByteArray(), + curve.getB().toByteArray(), + spec.getOrder().toByteArray(), + spec.getGenerator().getAffineX().toByteArray(), + spec.getGenerator().getAffineY().toByteArray(), + spec.getCofactor()); + + return wc_ecc_get_curve_name_from_id(curve_id); + } +} + diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Ed25519.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Ed25519.java new file mode 100644 index 0000000..62bf75f --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Ed25519.java @@ -0,0 +1,183 @@ +/* Ed25519.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.security.InvalidAlgorithmParameterException; + +/** + * Wrapper for the native WolfCrypt ed25519 implementation. + * + * @author Daniele Lacamera + * @version 1.0, March 2018 + */ +public class Ed25519 extends NativeStruct { + + private WolfCryptState state = WolfCryptState.UNINITIALIZED; + + public Ed25519() { + init(); + } + + @Override + public void releaseNativeStruct() { + free(); + + super.releaseNativeStruct(); + } + + protected native long mallocNativeStruct() throws OutOfMemoryError; + + private native void wc_ed25519_init(); + + private native void wc_ed25519_free(); + + private native void wc_ed25519_make_key(Rng rng, int size); + + private native void wc_ed25519_check_key(); + + private native void wc_ed25519_import_private(byte[] privKey, byte[] key); + private native void wc_ed25519_import_private_only(byte[] privKey); + private native void wc_ed25519_import_public(byte[] privKey); + + private native byte[] wc_ed25519_sign_msg(byte[] msg); + private native boolean wc_ed25519_verify_msg(byte[] sig, byte[] msg); + + private native byte[] wc_ed25519_export_private(); + private native byte[] wc_ed25519_export_private_only(); + private native byte[] wc_ed25519_export_public(); + + protected void init() { + if (state == WolfCryptState.UNINITIALIZED) { + wc_ed25519_init(); + state = WolfCryptState.INITIALIZED; + } else { + throw new IllegalStateException( + "Native resources already initialized."); + } + } + + protected void free() { + if (state != WolfCryptState.UNINITIALIZED) { + wc_ed25519_free(); + state = WolfCryptState.UNINITIALIZED; + } + } + + public void makeKey(Rng rng, int size) { + if (state == WolfCryptState.INITIALIZED) { + wc_ed25519_make_key(rng, size); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public void checkKey() { + if (state == WolfCryptState.READY) { + wc_ed25519_check_key(); + } else { + throw new IllegalStateException( + "No available key to perform the operation."); + } + } + + public void importPrivate(byte[] privKey, byte[] Key) { + if (state == WolfCryptState.INITIALIZED) { + wc_ed25519_import_private(privKey, Key); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public void importPrivateOnly(byte[] privKey) { + if (state == WolfCryptState.INITIALIZED) { + wc_ed25519_import_private_only(privKey); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public void importPublic(byte[] Key) { + if (state == WolfCryptState.INITIALIZED) { + wc_ed25519_import_public(Key); + state = WolfCryptState.READY; + } else { + throw new IllegalStateException("Object already has a key."); + } + } + + public byte[] exportPrivate() { + if (state == WolfCryptState.READY) { + return wc_ed25519_export_private(); + } else { + throw new IllegalStateException( + "No available key to perform the operation."); + } + } + + public byte[] exportPrivateOnly() { + if (state == WolfCryptState.READY) { + return wc_ed25519_export_private_only(); + } else { + throw new IllegalStateException( + "No available key to perform the operation."); + } + } + + public byte[] exportPublic() { + if (state == WolfCryptState.READY) { + return wc_ed25519_export_public(); + } else { + throw new IllegalStateException( + "No available key to perform the operation."); + } + } + + public byte[] sign_msg(byte[] msg_in) { + + byte[] msg_out = null; + if (state == WolfCryptState.READY) { + msg_out = wc_ed25519_sign_msg(msg_in); + } else { + throw new IllegalStateException( + "No available key to perform the operation."); + } + + return msg_out; + } + + public boolean verify_msg(byte[] msg, byte[] signature) { + boolean result = false; + + if (state == WolfCryptState.READY) { + result = wc_ed25519_verify_msg(signature, msg); + } else { + throw new IllegalStateException( + "No available key to perform the operation."); + } + + return result; + } +} + diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Fips.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Fips.java new file mode 100644 index 0000000..02a67a7 --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Fips.java @@ -0,0 +1,1690 @@ +/* Fips.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.nio.ByteBuffer; + +import com.wolfssl.wolfcrypt.Aes; + +/** + * Main wrapper for the native WolfCrypt implementation. + * + * @author Moisés Guimarães + * @version 1.0, February 2015 + */ +public class Fips extends WolfObject { + + public static final boolean enabled = Fips.enabled(); + + public static final int fipsVersion = Fips.getFipsVersion(); + + private Fips() { + } + + public interface ErrorCallback { + public void errorCallback(int ok, int err, String hash); + } + + /** + * Sets an callback class for handling fips errors. + * + * @param callback + * the callback class. + */ + public static native void wolfCrypt_SetCb_fips(ErrorCallback callback); + + /** + * The current inCore hash of the wolfCrypt fips code. + * + * @return current inCore hash. + */ + public static native String wolfCrypt_GetCoreHash_fips(); + + /** + * Polls the underlying wolfCrypt library to see if HAVE_FIPS is defined. + * + * @return true if HAVE_FIPS has been defined and FIPS mode is enabled, + * otherwise false. + */ + private static native boolean enabled(); + + private static native int getFipsVersion(); + + /* + * ### FIPS Aprooved Security Methods ###################################### + */ + + /* + * wolfCrypt FIPS API - Symmetric encrypt/decrypt Service + */ + + /* AES */ + + /** + * Initializes Aes object for CBC mode with key and iv. + * + * @param aes + * the Aes object. + * @param userKey + * the key to be set. + * @param keylen + * the key length. + * @param iv + * the initialization vector (optional). + * @param dir + * the direction (encryption|decryption). + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int AesSetKey_fips(Aes aes, ByteBuffer userKey, + long keylen, ByteBuffer iv, int dir); + + /** + * Initializes Aes object for CBC mode with key and iv. + * + * @param aes + * the Aes object. + * @param userKey + * the key to be set. + * @param keylen + * the key length. + * @param iv + * the initialization vector (optional). + * @param dir + * the direction (encryption|decryption). + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int AesSetKey_fips(Aes aes, byte[] userKey, + long keylen, byte[] iv, int dir); + + /** + * Initializes Aes object with iv. + * + * @param aes + * the Aes object. + * @param iv + * the initialization vector. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int AesSetIV_fips(Aes aes, ByteBuffer iv); + + /** + * Initializes Aes object with iv. + * + * @param aes + * the Aes object. + * @param iv + * the initialization vector. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int AesSetIV_fips(Aes aes, byte[] iv); + + /** + * Performs Aes Cbc Encryption. + * + * @param aes + * the Aes object. + * @param out + * the output buffer. + * @param in + * the input buffer. + * @param sz + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int AesCbcEncrypt_fips(Aes aes, ByteBuffer out, + ByteBuffer in, long sz); + + /** + * Performs Aes Cbc Encryption. + * + * @param aes + * the Aes object. + * @param out + * the output buffer. + * @param in + * the input buffer. + * @param sz + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int AesCbcEncrypt_fips(Aes aes, byte[] out, byte[] in, + long sz); + + /** + * Performs Aes Cbc Decryption. + * + * @param aes + * the Aes object. + * @param out + * the output buffer. + * @param in + * the input buffer. + * @param sz + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int AesCbcDecrypt_fips(Aes aes, ByteBuffer out, + ByteBuffer in, long sz); + + /** + * Performs Aes Cbc Decryption. + * + * @param aes + * the Aes object. + * @param out + * the output buffer. + * @param in + * the input buffer. + * @param sz + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int AesCbcDecrypt_fips(Aes aes, byte[] out, byte[] in, + long sz); + + /** + * Initializes Aes object for GCM mode with key. + * + * @param aes + * the Aes object. + * @param userKey + * the key to be set. + * @param keylen + * the key length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int AesGcmSetKey_fips(Aes aes, ByteBuffer userKey, + long keylen); + + /** + * Initializes Aes object for GCM mode with key. + * + * @param aes + * the Aes object. + * @param userKey + * the key to be set. + * @param keylen + * the key length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int AesGcmSetKey_fips(Aes aes, byte[] userKey, + long keylen); + + /** + * Performs aes GCM Encryption. + * + * @param aes + * the Aes object. + * @param out + * the output buffer. + * @param in + * the input buffer. + * @param sz + * the input length. + * @param iv + * the initialization vector buffer. + * @param ivSz + * the initialization vector length. + * @param authTag + * the authTag buffer. + * @param authTagSz + * the authTag length. + * @param authIn + * the authIn buffer. + * @param authInSz + * the authIn length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int AesGcmEncrypt_fips(Aes aes, ByteBuffer out, + ByteBuffer in, long sz, ByteBuffer iv, long ivSz, + ByteBuffer authTag, long authTagSz, ByteBuffer authIn, long authInSz); + + /** + * Performs aes GCM Encryption. + * + * @param aes + * the Aes object. + * @param out + * the output buffer. + * @param in + * the input buffer. + * @param sz + * the input length. + * @param iv + * the initialization vector buffer. + * @param ivSz + * the initialization vector length. + * @param authTag + * the authTag buffer. + * @param authTagSz + * the authTag length. + * @param authIn + * the authIn buffer. + * @param authInSz + * the authIn length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int AesGcmEncrypt_fips(Aes aes, byte[] out, + byte[] in, long sz, byte[] iv, long ivSz, + byte[] authTag, long authTagSz, byte[] authIn, long authInSz); + + /** + * Performs aes GCM Decryption. + * + * @param aes + * the Aes object. + * @param out + * the output buffer. + * @param in + * the input buffer. + * @param sz + * the input length. + * @param iv + * the initialization vector buffer. + * @param ivSz + * the initialization vector length. + * @param authTag + * the authTag buffer. + * @param authTagSz + * the authTag length. + * @param authIn + * the authIn buffer. + * @param authInSz + * the authIn length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int AesGcmDecrypt_fips(Aes aes, ByteBuffer out, + ByteBuffer in, long sz, ByteBuffer iv, long ivSz, + ByteBuffer authTag, long authTagSz, ByteBuffer authIn, long authInSz); + + /** + * Performs aes GCM Decryption. + * + * @param aes + * the Aes object. + * @param out + * the output buffer. + * @param in + * the input buffer. + * @param sz + * the input length. + * @param iv + * the initialization vector buffer. + * @param ivSz + * the initialization vector length. + * @param authTag + * the authTag buffer. + * @param authTagSz + * the authTag length. + * @param authIn + * the authIn buffer. + * @param authInSz + * the authIn length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int AesGcmDecrypt_fips(Aes aes, byte[] out, + byte[] in, long sz, byte[] iv, long ivSz, + byte[] authTag, long authTagSz, byte[] authIn, long authInSz); + + /* DES3 */ + + /** + * Initializes Des3 object for CBC mode with key and iv. + * + * @param des + * the Des3 object. + * @param userKey + * the key to be set. + * @param iv + * the initialization vector (optional). + * @param dir + * the direction (encryption|decryption). + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Des3_SetKey_fips(Des3 des, ByteBuffer userKey, + ByteBuffer iv, int dir); + + /** + * Initializes Des3 object for CBC mode with key and iv. + * + * @param des + * the Des3 object. + * @param userKey + * the key to be set. + * @param iv + * the initialization vector (optional). + * @param dir + * the direction (encryption|decryption). + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Des3_SetKey_fips(Des3 des, byte[] userKey, + byte[] iv, int dir); + + /** + * Initializes Des3 object with iv. + * + * @param des + * the Des3 object. + * @param iv + * the initialization vector. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Des3_SetIV_fips(Des3 des, ByteBuffer iv); + + /** + * Initializes Des3 object with iv. + * + * @param des + * the Des3 object. + * @param iv + * the initialization vector. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Des3_SetIV_fips(Des3 des, byte[] iv); + + /** + * Performs Des3 CBC Encryption. + * + * @param des + * the Des3 object. + * @param out + * the output buffer. + * @param in + * the input buffer. + * @param sz + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Des3_CbcEncrypt_fips(Des3 des, ByteBuffer out, + ByteBuffer in, long sz); + + /** + * Performs Des3 CBC Encryption. + * + * @param des + * the Des3 object. + * @param out + * the output buffer. + * @param in + * the input buffer. + * @param sz + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Des3_CbcEncrypt_fips(Des3 des, byte[] out, + byte[] in, long sz); + + /** + * Performs des3 CBC Decryption. + * + * @param des + * the Des3 object. + * @param out + * the output buffer. + * @param in + * the input buffer. + * @param sz + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Des3_CbcDecrypt_fips(Des3 des, ByteBuffer out, + ByteBuffer in, long sz); + + /** + * Performs des3 CBC Decryption. + * + * @param des + * the Des3 object. + * @param out + * the output buffer. + * @param in + * the input buffer. + * @param sz + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Des3_CbcDecrypt_fips(Des3 des, byte[] out, + byte[] in, long sz); + + /* + * wolfCrypt FIPS API - Keyed hash Service + */ + + /* HMAC */ + + /** + * Initializes Hmac object with type and key. + * + * @param hmac + * the Hmac object. + * @param type + * the digest id. + * @param key + * the key buffer. + * @param keySz + * the key length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int HmacSetKey_fips(Hmac hmac, int type, + ByteBuffer key, long keySz); + + /** + * Initializes Hmac object with type and key. + * + * @param hmac + * the Hmac object. + * @param type + * the digest id. + * @param key + * the key buffer. + * @param keySz + * the key length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int HmacSetKey_fips(Hmac hmac, int type, + byte[] key, long keySz); + + /** + * Updates Hmac object with data. + * + * @param hmac + * the Hmac object. + * @param data + * the input buffer. + * @param len + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int HmacUpdate_fips(Hmac hmac, ByteBuffer data, + long len); + + /** + * Updates Hmac object with data. + * + * @param hmac + * the Hmac object. + * @param data + * the input buffer. + * @param len + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int HmacUpdate_fips(Hmac hmac, byte[] data, + long len); + + /** + * Outputs Hmac digest to hash. + * + * @param hmac + * the Hmac object. + * @param hash + * the output buffer. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int HmacFinal_fips(Hmac hmac, ByteBuffer hash); + + /** + * Outputs Hmac digest to hash. + * + * @param hmac + * the Hmac object. + * @param hash + * the output buffer. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int HmacFinal_fips(Hmac hmac, byte[] hash); + + /* + * wolfCrypt FIPS API - Random number generation Service + */ + + /* RNG */ + + /** + * Initializes RNG object's resources and state. FreeRng_fips must be called + * for resources deallocation. + * + * @param rng + * the RNG object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int InitRng_fips(Rng rng); + + /** + * Releases RNG object's resources and zeros out state. + * + * @param rng + * the RNG object. + * + * @return 0 on success, {@literal <} 0 on error. Also part of Zeroize + * Service. + */ + public static native int FreeRng_fips(Rng rng); + + /** + * Outputs block of random data from RNG object. + * + * @param rng + * the RNG object. + * @param buf + * the output buffer. + * @param bufSz + * the output length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RNG_GenerateBlock_fips(Rng rng, ByteBuffer buf, + long bufSz); + + /** + * Outputs block of random data from RNG object. + * + * @param rng + * the RNG object. + * @param buf + * the output buffer. + * @param bufSz + * the output length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RNG_GenerateBlock_fips(Rng rng, byte[] buf, + long bufSz); + + /** + * When reseed is 0, tests the output of a temporary instance of an RNG + * against the expected output of size in bytes outputSz using the seed + * buffer entropyA of size in bytes entropyASz, where entropyB and + * entropyBSz are ignored. When reseed is 1, the test also reseeds the + * temporary instance of the RNG with the seed buffer entropyB of size in + * bytes entropyBSz and then tests the RNG against the expected output of + * size in bytes outputSz. + * + * @param reseed + * the reseed flag. + * @param entropyA + * the entropyA buffer. + * @param entropyASz + * the entropyA length. + * @param entropyB + * the entropyB buffer. + * @param entropyBSz + * the entropyB length. + * @param output + * the output buffer. + * @param outputSz + * the output length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RNG_HealthTest_fips(int reseed, + ByteBuffer entropyA, long entropyASz, ByteBuffer entropyB, + long entropyBSz, ByteBuffer output, long outputSz); + + /** + * When reseed is 0, tests the output of a temporary instance of an RNG + * against the expected output of size in bytes outputSz using the seed + * buffer entropyA of size in bytes entropyASz, where entropyB and + * entropyBSz are ignored. When reseed is 1, the test also reseeds the + * temporary instance of the RNG with the seed buffer entropyB of size in + * bytes entropyBSz and then tests the RNG against the expected output of + * size in bytes outputSz. + * + * @param reseed + * the reseed flag. + * @param entropyA + * the entropyA buffer. + * @param entropyASz + * the entropyA length. + * @param entropyB + * the entropyB buffer. + * @param entropyBSz + * the entropyB length. + * @param output + * the output buffer. + * @param outputSz + * the output length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RNG_HealthTest_fips(int reseed, + byte[] entropyA, long entropyASz, byte[] entropyB, + long entropyBSz, byte[] output, long outputSz); + + /* + * wolfCrypt FIPS API - Digital signature and Key transport Services + */ + + /* RSA */ + + /** + * Initializes Rsa object for use with optional heap hint p. FreeRsaKey_fips + * must be called for resources deallocation. + * + * @param key + * the Rsa object. + * @param heap + * the (optional) heap. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int InitRsaKey_fips(Rsa key, ByteBuffer heap); + + /** + * Releases Rsa object's resources. + * + * @param key + * the Rsa object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int FreeRsaKey_fips(Rsa key); + + /** + * Performs Rsa Signing Operation. + * + * @param in + * the input buffer. + * @param inLen + * the input length. + * @param out + * the output buffer. + * @param outLen + * the output length. + * @param key + * the Rsa object. + * @param rng + * the random source for padding. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RsaSSL_Sign_fips(ByteBuffer in, long inLen, + ByteBuffer out, long outLen, Rsa key, Rng rng); + + /** + * Performs Rsa Signing Operation. + * + * @param in + * the input buffer. + * @param inLen + * the input length. + * @param out + * the output buffer. + * @param outLen + * the output length. + * @param key + * the Rsa object. + * @param rng + * the random source for padding. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RsaSSL_Sign_fips(byte[] in, long inLen, + byte[] out, long outLen, Rsa key, Rng rng); + + /** + * Performs Rsa Signature Verification. + * + * @param in + * the input buffer. + * @param inLen + * the input length. + * @param out + * the output buffer. + * @param outLen + * the output length. + * @param key + * the Rsa object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RsaSSL_Verify_fips(ByteBuffer in, long inLen, + ByteBuffer out, long outLen, Rsa key); + + /** + * Performs Rsa Signature Verification. + * + * @param in + * the input buffer. + * @param inLen + * the input length. + * @param out + * the output buffer. + * @param outLen + * the output length. + * @param key + * the Rsa object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RsaSSL_Verify_fips(byte[] in, long inLen, + byte[] out, long outLen, Rsa key); + + /** + * Retrieves Rsa Output Size. + * + * @param key + * the Rsa object. + * + * @return key output size {@literal >} 0 on success, {@literal <} 0 on + * error. + */ + public static native int RsaEncryptSize_fips(Rsa key); + + /** + * Decodes Rsa Private Key from buffer. + * + * @param input + * the input buffer. + * @param inOutIdx + * the key's starting index in the input. + * @param key + * the Rsa object. + * @param inSz + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RsaPrivateKeyDecode_fips(ByteBuffer input, + long[] inOutIdx, Rsa key, long inSz); + + /** + * Decodes Rsa Private Key from buffer. + * + * @param input + * the input buffer. + * @param inOutIdx + * the key's starting index in the input. + * @param key + * the Rsa object. + * @param inSz + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RsaPrivateKeyDecode_fips(byte[] input, + long[] inOutIdx, Rsa key, long inSz); + + /** + * Decodes Rsa Public Key from buffer. + * + * @param input + * the input buffer. + * @param inOutIdx + * the key's starting index in the input. + * @param key + * the Rsa object. + * @param inSz + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RsaPublicKeyDecode_fips(ByteBuffer input, + long[] inOutIdx, Rsa key, long inSz); + + /** + * Decodes Rsa Public Key from buffer. + * + * @param input + * the input buffer. + * @param inOutIdx + * the key's starting index in the input. + * @param key + * the Rsa object. + * @param inSz + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RsaPublicKeyDecode_fips(byte[] input, + long[] inOutIdx, Rsa key, long inSz); + + /* + * wolfCrypt FIPS API - Message digest Service + */ + + /* SHA */ + + /** + * Initializes Sha object for use. + * + * @param sha + * the Sha object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int InitSha_fips(Sha sha); + + /** + * Updates Sha object with data. + * + * @param sha + * the Sha object. + * @param data + * the input buffer. + * @param len + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int ShaUpdate_fips(Sha sha, ByteBuffer data, long len); + + /** + * Updates Sha object with data. + * + * @param sha + * the Sha object. + * @param data + * the input buffer. + * @param len + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int ShaUpdate_fips(Sha sha, byte[] data, long len); + + /** + * Outputs Sha digest to hash. + * + * @param sha + * the Sha object. + * @param hash + * the output buffer. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int ShaFinal_fips(Sha sha, ByteBuffer hash); + + /** + * Outputs Sha digest to hash. + * + * @param sha + * the Sha object. + * @param hash + * the output buffer. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int ShaFinal_fips(Sha sha, byte[] hash); + + /* SHA256 */ + + /** + * Initializes Sha256 object for use. + * + * @param sha + * the Sha256 object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int InitSha256_fips(Sha256 sha); + + /** + * Updates Sha256 object with data. + * + * @param sha + * the Sha256 object. + * @param data + * the input buffer. + * @param len + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Sha256Update_fips(Sha256 sha, ByteBuffer data, + long len); + + /** + * Updates Sha256 object with data. + * + * @param sha + * the Sha256 object. + * @param data + * the input buffer. + * @param len + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Sha256Update_fips(Sha256 sha, byte[] data, + long len); + + /** + * Outputs Sha256 digest to hash. + * + * @param sha + * the Sha256 object. + * @param hash + * the output buffer. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Sha256Final_fips(Sha256 sha, ByteBuffer hash); + + /** + * Outputs Sha256 digest to hash. + * + * @param sha + * the Sha256 object. + * @param hash + * the output buffer. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Sha256Final_fips(Sha256 sha, byte[] hash); + + /* SHA384 */ + + /** + * Initializes Sha384 object for use. + * + * @param sha + * the Sha384 object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int InitSha384_fips(Sha384 sha); + + /** + * Updates Sha384 object with data. + * + * @param sha + * the Sha384 object. + * @param data + * the input buffer. + * @param len + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Sha384Update_fips(Sha384 sha, ByteBuffer data, + long len); + + /** + * Updates Sha384 object with data. + * + * @param sha + * the Sha384 object. + * @param data + * the input buffer. + * @param len + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Sha384Update_fips(Sha384 sha, byte[] data, + long len); + + /** + * Outputs Sha384 digest to hash. + * + * @param sha + * the Sha384 object. + * @param hash + * the output buffer. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Sha384Final_fips(Sha384 sha, ByteBuffer hash); + + /** + * Outputs Sha384 digest to hash. + * + * @param sha + * the Sha384 object. + * @param hash + * the output buffer. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Sha384Final_fips(Sha384 sha, byte[] hash); + + /* SHA512 */ + + /** + * Initializes Sha512 object for use. + * + * @param sha + * the Sha512 object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int InitSha512_fips(Sha512 sha); + + /** + * Updates Sha512 object with data. + * + * @param sha + * the Sha512 object. + * @param data + * the input buffer. + * @param len + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Sha512Update_fips(Sha512 sha, ByteBuffer data, + long len); + + /** + * Updates Sha512 object with data. + * + * @param sha + * the Sha512 object. + * @param data + * the input buffer. + * @param len + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Sha512Update_fips(Sha512 sha, byte[] data, + long len); + + /** + * Outputs Sha512 digest to hash. + * + * @param sha + * the Sha512 object. + * @param hash + * the output buffer. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Sha512Final_fips(Sha512 sha, ByteBuffer hash); + + /** + * Outputs Sha512 digest to hash. + * + * @param sha + * the Sha512 object. + * @param hash + * the output buffer. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Sha512Final_fips(Sha512 sha, byte[] hash); + + /* + * wolfCrypt FIPS API - Show status Service + */ + + /** + * @return The current status of the module. A return code of 0 means the + * module is in a state without errors. Any other return code is the + * specific error state of the module. + */ + public static native int wolfCrypt_GetStatus_fips(); + + /** + * Sets the fips module status. Only available if HAVE_FORCE_FIPS_FAILURE is + * defined on the native library. + * + * @param status + * the new status. + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int wolfCrypt_SetStatus_fips(int status); + + /* + * ### FIPS Allowed Security Methods ####################################### + */ + + /* + * wolfCrypt FIPS API - Key transport Service + */ + + /** + * Performs Rsa Public Encryption. + * + * @param in + * the input buffer. + * @param inLen + * the input length. + * @param out + * the output buffer. + * @param outLen + * the output length. + * @param key + * the Rsa object. + * @param rng + * the random source for padding. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RsaPublicEncrypt_fips(ByteBuffer in, long inLen, + ByteBuffer out, long outLen, Rsa key, Rng rng); + + /** + * Performs Rsa Public Encryption. + * + * @param in + * the input buffer. + * @param inLen + * the input length. + * @param out + * the output buffer. + * @param outLen + * the output length. + * @param key + * the Rsa object. + * @param rng + * the random source for padding. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RsaPublicEncrypt_fips(byte[] in, long inLen, + byte[] out, long outLen, Rsa key, Rng rng); + + /** + * Performs Rsa Private Decryption. + * + * @param in + * the input buffer. + * @param inLen + * the input length. + * @param out + * the output buffer. + * @param outLen + * the output length. + * @param key + * the Rsa object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RsaPrivateDecrypt_fips(ByteBuffer in, long inLen, + ByteBuffer out, long outLen, Rsa key); + + /** + * Performs Rsa Private Decryption. + * + * @param in + * the input buffer. + * @param inLen + * the input length. + * @param out + * the output buffer. + * @param outLen + * the output length. + * @param key + * the Rsa object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int RsaPrivateDecrypt_fips(byte[] in, long inLen, + byte[] out, long outLen, Rsa key); + + /* + * wolfCrypt FIPS API - Message digest MD5 Service + */ + + /** + * Initializes Md5 object for use. + * + * @param md5 + * the Md5 object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int InitMd5(Md5 md5); + + /** + * Updates Md5 object with data. + * + * @param md5 + * the Md5 object. + * @param data + * the input buffer. + * @param len + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Md5Update(Md5 md5, ByteBuffer data, long len); + + /** + * Updates Md5 object with data. + * + * @param md5 + * the Md5 object. + * @param data + * the input buffer. + * @param len + * the input length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Md5Update(Md5 md5, byte[] data, long len); + + /** + * Outputs Md5 digest to hash. + * + * @param md5 + * the Md5 object. + * @param hash + * the output buffer. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Md5Final(Md5 md5, ByteBuffer hash); + + /** + * Outputs Md5 digest to hash. + * + * @param md5 + * the Md5 object. + * @param hash + * the output buffer. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int Md5Final(Md5 md5, byte[] hash); + + /* + * wolfCrypt FIPS API - Key agreement Service + */ + + /** + * Initializes Dh object for use. FreeDhKey must be called for resources + * deallocation. + * + * @param key + * the Dh object. + */ + public static native void InitDhKey(Dh key); + + /** + * Releases Dh object's resources. + * + * @param key + * the Dh object. + */ + public static native void FreeDhKey(Dh key); + + /** + * Generates the public part pub of size pubSz, private part priv of size + * privSz using rng for Dh key. + * + * @param key + * the Dh object. + * @param rng + * the random source. + * @param priv + * the private part buffer. + * @param privSz + * the private part length. + * @param pub + * the public part buffer. + * @param pubSz + * the the public part length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int DhGenerateKeyPair(Dh key, Rng rng, + ByteBuffer priv, long[] privSz, ByteBuffer pub, long[] pubSz); + + /** + * Generates the public part pub of size pubSz, private part priv of size + * privSz using rng for Dh key. + * + * @param key + * the Dh object. + * @param rng + * the random source. + * @param priv + * the private part buffer. + * @param privSz + * the private part length. + * @param pub + * the public part buffer. + * @param pubSz + * the the public part length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int DhGenerateKeyPair(Dh key, Rng rng, + byte[] priv, long[] privSz, byte[] pub, long[] pubSz); + + /** + * Creates the agreement agree of size agreeSz using Dh key private priv of + * size privSz and peer’s public key otherPub of size pubSz. + * + * @param key + * the Dh object. + * @param agree + * the agree buffer. + * @param agreeSz + * the agree length. + * @param priv + * the private part buffer. + * @param privSz + * the private part length. + * @param otherPub + * the peer's public part buffer. + * @param pubSz + * the the public part length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int DhAgree(Dh key, ByteBuffer agree, long[] agreeSz, + ByteBuffer priv, long privSz, ByteBuffer otherPub, long pubSz); + + /** + * Creates the agreement agree of size agreeSz using Dh key private priv of + * size privSz and peer’s public key otherPub of size pubSz. + * + * @param key + * the Dh object. + * @param agree + * the agree buffer. + * @param agreeSz + * the agree length. + * @param priv + * the private part buffer. + * @param privSz + * the private part length. + * @param otherPub + * the peer's public part buffer. + * @param pubSz + * the the public part length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int DhAgree(Dh key, byte[] agree, long[] agreeSz, + byte[] priv, long privSz, byte[] otherPub, long pubSz); + + /** + * Decodes the DER group parameters from buffer input starting at index + * inOutIdx of size inSz into Dh key. + * + * @param input + * the parameters buffer. + * @param inOutIdx + * the parameters' starting index. + * @param key + * the Dh object. + * @param inSz + * the parameters buffer length. (not from inOutIdx) + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int DhKeyDecode(ByteBuffer input, long[] inOutIdx, + Dh key, long inSz); + + /** + * Decodes the DER group parameters from buffer input starting at index + * inOutIdx of size inSz into Dh key. + * + * @param input + * the parameters buffer. + * @param inOutIdx + * the parameters' starting index. + * @param key + * the Dh object. + * @param inSz + * the parameters buffer length. (not from inOutIdx) + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int DhKeyDecode(byte[] input, long[] inOutIdx, + Dh key, long inSz); + + /** + * Sets the group parameters for the Dh key from the unsigned binary inputs + * p of size pSz and g of size gSz. + * + * @param key + * the Dh object. + * @param p + * the prime buffer. + * @param pSz + * the prime length. + * @param g + * the primitive root molulo p buffer. + * @param gSz + * the primitive root modulo p length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int DhSetKey(Dh key, ByteBuffer p, long pSz, + ByteBuffer g, long gSz); + + /** + * Sets the group parameters for the Dh key from the unsigned binary inputs + * p of size pSz and g of size gSz. + * + * @param key + * the Dh object. + * @param p + * the prime buffer. + * @param pSz + * the prime length. + * @param g + * the primitive root molulo p buffer. + * @param gSz + * the primitive root modulo p length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int DhSetKey(Dh key, byte[] p, long pSz, + byte[] g, long gSz); + + /** + * Loads the Dh group parameters. + * + * @param input + * the parameters buffer. + * @param inSz + * the parameters size. + * @param p + * the prime buffer. + * @param pInOutSz + * the prime length. + * @param g + * the primitive root molulo p buffer. + * @param gInOutSz + * the primitive root modulo p length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int DhParamsLoad(ByteBuffer input, long inSz, + ByteBuffer p, long[] pInOutSz, ByteBuffer g, long[] gInOutSz); + + /** + * Loads the Dh group parameters. + * + * @param input + * the parameters buffer. + * @param inSz + * the parameters size. + * @param p + * the prime buffer. + * @param pInOutSz + * the prime length. + * @param g + * the primitive root molulo p buffer. + * @param gInOutSz + * the primitive root modulo p length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int DhParamsLoad(byte[] input, long inSz, + byte[] p, long[] pInOutSz, byte[] g, long[] gInOutSz); + + /** + * Initializes Ecc object for use. ecc_free must be called for resources + * deallocation. + * + * @param key + * the Ecc object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int ecc_init(Ecc key); + + /** + * Releases Ecc object's resources. + * + * @param key + * the Ecc object. + */ + public static native void ecc_free(Ecc key); + + /** + * Generates a new ecc key of size keysize using rng. + * + * @param rng + * the random source. + * @param keysize + * the key length. + * @param key + * the Ecc object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int ecc_make_key(Rng rng, int keysize, Ecc key); + + /** + * Creates the shared secret out of size outlen using ecc private_key and + * the peer’s ecc public_key. + * + * @param private_key + * the Ecc object for the private key. + * @param public_key + * the Ecc object for the peer's public key. + * @param out + * the output buffer. + * @param outlen + * the output length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int ecc_shared_secret(Ecc private_key, Ecc public_key, + ByteBuffer out, long[] outlen); + + /** + * Creates the shared secret out of size outlen using ecc private_key and + * the peer’s ecc public_key. + * + * @param private_key + * the Ecc object for the private key. + * @param public_key + * the Ecc object for the peer's public key. + * @param out + * the output buffer. + * @param outlen + * the output length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int ecc_shared_secret(Ecc private_key, Ecc public_key, + byte[] out, long[] outlen); + + /** + * Imports the public ecc key from in of length inLen in x963 format. + * + * @param in + * the input buffer. + * @param inLen + * the input length. + * @param key + * the Ecc object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int ecc_import_x963(ByteBuffer in, long inLen, + Ecc key); + + /** + * Imports the public ecc key from in of length inLen in x963 format. + * + * @param in + * the input buffer. + * @param inLen + * the input length. + * @param key + * the Ecc object. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int ecc_import_x963(byte[] in, long inLen, + Ecc key); + + /** + * Exports the public ecc key into out of length outLen in x963 format. + * + * @param key + * the Ecc object. + * @param out + * the output buffer. + * @param outLen + * the output length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int ecc_export_x963(Ecc key, ByteBuffer out, + long[] outLen); + + /** + * Exports the public ecc key into out of length outLen in x963 format. + * + * @param key + * the Ecc object. + * @param out + * the output buffer. + * @param outLen + * the output length. + * + * @return 0 on success, {@literal <} 0 on error. + */ + public static native int ecc_export_x963(Ecc key, byte[] out, + long[] outLen); +} diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Hmac.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Hmac.java new file mode 100644 index 0000000..14791b8 --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Hmac.java @@ -0,0 +1,214 @@ +/* Hmac.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import com.wolfssl.wolfcrypt.WolfCrypt; +import java.nio.ByteBuffer; + +/** + * Wrapper for the native WolfCrypt Hmac implementation. + * + * @author Moisés Guimarães + * @version 2.0, March 2017 + */ +public class Hmac extends NativeStruct { + + private enum hashType { + typeMD5, typeSHA, typeSHA256, typeSHA384, typeSHA512, typeBLAKE2b; + } + + public static final int MD5 = getHashCode(hashType.typeMD5); + public static final int SHA = getHashCode(hashType.typeSHA); + public static final int SHA256 = getHashCode(hashType.typeSHA256); + public static final int SHA384 = getHashCode(hashType.typeSHA384); + public static final int SHA512 = getHashCode(hashType.typeSHA512); + public static final int BLAKE2b = getHashCode(hashType.typeBLAKE2b); + + private WolfCryptState state = WolfCryptState.UNINITIALIZED; + private int type = -1; + private byte[] key; + + public Hmac() { + } + + public Hmac(int type, byte[] key) { + setKey(type, key); + } + + private native void wc_HmacSetKey(int type, byte[] key); + + private native void wc_HmacUpdate(byte data); + + private native void wc_HmacUpdate(byte[] data, int offset, int length); + + private native void wc_HmacUpdate(ByteBuffer data, int offset, int length); + + private native byte[] wc_HmacFinal(); + + private native int wc_HmacSizeByType(int type); + + private native static int getCodeMd5(); + + private native static int getCodeSha(); + + private native static int getCodeSha256(); + + private native static int getCodeSha384(); + + private native static int getCodeSha512(); + + private native static int getCodeBlake2b(); + + protected native long mallocNativeStruct() throws OutOfMemoryError; + + public void setKey(int type, byte[] key) { + wc_HmacSetKey(type, key); + this.type = type; + this.key = key; + + state = WolfCryptState.READY; + } + + public void reset() { + if (state == WolfCryptState.READY) { + setKey(type, key); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + public void update(byte data) { + if (state == WolfCryptState.READY) { + wc_HmacUpdate(data); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + public void update(byte[] data) { + if (state == WolfCryptState.READY) { + wc_HmacUpdate(data, 0, data.length); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + public void update(byte[] data, int offset, int length) { + if (state == WolfCryptState.READY) { + wc_HmacUpdate(data, offset, length); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + public void update(ByteBuffer data) { + if (state == WolfCryptState.READY) { + int offset = data.position(); + int length = data.remaining(); + + wc_HmacUpdate(data, offset, length); + + data.position(offset + length); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + public byte[] doFinal() { + if (state == WolfCryptState.READY) { + return wc_HmacFinal(); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + public byte[] doFinal(byte[] data) { + if (state == WolfCryptState.READY) { + update(data); + return wc_HmacFinal(); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + public String getAlgorithm() { + if (state == WolfCryptState.READY) { + + if (type == MD5) { + return "HmacMD5"; + } + else if (type == SHA256) { + return "HmacSHA256"; + } + else if (type == SHA384) { + return "HmacSHA384"; + } + else if (type == SHA512) { + return "HmacSHA512"; + } + else if (type == BLAKE2b) { + return "HmacBLAKE2b"; + } else { + return ""; + } + + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + public int getMacLength() { + if (state == WolfCryptState.READY) { + return wc_HmacSizeByType(type); + } else { + throw new IllegalStateException( + "No available key to perform the opperation."); + } + } + + private static int getHashCode(hashType hash) { + switch (hash) { + case typeMD5: + return getCodeMd5(); + case typeSHA: + return getCodeSha(); + case typeSHA256: + return getCodeSha256(); + case typeSHA384: + return getCodeSha384(); + case typeSHA512: + return getCodeSha512(); + case typeBLAKE2b: + return getCodeBlake2b(); + default: + return WolfCrypt.FAILURE; + } + } +} diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Md5.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Md5.java new file mode 100644 index 0000000..78e9338 --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Md5.java @@ -0,0 +1,61 @@ +/* Md5.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.nio.ByteBuffer; + +/** + * Wrapper for the native WolfCrypt Md5 implementation. + * + * @author Moisés Guimarães + * @version 2.0, March 2017 + */ +public class Md5 extends MessageDigest { + + public static final int TYPE = 0; /* hash type unique */ + public static final int DIGEST_SIZE = 16; + + protected native long mallocNativeStruct() throws OutOfMemoryError; + + protected native void native_init(); + + protected native void native_update(ByteBuffer data, int offset, int len); + + protected native void native_update(byte[] data, int offset, int len); + + protected native void native_final(ByteBuffer hash, int offset); + + protected native void native_final(byte[] hash); + + public Md5() { + init(); + } + + public Md5(byte[] data) { + init(); + update(data); + } + + public int digestSize() { + return DIGEST_SIZE; + } +} diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/MessageDigest.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/MessageDigest.java new file mode 100644 index 0000000..6e2aeaa --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/MessageDigest.java @@ -0,0 +1,142 @@ +/* MessageDigest.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.nio.ByteBuffer; + +import javax.crypto.ShortBufferException; + +/** + * Common API for Message Digests. + * + * @author Moisés Guimarães + * @version 1.0, March 2017 + */ +public abstract class MessageDigest extends NativeStruct { + + private WolfCryptState state = WolfCryptState.UNINITIALIZED; + + protected abstract void native_init(); + + protected abstract void native_update(ByteBuffer data, int offset, + int length); + + protected abstract void native_update(byte[] data, int offset, int length); + + protected abstract void native_final(ByteBuffer hash, int offset); + + protected abstract void native_final(byte[] hash); + + public abstract int digestSize(); + + public void init() { + native_init(); + state = WolfCryptState.READY; + } + + public void update(ByteBuffer data, int length) { + if (state == WolfCryptState.READY) { + length = Math.min(length, data.remaining()); + + native_update(data, data.position(), length); + data.position(data.position() + length); + } else { + throw new IllegalStateException( + "Object must be initialized before use"); + } + } + + public void update(ByteBuffer data) { + update(data, data.remaining()); + } + + public void update(byte[] data, int offset, int len) { + if (state == WolfCryptState.READY) { + if (offset >= data.length || offset < 0 || len < 0) + return; + + if (data.length - offset < len) + len = data.length - offset; + + native_update(data, offset, len); + } else { + throw new IllegalStateException( + "Object must be initialized before use"); + } + } + + public void update(byte[] data, int len) { + update(data, 0, len); + } + + public void update(byte[] data) { + update(data, 0, data.length); + } + + public void digest(ByteBuffer hash) throws ShortBufferException { + if (state == WolfCryptState.READY) { + if (hash.remaining() < digestSize()) + throw new ShortBufferException( + "Input buffer is too small for digest size"); + + native_final(hash, hash.position()); + hash.position(hash.position() + digestSize()); + } else { + throw new IllegalStateException( + "Object must be initialized before use"); + } + } + + public void digest(byte[] hash) throws ShortBufferException { + if (state == WolfCryptState.READY) { + if (hash.length < digestSize()) + throw new ShortBufferException( + "Input buffer is too small for digest size"); + + native_final(hash); + } else { + throw new IllegalStateException( + "Object must be initialized before use"); + } + } + + public byte[] digest() { + if (state == WolfCryptState.READY) { + byte[] hash = new byte[digestSize()]; + + native_final(hash); + + return hash; + } else { + throw new IllegalStateException( + "Object must be initialized before use"); + } + } + + @Override + public void releaseNativeStruct() { + + /* reset state first, then free */ + state = WolfCryptState.UNINITIALIZED; + setNativeStruct(NULL); + } +} diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Rng.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Rng.java new file mode 100644 index 0000000..7513257 --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Rng.java @@ -0,0 +1,93 @@ +/* Rng.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.nio.ByteBuffer; + +/** + * Wrapper for the native WolfCrypt Rng implementation. + * + * @author Moisés Guimarães + * @version 1.0, March 2015 + */ +public class Rng extends NativeStruct { + + protected native long mallocNativeStruct() throws OutOfMemoryError; + + private WolfCryptState state = WolfCryptState.UNINITIALIZED; + + /* native wrappers called by public functions below */ + private native void initRng(); + + private native void freeRng(); + + private native void rngGenerateBlock(ByteBuffer buffer, int offset, + int length); + + private native void rngGenerateBlock(byte[] buffer, int offset, int length); + + @Override + public void releaseNativeStruct() { + free(); + + super.releaseNativeStruct(); + } + + public void init() { + if (state == WolfCryptState.UNINITIALIZED) { + initRng(); + state = WolfCryptState.INITIALIZED; + } + } + + public void free() { + if (state == WolfCryptState.INITIALIZED) { + freeRng(); + state = WolfCryptState.UNINITIALIZED; + } + } + + public void generateBlock(ByteBuffer buffer) { + init(); + + rngGenerateBlock(buffer, buffer.position(), buffer.remaining()); + buffer.position(buffer.position() + buffer.remaining()); + } + + public void generateBlock(byte[] buffer, int offset, int length) { + init(); + + rngGenerateBlock(buffer, offset, length); + } + + public void generateBlock(byte[] buffer) { + generateBlock(buffer, 0, buffer.length); + } + + public byte[] generateBlock(int length) { + byte[] buffer = new byte[length]; + + generateBlock(buffer, 0, length); + + return buffer; + } +} diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Rsa.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Rsa.java new file mode 100644 index 0000000..e6b7a0a --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Rsa.java @@ -0,0 +1,229 @@ +/* Rsa.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.nio.ByteBuffer; + +/** + * Wrapper for the native WolfCrypt Rsa implementation. + * + * @author Moisés Guimarães + * @version 2.0, March 2017 + */ +public class Rsa extends NativeStruct { + + private WolfCryptState state = WolfCryptState.UNINITIALIZED; + private boolean hasPrivateKey = false; + private Rng rng; + + protected native long mallocNativeStruct() throws OutOfMemoryError; + + private native void wc_RsaPublicKeyDecodeRaw(ByteBuffer n, long nSize, + ByteBuffer e, long eSize); + + private native void wc_RsaPublicKeyDecodeRaw(byte[] n, long nSize, byte[] e, + long eSize); + + private native void RsaFlattenPublicKey(ByteBuffer n, ByteBuffer e); + + private native void RsaFlattenPublicKey(byte[] n, long[] nSize, byte[] e, + long[] eSize); + + private native void MakeRsaKey(int size, long e, Rng rng); + + private native void wc_InitRsaKey(); + + private native void wc_FreeRsaKey(); + + private native boolean wc_RsaSetRNG(Rng rng); + + private native void wc_RsaPrivateKeyDecode(byte[] key); + + private native void wc_RsaPrivateKeyDecodePKCS8(byte[] key); + + private native void wc_RsaPublicKeyDecode(byte[] key); + + private native int wc_RsaEncryptSize(); + + private native byte[] wc_RsaPublicEncrypt(byte[] data, Rng rng); + + private native byte[] wc_RsaPrivateDecrypt(byte[] data); + + private native byte[] wc_RsaSSL_Sign(byte[] data, Rng rng); + + private native byte[] wc_RsaSSL_Verify(byte[] data); + + public Rsa() { + /* Lazy init for Fips compatibility */ + } + + public Rsa(byte[] key) { + decodePrivateKey(key); + } + + public Rsa(byte[] n, byte[] e) { + decodeRawPublicKey(n, e); + } + + public void setRng(Rng rng) { + init(); + + if (wc_RsaSetRNG(rng)) + this.rng = rng; + } + + @Override + public void releaseNativeStruct() { + free(); + + super.releaseNativeStruct(); + } + + protected void init() { + if (state == WolfCryptState.UNINITIALIZED) { + wc_InitRsaKey(); + state = WolfCryptState.INITIALIZED; + } + } + + protected void willSetKey() { + init(); + + if (state != WolfCryptState.INITIALIZED) + throw new IllegalStateException("Object already has a key."); + } + + protected void willUseKey(boolean priv) { + if (priv && !hasPrivateKey) + throw new IllegalStateException( + "No available private key to perform the opperation."); + + if (state != WolfCryptState.READY) + throw new IllegalStateException( + "No available key to perform the opperation."); + } + + protected void free() { + if (state != WolfCryptState.UNINITIALIZED) { + wc_FreeRsaKey(); + state = WolfCryptState.UNINITIALIZED; + } + } + + public void makeKey(int size, long e, Rng rng) { + willSetKey(); + + MakeRsaKey(size, e, rng); + + state = WolfCryptState.READY; + hasPrivateKey = true; + } + + public void decodePublicKey(byte[] key) { + willSetKey(); + + wc_RsaPublicKeyDecode(key); + state = WolfCryptState.READY; + } + + public void decodePrivateKey(byte[] key) { + willSetKey(); + + wc_RsaPrivateKeyDecode(key); + state = WolfCryptState.READY; + hasPrivateKey = true; + } + + public void decodePrivateKeyPKCS8(byte[] key) { + willSetKey(); + + wc_RsaPrivateKeyDecodePKCS8(key); + + state = WolfCryptState.READY; + hasPrivateKey = true; + } + + public void decodeRawPublicKey(byte[] n, byte[] e) { + decodeRawPublicKey(n, n.length, e, e.length); + } + + public void decodeRawPublicKey(byte[] n, long nSize, byte[] e, long eSize) { + willSetKey(); + + wc_RsaPublicKeyDecodeRaw(n, nSize, e, eSize); + state = WolfCryptState.READY; + } + + public void decodeRawPublicKey(ByteBuffer n, ByteBuffer e) { + decodeRawPublicKey(n, n.limit(), e, e.limit()); + } + + public void decodeRawPublicKey(ByteBuffer n, long nSz, ByteBuffer e, + long eSz) { + willSetKey(); + + wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz); + state = WolfCryptState.READY; + } + + public void exportRawPublicKey(byte[] n, long[] nSz, byte[] e, long[] eSz) { + willUseKey(false); + + RsaFlattenPublicKey(n, nSz, e, eSz); + } + + public void exportRawPublicKey(ByteBuffer n, ByteBuffer e) { + willUseKey(false); + + RsaFlattenPublicKey(n, e); + } + + public int getEncryptSize() { + willUseKey(false); + + return wc_RsaEncryptSize(); + } + + public byte[] encrypt(byte[] plain, Rng rng) { + willUseKey(false); + + return wc_RsaPublicEncrypt(plain, rng); + } + + public byte[] decrypt(byte[] ciphertext) { + willUseKey(true); + + return wc_RsaPrivateDecrypt(ciphertext); + } + + public byte[] sign(byte[] data, Rng rng) { + willUseKey(true); + + return wc_RsaSSL_Sign(data, rng); + } + + public byte[] verify(byte[] signature) { + willUseKey(false); + + return wc_RsaSSL_Verify(signature); + } +} diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha.java new file mode 100644 index 0000000..f8d96bc --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha.java @@ -0,0 +1,61 @@ +/* Sha.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.nio.ByteBuffer; + +/** + * Wrapper for the native WolfCrypt Sha implementation. + * + * @author Moisés Guimarães + * @version 2.0, March 2017 + */ +public class Sha extends MessageDigest { + + public static final int TYPE = 1; /* hash type unique */ + public static final int DIGEST_SIZE = 20; + + protected native long mallocNativeStruct() throws OutOfMemoryError; + + protected native void native_init(); + + protected native void native_update(ByteBuffer data, int offset, int len); + + protected native void native_update(byte[] data, int offset, int len); + + protected native void native_final(ByteBuffer hash, int offset); + + protected native void native_final(byte[] hash); + + public Sha() { + init(); + } + + public Sha(byte[] data) { + init(); + update(data); + } + + public int digestSize() { + return DIGEST_SIZE; + } +} diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha256.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha256.java new file mode 100644 index 0000000..1c5dafe --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha256.java @@ -0,0 +1,62 @@ +/* Sha256.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.nio.ByteBuffer; + +/** + * Wrapper for the native WolfCrypt Sha256 implementation. + * + * @author Moisés Guimarães + * @version 2.0, March 2017 + */ +public class Sha256 extends MessageDigest { + + public static final int TYPE = 2; /* hash type unique */ + public static final int DIGEST_SIZE = 32; + + protected native long mallocNativeStruct() throws OutOfMemoryError; + + protected native void native_init(); + + protected native void native_update(ByteBuffer data, int offset, int len); + + protected native void native_update(byte[] data, int offset, int len); + + protected native void native_final(ByteBuffer hash, int offset); + + protected native void native_final(byte[] hash); + + public Sha256() { + init(); + } + + public Sha256(byte[] data) { + init(); + update(data); + } + + public int digestSize() { + return DIGEST_SIZE; + } +} + diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha384.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha384.java new file mode 100644 index 0000000..654c25b --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha384.java @@ -0,0 +1,62 @@ +/* Sha384.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.nio.ByteBuffer; + +/** + * Wrapper for the native WolfCrypt Sha384 implementation. + * + * @author Moisés Guimarães + * @version 2.0, March 2017 + */ +public class Sha384 extends MessageDigest { + + public static final int TYPE = 5; /* hash type unique */ + public static final int DIGEST_SIZE = 48; + + protected native long mallocNativeStruct() throws OutOfMemoryError; + + protected native void native_init(); + + protected native void native_update(ByteBuffer data, int offset, int len); + + protected native void native_update(byte[] data, int offset, int len); + + protected native void native_final(ByteBuffer hash, int offset); + + protected native void native_final(byte[] hash); + + public Sha384() { + init(); + } + + public Sha384(byte[] data) { + init(); + update(data); + } + + public int digestSize() { + return DIGEST_SIZE; + } +} + diff --git a/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha512.java b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha512.java new file mode 100644 index 0000000..343d98c --- /dev/null +++ b/wolfssl-jni/src/main/java/com/wolfssl/wolfcrypt/Sha512.java @@ -0,0 +1,62 @@ +/* Sha512.java + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package com.wolfssl.wolfcrypt; + +import java.nio.ByteBuffer; + +/** + * Wrapper for the native WolfCrypt Sha512 implementation. + * + * @author Moisés Guimarães + * @version 2.0, March 2017 + */ +public class Sha512 extends MessageDigest { + + public static final int TYPE = 4; /* hash type unique */ + public static final int DIGEST_SIZE = 64; + + protected native long mallocNativeStruct() throws OutOfMemoryError; + + protected native void native_init(); + + protected native void native_update(ByteBuffer data, int offset, int len); + + protected native void native_update(byte[] data, int offset, int len); + + protected native void native_final(ByteBuffer hash, int offset); + + protected native void native_final(byte[] hash); + + public Sha512() { + init(); + } + + public Sha512(byte[] data) { + init(); + update(data); + } + + public int digestSize() { + return DIGEST_SIZE; + } +} + diff --git a/wolfssl-jni/src/test/java/com/wolfssl/wolfcrypt/test/AesTest.java b/wolfssl-jni/src/test/java/com/wolfssl/wolfcrypt/test/AesTest.java index 232290f..25701af 100644 --- a/wolfssl-jni/src/test/java/com/wolfssl/wolfcrypt/test/AesTest.java +++ b/wolfssl-jni/src/test/java/com/wolfssl/wolfcrypt/test/AesTest.java @@ -54,7 +54,7 @@ public class AesTest { Assume.assumeNoException(e); } catch (UnsatisfiedLinkError e) { - System.out.println("Native code library failed to load.\n" + e.getError()); + System.out.println("Native code library failed to load.\n" + e.getMessage()); Assume.assumeNoException(e); } }