From f176618056dd86c8c511ad05eb4e81fa79be9315 Mon Sep 17 00:00:00 2001 From: Alexander Scheel Date: Thu, 20 Aug 2020 12:28:29 -0400 Subject: [PATCH 1/7] Add RSA/OAEP algorithm identifiers This adds RSA/OAEP to jss.crypto.Algorithm as a known algorithm and adds stubs for indicating what type of key is required to use it. Signed-off-by: Alexander Scheel --- org/mozilla/jss/crypto/Algorithm.c | 1 + org/mozilla/jss/crypto/Algorithm.h | 2 +- org/mozilla/jss/crypto/Algorithm.java | 5 ++++- org/mozilla/jss/crypto/KeyWrapAlgorithm.java | 5 +++++ org/mozilla/jss/pkcs11/KeyType.java | 3 ++- 5 files changed, 13 insertions(+), 3 deletions(-) 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/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/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" ); From 28f92c5c470fed947a033a89186d70516cf4100c Mon Sep 17 00:00:00 2001 From: Alexander Scheel Date: Mon, 21 Sep 2020 13:59:32 -0400 Subject: [PATCH 2/7] Re-add KeyWrapping tests to build These tests have been updated from using DES/DES3 to AES-256. Signed-off-by: Alexander Scheel --- cmake/JSSTests.cmake | 5 ++ org/mozilla/jss/tests/KeyWrapping.java | 69 ++++++++++++++++++-------- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/cmake/JSSTests.cmake b/cmake/JSSTests.cmake index 7384bdc05..7921fcf8f 100644 --- a/cmake/JSSTests.cmake +++ b/cmake/JSSTests.cmake @@ -210,6 +210,11 @@ macro(jss_tests) COMMAND "org.mozilla.jss.tests.JCAKeyWrap" "${RESULTS_NSSDB_OUTPUT_DIR}" "${PASSWORD_FILE}" DEPENDS "Setup_DBs" ) + jss_test_java( + NAME "JSS-KeyWrapping" + COMMAND "org.mozilla.jss.tests.KeyWrapping" "${RESULTS_NSSDB_OUTPUT_DIR}" "${PASSWORD_FILE}" + DEPENDS "Setup_DBs" + ) jss_test_java( NAME "Mozilla_JSS_JCA_Signature" COMMAND "org.mozilla.jss.tests.JCASigTest" "${RESULTS_NSSDB_OUTPUT_DIR}" "${PASSWORD_FILE}" 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 ) { From b0f7b399f68ba04244763904888c3370092b8fef Mon Sep 17 00:00:00 2001 From: Alexander Scheel Date: Tue, 22 Sep 2020 13:30:49 -0400 Subject: [PATCH 3/7] Fix FindNSS, FindNSPR to link with sandboxed libs When using `-Wl,-rpath`, the final generated binaries will have a rpath include to the sandboxed libraries. This is good as it enables binaries to run from the built directory. However, at link time, we're still using the system's NSS library. This doesn't work when NSS introduces a change to its external (public) API; the system's NSS likely doesn't have this change yet. Include the `-L` flag as well, to use the sandboxed library at link time as well. Signed-off-by: Alexander Scheel --- cmake/FindNSPR.cmake | 1 + cmake/FindNSS.cmake | 1 + 2 files changed, 2 insertions(+) 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 From e7e36463228bf5321ee753a0b05fd3899cdcff9c Mon Sep 17 00:00:00 2001 From: Alexander Scheel Date: Wed, 23 Sep 2020 05:58:50 -0400 Subject: [PATCH 4/7] Add hash algorithms to PK11Algorithm.java While hash algorithms in JSS currently use the SEC_OID based interface (rather than PKCS#11), include them in PK11Algorithm because they have equivalent PKCS#11 constant definitions. Signed-off-by: Alexander Scheel --- org/mozilla/jss/crypto/PKCS11Algorithm.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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. From 88b2bfb72346cc7dfa52ad3a050fadbb77db4e69 Mon Sep 17 00:00:00 2001 From: Alexander Scheel Date: Wed, 23 Sep 2020 15:34:06 -0400 Subject: [PATCH 5/7] Implement RSA/OAEP key wrapping This depends on support from NSS for two new function calls, PK11_PubWrapSymKeyWithMechanism and PK11_PubUnwrapSymKeyWithMechanism. These enable passing OAEP's mechanism parameters into NSS's high-level PK11 key wrap/unwrap interface, allowing them to succeed. Otherwise, previous versions of NSS silently converted OAEP to PKCS#1 v1.5, allowing the trivial patchset to succeed without any mechanism parameters. Introduce a JSSOAEPParameterSpec which can consume a JCA-standard OAEPParameterSpec instance but which also extends NativeEnclosure to allow us to call into the JNI layer with a parameter instance. Signed-off-by: Alexander Scheel --- lib/jss.map | 7 + org/mozilla/jss/crypto/JSSOAEPParameterSpec.c | 151 ++++++++++++++ .../jss/crypto/JSSOAEPParameterSpec.java | 185 ++++++++++++++++++ org/mozilla/jss/pkcs11/PK11KeyWrapper.c | 64 +++--- org/mozilla/jss/pkcs11/PK11KeyWrapper.java | 108 +++++++--- 5 files changed, 462 insertions(+), 53 deletions(-) create mode 100644 org/mozilla/jss/crypto/JSSOAEPParameterSpec.c create mode 100644 org/mozilla/jss/crypto/JSSOAEPParameterSpec.java 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/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/pkcs11/PK11KeyWrapper.c b/org/mozilla/jss/pkcs11/PK11KeyWrapper.c index ce7e65678..673165213 100644 --- a/org/mozilla/jss/pkcs11/PK11KeyWrapper.c +++ b/org/mozilla/jss/pkcs11/PK11KeyWrapper.c @@ -17,6 +17,7 @@ #include #include #include +#include "StaticVoidPointer.h" #define MAX_PRIVATE_KEY_LEN MAX_RSA_MODULUS_LEN @@ -120,11 +121,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 +151,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 +174,11 @@ Java_org_mozilla_jss_pkcs11_PK11KeyWrapper_nativeWrapSymWithPub goto finish; } + paramItem.data = params; + paramItem.len = paramsSize; + /* perform the wrap */ - status = PK11_PubWrapSymKey(mech, wrapping, toBeWrapped, &wrapped); + status = PK11_PubWrapSymKeyWithMechanism(wrapping, mech, ¶mItem, toBeWrapped, &wrapped); if( status != SECSuccess ) { JSS_throwMsg(env, TOKEN_EXCEPTION, "Wrap operation failed on token"); goto finish; @@ -595,12 +610,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 +635,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 +661,18 @@ Java_org_mozilla_jss_pkcs11_PK11KeyWrapper_nativeUnwrapSymWithPriv operation = JSS_symkeyUsage[usageEnum]; } - symKey = PK11_PubUnwrapSymKey(wrappingKey, wrappedKey, keyTypeMech, - operation, keyLen); + /* 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; + + symKey = PK11_PubUnwrapSymKeyWithMechanism(wrappingKey, mech, ¶mItem, + wrappedKey, keyTypeMech, operation, keyLen); if( symKey == NULL ) { JSS_throwMsg(env, TOKEN_EXCEPTION, "Failed to unwrap key"); goto finish; @@ -665,12 +685,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 From 13a22a1cdb3b8341c6a4feee5963e9cb657ad348 Mon Sep 17 00:00:00 2001 From: Alexander Scheel Date: Mon, 9 Nov 2020 10:02:50 -0500 Subject: [PATCH 6/7] Support running on older versions of NSS This allows us to detect whether or not NSS supports RSA-OAEP key wrap/unwrap and if not, disable RSA-OAEP support, gracefully falling back to the old method for other mechanisms. Signed-off-by: Alexander Scheel --- cmake/JSSConfig.cmake | 25 ++++++++++++++++++++++--- cmake/JSSTests.cmake | 12 +++++++----- org/mozilla/jss/jssconfig.h.in | 1 + org/mozilla/jss/pkcs11/PK11KeyWrapper.c | 21 +++++++++++++++++++++ tools/tests/oaep.c | 10 ++++++++++ 5 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 tools/tests/oaep.c 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 7921fcf8f..375fcf329 100644 --- a/cmake/JSSTests.cmake +++ b/cmake/JSSTests.cmake @@ -210,11 +210,13 @@ macro(jss_tests) COMMAND "org.mozilla.jss.tests.JCAKeyWrap" "${RESULTS_NSSDB_OUTPUT_DIR}" "${PASSWORD_FILE}" DEPENDS "Setup_DBs" ) - jss_test_java( - NAME "JSS-KeyWrapping" - COMMAND "org.mozilla.jss.tests.KeyWrapping" "${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_java( NAME "Mozilla_JSS_JCA_Signature" COMMAND "org.mozilla.jss.tests.JCASigTest" "${RESULTS_NSSDB_OUTPUT_DIR}" "${PASSWORD_FILE}" 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/PK11KeyWrapper.c b/org/mozilla/jss/pkcs11/PK11KeyWrapper.c index 673165213..cf2f59099 100644 --- a/org/mozilla/jss/pkcs11/PK11KeyWrapper.c +++ b/org/mozilla/jss/pkcs11/PK11KeyWrapper.c @@ -18,6 +18,7 @@ #include #include #include "StaticVoidPointer.h" +#include "jssconfig.h" #define MAX_PRIVATE_KEY_LEN MAX_RSA_MODULUS_LEN @@ -178,7 +179,16 @@ Java_org_mozilla_jss_pkcs11_PK11KeyWrapper_nativeWrapSymWithPub 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; @@ -671,8 +681,19 @@ Java_org_mozilla_jss_pkcs11_PK11KeyWrapper_nativeUnwrapSymWithPriv 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); +#endif + if( symKey == NULL ) { JSS_throwMsg(env, TOKEN_EXCEPTION, "Failed to unwrap key"); goto finish; 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; +} From 1eff69d22e531576c626f21634c737cfe9f440ad Mon Sep 17 00:00:00 2001 From: Pritam Singh Date: Tue, 17 Nov 2020 23:02:06 +0530 Subject: [PATCH 7/7] Added_OAEP_JSS_test Signed-off-by: Pritam Singh --- cmake/JSSTests.cmake | 16 + org/mozilla/jss/tests/VerifyRSAOAEPSHA2.java | 416 +++++++++++++++++++ 2 files changed, 432 insertions(+) create mode 100644 org/mozilla/jss/tests/VerifyRSAOAEPSHA2.java diff --git a/cmake/JSSTests.cmake b/cmake/JSSTests.cmake index 375fcf329..7427308dc 100644 --- a/cmake/JSSTests.cmake +++ b/cmake/JSSTests.cmake @@ -217,6 +217,22 @@ macro(jss_tests) 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/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!"); + } + } +}