Bug 1756500 - Implement key verification for origin trials. r=keeler,nkulatova

Somewhat straight-forward. Add a test key so that we can add some tests
for this.

Differential Revision: https://phabricator.services.mozilla.com/D139402
This commit is contained in:
Emilio Cobos Álvarez 2022-03-08 15:24:16 +00:00
parent 93ce20c599
commit 6ea61a050a
4 changed files with 102 additions and 12 deletions

View File

@ -862,7 +862,7 @@ nsresult CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey,
}
UniqueSECKEYPublicKey CreateECPublicKey(const SECItem* aKeyData,
const nsString& aNamedCurve) {
const nsAString& aNamedCurve) {
UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
if (!arena) {
return nullptr;

View File

@ -27,6 +27,7 @@
#include "secitem.h"
#include "secoid.h"
#include "secoidt.h"
#include "ScopedNSSTypes.h"
struct JSStructuredCloneReader;
struct JSStructuredCloneWriter;
@ -128,8 +129,7 @@ const SECItem SEC_OID_DATA_EC_DH = {
siBuffer, (unsigned char*)id_ecDH,
static_cast<unsigned int>(mozilla::ArrayLength(id_ecDH))};
namespace mozilla {
namespace dom {
namespace mozilla::dom {
inline bool ReadBuffer(JSStructuredCloneReader* aReader,
CryptoBuffer& aBuffer) {
@ -273,7 +273,7 @@ inline bool CheckEncodedECParameters(const SECItem* aEcParams) {
return true;
}
inline SECItem* CreateECParamsForCurve(const nsString& aNamedCurve,
inline SECItem* CreateECParamsForCurve(const nsAString& aNamedCurve,
PLArenaPool* aArena) {
MOZ_ASSERT(aArena);
SECOidTag curveOIDTag;
@ -313,7 +313,10 @@ inline SECItem* CreateECParamsForCurve(const nsString& aNamedCurve,
return params;
}
} // namespace dom
} // namespace mozilla
// Implemented in CryptoKey.cpp
UniqueSECKEYPublicKey CreateECPublicKey(const SECItem* aKeyData,
const nsAString& aNamedCurve);
} // namespace mozilla::dom
#endif // mozilla_dom_WebCryptoCommon_h

View File

@ -16,37 +16,107 @@
#include "js/Wrapper.h"
#include "nsGlobalWindowInner.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/WebCryptoCommon.h"
#include "mozilla/StaticPrefs_dom.h"
#include "ScopedNSSTypes.h"
namespace mozilla {
LazyLogModule sOriginTrialsLog("OriginTrials");
#define LOG(...) MOZ_LOG(sOriginTrialsLog, LogLevel::Debug, (__VA_ARGS__))
// This is the EcdsaP256 public key from this key pair:
//
// * https://github.com/emilio/origin-trial-token/blob/64f03749e2e8c58f811f67044cecc7d6955fd51a/tools/test-keys/test-ecdsa.pkcs8
// * https://github.com/emilio/origin-trial-token/blob/64f03749e2e8c58f811f67044cecc7d6955fd51a/tools/test-keys/test-ecdsa.pub
//
// See that repo for tools for key and token generation and signing.
static const unsigned char kTestKey[65] = {
0x4, 0x4a, 0xae, 0x76, 0x64, 0x24, 0xa0, 0x55, 0xc4, 0x66, 0xe,
0x43, 0x32, 0x4c, 0x1d, 0x85, 0x8, 0x63, 0x6a, 0x93, 0xd4, 0x1d,
0xf, 0xfc, 0xb4, 0x2c, 0x77, 0x3d, 0xe6, 0x87, 0xdc, 0xeb, 0x46,
0xcd, 0xcf, 0x88, 0xd0, 0xe3, 0x39, 0x8f, 0xe5, 0x17, 0xb8, 0xc8,
0xd7, 0xd3, 0xdc, 0x32, 0x7c, 0x4f, 0x8, 0x8b, 0x61, 0x19, 0xdd,
0xc0, 0x2, 0x5f, 0x11, 0x20, 0x6b, 0x44, 0xcf, 0x2a, 0x64,
};
constexpr auto kEcAlgorithm =
NS_LITERAL_STRING_FROM_CSTRING(WEBCRYPTO_NAMED_CURVE_P256);
bool VerifySignature(const uint8_t* aSignature, uintptr_t aSignatureLen,
const uint8_t* aData, uintptr_t aDataLen,
void* aUserData) {
MOZ_RELEASE_ASSERT(aSignatureLen == 64);
LOG("VerifySignature()\n");
if (!StaticPrefs::dom_origin_trials_test_key_enabled()) {
// TODO(emilio): Implement.
return false;
}
const SECItem rawKey{siBuffer, const_cast<unsigned char*>(kTestKey),
sizeof(kTestKey)};
MOZ_RELEASE_ASSERT(rawKey.data[0] == EC_POINT_FORM_UNCOMPRESSED);
UniqueSECKEYPublicKey pubKey = dom::CreateECPublicKey(&rawKey, kEcAlgorithm);
if (NS_WARN_IF(!pubKey)) {
LOG(" Failed to create public key?");
return false;
}
if (NS_WARN_IF(aDataLen > UINT_MAX)) {
LOG(" Way too large data.");
return false;
}
const SECItem signature{siBuffer, const_cast<unsigned char*>(aSignature),
unsigned(aSignatureLen)};
const SECItem data{siBuffer, const_cast<unsigned char*>(aData),
unsigned(aDataLen)};
// SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE
const SECStatus result = PK11_VerifyWithMechanism(
pubKey.get(), CKM_ECDSA_SHA256, nullptr, &signature, &data, nullptr);
if (NS_WARN_IF(result != SECSuccess)) {
LOG(" Failed to verify data.");
return false;
}
return true;
}
bool MatchesOrigin(const uint8_t* aOrigin, size_t aOriginLen, bool aIsSubdomain,
bool aIsThirdParty, bool aIsUsageSubset, void* aUserData) {
const nsDependentCSubstring origin(reinterpret_cast<const char*>(aOrigin),
aOriginLen);
LOG("MatchesOrigin(%d, %d, %d, %s)\n", aIsThirdParty, aIsSubdomain,
aIsUsageSubset, nsCString(origin).get());
if (aIsThirdParty || aIsSubdomain || aIsUsageSubset) {
// TODO(emilio): Support third-party tokens and so on.
return false;
}
auto* principal = static_cast<nsIPrincipal*>(aUserData);
nsDependentCSubstring origin(reinterpret_cast<const char*>(aOrigin),
aOriginLen);
nsCOMPtr<nsIURI> originURI;
if (NS_WARN_IF(NS_FAILED(NS_NewURI(getter_AddRefs(originURI), origin)))) {
return false;
}
return principal->IsSameOrigin(originURI);
if (NS_WARN_IF(!principal->IsSameOrigin(originURI))) {
LOG("Origin doesn't match\n");
return false;
}
return true;
}
void OriginTrials::UpdateFromToken(const nsAString& aBase64EncodedToken,
nsIPrincipal* aPrincipal) {
if (!StaticPrefs::dom_origin_trials_enabled()) {
return;
}
LOG("OriginTrials::UpdateFromToken()\n");
nsAutoCString decodedToken;
nsresult rv = mozilla::Base64Decode(aBase64EncodedToken, decodedToken);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -61,10 +131,12 @@ void OriginTrials::UpdateFromToken(const nsAString& aBase64EncodedToken,
};
auto result = origin_trials_ffi::origin_trials_parse_and_validate_token(
decodedTokenSpan.data(), decodedTokenSpan.size(), &params);
if (!result.IsOk()) {
if (NS_WARN_IF(!result.IsOk())) {
LOG(" result = %d\n", int(result.tag));
return; // TODO(emilio): Maybe report to console or what not?
}
OriginTrial trial = result.AsOk().trial;
LOG(" result = Ok(%d)\n", int(trial));
mEnabledTrials += trial;
}
@ -73,6 +145,7 @@ bool OriginTrials::IsEnabled(JSContext* aCx, JSObject* aObject,
if (nsContentUtils::ThreadsafeIsSystemCaller(aCx)) {
return true;
}
LOG("OriginTrials::IsEnabled(%d)\n", int(aTrial));
if (NS_IsMainThread()) {
if (auto* win = xpc::WindowGlobalOrNull(js::UncheckedUnwrap(aObject))) {
if (dom::Document* doc = win->GetExtantDoc()) {
@ -86,4 +159,6 @@ bool OriginTrials::IsEnabled(JSContext* aCx, JSObject* aObject,
return false;
}
#undef LOG
} // namespace mozilla

View File

@ -2873,6 +2873,18 @@
value: true
mirror: always
# Whether origin trials are enabled.
- name: dom.origin-trials.enabled
type: bool
value: true
mirror: always
# Whether we use the test key to verify tokens.
- name: dom.origin-trials.test-key.enabled
type: bool
value: true
mirror: always
# Is support for Window.paintWorklet enabled?
- name: dom.paintWorklet.enabled
type: bool