From 391fb6f81945f7d2ac09c4437fdac21691fdd07e Mon Sep 17 00:00:00 2001 From: David Dahl Date: Fri, 15 Feb 2013 15:38:15 -0800 Subject: [PATCH] Bug 440046 - expose secure PRNG in the DOM (window.crypto.getRandomValues) r=cviecco r=bsmith --- dom/base/Crypto.cpp | 72 +++++++ dom/interfaces/base/nsIDOMCrypto.idl | 3 +- dom/interfaces/base/nsIDOMCryptoLegacy.idl | 4 +- dom/tests/mochitest/crypto/Makefile.in | 1 + .../crypto/test_getRandomValues.html | 204 ++++++++++++++++++ security/manager/ssl/src/Makefile.in | 1 + security/manager/ssl/src/nsCrypto.cpp | 6 + .../manager/ssl/src/nsRandomGenerator.cpp | 10 +- testing/mochitest/b2g.json | 3 + 9 files changed, 301 insertions(+), 3 deletions(-) create mode 100644 dom/tests/mochitest/crypto/test_getRandomValues.html diff --git a/dom/base/Crypto.cpp b/dom/base/Crypto.cpp index c9b80e438df..48d049d22d0 100644 --- a/dom/base/Crypto.cpp +++ b/dom/base/Crypto.cpp @@ -3,7 +3,13 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "Crypto.h" #include "nsIDOMClassInfo.h" +#include "DOMError.h" #include "nsString.h" +#include "nsIRandomGenerator.h" +#include "jsapi.h" +#include "jsfriendapi.h" + +using namespace js::ArrayBufferView; namespace mozilla { namespace dom { @@ -27,6 +33,72 @@ Crypto::~Crypto() MOZ_COUNT_DTOR(Crypto); } +NS_IMETHODIMP +Crypto::GetRandomValues(const jsval& aData, JSContext *cx, jsval* _retval) +{ + // Make sure this is a JavaScript object + if (!aData.isObject()) { + return NS_ERROR_DOM_NOT_OBJECT_ERR; + } + + JSObject* view = &aData.toObject(); + + // Make sure this object is an ArrayBufferView + if (!JS_IsTypedArrayObject(view)) { + return NS_ERROR_DOM_TYPE_MISMATCH_ERR; + } + + // Throw if the wrong type of ArrayBufferView is passed in + // (Part of the Web Crypto API spec) + switch (JS_GetArrayBufferViewType(view)) { + case TYPE_INT8: + case TYPE_UINT8: + case TYPE_UINT8_CLAMPED: + case TYPE_INT16: + case TYPE_UINT16: + case TYPE_INT32: + case TYPE_UINT32: + break; + default: + return NS_ERROR_DOM_TYPE_MISMATCH_ERR; + } + + uint32_t dataLen = JS_GetTypedArrayByteLength(view); + + if (dataLen == 0) { + NS_WARNING("ArrayBufferView length is 0, cannot continue"); + return NS_OK; + } else if (dataLen > 65536) { + return NS_ERROR_DOM_QUOTA_EXCEEDED_ERR; + } + + nsCOMPtr randomGenerator; + nsresult rv; + randomGenerator = + do_GetService("@mozilla.org/security/random-generator;1", &rv); + if (NS_FAILED(rv)) { + NS_WARNING("unable to continue without random number generator"); + return rv; + } + + void *dataptr = JS_GetArrayBufferViewData(view); + NS_ENSURE_TRUE(dataptr, NS_ERROR_FAILURE); + + unsigned char* data = + static_cast(dataptr); + + uint8_t *buf; + rv = randomGenerator->GenerateRandomBytes(dataLen, &buf); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + + memcpy(data, buf, dataLen); + NS_Free(buf); + + *_retval = OBJECT_TO_JSVAL(view); + + return NS_OK; +} + #ifndef MOZ_DISABLE_CRYPTOLEGACY // Stub out the legacy nsIDOMCrypto methods. The actual // implementations are in security/manager/ssl/src/nsCrypto.{cpp,h} diff --git a/dom/interfaces/base/nsIDOMCrypto.idl b/dom/interfaces/base/nsIDOMCrypto.idl index 48be1eff1bd..3a19ae76ed1 100644 --- a/dom/interfaces/base/nsIDOMCrypto.idl +++ b/dom/interfaces/base/nsIDOMCrypto.idl @@ -5,7 +5,8 @@ #include "nsISupports.idl" -[scriptable, uuid(eadb45d6-aec2-4b70-95f4-ffdf1f86738f)] +[scriptable, uuid(a0a3bc68-eab3-4e66-b5cb-b1d86765119c)] interface nsIDOMCrypto : nsISupports { + [implicit_jscontext] jsval getRandomValues(in jsval aData); }; diff --git a/dom/interfaces/base/nsIDOMCryptoLegacy.idl b/dom/interfaces/base/nsIDOMCryptoLegacy.idl index f8cd5ee791d..2024ac2c0fa 100644 --- a/dom/interfaces/base/nsIDOMCryptoLegacy.idl +++ b/dom/interfaces/base/nsIDOMCryptoLegacy.idl @@ -7,7 +7,7 @@ interface nsIDOMCRMFObject; -[scriptable, uuid(b50312fa-06ec-460c-b5e9-5dbb009eb457)] +[scriptable, uuid(e1df1d4d-41ef-4225-934a-107c5d612686)] interface nsIDOMCrypto : nsISupports { readonly attribute DOMString version; @@ -23,4 +23,6 @@ interface nsIDOMCrypto : nsISupports in DOMString caOption /* ... */); void logout(); void disableRightClick(); + + [implicit_jscontext] jsval getRandomValues(in jsval aData); }; diff --git a/dom/tests/mochitest/crypto/Makefile.in b/dom/tests/mochitest/crypto/Makefile.in index 3f5cfa22067..494c85ab380 100644 --- a/dom/tests/mochitest/crypto/Makefile.in +++ b/dom/tests/mochitest/crypto/Makefile.in @@ -12,6 +12,7 @@ include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk _TEST_FILES = \ + test_getRandomValues.html \ $(NULL) ifndef MOZ_DISABLE_CRYPTOLEGACY diff --git a/dom/tests/mochitest/crypto/test_getRandomValues.html b/dom/tests/mochitest/crypto/test_getRandomValues.html new file mode 100644 index 00000000000..bc1f037d2e2 --- /dev/null +++ b/dom/tests/mochitest/crypto/test_getRandomValues.html @@ -0,0 +1,204 @@ + + + Test window.crypto.getRandomValues + + + + + + + diff --git a/security/manager/ssl/src/Makefile.in b/security/manager/ssl/src/Makefile.in index 0f4669a42e3..66cc988ae38 100644 --- a/security/manager/ssl/src/Makefile.in +++ b/security/manager/ssl/src/Makefile.in @@ -99,6 +99,7 @@ EXPORTS += \ CryptoTask.h \ nsNSSShutDown.h \ ScopedNSSTypes.h \ + nsRandomGenerator.h \ $(NULL) EXPORTS_NAMESPACES = mozilla diff --git a/security/manager/ssl/src/nsCrypto.cpp b/security/manager/ssl/src/nsCrypto.cpp index f431495e029..b1e0a9def38 100644 --- a/security/manager/ssl/src/nsCrypto.cpp +++ b/security/manager/ssl/src/nsCrypto.cpp @@ -2856,6 +2856,12 @@ nsCrypto::DisableRightClick() return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +nsCrypto::GetRandomValues(const jsval& aData, JSContext *cx, jsval* _retval) +{ + return mozilla::dom::Crypto::GetRandomValues(aData, cx, _retval); +} + nsCRMFObject::nsCRMFObject() { } diff --git a/security/manager/ssl/src/nsRandomGenerator.cpp b/security/manager/ssl/src/nsRandomGenerator.cpp index 691c3d459c4..2b1667dad58 100644 --- a/security/manager/ssl/src/nsRandomGenerator.cpp +++ b/security/manager/ssl/src/nsRandomGenerator.cpp @@ -4,6 +4,9 @@ #include "nsRandomGenerator.h" #include "pk11pub.h" +#include "secerr.h" +#include "prerror.h" +#include "nsNSSComponent.h" //////////////////////////////////////////////////////////////////////////////// //// nsRandomGenerator @@ -25,7 +28,12 @@ nsRandomGenerator::GenerateRandomBytes(uint32_t aLength, if (!buf) return NS_ERROR_OUT_OF_MEMORY; - SECStatus srv = PK11_GenerateRandom(buf, aLength); + mozilla::ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (slot == NULL) { + return NS_ERROR_FAILURE; + } + SECStatus srv = PK11_GenerateRandomOnSlot(slot, buf, aLength); + if (SECSuccess != srv) { NS_Free(buf); return NS_ERROR_FAILURE; diff --git a/testing/mochitest/b2g.json b/testing/mochitest/b2g.json index dde9b084f5b..22569b02cf7 100644 --- a/testing/mochitest/b2g.json +++ b/testing/mochitest/b2g.json @@ -4,6 +4,9 @@ "layout": "" }, "excludetests": { + "dom/tests/mochitest/crypto/test_getRandomValues.html": "bug 440046, re-enable when bug 673432 lands", + "dom/tests/mochitest/crypto/test_no_legacy.html": "bug 440046, re-enable when bug 673432 lands", + "dom/tests/mochitest/crypto/test_legacy.html": "bug 440046, re-enable when bug 673432 lands", "dom/tests/mochitest/dom-level0/test_innerWidthHeight_script.html": "bug 807137", "dom/alarm/test/test_alarm_permitted_app.html":" navigator.mozAlarms should be an nsIDOMMozAlarmsManager object", "dom/battery/test/test_battery_basics.html":" Default chargingTime should be 0 - got Infinity, expected 0",