flesh out openssl code; still needs more work
authorDan Mills <thunder@mozilla.com>
Tue, 19 Feb 2008 11:39:39 -0800
changeset 44357 37f9015f4be4c7c9d825ec0e89f7945317f5e3d5
parent 44356 938ff0803ee94c4a27a5cbd489e6992a1d40cefa
child 44358 cac683fafd20f6fc5828d2dbbc27672ee8cd6ac5
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
flesh out openssl code; still needs more work
services/sync/modules/crypto.js
services/sync/modules/util.js
--- a/services/sync/modules/crypto.js
+++ b/services/sync/modules/crypto.js
@@ -87,17 +87,17 @@ WeaveCrypto.prototype = {
 
   _init: function Crypto__init() {
     this._log = Log4Moz.Service.getLogger("Service." + this._logName);
     let branch = Cc["@mozilla.org/preferences-service;1"]
       .getService(Ci.nsIPrefBranch2);
     branch.addObserver("extensions.weave.encryption", this, false);
   },
 
-  _openssl: function Crypto__openssl(op, algorithm, input, password) {
+  _openssl: function Crypto__openssl() {
     let extMgr = Components.classes["@mozilla.org/extensions/manager;1"]
       .getService(Components.interfaces.nsIExtensionManager);
     let loc = extMgr.getInstallLocation("{340c2bbc-ce74-4362-90b5-7c26312808ef}");
 
     let wrap = loc.getItemLocation("{340c2bbc-ce74-4362-90b5-7c26312808ef}");
     wrap.append("openssl");
     let bin;
 
@@ -113,52 +113,202 @@ WeaveCrypto.prototype = {
       wrap.append("unix");
       wrap.append("exec.sh");
       bin = "openssl";
       break;
     default:
       throw "encryption not supported on this platform: " + os;
     }
 
+    let args = [wrap, Utils.getTmp().path, bin];
+    args = args.concat(arguments);
+    return Utils.runCmd.apply(null, args);
+  },
+
+  _opensslPBE: function Crypto__openssl(op, algorithm, input, password) {
     let inputFile = Utils.getTmp("input");
     let [inputFOS] = Utils.open(inputFile, ">");
     inputFOS.write(input, input.length);
     inputFOS.close();
 
     let outputFile = Utils.getTmp("output");
     if (outputFile.exists())
       outputFile.remove(false);
 
     // nsIProcess doesn't support stdin, so we write a file instead
     let passFile = Utils.getTmp("pass");
     let [passFOS] = Utils.open(passFile, ">", PERMS_PASSFILE);
     passFOS.write(password, password.length);
     passFOS.close();
 
     try {
-      this._log.debug("Running command: " + wrap.path + " " +
-                      Utils.getTmp().path + " " + bin + " " + algorithm + " " +
-                      op + "-a -salt -in input -out output -pass file:pass");
-      Utils.runCmd(wrap, Utils.getTmp().path, bin, algorithm, op, "-a", "-salt",
-                   "-in", "input", "-out", "output", "-pass", "file:pass");
+      this._openssl(algorithm, op, "-a", "-salt", "-in", "input",
+                    "-out", "output", "-pass", "file:pass");
+      // FIXME: check rv
+
     } catch (e) {
       throw e;
+
     } finally {
-      //passFile.remove(false);
-      //inputFile.remove(false);
+      passFile.remove(false);
+      inputFile.remove(false);
     }
 
     let [outputFIS] = Utils.open(outputFile, "<");
     let ret = Utils.readStream(outputFIS);
     outputFIS.close();
-    //outputFile.remove(false);
+    outputFile.remove(false);
+
+    return ret;
+  },
+
+  // generates a random string that can be used as a passphrase
+  _opensslRand: function Crypto__opensslRand(length) {
+    if (!length)
+      length = 256;
+
+    let outputFile = Utils.getTmp("output");
+    if (outputFile.exists())
+      outputFile.remove(false);
+
+    let rv = this._openssl("rand", "-base64", "-out", "output", length);
+    // FIXME: check rv
+
+    let [outputFIS] = Utils.open(outputFile, "<");
+    let ret = Utils.readStream(outputFIS);
+    outputFIS.close();
+    outputFile.remove(false);
 
     return ret;
   },
 
+  // generates an rsa public/private key pair, with the private key encrypted
+  _opensslRSAKeyGen: function Crypto__opensslRSAKeyGen(password, algorithm, bits) {
+    if (!algorithm)
+      algorithm = "aes-256-cbc";
+    if (!bits)
+      bits = "2048";
+
+    let privKeyF = Utils.getTmp("privkey.pem");
+    if (privKeyF.exists())
+      privKeyF.remove(false);
+
+    let rv = this._openssl("genrsa", "-out", "privkey.pem", bits);
+    // FIXME: check rv
+
+    let pubKeyF = Utils.getTmp("pubkey.pem");
+    if (pubKeyF.exists())
+      pubKeyF.remove(false);
+
+    rv = this._openssl("rsa", "-in", "privkey.pem", "-out", "pubkey.pem",
+                       "-outform", "PEM", "-pubout");
+    // FIXME: check rv
+
+    let cryptedKeyF = Utils.getTmp("enckey.pem");
+    if (cryptedKeyF.exists())
+      cryptedKeyF.remove(false);
+
+    // nsIProcess doesn't support stdin, so we write a file instead
+    let passFile = Utils.getTmp("pass");
+    let [passFOS] = Utils.open(passFile, ">", PERMS_PASSFILE);
+    passFOS.write(password, password.length);
+    passFOS.close();
+
+    try {
+      rv = this._openssl("pkcs8", "-in", "privkey.pem", "-out", "enckey.pem",
+                         "-topk8", "-v2", algorithm, "-pass", "file:pass");
+      // FIXME: check rv
+    } catch (e) {
+      throw e;
+    } finally {
+      passFile.remove(false);
+      privKeyF.remove(false);
+    }
+
+    let [cryptedKeyFIS] = Utils.open(cryptedKeyF, "<");
+    let cryptedKey = Utils.readStream(cryptedKeyFIS);
+    cryptedKeyFIS.close();
+    cryptedKey.remove(false);
+
+    let [pubKeyFIS] = Utils.open(pubKeyF, "<");
+    let pubKey = Utils.readStream(pubKeyFIS);
+    pubKeyFIS.close();
+    pubKeyF.remove(false);
+
+    return [cryptedKey, pubKey];
+  },
+
+  // returns 'input' encrypted with the 'pubkey' public RSA key
+  _opensslRSAEncrypt: function Crypto__opensslRSAEncrypt(input, pubkey) {
+    let inputFile = Utils.getTmp("input");
+    let [inputFOS] = Utils.open(inputFile, ">");
+    inputFOS.write(input, input.length);
+    inputFOS.close();
+
+    let keyFile = Utils.getTmp("key");
+    let [keyFOS] = Utils.open(keyFile, ">");
+    keyFOS.write(pubkey, pubkey.length);
+    keyFOS.close();
+
+    let outputFile = Utils.getTmp("output");
+    if (outputFile.exists())
+      outputFile.remove(false);
+
+    let rv = this._openssl("rsautl", "-encrypt", "-pubin", "-inkey", "key",
+                           "-in", "input", "-out", "output");
+    // FIXME: check rv
+
+    let [outputFIS] = Utils.open(outputFile, "<");
+    let output = Utils.readStream(outpusFIS);
+    outputFIS.close();
+    outputFile.remove(false);
+
+    return output;
+  },
+
+  // returns 'input' decrypted with the 'privkey' private RSA key and password
+  _opensslRSADecrypt: function Crypto__opensslRSADecrypt(input, privkey, password) {
+    let inputFile = Utils.getTmp("input");
+    let [inputFOS] = Utils.open(inputFile, ">");
+    inputFOS.write(input, input.length);
+    inputFOS.close();
+
+    let keyFile = Utils.getTmp("key");
+    let [keyFOS] = Utils.open(keyFile, ">");
+    keyFOS.write(privkey, privkey.length);
+    keyFOS.close();
+
+    let outputFile = Utils.getTmp("output");
+    if (outputFile.exists())
+      outputFile.remove(false);
+
+    // nsIProcess doesn't support stdin, so we write a file instead
+    let passFile = Utils.getTmp("pass");
+    let [passFOS] = Utils.open(passFile, ">", PERMS_PASSFILE);
+    passFOS.write(password, password.length);
+    passFOS.close();
+
+    try {
+      let rv = this._openssl("rsautl", "-decrypt", "-inkey", "key", "-pass",
+                             "file:pass", "-in", "input", "-out", "output");
+      // FIXME: check rv
+    } catch(e) {
+      throw e;
+    } finally {
+      passFile.remove(false);
+    }
+
+    let [outputFIS] = Utils.open(outputFile, "<");
+    let output = Utils.readStream(outpusFIS);
+    outputFIS.close();
+    outputFile.remove(false);
+
+    return output;
+  },
+
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupports]),
 
   // nsIObserver
 
   observe: function Sync_observe(subject, topic, data) {
     switch (topic) {
     case "extensions.weave.encryption": {
       let branch = Cc["@mozilla.org/preferences-service;1"]
@@ -221,17 +371,17 @@ WeaveCrypto.prototype = {
         gen.close();
       } break;
 
       case "aes-128-cbc":
       case "aes-192-cbc":
       case "aes-256-cbc":
       case "bf-cbc":
       case "des-ede3-cbc":
-        ret = this._openssl("-e", algorithm, data, identity.password);
+        ret = this._opensslPBE("-e", algorithm, data, identity.password);
         break;
 
       default:
         throw "Unknown encryption algorithm: " + algorithm;
       }
 
       if (algorithm != "none")
         this._log.debug("Done encrypting data");
@@ -277,17 +427,17 @@ WeaveCrypto.prototype = {
         gen.close();
       } break;
 
       case "aes-128-cbc":
       case "aes-192-cbc":
       case "aes-256-cbc":
       case "bf-cbc":
       case "des-ede3-cbc":
-        ret = this._openssl("-d", algorithm, data, identity.password);
+        ret = this._opensslPBE("-d", algorithm, data, identity.password);
         break;
 
       default:
         throw "Unknown encryption algorithm: " + algorithm;
       }
 
       if (algorithm != "none")
         this._log.debug("Done decrypting data");
@@ -296,10 +446,26 @@ WeaveCrypto.prototype = {
       this._log.error("Exception caught: " + (e.message? e.message : e));
 
     } finally {
       timer = null;
       Utils.generatorDone(this, self, onComplete, ret);
       yield; // onComplete is responsible for closing the generator
     }
     this._log.warn("generator not properly closed");
+  },
+
+  PBEkeygen: function Crypto_PBEkeygen() {
+    return this._opensslRand();
+  },
+
+  RSAkeygen: function Crypto_RSAkeygen(password) {
+    return this._opensslRSAKeyGen(password);
+  },
+
+  RSAencrypt: function Crypto_RSAencrypt(data, key) {
+    return this._opensslRSAEncrypt(data, key);
+  },
+
+  RSAdecrypt: function Crypto_RSAdecrypt(data, key, password) {
+    return this._opensslRSADecrypt(data, key, password);
   }
 };
--- a/services/sync/modules/util.js
+++ b/services/sync/modules/util.js
@@ -173,16 +173,19 @@ let Utils = {
     } else {
       binary = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
       binary.initWithPath(args.shift());
     }
 
     var p = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
     p.init(binary);
 
+    let log = Log4Moz.Service.getLogger("Service.Util");
+    log.debug("Running command: " + binary.path + " " + args.join(" "));
+
     p.run(true, args, args.length);
     return p.exitValue;
   },
 
   getTmp: function Weave_getTmp(name) {
     let ds = Cc["@mozilla.org/file/directory_service;1"].
       getService(Ci.nsIProperties);