diff --git a/cmake/FindNSPR.cmake b/cmake/FindNSPR.cmake index b8b07833f..a45f257ad 100644 --- a/cmake/FindNSPR.cmake +++ b/cmake/FindNSPR.cmake @@ -27,6 +27,7 @@ if (SANDBOX) # Directly set the NSS include and library directories set(NSPR_INCLUDE_DIRS "${DIST_DIR}/${LATEST_BUILD}/include/nspr") set(NSPR_LIBRARIES "${DIST_DIR}/${LATEST_BUILD}/lib") + list(APPEND JSS_LD_FLAGS "-L${DIST_DIR}/${LATEST_BUILD}/lib") list(APPEND JSS_LD_FLAGS "-Wl,-rpath,${DIST_DIR}/${LATEST_BUILD}/lib") elseif (NSPR_LIBRARIES AND NSPR_INCLUDE_DIRS) # in cache already diff --git a/cmake/FindNSS.cmake b/cmake/FindNSS.cmake index d8ca93fb9..6bbc7f26a 100644 --- a/cmake/FindNSS.cmake +++ b/cmake/FindNSS.cmake @@ -27,6 +27,7 @@ if (SANDBOX) # Directly set the NSS include and library directories set(NSS_INCLUDE_DIRS "${DIST_DIR}/public/nss") set(NSS_LIBRARIES "${DIST_DIR}/${LATEST_BUILD}/lib") + list(APPEND JSS_LD_FLAGS "-L${DIST_DIR}/${LATEST_BUILD}/lib") list(APPEND JSS_LD_FLAGS "-Wl,-rpath,${DIST_DIR}/${LATEST_BUILD}/lib") elseif (NSS_LIBRARIES AND NSS_INCLUDE_DIRS) # in cache already diff --git a/cmake/JSSConfig.cmake b/cmake/JSSConfig.cmake index 0a39cab26..2a4729980 100644 --- a/cmake/JSSConfig.cmake +++ b/cmake/JSSConfig.cmake @@ -134,6 +134,7 @@ macro(jss_config_cflags) list(APPEND JSS_RAW_C_FLAGS "-Wno-cast-function-type") list(APPEND JSS_RAW_C_FLAGS "-Wno-unused-parameter") list(APPEND JSS_RAW_C_FLAGS "-Wno-unknown-warning-option") + list(APPEND JSS_RAW_C_FLAGS "-Wno-unused-but-set-variable") list(APPEND JSS_RAW_C_FLAGS "-Werror-implicit-function-declaration") list(APPEND JSS_RAW_C_FLAGS "-Wno-switch") list(APPEND JSS_RAW_C_FLAGS "-I${INCLUDE_OUTPUT_DIR}") @@ -399,9 +400,25 @@ macro(jss_config_symbols) message(WARNING "Your NSS version doesn't support NIST SP800-108 KBKDF; some features of JSS won't work.") endif() + try_compile(CK_HAVE_COMPILING_OAEP + ${CMAKE_BINARY_DIR}/results + ${CMAKE_SOURCE_DIR}/tools/tests/oaep.c + CMAKE_FLAGS + "-DINCLUDE_DIRECTORIES=${CMAKE_REQUIRED_INCLUDES}" + "-DREQUIRED_FLAGS=${CMAKE_REQUIRED_FLAGS}" + LINK_OPTIONS ${JSS_LD_FLAGS} + OUTPUT_VARIABLE COMP_OUT) + if (CK_HAVE_COMPILING_OAEP) + set(HAVE_NSS_OAEP TRUE) + else() + message(WARNING "Your NSS version doesn't support RSA-OAEP key wra/unwrap; some features of JSS won't work.") + message(WARNING "Compile output: ${COMP_OUT}") + endif() + + if(HAVE_NSS_CMAC) - try_run(CK_HAVE_WORKING_NSS - CK_HAVE_COMPILING_NSS + try_run(CK_HAVE_WORKING_CMAC + CK_HAVE_COMPILING_CMAC ${CMAKE_BINARY_DIR}/results ${CMAKE_SOURCE_DIR}/tools/tests/cmac.c CMAKE_FLAGS @@ -410,10 +427,12 @@ macro(jss_config_symbols) COMPILE_OUTPUT_VARIABLE COMP_OUT RUN_OUTPUT_VARIABLE RUN_OUT) - if (NOT CK_HAVE_WORKING_NSS STREQUAL "0" OR NOT CK_HAVE_COMPILING_NSS) + if (NOT CK_HAVE_WORKING_CMAC STREQUAL "0" OR NOT CK_HAVE_COMPILING_CMAC) set(HAVE_NSS_CMAC FALSE) set(HAVE_NSS_KBKDF FALSE) message(WARNING "Your NSS version is broken: between NSS v3.47 and v3.50, the values of CKM_AES_CMAC and CKM_AES_CMAC_GENERAL were swapped. Disabling CMAC and KBKDF support.") + message(WARNING "Compile output: ${COMP_OUT}") + message(WARNING "Run output: ${RUN_OUT}") endif() endif() diff --git a/cmake/JSSTests.cmake b/cmake/JSSTests.cmake index 7384bdc05..7427308dc 100644 --- a/cmake/JSSTests.cmake +++ b/cmake/JSSTests.cmake @@ -210,6 +210,29 @@ macro(jss_tests) COMMAND "org.mozilla.jss.tests.JCAKeyWrap" "${RESULTS_NSSDB_OUTPUT_DIR}" "${PASSWORD_FILE}" DEPENDS "Setup_DBs" ) + if(HAVE_NSS_OAEP) + jss_test_java( + NAME "JSS-KeyWrapping" + COMMAND "org.mozilla.jss.tests.KeyWrapping" "${RESULTS_NSSDB_OUTPUT_DIR}" "${PASSWORD_FILE}" + DEPENDS "Setup_DBs" + ) + endif() + jss_test_exec( + NAME "Setup_Bouncy_Castle_Jar" + COMMAND "wget" "https://www.bouncycastle.org/download/bcprov-jdk15on-167.jar" "-P" "/tmp/" + ) + jss_test_exec( + NAME "Compile_RSAOAEPSHA2_with_BC_classpath" + COMMAND "javac" "-classpath" "/tmp/bcprov-jdk15on-167.jar:./" "${JSS_TEST_DIR}/VerifyRSAOAEPSHA2.java" + DEPENDS "Setup_Bouncy_Castle_Jar" + ) + if(HAVE_NSS_OAEP) + jss_test_java( + NAME "JSS-OAEP-Vector-Test" + COMMAND "org.mozilla.jss.tests.VerifyRSAOAEPSHA2" + DEPENDS "Setup_Bouncy_Castle_Jar" + ) + endif() jss_test_java( NAME "Mozilla_JSS_JCA_Signature" COMMAND "org.mozilla.jss.tests.JCASigTest" "${RESULTS_NSSDB_OUTPUT_DIR}" "${PASSWORD_FILE}" diff --git a/lib/jss.map b/lib/jss.map index a1a998efc..3d4576425 100644 --- a/lib/jss.map +++ b/lib/jss.map @@ -499,3 +499,10 @@ Java_org_mozilla_jss_nss_SSLErrors_getBadCertDomain; local: *; }; +JSS_4.8.0 { + global: +Java_org_mozilla_jss_crypto_JSSOAEPParameterSpec_acquireNativeResources; +Java_org_mozilla_jss_crypto_JSSOAEPParameterSpec_releaseNativeResources; + local: + *; +}; diff --git a/org/mozilla/jss/crypto/Algorithm.c b/org/mozilla/jss/crypto/Algorithm.c index 94b0297bb..7192093c0 100644 --- a/org/mozilla/jss/crypto/Algorithm.c +++ b/org/mozilla/jss/crypto/Algorithm.c @@ -129,6 +129,7 @@ JSS_AlgInfo JSS_AlgTable[NUM_ALGS] = { /* 76 */ {CKM_NSS_SP800_108_FEEDBACK_KDF_DERIVE_DATA, PK11_MECH}, /* 77 */ {CKM_NSS_SP800_108_DOUBLE_PIPELINE_KDF_DERIVE_DATA, PK11_MECH}, /* 78 */ {SEC_OID_PKCS1_RSA_PSS_SIGNATURE, SEC_OID_TAG}, +/* 79 */ {CKM_RSA_PKCS_OAEP, PK11_MECH}, /* REMEMBER TO UPDATE NUM_ALGS!!! (in Algorithm.h) */ }; diff --git a/org/mozilla/jss/crypto/Algorithm.h b/org/mozilla/jss/crypto/Algorithm.h index 349d1c743..a0c2b9ae1 100644 --- a/org/mozilla/jss/crypto/Algorithm.h +++ b/org/mozilla/jss/crypto/Algorithm.h @@ -24,7 +24,7 @@ typedef struct JSS_AlgInfoStr { JSS_AlgType type; } JSS_AlgInfo; -#define NUM_ALGS 79 +#define NUM_ALGS 80 extern JSS_AlgInfo JSS_AlgTable[]; extern CK_ULONG JSS_symkeyUsage[]; diff --git a/org/mozilla/jss/crypto/Algorithm.java b/org/mozilla/jss/crypto/Algorithm.java index 6eb7a2c74..87256b454 100644 --- a/org/mozilla/jss/crypto/Algorithm.java +++ b/org/mozilla/jss/crypto/Algorithm.java @@ -257,5 +257,8 @@ public PKCS11Algorithm getEnum() { protected static final int CKM_NSS_SP800_108_DOUBLE_PIPELINE_KDF_DERIVE_DATA=77; // RSA-PSS - protected static final short SEC_OID_PKCS1_RSA_PSS_SIGNATURE = 78; + protected static final int SEC_OID_PKCS1_RSA_PSS_SIGNATURE = 78; + + // RSA-OAEP + protected static final int CKM_RSA_PKCS_OAEP = 79; } diff --git a/org/mozilla/jss/crypto/JSSOAEPParameterSpec.c b/org/mozilla/jss/crypto/JSSOAEPParameterSpec.c new file mode 100644 index 000000000..099071f5f --- /dev/null +++ b/org/mozilla/jss/crypto/JSSOAEPParameterSpec.c @@ -0,0 +1,151 @@ +#include +#include +#include +#include +#include + +#include "_jni/org_mozilla_jss_crypto_JSSOAEPParameterSpec.h" + +#include "jssutil.h" +#include "java_ids.h" +#include "jss_exceptions.h" +#include "pk11util.h" + +#include "NativeEnclosure.h" +#include "StaticVoidPointer.h" + +PRStatus +oaep_GetHashAlg(JNIEnv *env, jobject this, jclass this_class, CK_MECHANISM_TYPE *ret) +{ + jfieldID field_id = NULL; + + field_id = (*env)->GetFieldID(env, this_class, "hashAlg", "J"); + if (field_id == NULL) { + return PR_FAILURE; + } + + *ret = (*env)->GetLongField(env, this, field_id); + return PR_SUCCESS; +} + +PRStatus +oaep_GetMGFType(JNIEnv *env, jobject this, jclass this_class, CK_RSA_PKCS_MGF_TYPE *ret) +{ + jfieldID field_id = NULL; + + field_id = (*env)->GetFieldID(env, this_class, "mgf", "J"); + if (field_id == NULL) { + return PR_FAILURE; + } + + *ret = (*env)->GetLongField(env, this, field_id); + return PR_SUCCESS; +} + +PRStatus +oaep_GetSpecifiedSourceData(JNIEnv *env, jobject this, jclass this_class, CK_VOID_PTR *ret, CK_ULONG *ret_len) +{ + jfieldID field_id = NULL; + jbyteArray data = NULL; + + field_id = (*env)->GetFieldID(env, this_class, "sourceData", "[B"); + if (field_id == NULL) { + return PR_FAILURE; + } + + data = (*env)->GetObjectField(env, this, field_id); + if (data == NULL) { + *ret = NULL; + *ret_len = 0; + return PR_SUCCESS; + } + + if (!JSS_FromByteArray(env, data, (uint8_t **)ret, ret_len)) { + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +JNIEXPORT void JNICALL +Java_org_mozilla_jss_crypto_JSSOAEPParameterSpec_acquireNativeResources(JNIEnv *env, jobject this) +{ + jclass this_class = NULL; + + CK_MECHANISM_TYPE hashAlg; + CK_RSA_PKCS_MGF_TYPE mgf; + CK_RSA_PKCS_OAEP_SOURCE_TYPE source = CKZ_DATA_SPECIFIED; + CK_VOID_PTR pSourceData = NULL; + CK_ULONG ulSourceDataLen = 0; + CK_RSA_PKCS_OAEP_PARAMS_PTR oaep_params = NULL; + + jobject params_obj; + + this_class = (*env)->GetObjectClass(env, this); + if (this_class == NULL) { + return; + } + + if (oaep_GetHashAlg(env, this, this_class, &hashAlg) != PR_SUCCESS) { + goto failure; + } + + if (oaep_GetMGFType(env, this, this_class, &mgf) != PR_SUCCESS) { + goto failure; + } + + if (oaep_GetSpecifiedSourceData(env, this, this_class, &pSourceData, &ulSourceDataLen) != PR_SUCCESS) { + goto failure; + } + + oaep_params = calloc(1, sizeof(CK_RSA_PKCS_OAEP_PARAMS)); + oaep_params->hashAlg = hashAlg; + oaep_params->mgf = mgf; + oaep_params->source = source; + oaep_params->pSourceData = pSourceData; + oaep_params->ulSourceDataLen = ulSourceDataLen; + + params_obj = JSS_PR_wrapStaticVoidPointer(env, (void **)&oaep_params); + if (params_obj == NULL) { + goto failure; + } + + if (JSS_PR_StoreNativeEnclosure(env, this, params_obj, sizeof(CK_RSA_PKCS_OAEP_PARAMS)) != PR_SUCCESS) { + goto failure; + } + + return; + +failure: + free(pSourceData); + free(oaep_params); +} + +JNIEXPORT void JNICALL +Java_org_mozilla_jss_crypto_JSSOAEPParameterSpec_releaseNativeResources(JNIEnv *env, jobject this) +{ + jobject ptr_object = NULL; + + CK_RSA_PKCS_OAEP_PARAMS_PTR oaep_params = NULL; + jlong params_length; + + PR_ASSERT(env != NULL && this != NULL); + + if (JSS_PR_LoadNativeEnclosure(env, this, &ptr_object, ¶ms_length) != PR_SUCCESS) { + return; + } + + if (JSS_PR_getStaticVoidRef(env, ptr_object, (void **)&oaep_params) != PR_SUCCESS || oaep_params == NULL) { + return; + } + + PR_ASSERT(params_length == sizeof(CK_RSA_PKCS_OAEP_PARAMS)); + + if (oaep_params->ulSourceDataLen != 0 && oaep_params->pSourceData != NULL) { + memset(oaep_params->pSourceData, 0, sizeof(CK_VOID_PTR) * oaep_params->ulSourceDataLen); + free(oaep_params->pSourceData); + } + + memset(oaep_params, 0, sizeof(CK_RSA_PKCS_OAEP_PARAMS)); + free(oaep_params); +} diff --git a/org/mozilla/jss/crypto/JSSOAEPParameterSpec.java b/org/mozilla/jss/crypto/JSSOAEPParameterSpec.java new file mode 100644 index 000000000..8161899c4 --- /dev/null +++ b/org/mozilla/jss/crypto/JSSOAEPParameterSpec.java @@ -0,0 +1,185 @@ +package org.mozilla.jss.crypto; + +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.MGF1ParameterSpec; +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; + +import org.mozilla.jss.pkcs11.PKCS11Constants; +import org.mozilla.jss.util.NativeEnclosure; + +public class JSSOAEPParameterSpec extends NativeEnclosure implements AlgorithmParameterSpec { + public long hashAlg; + public long mgf; + public byte[] sourceData; + + public JSSOAEPParameterSpec(String mdName, String mgfName, AlgorithmParameterSpec mgfSpec, PSource pSrc) { + super(); + + setDigestAlgorithm(mdName); + setMaskGenAlgorithm(mgfName); + setMaskGenAlgorithmType(mgfSpec); + setPSource(pSrc); + } + + public JSSOAEPParameterSpec(OAEPParameterSpec copy) { + super(); + + setDigestAlgorithm(copy.getDigestAlgorithm()); + setMaskGenAlgorithm(copy.getMGFAlgorithm()); + setMaskGenAlgorithmType(copy.getMGFParameters()); + setPSource(copy.getPSource()); + } + + public void setDigestAlgorithm(String algo) throws IllegalArgumentException { + switch (algo.toLowerCase()) { + case "md5": + case "ckm_md5": + hashAlg = PKCS11Constants.CKM_MD5; + break; + case "sha1": + case "sha-1": + case "ckm_sha_1": + hashAlg = PKCS11Constants.CKM_SHA_1; + break; + case "sha256": + case "sha-256": + case "ckm_sha256": + hashAlg = PKCS11Constants.CKM_SHA256; + break; + case "sha384": + case "sha-384": + case "ckm_sha384": + hashAlg = PKCS11Constants.CKM_SHA384; + break; + case "sha512": + case "sha-512": + case "ckm_sha512": + hashAlg = PKCS11Constants.CKM_SHA512; + break; + default: + String msg = "Unknown algorithm identifier: " + algo; + throw new IllegalArgumentException(msg); + } + } + + public void setDigestAlgorithm(DigestAlgorithm algo) throws IllegalArgumentException { + if ((algo instanceof HMACAlgorithm) || (algo instanceof CMACAlgorithm)) { + String msg = "Unable to use MAC digest algorithm " + algo; + msg += " in place of an unkeyed hash algorithm"; + throw new IllegalArgumentException(msg); + } + + hashAlg = algo.getEnum().getValue(); + } + + public void setDigestAlgorithm(long algo) throws IllegalArgumentException { + hashAlg = algo; + } + + public void setMaskGenAlgorithm(String algo) throws IllegalArgumentException { + if (!algo.toLowerCase().equals("mgf1")) { + String msg = "Unknown mask generation algorithm: " + algo; + throw new IllegalArgumentException(msg); + } + + // Do nothing. We just validate this data so if we get passed + // something unexpected, we error out instead. + } + + public void setMaskGenAlgorithmType(String algo) throws IllegalArgumentException { + switch (algo.toLowerCase()) { + case "sha1": + case "sha-1": + case "ckm_sha_1": + hashAlg = PKCS11Constants.CKG_MGF1_SHA1; + break; + case "sha256": + case "sha-256": + case "ckm_sha256": + hashAlg = PKCS11Constants.CKG_MGF1_SHA256; + break; + case "sha384": + case "sha-384": + case "ckm_sha384": + hashAlg = PKCS11Constants.CKG_MGF1_SHA384; + break; + case "sha512": + case "sha-512": + case "ckm_sha512": + hashAlg = PKCS11Constants.CKG_MGF1_SHA512; + break; + default: + String msg = "Unknown mask generation algorithm identifier: " + algo; + throw new IllegalArgumentException(msg); + } + } + + public void setMaskGenAlgorithmType(AlgorithmParameterSpec algo) throws IllegalArgumentException { + if (!(algo instanceof MGF1ParameterSpec) || algo == null) { + String msg = "Unknown mask generation algorithm parameter "; + msg += "specification: " + algo; + throw new IllegalArgumentException(msg); + } + + MGF1ParameterSpec mgf1 = (MGF1ParameterSpec) algo; + switch (mgf1.getDigestAlgorithm().toLowerCase()) { + case "sha1": + case "sha-1": + mgf = PKCS11Constants.CKG_MGF1_SHA1; + break; + case "sha256": + case "sha-256": + mgf = PKCS11Constants.CKG_MGF1_SHA256; + break; + case "sha384": + case "sha-384": + mgf = PKCS11Constants.CKG_MGF1_SHA384; + break; + case "sha512": + case "sha-512": + mgf = PKCS11Constants.CKG_MGF1_SHA512; + break; + default: + String msg = "Unknown mask generation algorithm identifier: "; + msg += mgf1.getDigestAlgorithm(); + throw new IllegalArgumentException(msg); + } + } + + public void setMaskGenAlgorithmType(long algo) throws IllegalArgumentException { + mgf = algo; + } + + public void setPSource(PSource spec) throws IllegalArgumentException { + if (spec == null) { + return; + } + + if (!(spec instanceof PSource.PSpecified)) { + String msg = "Expected PSource spec to be an instance of "; + msg += "PSource.PSpecified, but wasn't: " + spec; + throw new IllegalArgumentException(msg); + } + + PSource.PSpecified value = (PSource.PSpecified) spec; + setPSource(value.getValue()); + } + + public void setPSource(byte[] data) throws IllegalArgumentException { + // PSource.PSpecified.DEFAULT is an allocated byte array of 0 length. + // This confuses JSS_FromByteArray(...) into thinking that an error + // occurred. Because PKCS#11 accepts a NULL pointer to go with a 0 + // length array, just set sourceData to NULL. + if (data == null || data.length == 0) { + sourceData = null; + return; + } + + sourceData = data; + } + + protected native void acquireNativeResources() throws Exception; + + protected native void releaseNativeResources() throws Exception; +} diff --git a/org/mozilla/jss/crypto/KeyWrapAlgorithm.java b/org/mozilla/jss/crypto/KeyWrapAlgorithm.java index 3a106977e..63b798479 100644 --- a/org/mozilla/jss/crypto/KeyWrapAlgorithm.java +++ b/org/mozilla/jss/crypto/KeyWrapAlgorithm.java @@ -9,6 +9,7 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.RC2ParameterSpec; +import javax.crypto.spec.OAEPParameterSpec; import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; @@ -95,6 +96,10 @@ public int getBlockSize() { RSA = new KeyWrapAlgorithm(SEC_OID_PKCS1_RSA_ENCRYPTION, "RSA", (Class) null, false, 0); + public static final KeyWrapAlgorithm + RSA_OAEP = new KeyWrapAlgorithm(CKM_RSA_PKCS_OAEP, "RSAES-OAEP", + OAEPParameterSpec.class, true, 0); + public static final KeyWrapAlgorithm PLAINTEXT = new KeyWrapAlgorithm(0, "Plaintext", (Class) null, false, 0); diff --git a/org/mozilla/jss/crypto/PKCS11Algorithm.java b/org/mozilla/jss/crypto/PKCS11Algorithm.java index b0947d77b..b628f1975 100644 --- a/org/mozilla/jss/crypto/PKCS11Algorithm.java +++ b/org/mozilla/jss/crypto/PKCS11Algorithm.java @@ -32,7 +32,13 @@ public enum PKCS11Algorithm { CKM_SP800_108_DOUBLE_PIPELINE_KDF (Algorithm.CKM_SP800_108_DOUBLE_PIPELINE_KDF, PKCS11Constants.CKM_SP800_108_DOUBLE_PIPELINE_KDF), CKM_NSS_SP800_108_COUNTER_KDF_DERIVE_DATA (Algorithm.CKM_NSS_SP800_108_COUNTER_KDF_DERIVE_DATA, PKCS11Constants.CKM_NSS_SP800_108_COUNTER_KDF_DERIVE_DATA), CKM_NSS_SP800_108_FEEDBACK_KDF_DERIVE_DATA (Algorithm.CKM_NSS_SP800_108_FEEDBACK_KDF_DERIVE_DATA, PKCS11Constants.CKM_NSS_SP800_108_FEEDBACK_KDF_DERIVE_DATA), - CKM_NSS_SP800_108_DOUBLE_PIPELINE_KDF_DERIVE_DATA (Algorithm.CKM_NSS_SP800_108_DOUBLE_PIPELINE_KDF_DERIVE_DATA, PKCS11Constants.CKM_NSS_SP800_108_DOUBLE_PIPELINE_KDF_DERIVE_DATA); + CKM_NSS_SP800_108_DOUBLE_PIPELINE_KDF_DERIVE_DATA (Algorithm.CKM_NSS_SP800_108_DOUBLE_PIPELINE_KDF_DERIVE_DATA, PKCS11Constants.CKM_NSS_SP800_108_DOUBLE_PIPELINE_KDF_DERIVE_DATA), + CKM_MD2(Algorithm.SEC_OID_MD2, PKCS11Constants.CKM_MD2), + CKM_MD5(Algorithm.SEC_OID_MD5, PKCS11Constants.CKM_MD5), + CKM_SHA_1(Algorithm.SEC_OID_SHA1, PKCS11Constants.CKM_SHA_1), + CKM_SHA256(Algorithm.SEC_OID_SHA256, PKCS11Constants.CKM_SHA256), + CKM_SHA384(Algorithm.SEC_OID_SHA384, PKCS11Constants.CKM_SHA384), + CKM_SHA512(Algorithm.SEC_OID_SHA512, PKCS11Constants.CKM_SHA512); // Value from Algorithm's constant -- this is an index into Algorithm's // table. diff --git a/org/mozilla/jss/jssconfig.h.in b/org/mozilla/jss/jssconfig.h.in index 61c3d83e6..88e393525 100644 --- a/org/mozilla/jss/jssconfig.h.in +++ b/org/mozilla/jss/jssconfig.h.in @@ -6,5 +6,6 @@ #cmakedefine HAVE_NSS_CHANNEL_INFO_PEER_DELEG_CRED 1 #cmakedefine HAVE_NSS_PRELIMINARY_CHANNEL_INFO_ZERO_RTT_CIPHER_SUITE 1 #cmakedefine HAVE_NSS_PRELIMINARY_CHANNEL_INFO_PEER_DELEG_CRED 1 +#cmakedefine HAVE_NSS_OAEP 1 #endif diff --git a/org/mozilla/jss/pkcs11/KeyType.java b/org/mozilla/jss/pkcs11/KeyType.java index b2464568d..3ae62572b 100644 --- a/org/mozilla/jss/pkcs11/KeyType.java +++ b/org/mozilla/jss/pkcs11/KeyType.java @@ -123,7 +123,8 @@ public String toString() { SignatureAlgorithm.RSAPSSSignatureWithSHA256Digest, SignatureAlgorithm.RSAPSSSignatureWithSHA384Digest, SignatureAlgorithm.RSAPSSSignatureWithSHA512Digest, - KeyWrapAlgorithm.RSA + KeyWrapAlgorithm.RSA, + KeyWrapAlgorithm.RSA_OAEP, }, "RSA" ); diff --git a/org/mozilla/jss/pkcs11/PK11KeyWrapper.c b/org/mozilla/jss/pkcs11/PK11KeyWrapper.c index ce7e65678..cf2f59099 100644 --- a/org/mozilla/jss/pkcs11/PK11KeyWrapper.c +++ b/org/mozilla/jss/pkcs11/PK11KeyWrapper.c @@ -17,6 +17,8 @@ #include #include #include +#include "StaticVoidPointer.h" +#include "jssconfig.h" #define MAX_PRIVATE_KEY_LEN MAX_RSA_MODULUS_LEN @@ -120,11 +122,14 @@ Java_org_mozilla_jss_pkcs11_PK11KeyWrapper_nativeWrapSymWithSym JNIEXPORT jbyteArray JNICALL Java_org_mozilla_jss_pkcs11_PK11KeyWrapper_nativeWrapSymWithPub (JNIEnv *env, jobject this, jobject tokenObj, jobject toBeWrappedObj, - jobject wrappingKeyObj, jobject algObj, jbyteArray ivBA) + jobject wrappingKeyObj, jobject algObj, jobject paramsPtr, + jlong paramsSize) { SECKEYPublicKey *wrapping = NULL; PK11SymKey *toBeWrapped = NULL; CK_MECHANISM_TYPE mech; + CK_VOID_PTR params = NULL; + SECItem paramItem; SECItem wrapped; jbyteArray wrappedBA=NULL; SECStatus status; @@ -147,6 +152,14 @@ Java_org_mozilla_jss_pkcs11_PK11KeyWrapper_nativeWrapSymWithPub return NULL; } + if (paramsPtr != NULL) { + if (JSS_PR_getStaticVoidRef(env, paramsPtr, ¶ms) != PR_SUCCESS) { + JSS_throwMsg(env, TOKEN_EXCEPTION, "Unable to extract parameters " + "to pass with the wrapping mechanism"); + return NULL; + } + } + /* get the mechanism */ mech = JSS_getPK11MechFromAlg(env, algObj); if(mech == CKM_INVALID_MECHANISM) { @@ -162,8 +175,20 @@ Java_org_mozilla_jss_pkcs11_PK11KeyWrapper_nativeWrapSymWithPub goto finish; } + paramItem.data = params; + paramItem.len = paramsSize; + /* perform the wrap */ +#ifdef HAVE_NSS_OAEP + status = PK11_PubWrapSymKeyWithMechanism(wrapping, mech, ¶mItem, toBeWrapped, &wrapped); +#else + if (mech == CKM_RSA_PKCS_OAEP) { + JSS_throwMsg(env, TOKEN_EXCEPTION, "RSA-OAEP not supported by the NSS version used to build JSS"); + goto finish; + } + status = PK11_PubWrapSymKey(mech, wrapping, toBeWrapped, &wrapped); +#endif if( status != SECSuccess ) { JSS_throwMsg(env, TOKEN_EXCEPTION, "Wrap operation failed on token"); goto finish; @@ -595,12 +620,14 @@ JNIEXPORT jobject JNICALL Java_org_mozilla_jss_pkcs11_PK11KeyWrapper_nativeUnwrapSymWithPriv (JNIEnv *env, jclass clazz, jobject tokenObj, jobject unwrapperObj, jbyteArray wrappedBA, jobject wrapAlgObj, jobject typeAlgObj, - jint keyLen, jbyteArray ivBA, jint usageEnum) + jint keyLen, jobject paramsPtr, jlong paramsSize, jint usageEnum) { PK11SymKey *symKey=NULL; - CK_MECHANISM_TYPE wrappingMech=0, keyTypeMech=0; - SECItem *wrappedKey=NULL, *iv=NULL, *param=NULL; + CK_MECHANISM_TYPE keyTypeMech=0, mech=0; + SECItem *wrappedKey=NULL; jobject keyObj=NULL; + CK_VOID_PTR params; + SECItem paramItem; SECKEYPrivateKey *wrappingKey=NULL; CK_ULONG operation; @@ -618,18 +645,11 @@ Java_org_mozilla_jss_pkcs11_PK11KeyWrapper_nativeUnwrapSymWithPriv } /* get the mechanism parameter (IV) */ - if (ivBA == NULL) { - param = PK11_ParamFromIV(wrappingMech, NULL); - } else { - iv = JSS_ByteArrayToSECItem(env, ivBA); - if( iv == NULL ) { - goto finish; /* exception was thrown */ - } - param = PK11_ParamFromIV(wrappingMech, iv); - if( param == NULL ) { - JSS_throwMsg(env, TOKEN_EXCEPTION, - "Failed to convert initialization vector to parameter"); - goto finish; + if (paramsPtr != NULL) { + if (JSS_PR_getStaticVoidRef(env, paramsPtr, ¶ms) != PR_SUCCESS) { + JSS_throwMsg(env, TOKEN_EXCEPTION, "Unable to extract parameters " + "to pass with the wrapping mechanism"); + return NULL; } } @@ -651,8 +671,29 @@ Java_org_mozilla_jss_pkcs11_PK11KeyWrapper_nativeUnwrapSymWithPriv operation = JSS_symkeyUsage[usageEnum]; } + /* get the mechanism */ + mech = JSS_getPK11MechFromAlg(env, wrapAlgObj); + if(mech == CKM_INVALID_MECHANISM) { + JSS_throwMsg(env, TOKEN_EXCEPTION, "Unrecognized algorithm"); + goto finish; + } + + paramItem.data = params; + paramItem.len = paramsSize; + +#ifdef HAVE_NSS_OAEP + symKey = PK11_PubUnwrapSymKeyWithMechanism(wrappingKey, mech, ¶mItem, + wrappedKey, keyTypeMech, operation, keyLen); +#else + if (mech == CKM_RSA_PKCS_OAEP) { + JSS_throwMsg(env, TOKEN_EXCEPTION, "RSA-OAEP not supported by the NSS version used to build JSS"); + goto finish; + } + symKey = PK11_PubUnwrapSymKey(wrappingKey, wrappedKey, keyTypeMech, - operation, keyLen); + operation, keyLen); +#endif + if( symKey == NULL ) { JSS_throwMsg(env, TOKEN_EXCEPTION, "Failed to unwrap key"); goto finish; @@ -665,12 +706,6 @@ Java_org_mozilla_jss_pkcs11_PK11KeyWrapper_nativeUnwrapSymWithPriv if(wrappedKey) { SECITEM_FreeItem(wrappedKey, PR_TRUE /*free wrappedKey*/); } - if(iv) { - SECITEM_FreeItem(iv, PR_TRUE /*free iv*/); - } - if(param) { - SECITEM_FreeItem(param, PR_TRUE /*free param*/); - } if( symKey ) { PK11_FreeSymKey(symKey); } diff --git a/org/mozilla/jss/pkcs11/PK11KeyWrapper.java b/org/mozilla/jss/pkcs11/PK11KeyWrapper.java index eee2984d5..ec827b4fd 100644 --- a/org/mozilla/jss/pkcs11/PK11KeyWrapper.java +++ b/org/mozilla/jss/pkcs11/PK11KeyWrapper.java @@ -14,9 +14,11 @@ import java.util.Arrays; import javax.crypto.spec.RC2ParameterSpec; +import javax.crypto.spec.OAEPParameterSpec; import org.mozilla.jss.crypto.Algorithm; import org.mozilla.jss.crypto.EncryptionAlgorithm; +import org.mozilla.jss.crypto.JSSOAEPParameterSpec; import org.mozilla.jss.crypto.HMACAlgorithm; import org.mozilla.jss.crypto.IVParameterSpec; import org.mozilla.jss.crypto.KeyPairAlgorithm; @@ -25,7 +27,8 @@ import org.mozilla.jss.crypto.PrivateKey; import org.mozilla.jss.crypto.SymmetricKey; import org.mozilla.jss.crypto.TokenException; -import org.mozilla.jss.util.Assert; +import org.mozilla.jss.util.NativeProxy; +import org.mozilla.jss.util.NativeEnclosure; public final class PK11KeyWrapper implements KeyWrapper { @@ -86,9 +89,7 @@ private void initWrap(AlgorithmParameterSpec parameters) { reset(); - checkParams(parameters); - - this.parameters = parameters; + this.parameters = checkParams(parameters); state = WRAP; } @@ -128,9 +129,7 @@ private void initUnwrap(AlgorithmParameterSpec parameters) { reset(); - checkParams(parameters); - - this.parameters = parameters; + this.parameters = checkParams(parameters); state = UNWRAP; } @@ -144,8 +143,9 @@ private void checkWrapper(PublicKey key) throws InvalidKeyException { if( ! (key instanceof PK11PubKey) ) { throw new InvalidKeyException("Key is not a PKCS #11 key"); } + KeyType type = null; try { - KeyType type = KeyType.getKeyTypeFromAlgorithm(algorithm); + type = KeyType.getKeyTypeFromAlgorithm(algorithm); if( (type == KeyType.RSA && !(key instanceof RSAPublicKey)) || // requires JAVA 1.5 // (type == KeyType.EC && !(key instanceof ECPublicKey)) || @@ -154,7 +154,7 @@ private void checkWrapper(PublicKey key) throws InvalidKeyException { "this algorithm"); } } catch( NoSuchAlgorithmException e ) { - throw new RuntimeException("Unable to find algorithm from key type: " + e.getMessage(), e); + throw new RuntimeException("Unable to find algorithm (" + algorithm + ") from key type (" + type + ") : " + e.getMessage(), e); } } @@ -206,7 +206,7 @@ private void checkWrapper(PrivateKey key) } } - private void checkParams(AlgorithmParameterSpec params) + private AlgorithmParameterSpec checkParams(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { if( ! algorithm.isValidParameterObject(params) ) { @@ -217,13 +217,20 @@ private void checkParams(AlgorithmParameterSpec params) throw new InvalidAlgorithmParameterException( algorithm + " cannot use a " + name + " parameter"); } - if( params instanceof IVParameterSpec ) { + + if (params instanceof IVParameterSpec) { IV = ((IVParameterSpec)params).getIV(); - } else if( params instanceof javax.crypto.spec.IvParameterSpec ) { + } else if (params instanceof javax.crypto.spec.IvParameterSpec) { IV = ((javax.crypto.spec.IvParameterSpec)params).getIV(); - } else if( params instanceof RC2ParameterSpec ) { + } else if (params instanceof RC2ParameterSpec) { IV = ((RC2ParameterSpec)params).getIV(); } + + if (algorithm == KeyWrapAlgorithm.RSA_OAEP && params != null && params instanceof OAEPParameterSpec) { + params = new JSSOAEPParameterSpec((OAEPParameterSpec) params); + } + + return params; } public byte[] @@ -272,10 +279,32 @@ private void checkParams(AlgorithmParameterSpec params) assert( privKey==null && pubKey==null ); return nativeWrapSymWithSym(token, toBeWrapped, symKey, algorithm, IV); - } else { - assert( pubKey!=null && privKey==null && symKey==null ); - return nativeWrapSymWithPub(token, toBeWrapped, pubKey, algorithm, - IV); + } + + assert( pubKey!=null && privKey==null && symKey==null ); + NativeProxy params = null; + long params_size = 0; + if (parameters != null) { + try { + ((NativeEnclosure) parameters).open(); + params = ((NativeEnclosure) parameters).mPointer; + params_size = ((NativeEnclosure) parameters).mPointerSize; + } catch (Exception e) { + throw new TokenException(e.getMessage(), e); + } + } + + try { + return nativeWrapSymWithPub(token, toBeWrapped, pubKey, + algorithm, params, params_size); + } finally { + if (parameters != null) { + try { + ((NativeEnclosure) parameters).close(); + } catch (Exception e) { + throw new TokenException(e.getMessage(), e); + } + } } } @@ -320,7 +349,7 @@ private void checkParams(AlgorithmParameterSpec params) */ private static native byte[] nativeWrapSymWithPub(PK11Token token, SymmetricKey toBeWrapped, - PublicKey wrappingKey, KeyWrapAlgorithm alg, byte[] IV) + PublicKey wrappingKey, KeyWrapAlgorithm alg, NativeProxy params, long params_size) throws TokenException; /** @@ -558,15 +587,38 @@ token, symKey, wrapped, algorithm, algFromType(type), if( algorithm == KeyWrapAlgorithm.PLAINTEXT ) { return nativeUnwrapSymPlaintext(token, wrapped, algFromType(type), usageEnum, temporary ); - } else { - if( symKey != null ) { - assert(pubKey==null && privKey==null); - return nativeUnwrapSymWithSym(token, symKey, wrapped, algorithm, - algFromType(type), keyLen, IV, usageEnum,temporary); - } else { - assert(privKey!=null && pubKey==null && symKey==null); - return nativeUnwrapSymWithPriv(token, privKey, wrapped, - algorithm, algFromType(type), keyLen, IV, usageEnum ); + } + + if( symKey != null ) { + assert(pubKey==null && privKey==null); + return nativeUnwrapSymWithSym(token, symKey, wrapped, algorithm, + algFromType(type), keyLen, IV, usageEnum,temporary); + } + + assert(privKey!=null && pubKey==null && symKey==null); + NativeProxy params = null; + long params_size = 0; + if (parameters != null) { + try { + ((NativeEnclosure) parameters).open(); + params = ((NativeEnclosure) parameters).mPointer; + params_size = ((NativeEnclosure) parameters).mPointerSize; + } catch (Exception e) { + throw new TokenException(e.getMessage(), e); + } + } + + try { + return nativeUnwrapSymWithPriv(token, privKey, wrapped, + algorithm, algFromType(type), keyLen, params, + params_size, usageEnum); + } finally { + if (parameters != null) { + try { + ((NativeEnclosure) parameters).close(); + } catch (Exception e) { + throw new TokenException(e.getMessage(), e); + } } } } @@ -637,7 +689,7 @@ token, symKey, wrapped, algorithm, algFromType(type), private static native SymmetricKey nativeUnwrapSymWithPriv(PK11Token token, PrivateKey unwrappingKey, byte[] wrappedKey, KeyWrapAlgorithm alg, Algorithm type, int keyLen, - byte[] IV, int usageEnum) + NativeProxy params, long params_size, int usageEnum) throws TokenException; private static native SymmetricKey diff --git a/org/mozilla/jss/tests/KeyWrapping.java b/org/mozilla/jss/tests/KeyWrapping.java index 4100f9093..3ae257363 100644 --- a/org/mozilla/jss/tests/KeyWrapping.java +++ b/org/mozilla/jss/tests/KeyWrapping.java @@ -4,9 +4,13 @@ package org.mozilla.jss.tests; +import java.security.KeyPair; +import java.security.spec.MGF1ParameterSpec; +import javax.crypto.spec.PSource; +import javax.crypto.spec.OAEPParameterSpec; + import org.mozilla.jss.crypto.*; import org.mozilla.jss.CryptoManager; -import java.security.KeyPair; /** * Keywrapping tests.. @@ -19,8 +23,10 @@ public static void main(String args[]) throws Exception { CryptoManager cm = CryptoManager.getInstance(); CryptoToken token = cm.getInternalCryptoToken(); CryptoToken keyToken = cm.getInternalKeyStorageToken(); - KeyGenerator kg = token.getKeyGenerator(KeyGenAlgorithm.DES); - KeyGenerator keyKg = keyToken.getKeyGenerator(KeyGenAlgorithm.DES3); + KeyGenerator kg = token.getKeyGenerator(KeyGenAlgorithm.AES); + KeyGenerator keyKg = keyToken.getKeyGenerator(KeyGenAlgorithm.AES); + kg.initialize(256); + keyKg.initialize(256); SymmetricKey wrapped = kg.generate(); SymmetricKey wrapper = kg.generate(); @@ -30,32 +36,35 @@ public static void main(String args[]) throws Exception { // wrap a symmetric with a symmetric byte[] plaintextPre = new byte[] { (byte)0x73, (byte)0x24, (byte)0x51, (byte)0x48, - (byte)0x32, (byte)0x87, (byte)0x23, (byte)0x33, (byte)0x65}; + (byte)0x32, (byte)0x87, (byte)0x23, (byte)0x33, + (byte)0x65, (byte)0x5f, (byte)0x73, (byte)0x9e, + (byte)0x8b, (byte)0xb6, (byte)0x69, (byte)0x90 + }; byte[] plaintext = Cipher.pad(plaintextPre, - EncryptionAlgorithm.DES_ECB.getBlockSize()); + EncryptionAlgorithm.AES_256_ECB.getBlockSize()); System.out.println("plaintext length is " + plaintext.length); - Cipher encryptor = token.getCipherContext(EncryptionAlgorithm.DES_ECB); + Cipher encryptor = token.getCipherContext(EncryptionAlgorithm.AES_256_ECB); encryptor.initEncrypt(wrapped); byte[] ciphertext = encryptor.doFinal(plaintext); System.out.println("ciphertext length is " + ciphertext.length); - KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.DES_ECB); + KeyWrapper keyWrap = token.getKeyWrapper(KeyWrapAlgorithm.AES_ECB); keyWrap.initWrap(wrapper,null); byte[] wrappedKey = keyWrap.wrap(wrapped); keyWrap.initUnwrap(wrapper, null); SymmetricKey unwrapped = keyWrap.unwrapSymmetric(wrappedKey, - SymmetricKey.DES, SymmetricKey.Usage.DECRYPT, 0); + SymmetricKey.AES, SymmetricKey.Usage.DECRYPT, 0); - Cipher decryptor = token.getCipherContext(EncryptionAlgorithm.DES_ECB); + Cipher decryptor = token.getCipherContext(EncryptionAlgorithm.AES_256_ECB); decryptor.initDecrypt(unwrapped); byte[] recoveredPre = decryptor.doFinal(ciphertext); System.out.println("Decrypted "+ recoveredPre.length+ " bytes"); byte[] recovered = Cipher.unPad(recoveredPre, - EncryptionAlgorithm.DES_ECB.getBlockSize()); + EncryptionAlgorithm.AES_256_ECB.getBlockSize()); System.out.println("plaintext:"); displayByteArray(plaintextPre); @@ -63,15 +72,14 @@ public static void main(String args[]) throws Exception { displayByteArray(ciphertext); System.out.println("recovered:"); displayByteArray(recovered); - // wrap a private with a symmetric - keyWrap = keyToken.getKeyWrapper(KeyWrapAlgorithm.DES3_CBC_PAD); + keyWrap = keyToken.getKeyWrapper(KeyWrapAlgorithm.AES_CBC_PAD); IVParameterSpec iv = new IVParameterSpec(recovered); keyWrap.initWrap(keyWrapper, iv); KeyPairGenerator kpg = keyToken.getKeyPairGenerator(KeyPairAlgorithm.RSA); - kpg.initialize(512); + kpg.initialize(Policy.RSA_MINIMUM_KEY_SIZE); kpg.temporaryPairs(true); KeyPair kp = kpg.genKeyPair(); java.security.PublicKey pub = kp.getPublic(); @@ -81,19 +89,18 @@ public static void main(String args[]) throws Exception { System.out.println("Original key:"); displayByteArray(privk.getUniqueID()); privk = null; kp = null; - //keyToken.getCryptoStore().deletePrivateKey(privk); - keyWrap.initUnwrap(keyWrapper,iv); + keyWrap.initUnwrap(keyWrapper, iv); PrivateKey newPrivk = keyWrap.unwrapTemporaryPrivate(wrappedKey, PrivateKey.RSA, pub ); // wrap a private with a symmetric using AES_KEY_WRAP_PAD keyWrap = keyToken.getKeyWrapper(KeyWrapAlgorithm.AES_KEY_WRAP_PAD); // IVParameterSpec iv = new IVParameterSpec(recovered); - keyWrap.initWrap(keyWrapper,null /*iv*/); + keyWrap.initWrap(keyWrapper, null /*iv*/); KeyPairGenerator kpg2 = keyToken.getKeyPairGenerator(KeyPairAlgorithm.RSA); - kpg2.initialize(512); + kpg2.initialize(Policy.RSA_MINIMUM_KEY_SIZE); kpg2.temporaryPairs(true); KeyPair kp2 = kpg2.genKeyPair(); java.security.PublicKey pub2 = kp2.getPublic(); @@ -105,7 +112,7 @@ public static void main(String args[]) throws Exception { privk2 = null; kp2 = null; //keyToken.getCryptoStore().deletePrivateKey(privk); - keyWrap.initUnwrap(keyWrapper,iv); + keyWrap.initUnwrap(keyWrapper, null /*iv*/); PrivateKey newPrivk2 = keyWrap.unwrapTemporaryPrivate(wrappedKey, PrivateKey.RSA, pub ); @@ -119,19 +126,39 @@ public static void main(String args[]) throws Exception { keyWrap = keyToken.getKeyWrapper(KeyWrapAlgorithm.RSA); keyWrap.initWrap(pub,null); wrappedKey = keyWrap.wrap(keyWrapped); + System.out.println("Wrapped key:"); + displayByteArray(wrappedKey); keyWrap.initUnwrap(newPrivk, null); - unwrapped = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.DES, + unwrapped = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.AES, + SymmetricKey.Usage.DECRYPT, 0); + unwrapped = kg.clone(unwrapped); + decryptor = token.getCipherContext(EncryptionAlgorithm.AES_256_ECB); + decryptor.initDecrypt(unwrapped); + recovered = decryptor.doFinal(ciphertext); + System.out.println("Recovered again:"); + displayByteArray(Cipher.unPad(recovered, EncryptionAlgorithm.AES_256_ECB.getBlockSize())); + + // try a RSA-OAEP operation + keyWrap = keyToken.getKeyWrapper(KeyWrapAlgorithm.RSA_OAEP); + OAEPParameterSpec config = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT); + keyWrap.initWrap(pub, config); + wrappedKey = keyWrap.wrap(keyWrapped); + System.out.println("Wrapped key:"); + displayByteArray(wrappedKey); + keyWrap.initUnwrap(newPrivk, config); + unwrapped = keyWrap.unwrapSymmetric(wrappedKey, SymmetricKey.AES, SymmetricKey.Usage.DECRYPT, 0); unwrapped = kg.clone(unwrapped); - decryptor = token.getCipherContext(EncryptionAlgorithm.DES_ECB); + decryptor = token.getCipherContext(EncryptionAlgorithm.AES_256_ECB); decryptor.initDecrypt(unwrapped); recovered = decryptor.doFinal(ciphertext); System.out.println("Recovered again:"); - displayByteArray(Cipher.unPad(recovered, 8)); + displayByteArray(Cipher.unPad(recovered, EncryptionAlgorithm.AES_256_ECB.getBlockSize())); } public static void displayByteArray(byte[] ba) { + System.out.print("[" + ba.length + " bytes] "); for(int i=0; i < ba.length; i++) { System.out.print( Integer.toHexString(ba[i]&0xff) + " " ); if( (i % 26) == 25 ) { diff --git a/org/mozilla/jss/tests/VerifyRSAOAEPSHA2.java b/org/mozilla/jss/tests/VerifyRSAOAEPSHA2.java new file mode 100644 index 000000000..e1bfd3d1e --- /dev/null +++ b/org/mozilla/jss/tests/VerifyRSAOAEPSHA2.java @@ -0,0 +1,416 @@ +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.math.BigInteger; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Security; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.RSAPrivateKeySpec; +import java.util.Arrays; + +import javax.crypto.Cipher; +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; +import javax.xml.bind.DatatypeConverter; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +class TestVectorData { + public BigInteger pub_key_modulus; + public BigInteger pub_key_exponent; + public BigInteger priv_key_public_exponent; + public BigInteger priv_key_modulus; + public BigInteger priv_key_exponent; + public BigInteger priv_key_prime_1; + public BigInteger priv_key_prime_2; + public BigInteger priv_key_prime_exponent_1; + public BigInteger priv_key_prime_exponent_2; + public BigInteger priv_key_coefficient; + public byte[] plaintext; + public byte[] ciphertext; +} + +class TestVectorLoader { + private static final String FILE_HEADER = "# RSA OAEP SHA2 vectors built"; + private static final String EXAMPLE_HEADER = "# ====="; + private static final String EXAMPLE = "# Example"; + private static final String PUBLIC_KEY = "# Public key"; + private static final String PUB_MODULUS = "# Modulus:"; + private static final String PUB_EXPONENT = "# Exponent:"; + private static final String PRIVATE_KEY = "# Private key"; + private static final String PRIV_MODULUS = "# Modulus:"; + private static final String PRIV_PUBLIC_EXPONENT = "# Public exponent:"; + private static final String PRIV_EXPONENT = "# Exponent:"; + private static final String PRIV_PRIME_1 = "# Prime 1:"; + private static final String PRIV_PRIME_2 = "# Prime 2:"; + private static final String PRIV_PRIME_EXPONENT_1 = "# Prime exponent 1:"; + private static final String PRIV_PRIME_EXPONENT_2 = "# Prime exponent 2:"; + private static final String PRIV_COEFFICIENT = "# Coefficient:"; + private static final String OAEP_EXAMPLE_HEADER = "# OAEP Example"; + private static final String MESSAGE = "# Message:"; + private static final String ENCRYPTION = "# Encryption:"; + + private BufferedReader m_reader = null; + private FileReader m_file_reader = null; + private TestVectorData m_data = null; + + TestVectorLoader() { + + } + + protected void finalize() { + close(); + } + + public void open(String path) throws IOException { + close(); + m_file_reader = new FileReader(path); + m_reader = new BufferedReader(m_file_reader); + m_data = new TestVectorData(); + } + + public void close() { + try { + if (m_reader != null) { + m_reader.close(); + m_reader = null; + } + if (m_file_reader != null) { + m_file_reader.close(); + m_file_reader = null; + } + m_data = null; + } catch (IOException e) { + System.out.println("Exception closing files"); + e.printStackTrace(); + } + } + + public TestVectorData loadNextTest() throws IOException { + if (m_file_reader == null || m_reader == null || m_data == null) { + throw new IOException("A test vector file must be opened first"); + } + + String line = m_reader.readLine(); + + if (line == null) { + // end of file + return null; + } + + if (line.startsWith(FILE_HEADER)) { + // start of file + skipFileHeader(m_reader); + line = m_reader.readLine(); + } + + if (line.startsWith(OAEP_EXAMPLE_HEADER)) { + // Next example, keep existing keys and load next message + loadMessage(m_reader, m_data); + return m_data; + } + + // otherwise it's a new example + if (!line.startsWith(EXAMPLE_HEADER)) { + throw new IOException("Test Header Missing"); + } + startNewTest(m_reader); + m_data = new TestVectorData(); + + line = m_reader.readLine(); + if (!line.startsWith(PUBLIC_KEY)) + throw new IOException("Public Key Missing"); + loadPublicKey(m_reader, m_data); + + line = m_reader.readLine(); + if (!line.startsWith(PRIVATE_KEY)) + throw new IOException("Private Key Missing"); + loadPrivateKey(m_reader, m_data); + + line = m_reader.readLine(); + if (!line.startsWith(OAEP_EXAMPLE_HEADER)) + throw new IOException("Message Missing"); + loadMessage(m_reader, m_data); + + return m_data; + } + + private byte[] unhexlify(String line) { + byte[] bytes = DatatypeConverter.parseHexBinary(line); + return bytes; + } + + private BigInteger readBigInteger(BufferedReader br) throws IOException { + return new BigInteger(br.readLine(), 16); + } + + private void skipFileHeader(BufferedReader br) throws IOException { + br.readLine(); // # # Derived from the NIST OAEP SHA1 vectors + br.readLine(); // # # Verified against the Bouncy Castle OAEP SHA2 implementation + br.readLine(); // # + } + + private void startNewTest(BufferedReader br) throws IOException { + String line = br.readLine(); + if (!line.startsWith(EXAMPLE)) + throw new IOException("Example Header Missing"); + } + + private void loadPublicKey(BufferedReader br, TestVectorData data) throws IOException { + String line = br.readLine(); + if (!line.startsWith(PUB_MODULUS)) + throw new IOException("Public Key Modulus Missing"); + data.pub_key_modulus = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PUB_EXPONENT)) + throw new IOException("Public Key Exponent Missing"); + data.pub_key_exponent = readBigInteger(br); + } + + private void loadPrivateKey(BufferedReader br, TestVectorData data) throws IOException { + String line = br.readLine(); + if (!line.startsWith(PRIV_MODULUS)) + throw new IOException("Private Key Modulus Missing"); + data.priv_key_modulus = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PRIV_PUBLIC_EXPONENT)) + throw new IOException("Private Key Public Exponent Missing"); + data.priv_key_public_exponent = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PRIV_EXPONENT)) + throw new IOException("Private Key Exponent Missing"); + data.priv_key_exponent = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PRIV_PRIME_1)) + throw new IOException("Private Key Prime 1 Missing"); + data.priv_key_prime_1 = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PRIV_PRIME_2)) + throw new IOException("Private Key Prime 2 Missing"); + data.priv_key_prime_2 = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PRIV_PRIME_EXPONENT_1)) + throw new IOException("Private Key Prime Exponent 1 Missing"); + data.priv_key_prime_exponent_1 = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PRIV_PRIME_EXPONENT_2)) + throw new IOException("Private Key Prime Exponent 2 Missing"); + data.priv_key_prime_exponent_2 = readBigInteger(br); + + line = br.readLine(); + if (!line.startsWith(PRIV_COEFFICIENT)) + throw new IOException("Private Key Coefficient Missing"); + data.priv_key_coefficient = readBigInteger(br); + } + + private void loadMessage(BufferedReader br, TestVectorData data) throws IOException { + String line = br.readLine(); + if (!line.startsWith(MESSAGE)) + throw new IOException("Plaintext Missing"); + data.plaintext = unhexlify(br.readLine()); + + line = br.readLine(); + if (!line.startsWith(ENCRYPTION)) + throw new IOException("Ciphertext Missing"); + data.ciphertext = unhexlify(br.readLine()); + } + +} + +public class VerifyRSAOAEPSHA2 { + + public enum SHAHash { + SHA1, SHA224, SHA256, SHA384, SHA512 + } + + private SHAHash m_mgf1_hash; + private SHAHash m_alg_hash; + private Cipher m_cipher; + private PrivateKey m_private_key; + private AlgorithmParameters m_algo_param; + + VerifyRSAOAEPSHA2(SHAHash mgf1_hash, SHAHash alg_hash, TestVectorData test_data) throws Exception { + + m_mgf1_hash = mgf1_hash; + m_alg_hash = alg_hash; + + MGF1ParameterSpec mgf1_spec = getMGF1ParameterSpec(m_mgf1_hash); + AlgorithmParameterSpec algo_param_spec = getAlgorithmParameterSpec(m_alg_hash, mgf1_spec); + + m_algo_param = AlgorithmParameters.getInstance("OAEP"); + m_algo_param.init(algo_param_spec); + + m_private_key = loadPrivateKey(test_data); + + m_cipher = getCipher(m_alg_hash); + } + + private Cipher getCipher(SHAHash alg_hash) throws GeneralSecurityException { + Cipher cipher = null; + + switch (alg_hash) { + + case SHA1: + cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA1andMGF1Padding", "BC"); + break; + + case SHA224: + cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-224andMGF1Padding", "BC"); + break; + + case SHA256: + cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding", "BC"); + break; + + case SHA384: + cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-384andMGF1Padding", "BC"); + break; + + case SHA512: + cipher = Cipher.getInstance("RSA/ECB/OAEPwithSHA-512andMGF1Padding", "BC"); + break; + } + + return cipher; + } + + private MGF1ParameterSpec getMGF1ParameterSpec(SHAHash mgf1_hash) { + MGF1ParameterSpec mgf1 = null; + + switch (mgf1_hash) { + + case SHA1: + mgf1 = MGF1ParameterSpec.SHA1; + break; + case SHA224: + mgf1 = MGF1ParameterSpec.SHA224; + break; + + case SHA256: + mgf1 = MGF1ParameterSpec.SHA256; + break; + + case SHA384: + mgf1 = MGF1ParameterSpec.SHA384; + break; + + case SHA512: + mgf1 = MGF1ParameterSpec.SHA512; + break; + } + + return mgf1; + } + + private AlgorithmParameterSpec getAlgorithmParameterSpec(SHAHash alg_hash, MGF1ParameterSpec mgf1_spec) { + + OAEPParameterSpec oaep_spec = null; + + switch (alg_hash) { + + case SHA1: + oaep_spec = new OAEPParameterSpec("SHA1", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT); + break; + + case SHA224: + oaep_spec = new OAEPParameterSpec("SHA-224", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT); + break; + + case SHA256: + oaep_spec = new OAEPParameterSpec("SHA-256", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT); + break; + + case SHA384: + oaep_spec = new OAEPParameterSpec("SHA-384", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT); + break; + + case SHA512: + oaep_spec = new OAEPParameterSpec("SHA-512", "MGF1", mgf1_spec, PSource.PSpecified.DEFAULT); + break; + } + + return oaep_spec; + } + + private PrivateKey loadPrivateKey(TestVectorData test_data) throws Exception { + KeyFactory kf = KeyFactory.getInstance("RSA"); + + RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(test_data.priv_key_modulus, test_data.priv_key_exponent); + + return kf.generatePrivate(keySpec); + } + + public void testDecrypt(byte[] plaintext, byte[] ciphertext) throws Exception { + System.out.println("Verifying OAEP with mgf1_hash: " + m_mgf1_hash + " alg_hash: " + m_alg_hash + " - " + + ciphertext.length + " bytes ciphertext - " + + plaintext.length + " bytes plaintext"); + + m_cipher.init(Cipher.DECRYPT_MODE, m_private_key, m_algo_param); + byte[] java_plaintext = m_cipher.doFinal(ciphertext); + + if (Arrays.equals(java_plaintext, plaintext) == false) { + throw new Exception("Verification failure - plaintext does not match after decryption."); + } + } + + public static void main(String[] args) { + Security.addProvider(new BouncyCastleProvider()); + + // assume current directory if no path given on command line + String vector_path = "./vectors/cryptography_vectors/asymmetric/RSA/oaep-custom"; + + if (args.length > 0) { + vector_path = args[0]; + } + + System.out.println("Vector file path: " + vector_path); + + try { + // loop over each combination of hash loading the vector file + // to verify for each + for (SHAHash mgf1_hash : SHAHash.values()) { + for (SHAHash alg_hash : SHAHash.values()) { + if (mgf1_hash.name().toLowerCase().equals("sha1") && + alg_hash.name().toLowerCase().equals("sha1")) { + continue; + } + String filename = "oaep-" + mgf1_hash.name().toLowerCase() + + "-" + alg_hash.name().toLowerCase() + ".txt"; + + System.out.println("Loading " + filename + "..."); + + TestVectorLoader loader = new TestVectorLoader(); + loader.open(vector_path + filename); + + TestVectorData test_data; + + // load each test in the file and verify + while ((test_data = loader.loadNextTest()) != null) { + VerifyRSAOAEPSHA2 verify = new VerifyRSAOAEPSHA2(mgf1_hash, alg_hash, test_data); + verify.testDecrypt(test_data.plaintext, test_data.ciphertext); + } + + System.out.println("Verifying " + filename + " completed successfully."); + } + } + + System.out.println("All verification completed successfully"); + + } catch (Exception e) { + // if any exception is thrown the verification has failed + e.printStackTrace(); + System.out.println("Verification Failed!"); + } + } +} diff --git a/tools/tests/oaep.c b/tools/tests/oaep.c new file mode 100644 index 000000000..13dd79267 --- /dev/null +++ b/tools/tests/oaep.c @@ -0,0 +1,10 @@ +#include "nspr.h" +#include "nss.h" +#include "pkcs11t.h" +#include "pk11pub.h" + +int main() { + PK11_PubWrapSymKeyWithMechanism(NULL, 0, NULL, NULL, NULL); + PK11_PubUnwrapSymKeyWithMechanism(NULL, 0, NULL, NULL, 0, 0, 0); + return 0; +}