bug 1334321 - add test for nsISecretDecoderRing using existing key database r?Cykesiopka draft
authorDavid Keeler <dkeeler@mozilla.com>
Wed, 25 Jan 2017 15:34:21 -0800
changeset 466995 a6203470f6188ed6acca83bbe14d2ae341f7c2d4
parent 465527 8ff550409e1d1f8b54f6f7f115545dbef857be0b
child 543596 39f04407594eccac4126284fc1b6cb081e9096c4
push id43079
push userdkeeler@mozilla.com
push dateFri, 27 Jan 2017 00:15:35 +0000
reviewersCykesiopka
bugs1334321
milestone54.0a1
bug 1334321 - add test for nsISecretDecoderRing using existing key database r?Cykesiopka MozReview-Commit-ID: Fk8bC78QJzo
security/manager/ssl/tests/unit/test_sdr_preexisting.js
security/manager/ssl/tests/unit/test_sdr_preexisting/key3.db
security/manager/ssl/tests/unit/test_sdr_preexisting/key4.db
security/manager/ssl/tests/unit/xpcshell.ini
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sdr_preexisting.js
@@ -0,0 +1,214 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"use strict";
+
+// Tests that the SDR implementation is able to decrypt strings encrypted using
+// a preexisting NSS key database. Creating the database is straight-forward:
+// simply run Firefox (or xpcshell) and encrypt something using
+// nsISecretDecoderRing (e.g. by saving a password or directly using the
+// interface). The resulting key3.db file (in the profile directory) now
+// contains the private key used to encrypt the data.
+// "Upgrading" a key3.db with certutil to use on Android appears not to work.
+// Because the keys have to be the same for this test to work the way it does,
+// the key from key3.db must be extracted and added to a new key4.db. This can
+// be done with NSS' PK11_* APIs like so (although note that the following code
+// is not guaranteed to compile or work, but is more of a guideline for how to
+// do this in the future if necessary):
+//
+// #include <stdio.h>
+//
+// #include "nss.h"
+// #include "pk11pub.h"
+// #include "prerror.h"
+// #include "secerr.h"
+//
+// void printPRError(const char* message) {
+//   fprintf(stderr, "%s: %s\n", message, PR_ErrorToString(PR_GetError(), 0));
+// }
+//
+// int main(int argc, char* argv[]) {
+//   if (NSS_Initialize(".", "", "", "", NSS_INIT_NOMODDB | NSS_INIT_NOROOTINIT)
+//         != SECSuccess) {
+//     printPRError("NSS_Initialize failed");
+//     return 1;
+//   }
+//
+//   PK11SlotInfo* slot = PK11_GetInternalKeySlot();
+//   if (!slot) {
+//     printPRError("PK11_GetInternalKeySlot failed");
+//     return 1;
+//   }
+//
+//   // Create a key to wrap the SDR key to export it.
+//   unsigned char wrappingKeyIDBytes[] = { 0 };
+//   SECItem wrappingKeyID = {
+//     siBuffer,
+//     wrappingKeyIDBytes,
+//     sizeof(wrappingKeyIDBytes)
+//   };
+//   PK11SymKey* wrappingKey = PK11_TokenKeyGen(slot, CKM_DES3_CBC, 0, 0,
+//                                              &wrappingKeyID, PR_FALSE, NULL);
+//   if (!wrappingKey) {
+//     printPRError("PK11_TokenKeyGen failed");
+//     return 1;
+//   }
+//
+//   // This is the magic identifier NSS uses for the SDR key.
+//   unsigned char sdrKeyIDBytes[] = {
+//     0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+//     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+//   };
+//   SECItem sdrKeyID = { siBuffer, sdrKeyIDBytes, sizeof(sdrKeyIDBytes) };
+//   PK11SymKey* sdrKey = PK11_FindFixedKey(slot, CKM_DES3_CBC, &sdrKeyID,
+//                                          NULL);
+//   if (!sdrKey) {
+//     printPRError("PK11_FindFixedKey failed");
+//     return 1;
+//   }
+//
+//   // Wrap the SDR key.
+//   unsigned char wrappedKeyBuf[1024];
+//   SECItem wrapped = { siBuffer, wrappedKeyBuf, sizeof(wrappedKeyBuf) };
+//   if (PK11_WrapSymKey(CKM_DES3_ECB, NULL, wrappingKey, sdrKey, &wrapped)
+//         != SECSuccess) {
+//     printPRError("PK11_WrapSymKey failed");
+//     return 1;
+//   }
+//
+//   // Unwrap the SDR key (NSS considers the SDR key "sensitive" and so won't
+//   // just export it as raw key material - we have to export it and then
+//   // re-import it as non-sensitive to get that data.
+//   PK11SymKey* unwrapped = PK11_UnwrapSymKey(wrappingKey, CKM_DES3_ECB, NULL,
+//                                             &wrapped, CKM_DES3_CBC,
+//                                             CKA_ENCRYPT, 0);
+//   if (!unwrapped) {
+//     printPRError("PK11_UnwrapSymKey failed");
+//     return 1;
+//   }
+//   if (PK11_ExtractKeyValue(unwrapped) != SECSuccess) {
+//     printPRError("PK11_ExtractKeyValue failed");
+//     return 1;
+//   }
+//   SECItem* keyData = PK11_GetKeyData(unwrapped);
+//   if (!keyData) {
+//     printPRError("PK11_GetKeyData failed");
+//     return 1;
+//   }
+//   for (int i = 0; i < keyData->len; i++) {
+//     printf("0x%02hhx, ", keyData->data[i]);
+//   }
+//   printf("\n");
+//
+//   PK11_FreeSymKey(unwrapped);
+//   PK11_FreeSymKey(sdrKey);
+//   PK11_FreeSymKey(wrappingKey);
+//   PK11_FreeSlot(slot);
+//
+//   if (NSS_Shutdown() != SECSuccess) {
+//     printPRError("NSS_Shutdown failed");
+//     return 1;
+//   }
+//   return 0;
+// }
+//
+// The output of compiling and running the above should be the bytes of the SDR
+// key. Given that, create a key4.db with an empty password using
+// `certutil -N -d sql:.` and then compile and run the following:
+//
+// #include <stdio.h>
+//
+// #include "nss.h"
+// #include "pk11pub.h"
+// #include "prerror.h"
+// #include "secerr.h"
+// #include "secmod.h"
+//
+// void printPRError(const char* message) {
+//   fprintf(stderr, "%s: %s\n", message, PR_ErrorToString(PR_GetError(), 0));
+// }
+//
+// int main(int argc, char* argv[]) {
+//   if (NSS_Initialize("sql:.", "", "", "",
+//                      NSS_INIT_NOMODDB | NSS_INIT_NOROOTINIT) != SECSuccess) {
+//     printPRError("NSS_Initialize failed");
+//     return 1;
+//   }
+//
+//   PK11SlotInfo* slot = PK11_GetInternalKeySlot();
+//   if (!slot) {
+//     printPRError("PK11_GetInternalKeySlot failed");
+//     return 1;
+//   }
+//
+//   // These are the bytes of the SDR key from the previous step:
+//   unsigned char keyBytes[] = {
+//     0x70, 0xab, 0xea, 0x1f, 0x8f, 0xe3, 0x4a, 0x7a, 0xb5, 0xb0, 0x43, 0xe5,
+//     0x51, 0x83, 0x86, 0xe5, 0xb3, 0x43, 0xa8, 0x1f, 0xc1, 0x57, 0x86, 0x46
+//   };
+//   SECItem keyItem = { siBuffer, keyBytes, sizeof(keyBytes) };
+//   PK11SymKey* key = PK11_ImportSymKey(slot, CKM_DES3_CBC, PK11_OriginUnwrap,
+//                                       CKA_ENCRYPT, &keyItem, NULL);
+//   if (!key) {
+//     printPRError("PK11_ImportSymKey failed");
+//     return 1;
+//   }
+//
+//   PK11_FreeSymKey(key);
+//   PK11_FreeSlot(slot);
+//
+//   if (NSS_Shutdown() != SECSuccess) {
+//     printPRError("NSS_Shutdown failed");
+//     return 1;
+//   }
+//   return 0;
+// }
+//
+// This should create a key4.db file with the SDR key. (Note however that this
+// does not set the magic key ID for the SDR key. Currently this is not a
+// problem because the NSS implementation that backs the SDR simply tries all
+// applicable keys it has when decrypting, so this still works.)
+
+function run_test() {
+  const isAndroid = AppConstants.platform == "android";
+  const keyDBName = isAndroid ? "key4.db" : "key3.db";
+  let profile = do_get_profile();
+  let keyDBFile = do_get_file(`test_sdr_preexisting/${keyDBName}`);
+  keyDBFile.copyTo(profile, keyDBName);
+
+  let sdr = Cc["@mozilla.org/security/sdr;1"]
+              .getService(Ci.nsISecretDecoderRing);
+
+  let testcases = [
+    // a full padding block
+    { ciphertext: "MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECGeDHwVfyFqzBBAYvqMq/kDMsrARVNdC1C8d",
+      plaintext: "password" },
+    // 7 bytes of padding
+    { ciphertext: "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECCAzLDVmYG2/BAh3IoIsMmT8dQ==",
+      plaintext: "a" },
+    // 6 bytes of padding
+    { ciphertext: "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECPN8zlZzn8FdBAiu2acpT8UHsg==",
+      plaintext: "bb" },
+    // 1 byte of padding
+    { ciphertext: "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECD5px1eMKkJQBAgUPp35GlrDvQ==",
+      plaintext: "!seven!" },
+    // 2 bytes of padding
+    { ciphertext: "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECMh0hLtKDyUdBAixw9UZsMt+vA==",
+      plaintext: "sixsix" },
+    // long plaintext requiring more than two blocks
+    { ciphertext: "MFoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECDRX1qi+/FX1BDATFIcIneQjvBuq3wdFxzllJt2VtUD69ACdOKAXH3eA87oHDvuHqOeCDwRy4UzoG5s=",
+      plaintext: "thisismuchlongerandsotakesupmultipleblocks" },
+    // this differs from the previous ciphertext by one bit and demonstrates
+    // that this implementation does not enforce message integrity
+    { ciphertext: "MFoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECDRX1qi+/FX1BDAbFIcIneQjvBuq3wdFxzllJt2VtUD69ACdOKAXH3eA87oHDvuHqOeCDwRy4UzoG5s=",
+      plaintext: "nnLbuwLRkhlongerandsotakesupmultipleblocks" },
+  ];
+
+  for (let testcase of testcases) {
+    let decrypted = sdr.decryptString(testcase.ciphertext);
+    equal(decrypted, testcase.plaintext,
+          "decrypted ciphertext should match expected plaintext");
+  }
+}
new file mode 100644
index 0000000000000000000000000000000000000000..b81b629dfa071fef21e019ce6fe749052ee9a7ca
GIT binary patch
literal 16384
zc%1Fn|0~>a90%~%`}6s@ZXJCtSxvHLCEudEU49ulNsS}UR(GY8yCcT!PIp&GR}>Zr
zk*rQhLQ-0(xpkWSAX<L7)@rvOlrpJh8q$3<XAbwn_6KmO=cAszp7s9W{r>g+BH2R|
z5phJq7b0c2QY;apn~*$GxX9$M=k*Z@SAERp-lZE{BzI2EqH!w#000000Kokrqcc$w
ze~KT(1#w*L3Fj{W00000007*Lxly6D;Dz#OL10>AThMnGDZcs}tah`>n(Z`MU3>yF
z_%ceH(*{(YER9F=tSs?xW!XiF%-)jTU>u8$Vf;3CoIjuJ)t!U}Kh@^7y=*Z#ovn69
zUACpM-qOq)>K)G4b{j{(#TxNdIMLoj000000002oQ#>(Jbq%P}W&eK6Rf$5vdc}IK
zv>(jr^et_D?2n%^KG{=nj0v@aeHCxMz16PukI1Xl?^nO;{Cc-{G3RmFYWm9Q#Dw+F
zo_VXzzqGGS{Urs}N0poH$yr9-=kuPCq^d{79p#~6BONqNdJ4_&Y|kajOjGfLjh&$%
eQ%AFMDUfpEkp6!m3q^a=0RR9100000x9$>vYT$|h
new file mode 100644
index 0000000000000000000000000000000000000000..ba1c88ae8de5988dba863dd31f2e1f26e29317b1
GIT binary patch
literal 36864
zc%1FsUuauZ90%}olQeD8KPj~%4h`J+FdD6RbN`$s=%j6y&SGr4+F>_^aMRpDY?{s{
zT@|TN7%hnS;)9POI($&@O?+|#buXg$AfgXG_*QWGFk!2#L+|;WW~b>U6JeB<?`QY^
z^1Jt(^Si(1ZryGN?eixJm4>IESzKCh8@k1MnWnK}U1y91{XN2zu)l|P9c%3Fg6#2|
zOWm<8))Ad$UD4Q=onLpZc0L)s9-WO|{txQ`0002sK2dFNk0%n^+G7p3RP`3ThI`Cy
zxXss&+;o0qCa=$oj27~G^Q=BlDeF_y<IjyupVFVrpVCiGjGs7}*C!|R+~mYqVLUgZ
zPv@r!Bf0#b?*FTGAuV6b)Ds=ycrKx_O0Dd@T7RYLpDnt}jYT>xHtQGD%`7q+4#(q3
z?M0d}H}6);wR(MOsdCP3c<npQjZYlQ&+0TNT2K@80XJ=!so^%N@dDMTx+Slwri5E+
z(BN$~tkhU29DgXO6=mDYrI)?AhMLmWQe$WM4xOY^xnMYcxcSjk>h)!BNzDnhl#!#i
zRmr4N8(eFTCzIOSLv+j3u*w20w{3TUyWhBx(aGtVLESa{-M??H>F<`v>4Z3w7N;`e
zFe45#bSS1|Y!Mt0xClccWJJh{;EGW4LzYSzGEzmuq$sarQ*<bDibE7L6tfgviY1D3
z)R=5f_D8uXbE<4oWs@qKRN17;CRH}6vPG*bT4m8Hi&j~*iqj~kQBI?rM!6iNnw)A5
z(aemD^bXQTY8o;ZYfb9bHhd<vypz&<O|vbUZP9GokhA4PdB&n>Q*<bbYuj{Ho33ip
zeQ{{5Lu(yFo)K&3Jjb#vC#_ObrMo8BDs@!KRXU{7%&nBGWn3-eY8h9{cyk$7?Qqo&
zSM6}s4p;4P)ecwfaMg~VW>xB{w4~BGm6lcNsr1aPlzt<~w4~Z8iFV44Ym`g$j%N&_
z-d~<gS%Gl;c=F#q`Kdpp#k9)Bvwes7Jl*Z@T8y#S{{D3V00000003}Lt*vS+q5htp
zKDIW{I?~SudkFJ?_4Vyp*S|CJ|2-&QLYj7goi+-_K)CbK^=H<vcWYg3TF^Kg>VN62
zFW$Jh8U6jx`HP#cWxs1Xa%CeQ(6o^L>t~m0J=x*ZSF3@uzg>NI{>G7wbF~R?^Yd@}
zZ(QvOZ2M&oZM9xaYw|DVZ{qrz=AVs<vDg-i?U@e%00000008&D_FyO?z5*ei|F>Ce
zd%rsZ00000004k{AsA#4JpTg#00000032-U`TsBGZvX%Q00000aL|N<eUS_7w0!=*
z!u$;Y00000000hv2ZDo<z26p4e*gdXn7;u4000000Konz2HFz#TE|(pUSC;UDu47&
zXyxef-bar9^nq3Tc<TIU=MUc-Pu<~12b5o5Pg;vV|MAPie}AJteR;)x|I?cI(E+{J
roI>Km>X^eXU3>G#`RFI||NsAF{ssU50000000&1`FrhWSpb+>6mAUB-
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -21,16 +21,17 @@ support-files =
   test_intermediate_basic_usage_constraints/**
   test_keysize/**
   test_keysize_ev/**
   test_name_constraints/**
   test_ocsp_fetch_method/**
   test_ocsp_url/**
   test_onecrl/**
   test_pinning_dynamic/**
+  test_sdr_preexisting/**
   test_signed_apps/**
   test_signed_dir/**
   test_startcom_wosign/**
   test_validity/**
   tlsserver/**
 
 [test_add_preexisting_cert.js]
 [test_baseline_requirements_subject_common_name.js]
@@ -113,16 +114,17 @@ run-sequentially = hardcoded ports
 [test_pinning.js]
 run-sequentially = hardcoded ports
 # This test can take longer than 300 seconds on B2G emulator debug builds, so
 # give it enough time to finish. See bug 1081128.
 requesttimeoutfactor = 2
 [test_pinning_dynamic.js]
 [test_pinning_header_parsing.js]
 [test_sdr.js]
+[test_sdr_preexisting.js]
 [test_session_resumption.js]
 run-sequentially = hardcoded ports
 [test_signed_apps.js]
 [test_signed_apps-marketplace.js]
 [test_signed_dir.js]
 tags = addons psm
 [test_sss_enumerate.js]
 [test_sss_eviction.js]