diff --git a/lib/secrets.js b/lib/secrets.js
index 8ba989a25eee5c7536a765d1c5fe1e7c89533b9a..b252e803b35a163ec9fc5a2f99dfe210c3bda300 100644
--- a/lib/secrets.js
+++ b/lib/secrets.js
@@ -38,32 +38,44 @@ path = require('path'),
 fs = require('fs'),
 jwk = require('jwcrypto/jwk'),
 jwt = require('jwcrypto/jwt'),
-Buffer = require('buffer').Buffer;
+Buffer = require('buffer').Buffer,
+crypto = require('crypto');
 
-var devRandom = fs.openSync('/dev/urandom', 'r');
+// make this async capable
+function bytesToChars(buf) {
+  var str = "";
+  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 
-function randomBytes(length) {
-  var buf = new Buffer(length);
-  fs.readSync(devRandom, buf, 0, buf.length, 0);
-  return buf;
+  // yes, we are biasing the output here a bit.
+  // I'm ok with that. We can improve this over time.
+  for (var i=0; i < buf.length; i++) {
+    str += alphabet.charAt(buf[i] % alphabet.length);
+  }
+  
+  return str;
 }
 
-exports.randomBytes = randomBytes;
+exports.generate = function(chars, cb) {
+  if (cb) {
+    crypto.randomBytes(chars, function(ex, buf) {
+      cb(bytesToChars(buf));
+    });
+  } else {
+    return bytesToChars(crypto.randomBytes(chars));
+  }
+};
 
-exports.generate = function(chars) {
+// we don't bother to make this async, cause it's not needed
+exports.weakGenerate = function(chars) {
   var str = "";
   const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 
-  var bytes = randomBytes(chars);
-
-  // yes, we are biasing the output here a bit.
-  // I'm ok with that. We can improve this over time.
   for (var i=0; i < chars; i++) {
-    str += alphabet.charAt(bytes[i] % alphabet.length);
+    str += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
   }
 
   return str;
-}
+};
 
 // functions to set defaults
 
diff --git a/tests/secrets-test.js b/tests/secrets-test.js
new file mode 100644
index 0000000000000000000000000000000000000000..1d5ef05caa73814195138b36420fa05119935d3b
--- /dev/null
+++ b/tests/secrets-test.js
@@ -0,0 +1,89 @@
+#!/usr/bin/env node
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla BrowserID.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+require('./lib/test_env.js');
+
+const assert = require('assert'),
+vows = require('vows'),
+secrets = require('../lib/secrets');
+
+var suite = vows.describe('secrets');
+
+var LENGTH = 10;
+
+function make_secrets_batch(rand_func) {
+  return {
+    "generate a secret": {
+      topic: function() {
+        return rand_func(LENGTH);
+      },
+      "of proper length" : function(err, s) {
+        assert.equal(s.length, LENGTH);
+      }
+    },
+    "two secrets": {
+      topic: function() {
+        return {
+          s1: rand_func(LENGTH),
+          s2: rand_func(LENGTH)
+        };
+      },
+      "are not equal" : function(err, the_secrets) {
+        assert.notEqual(the_secrets.s1, the_secrets.s2);
+      }
+    }  
+  };
+};
+
+// check that we can generate random secrets
+suite.addBatch(make_secrets_batch(secrets.generate));
+suite.addBatch(make_secrets_batch(secrets.weakGenerate));
+
+// and the async one
+suite.addBatch({
+  "generate a secret": {
+    topic: function() {
+      secrets.generate(LENGTH, this.callback);
+    },
+    "of proper length" : function(s, err) {
+      assert.equal(s.length, LENGTH);
+    }
+  }  
+});
+// run or export the suite.
+if (process.argv[1] === __filename) suite.run();
+else suite.export(module);