diff --git a/Makefile.in b/Makefile.in index d3e8ae9..f95512b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -148,14 +148,16 @@ CRYPTOLIBOBJ = \ crypto_impl.lo \ crypto_openssl.lo \ crypto_libtomcrypt.lo \ + crypto_nss.lo \ crypto_cc.lo - + CRYPTOSRC = \ $(TOP)/src/crypto.h \ $(TOP)/src/sqlcipher.h \ $(TOP)/src/crypto.c \ $(TOP)/src/crypto_impl.c \ $(TOP)/src/crypto_libtomcrypt.c \ + $(TOP)/src/crypto_nss.c \ $(TOP)/src/crypto_openssl.c \ $(TOP)/src/crypto_cc.c @@ -820,6 +822,8 @@ crypto_impl.lo: $(TOP)/src/crypto_impl.c $(HDR) $(LTCOMPILE) -c $(TOP)/src/crypto_impl.c crypto_openssl.lo: $(TOP)/src/crypto_openssl.c $(HDR) $(LTCOMPILE) -c $(TOP)/src/crypto_openssl.c +crypto_nss.lo: $(TOP)/src/crypto_nss.c $(HDR) + $(LTCOMPILE) -c $(TOP)/src/crypto_nss.c crypto_libtomcrypt.lo: $(TOP)/src/crypto_libtomcrypt.c $(HDR) $(LTCOMPILE) -c $(TOP)/src/crypto_libtomcrypt.c crypto_cc.lo: $(TOP)/src/crypto_cc.c $(HDR) diff --git a/Makefile.msc b/Makefile.msc index 2180936..dc0d4da 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -1270,6 +1270,7 @@ SRC00 = \ $(TOP)\src\crypto_cc.c \ $(TOP)\src\crypto_impl.c \ $(TOP)\src\crypto_libtomcrypt.c \ + $(TOP)\src\crypto_nss.c \ $(TOP)\src\crypto_openssl.c \ $(TOP)\src\crypto.h \ $(TOP)\src\sqlcipher.h \ diff --git a/config.h.in b/config.h.in index 306eb88..b91f1f7 100644 --- a/config.h.in +++ b/config.h.in @@ -33,6 +33,9 @@ /* Define to 1 if you have the `crypto' library (-lcrypto). */ #undef HAVE_LIBCRYPTO +/* Define to 1 if you have the `nss3' library (-lnss3). */ +#undef HAVE_LIBNSS3 + /* Define to 1 if you have the `tomcrypt' library (-ltomcrypt). */ #undef HAVE_LIBTOMCRYPT diff --git a/configure b/configure index 74c822c..ce73879 100755 --- a/configure +++ b/configure @@ -12029,6 +12029,59 @@ else fi else + if test "$crypto_lib" = "nss"; then + CFLAGS+=" -DSQLCIPHER_CRYPTO_NSS" + BUILD_CFLAGS+=" -DSQLCIPHER_CRYPTO_NSS" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: nss3" >&5 +$as_echo "nss3" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PK11_Decrypt in -lnss3" >&5 +$as_echo_n "checking for PK11_Decrypt in -lnss3... " >&6; } +if ${ac_cv_lib_nss3_PK11_Decrypt+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnss3 $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char PK11_Decrypt (); +int +main () +{ +return PK11_Decrypt (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_nss3_PK11_Decrypt=yes +else + ac_cv_lib_nss3_PK11_Decrypt=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nss3_PK11_Decrypt" >&5 +$as_echo "$ac_cv_lib_nss3_PK11_Decrypt" >&6; } +if test "x$ac_cv_lib_nss3_PK11_Decrypt" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBNSS3 1 +_ACEOF + + LIBS="-lnss3 $LIBS" + +else + as_fn_error $? "Library crypto not found. Install nss!\"" "$LINENO" 5 +fi + + else CFLAGS+=" -DSQLCIPHER_CRYPTO_OPENSSL" BUILD_CFLAGS+=" -DSQLCIPHER_CRYPTO_OPENSSL" { $as_echo "$as_me:${as_lineno-$LINENO}: result: openssl" >&5 @@ -12080,6 +12133,7 @@ else as_fn_error $? "Library crypto not found. Install openssl!\"" "$LINENO" 5 fi + fi fi fi fi diff --git a/configure.ac b/configure.ac index a05248f..f13ea6a 100644 --- a/configure.ac +++ b/configure.ac @@ -219,11 +219,19 @@ else AC_CHECK_LIB([tomcrypt], [register_cipher], , AC_MSG_ERROR([Library crypto not found. Install libtomcrypt!"])) else + if test "$crypto_lib" = "nss"; then + CFLAGS+=" -DSQLCIPHER_CRYPTO_NSS" + BUILD_CFLAGS+=" -DSQLCIPHER_CRYPTO_NSS" + AC_MSG_RESULT([nss3]) + AC_CHECK_LIB([nss3], [PK11_Decrypt], , + AC_MSG_ERROR([Library crypto not found. Install nss!"])) + else CFLAGS+=" -DSQLCIPHER_CRYPTO_OPENSSL" BUILD_CFLAGS+=" -DSQLCIPHER_CRYPTO_OPENSSL" AC_MSG_RESULT([openssl]) AC_CHECK_LIB([crypto], [HMAC_Init_ex], , AC_MSG_ERROR([Library crypto not found. Install openssl!"])) + fi fi fi fi diff --git a/src/crypto.h b/src/crypto.h index aa734c2..cc6f8ac 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -48,6 +48,7 @@ void sqlite3pager_reset(Pager *pPager); #if !defined (SQLCIPHER_CRYPTO_CC) \ && !defined (SQLCIPHER_CRYPTO_LIBTOMCRYPT) \ + && !defined (SQLCIPHER_CRYPTO_NSS) \ && !defined (SQLCIPHER_CRYPTO_OPENSSL) #define SQLCIPHER_CRYPTO_OPENSSL #endif diff --git a/src/crypto_impl.c b/src/crypto_impl.c index 112e2fc..6679bf6 100644 --- a/src/crypto_impl.c +++ b/src/crypto_impl.c @@ -205,6 +205,9 @@ void sqlcipher_activate() { #elif defined (SQLCIPHER_CRYPTO_LIBTOMCRYPT) extern int sqlcipher_ltc_setup(sqlcipher_provider *p); sqlcipher_ltc_setup(p); +#elif defined (SQLCIPHER_CRYPTO_NSS) + extern int sqlcipher_nss_setup(sqlcipher_provider *p); + sqlcipher_nss_setup(p); #elif defined (SQLCIPHER_CRYPTO_OPENSSL) extern int sqlcipher_openssl_setup(sqlcipher_provider *p); sqlcipher_openssl_setup(p); diff --git a/src/crypto_nss.c b/src/crypto_nss.c new file mode 100644 index 0000000..abfa133 --- /dev/null +++ b/src/crypto_nss.c @@ -0,0 +1,314 @@ +/* +** SQLCipher +** http://sqlcipher.net +** +** Copyright (c) 2008 - 2013, ZETETIC LLC +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of the ZETETIC LLC nor the +** names of its contributors may be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY +** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +** DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY +** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +*/ +/* BEGIN SQLCIPHER */ +#ifdef SQLITE_HAS_CODEC +#ifdef SQLCIPHER_CRYPTO_NSS +#include "crypto.h" +#include "sqlcipher.h" +#include +#include +#include + +static NSSInitContext* nss_init_context = NULL; +static unsigned int nss_init_count = 0; +static sqlite3_mutex* nss_rand_mutex = NULL; + +int sqlcipher_nss_setup(sqlcipher_provider *p); + +static int sqlcipher_nss_activate(void *ctx) { + CODEC_TRACE_MUTEX("sqlcipher_nss_activate: entering static master mutex\n"); + sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); + CODEC_TRACE_MUTEX("sqlcipher_nss_activate: entered static master mutex\n"); + if (nss_init_context == NULL) { + nss_init_context = NSS_InitContext("", "", "", "", NULL, + NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | + NSS_INIT_FORCEOPEN | NSS_INIT_OPTIMIZESPACE | NSS_INIT_NOROOTINIT); + } + nss_init_count++; + CODEC_TRACE_MUTEX("sqlcipher_nss_activate: leaving static master mutex\n"); + sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); + CODEC_TRACE_MUTEX("sqlcipher_nss_activate: left static master mutex\n"); + return SQLITE_OK; +} + +static int sqlcipher_nss_deactivate(void *ctx) { + CODEC_TRACE_MUTEX("sqlcipher_nss_deactivate: entering static master mutex\n"); + sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); + CODEC_TRACE_MUTEX("sqlcipher_nss_deactivate: entered static master mutex\n"); + nss_init_count--; + if (nss_init_count == 0 && nss_init_context != NULL) { + NSS_ShutdownContext(nss_init_context); + nss_init_context = NULL; + } + CODEC_TRACE_MUTEX("sqlcipher_nss_deactivate: leaving static master mutex\n"); + sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); + CODEC_TRACE_MUTEX("sqlcipher_nss_deactivate: left static master mutex\n"); + return SQLITE_OK; +} + +static int sqlcipher_nss_add_random(void *ctx, void *buffer, int length) { + return SQLITE_OK; +} + +/* generate a defined number of random bytes */ +static int sqlcipher_nss_random (void *ctx, void *buffer, int length) { + // PK11_GenerateRandom should be thread-safe. + return (PK11_GenerateRandom((unsigned char *)buffer, length) == SECSuccess) ? SQLITE_OK : SQLITE_ERROR; +} + +static const char* sqlcipher_nss_get_provider_name(void *ctx) { + return "nss"; +} + +static const char* sqlcipher_nss_get_provider_version(void *ctx) { + return NSS_GetVersion(); +} + +static const char* sqlcipher_nss_get_cipher(void *ctx) { + return "aes-256-cbc"; +} + +static int sqlcipher_nss_get_key_sz(void *ctx) { + return AES_256_KEY_LENGTH; +} + +static int sqlcipher_nss_get_iv_sz(void *ctx) { + return AES_BLOCK_SIZE; +} + +static int sqlcipher_nss_get_block_sz(void *ctx) { + return AES_BLOCK_SIZE; +} + +static int sqlcipher_nss_get_hmac_sz(void *ctx, int algorithm) { + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + return SHA1_LENGTH; + break; + case SQLCIPHER_HMAC_SHA256: + return SHA256_LENGTH; + break; + case SQLCIPHER_HMAC_SHA512: + return SHA512_LENGTH; + break; + default: + return 0; + } +} + +static int sqlcipher_nss_hmac(void *ctx, int algorithm, unsigned char *hmac_key, int key_sz, unsigned char *in, int in_sz, unsigned char *in2, int in2_sz, unsigned char *out) { + int rc = SQLITE_OK; + unsigned int length; + unsigned int outLen; + PK11Context* context = NULL; + PK11SlotInfo * slot = NULL; + PK11SymKey* symKey = NULL; + if(in == NULL) goto error; + CK_MECHANISM_TYPE mech; + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + mech = CKM_SHA_1_HMAC; + break; + case SQLCIPHER_HMAC_SHA256: + mech = CKM_SHA256_HMAC; + break; + case SQLCIPHER_HMAC_SHA512: + mech = CKM_SHA512_HMAC; + break; + default: + goto error; + } + length = sqlcipher_nss_get_hmac_sz(ctx, algorithm); + slot = PK11_GetInternalSlot(); + if (slot == NULL) goto error; + SECItem keyItem; + keyItem.data = hmac_key; + keyItem.len = key_sz; + symKey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, + CKA_SIGN, &keyItem, NULL); + if (symKey == NULL) goto error; + SECItem noParams; + noParams.data = 0; + noParams.len = 0; + context = PK11_CreateContextBySymKey(mech, CKA_SIGN, symKey, &noParams); + if (context == NULL) goto error; + if (PK11_DigestBegin(context) != SECSuccess) goto error; + if (PK11_DigestOp(context, in, in_sz) != SECSuccess) goto error; + if (in2 != NULL) { + if (PK11_DigestOp(context, in2, in2_sz) != SECSuccess) goto error; + } + if (PK11_DigestFinal(context, out, &outLen, length) != SECSuccess) goto error; + + goto cleanup; + error: + rc = SQLITE_ERROR; + cleanup: + if (context) PK11_DestroyContext(context, PR_TRUE); + if (symKey) PK11_FreeSymKey(symKey); + if (slot) PK11_FreeSlot(slot); + return rc; +} + +static int sqlcipher_nss_kdf(void *ctx, int algorithm, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) { + int rc = SQLITE_OK; + PK11SlotInfo * slot = NULL; + SECAlgorithmID * algid = NULL; + PK11SymKey* symKey = NULL; + SECOidTag oidtag; + switch(algorithm) { + case SQLCIPHER_HMAC_SHA1: + oidtag = SEC_OID_HMAC_SHA1; + break; + case SQLCIPHER_HMAC_SHA256: + oidtag = SEC_OID_HMAC_SHA256; + break; + case SQLCIPHER_HMAC_SHA512: + oidtag = SEC_OID_HMAC_SHA512; + break; + default: + goto error; + } + SECItem secSalt; + secSalt.data = salt; + secSalt.len = salt_sz; + // Always pass SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this parameter + // is unused for key generation. It is currently only used + // for PBKDF2 authentication or key (un)wrapping when specifying an + // encryption algorithm (PBES2). + algid = PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1, + oidtag, key_sz, workfactor, &secSalt); + if (algid == NULL) goto error; + slot = PK11_GetInternalSlot(); + if (slot == NULL) goto error; + SECItem pwItem; + pwItem.data = (unsigned char *) pass; // PK11_PBEKeyGen doesn't modify the key. + pwItem.len = pass_sz; + symKey = PK11_PBEKeyGen(slot, algid, &pwItem, PR_FALSE, NULL); + if (symKey == NULL) goto error; + if (PK11_ExtractKeyValue(symKey) != SECSuccess) goto error; + // No need to free keyData as it is a buffer managed by symKey. + SECItem* keyData = PK11_GetKeyData(symKey); + if (keyData == NULL) goto error; + memcpy(key, keyData->data, key_sz); + + goto cleanup; + error: + rc = SQLITE_ERROR; + cleanup: + if (slot) PK11_FreeSlot(slot); + if (algid) SECOID_DestroyAlgorithmID(algid, PR_TRUE); + if (symKey) PK11_FreeSymKey(symKey); + return rc; +} + +static int sqlcipher_nss_cipher(void *ctx, int mode, unsigned char *key, int key_sz, unsigned char *iv, unsigned char *in, int in_sz, unsigned char *out) { + int rc = SQLITE_OK; + PK11SlotInfo * slot = NULL; + PK11SymKey* symKey = NULL; + unsigned int outLen; + SECItem params; + params.data = iv; + params.len = sqlcipher_nss_get_iv_sz(ctx); + slot = PK11_GetInternalSlot(); + if (slot == NULL) goto error; + SECItem keyItem; + keyItem.data = key; + keyItem.len = key_sz; + symKey = PK11_ImportSymKey(slot, CKM_AES_CBC, PK11_OriginUnwrap, + CKA_ENCRYPT, &keyItem, NULL); + if (symKey == NULL) goto error; + SECStatus rv; + if (mode == CIPHER_ENCRYPT) { + rv = PK11_Encrypt(symKey, CKM_AES_CBC, ¶ms, out, &outLen, + in_sz + 16, in, in_sz); + } else { + rv = PK11_Decrypt(symKey, CKM_AES_CBC, ¶ms, out, &outLen, + in_sz + 16, in, in_sz); + } + if (rv != SECSuccess) goto error; + + goto cleanup; + error: + rc = SQLITE_ERROR; + cleanup: + if (slot) PK11_FreeSlot(slot); + if (symKey) PK11_FreeSymKey(symKey); + return rc; +} + +static int sqlcipher_nss_ctx_copy(void *target_ctx, void *source_ctx) { + return SQLITE_OK; +} + +static int sqlcipher_nss_ctx_cmp(void *c1, void *c2) { + return 1; /* always indicate contexts are the same */ +} + +static int sqlcipher_nss_ctx_init(void **ctx) { + sqlcipher_nss_activate(NULL); + return SQLITE_OK; +} + +static int sqlcipher_nss_ctx_free(void **ctx) { + sqlcipher_nss_deactivate(NULL); + return SQLITE_OK; +} + +static int sqlcipher_nss_fips_status(void *ctx) { + return 0; +} + +int sqlcipher_nss_setup(sqlcipher_provider *p) { + p->activate = sqlcipher_nss_activate; + p->deactivate = sqlcipher_nss_deactivate; + p->random = sqlcipher_nss_random; + p->get_provider_name = sqlcipher_nss_get_provider_name; + p->hmac = sqlcipher_nss_hmac; + p->kdf = sqlcipher_nss_kdf; + p->cipher = sqlcipher_nss_cipher; + p->get_cipher = sqlcipher_nss_get_cipher; + p->get_key_sz = sqlcipher_nss_get_key_sz; + p->get_iv_sz = sqlcipher_nss_get_iv_sz; + p->get_block_sz = sqlcipher_nss_get_block_sz; + p->get_hmac_sz = sqlcipher_nss_get_hmac_sz; + p->ctx_copy = sqlcipher_nss_ctx_copy; + p->ctx_cmp = sqlcipher_nss_ctx_cmp; + p->ctx_init = sqlcipher_nss_ctx_init; + p->ctx_free = sqlcipher_nss_ctx_free; + p->add_random = sqlcipher_nss_add_random; + p->fips_status = sqlcipher_nss_fips_status; + p->get_provider_version = sqlcipher_nss_get_provider_version; + return SQLITE_OK; +} + +#endif +#endif +/* END SQLCIPHER */ diff --git a/test/sqlcipher-pragmas.test b/test/sqlcipher-pragmas.test index 8f532ed..31c99fe 100644 --- a/test/sqlcipher-pragmas.test +++ b/test/sqlcipher-pragmas.test @@ -377,6 +377,16 @@ if_built_with_commoncrypto verify-default-cipher { db close file delete -force test.db +if_built_with_nss verify-default-cipher { + sqlite_orig db test.db + execsql { + PRAGMA key='test'; + PRAGMA cipher; + } +} {aes-256-cbc} +db close +file delete -force test.db + do_test verify-cipher_settings_default { sqlite_orig db test.db execsql { diff --git a/test/sqlcipher.tcl b/test/sqlcipher.tcl index 3c48d67..4129dca 100644 --- a/test/sqlcipher.tcl +++ b/test/sqlcipher.tcl @@ -85,6 +85,12 @@ proc if_built_with_commoncrypto {name cmd expected} { } } +proc if_built_with_nss {name cmd expected} { + if {[get_cipher_provider] == "nss"} { + do_test $name $cmd $expected + } +} + proc cmpFilesChunked {file1 file2 {chunksize 16384}} { set f1 [open $file1]; fconfigure $f1 -translation binary set f2 [open $file2]; fconfigure $f2 -translation binary diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl index 5a1de8b..7f95c2f 100644 --- a/tool/mksqlite3c.tcl +++ b/tool/mksqlite3c.tcl @@ -300,6 +300,7 @@ foreach file { crypto.c crypto_impl.c crypto_libtomcrypt.c + crypto_nss.c crypto_openssl.c crypto_cc.c