Skip to content

Commit

Permalink
Serialize CryptoKey using CoreIPC
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=272064
rdar://125341497

Reviewed by Alex Christensen and David Kilzer.

Over-the-wire bytes will now be serialized/deserialized using CoreIPC ::encode/::decode.
Note: Serialization in wrapCryptoKey is still a property list to maintain backward
compatibility with serialized CryptoKeys on-disk of existing users.  No new tests since
existing tests that put/get keys from IndexedDB will also exercise this change.

Combined changes:
* Source/WebCore/crypto/CryptoKey.h:
* Source/WebCore/crypto/SerializedCryptoKeyWrap.h:
* Source/WebCore/crypto/mac/SerializedCryptoKeyWrapMac.mm:
(WebCore::wrapSerializedCryptoKey):
(WebCore::readSerializedCryptoKey):
(WebCore::unwrapCryptoKey):
(WebCore::unwrapSerializedCryptoKey):
* Source/WebKit/Scripts/webkit/messages.py:
(headers_for_type):
* Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in:
* Source/WebKit/UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::unwrapCryptoKey):
* Source/WebKit/UIProcess/WebPageProxy.h:
* Source/WebKit/UIProcess/WebPageProxy.messages.in:
* Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::unwrapCryptoKey const):

Canonical link: https://commits.webkit.org/272448.867@safari-7618-branch
  • Loading branch information
Nitin Mahendru authored and achristensen07 committed Apr 5, 2024
1 parent c13ed13 commit b4edd8f
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 19 deletions.
6 changes: 6 additions & 0 deletions Source/WebCore/crypto/CryptoKey.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ inline auto CryptoKey::type() const -> Type

WebCoreOpaqueRoot root(CryptoKey*);

struct WrappedCryptoKey {
std::array<uint8_t, 24> wrappedKEK;
Vector<uint8_t> encryptedKey;
std::array<uint8_t, 16> tag;
};

} // namespace WebCore

#define SPECIALIZE_TYPE_TRAITS_CRYPTO_KEY(ToClassName, KeyClass) \
Expand Down
5 changes: 5 additions & 0 deletions Source/WebCore/crypto/SerializedCryptoKeyWrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

namespace WebCore {

struct WrappedCryptoKey;

// The purpose of the following APIs is to protect serialized CryptoKey data in IndexedDB or
// any other local storage that go through the structured clone algorithm. However, a side effect
// of this extra layer of protection is redundant communications between mainThread(document) and
Expand All @@ -43,4 +45,7 @@ WEBCORE_EXPORT bool deleteDefaultWebCryptoMasterKey();
WEBCORE_EXPORT bool wrapSerializedCryptoKey(const Vector<uint8_t>& masterKey, const Vector<uint8_t>& key, Vector<uint8_t>& result);
WEBCORE_EXPORT bool unwrapSerializedCryptoKey(const Vector<uint8_t>& masterKey, const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key);

WEBCORE_EXPORT std::optional<struct WrappedCryptoKey> readSerializedCryptoKey(const Vector<uint8_t>& wrappedKey);
WEBCORE_EXPORT std::optional<Vector<uint8_t>> unwrapCryptoKey(const Vector<uint8_t>& masterKey, const struct WrappedCryptoKey& wrappedKey);

} // namespace WebCore
104 changes: 92 additions & 12 deletions Source/WebCore/crypto/mac/SerializedCryptoKeyWrapMac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#import "SerializedCryptoKeyWrap.h"

#import "CommonCryptoUtilities.h"
#import "CryptoKey.h"
#import "LocalizedStrings.h"
#import <CommonCrypto/CommonSymmetricKeywrap.h>
#import <crt_externs.h>
Expand Down Expand Up @@ -60,7 +61,11 @@
const NSString* encryptedKeyKey = @"encryptedKey";
const NSString* tagKey = @"tag";

const size_t masterKeySizeInBytes = 16;
constexpr size_t masterKeySizeInBytes = 16;
constexpr size_t kekSizeInBytes = 16;
constexpr size_t expectedTagLengthAES = 16;
// https://datatracker.ietf.org/doc/html/rfc3394#section-2.2.1
constexpr size_t wrappedKekSize = kekSizeInBytes + 8;

static NSString* masterKeyAccountNameForCurrentApplication()
{
Expand Down Expand Up @@ -211,7 +216,7 @@ bool deleteDefaultWebCryptoMasterKey()

bool wrapSerializedCryptoKey(const Vector<uint8_t>& masterKey, const Vector<uint8_t>& key, Vector<uint8_t>& result)
{
Vector<uint8_t> kek(16);
Vector<uint8_t> kek(kekSizeInBytes);
auto rc = CCRandomGenerateBytes(kek.data(), kek.size());
RELEASE_ASSERT(rc == kCCSuccess);

Expand All @@ -225,9 +230,8 @@ bool wrapSerializedCryptoKey(const Vector<uint8_t>& masterKey, const Vector<uint
wrappedKEK.shrink(wrappedKEKSize);

Vector<uint8_t> encryptedKey(key.size());
constexpr size_t maxTagLength = 16;
size_t tagLength = maxTagLength;
uint8_t tag[maxTagLength];
size_t tagLength = expectedTagLengthAES;
uint8_t tag[expectedTagLengthAES];

ALLOW_DEPRECATED_DECLARATIONS_BEGIN
status = CCCryptorGCM(kCCEncrypt, kCCAlgorithmAES128, kek.data(), kek.size(),
Expand All @@ -240,7 +244,7 @@ bool wrapSerializedCryptoKey(const Vector<uint8_t>& masterKey, const Vector<uint

if (status != kCCSuccess)
return false;
RELEASE_ASSERT(tagLength == 16);
RELEASE_ASSERT(tagLength == expectedTagLengthAES);

auto dictionary = @{
versionKey: [NSNumber numberWithUnsignedInteger:currentSerializationVersion],
Expand All @@ -257,9 +261,86 @@ bool wrapSerializedCryptoKey(const Vector<uint8_t>& masterKey, const Vector<uint
return true;
}

template<size_t size>
static std::optional<std::array<uint8_t, size>> createArrayFromData(NSData * data)
{
if (size != data.length)
return std::nullopt;
std::array<uint8_t, size> rv { };
[data getBytes:rv.data() length:size];
return rv;
}

std::optional<struct WebCore::WrappedCryptoKey> readSerializedCryptoKey(const Vector<uint8_t>& serializedKey)
{
NSDictionary* dictionary = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytesNoCopy:(void*)serializedKey.data() length:serializedKey.size() freeWhenDone:NO] options:NSPropertyListBinaryFormat_v1_0 format:nullptr error:nullptr];
if (!dictionary)
return std::nullopt;

id versionObject = [dictionary objectForKey:versionKey];
if (![versionObject isKindOfClass:[NSNumber class]])
return std::nullopt;
if ([versionObject unsignedIntegerValue] > currentSerializationVersion)
return std::nullopt;

id wrappedKEKObject = [dictionary objectForKey:wrappedKEKKey];
if (![wrappedKEKObject isKindOfClass:[NSData class]])
return std::nullopt;
auto wrappedKEK = createArrayFromData<wrappedKekSize>(wrappedKEKObject);
if (!wrappedKEK)
return std::nullopt;

id encryptedKeyObject = [dictionary objectForKey:encryptedKeyKey];
if (![encryptedKeyObject isKindOfClass:[NSData class]])
return std::nullopt;
Vector<uint8_t> encryptedKey = vectorFromNSData(encryptedKeyObject);

id tagObject = [dictionary objectForKey:tagKey];
if (![tagObject isKindOfClass:[NSData class]])
return std::nullopt;
auto tag = createArrayFromData<expectedTagLengthAES>(tagObject);
if (!tag)
return std::nullopt;
struct WebCore::WrappedCryptoKey k = { *wrappedKEK, encryptedKey, *tag };
return k;
}

std::optional<Vector<uint8_t>> unwrapCryptoKey(const Vector<uint8_t>& masterKey, const struct WebCore::WrappedCryptoKey& wrappedKey)
{
if (wrappedKey.wrappedKEK.size() != wrappedKekSize || wrappedKey.tag.size() != expectedTagLengthAES || masterKey.isEmpty())
return std::nullopt;
auto wrappedKEK = wrappedKey.wrappedKEK;
auto encryptedKey = wrappedKey.encryptedKey.span();
auto tag = wrappedKey.tag;

Vector<uint8_t> kek(CCSymmetricUnwrappedSize(kCCWRAPAES, wrappedKEK.size()));
size_t kekSize = kek.size();
CCCryptorStatus status = CCSymmetricKeyUnwrap(kCCWRAPAES, CCrfc3394_iv, CCrfc3394_ivLen, masterKey.data(), masterKey.size(), wrappedKEK.data(), wrappedKEK.size(), kek.data(), &kekSize);
if (status != kCCSuccess)
return std::nullopt;
kek.shrink(kekSize);
size_t tagLength = expectedTagLengthAES;
uint8_t actualTag[expectedTagLengthAES];
Vector<uint8_t> key(encryptedKey.size());
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
status = CCCryptorGCM(kCCDecrypt, kCCAlgorithmAES128, kek.data(), kek.size(),
nullptr, 0, // iv
nullptr, 0, // auth data
encryptedKey.data(), encryptedKey.size(),
key.data(),
actualTag, &tagLength);
ALLOW_DEPRECATED_DECLARATIONS_END
if (status != kCCSuccess)
return std::nullopt;
RELEASE_ASSERT(tagLength == expectedTagLengthAES);
if (constantTimeMemcmp(tag.data(), actualTag, tagLength))
return std::nullopt;
return key;
}

bool unwrapSerializedCryptoKey(const Vector<uint8_t>& masterKey, const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key)
{
NSDictionary* dictionary = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytesNoCopy:(void*)wrappedKey.data() length:wrappedKey.size() freeWhenDone:NO] options:0 format:nullptr error:nullptr];
NSDictionary* dictionary = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytesNoCopy:(void*)wrappedKey.data() length:wrappedKey.size() freeWhenDone:NO] options:NSPropertyListBinaryFormat_v1_0 format:nullptr error:nullptr];
if (!dictionary)
return false;

Expand All @@ -283,7 +364,7 @@ bool unwrapSerializedCryptoKey(const Vector<uint8_t>& masterKey, const Vector<ui
if (![tagObject isKindOfClass:[NSData class]])
return false;
Vector<uint8_t> tag = vectorFromNSData(tagObject);
if (tag.size() != 16)
if (tag.size() != expectedTagLengthAES)
return false;

Vector<uint8_t> kek(CCSymmetricUnwrappedSize(kCCWRAPAES, wrappedKEK.size()));
Expand All @@ -293,9 +374,8 @@ bool unwrapSerializedCryptoKey(const Vector<uint8_t>& masterKey, const Vector<ui
return false;
kek.shrink(kekSize);

constexpr size_t maxTagLength = 16;
size_t tagLength = maxTagLength;
uint8_t actualTag[maxTagLength];
size_t tagLength = expectedTagLengthAES;
uint8_t actualTag[expectedTagLengthAES];

key.resize(encryptedKey.size());
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
Expand All @@ -309,7 +389,7 @@ bool unwrapSerializedCryptoKey(const Vector<uint8_t>& masterKey, const Vector<ui

if (status != kCCSuccess)
return false;
RELEASE_ASSERT(tagLength == 16);
RELEASE_ASSERT(tagLength == expectedTagLengthAES);

if (constantTimeMemcmp(tag.data(), actualTag, tagLength))
return false;
Expand Down
1 change: 1 addition & 0 deletions Source/WebKit/Scripts/webkit/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,7 @@ def headers_for_type(type):
'WebCore::WillContinueLoading': ['<WebCore/FrameLoaderTypes.h>'],
'WebCore::WillInternallyHandleFailure': ['<WebCore/FrameLoaderTypes.h>'],
'WebCore::WindowProxyProperty': ['<WebCore/FrameLoaderTypes.h>'],
'WebCore::WrappedCryptoKey': ['<WebCore/CryptoKey.h>'],
'WebKit::ActivityStateChangeID': ['"DrawingAreaInfo.h"'],
'WebKit::AllowOverwrite': ['"DownloadID.h"'],
'WebKit::AppPrivacyReportTestingData': ['"AppPrivacyReport.h"'],
Expand Down
7 changes: 7 additions & 0 deletions Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in
Original file line number Diff line number Diff line change
Expand Up @@ -7234,3 +7234,10 @@ enum class WebCore::PredefinedColorSpace : uint8_t {
, DisplayP3
#endif
};

header: <WebCore/CryptoKey.h>
[CustomHeader] struct WebCore::WrappedCryptoKey {
std::array<uint8_t, 24> wrappedKEK;
Vector<uint8_t> encryptedKey;
std::array<uint8_t, 16> tag;
};
12 changes: 8 additions & 4 deletions Source/WebKit/UIProcess/WebPageProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@
#include <WebCore/BitmapImage.h>
#include <WebCore/CompositionHighlight.h>
#include <WebCore/CrossSiteNavigationDataTransfer.h>
#include <WebCore/CryptoKey.h>
#include <WebCore/DOMPasteAccess.h>
#include <WebCore/DeprecatedGlobalSettings.h>
#include <WebCore/DiagnosticLoggingClient.h>
Expand Down Expand Up @@ -11030,7 +11031,7 @@ void WebPageProxy::wrapCryptoKey(const Vector<uint8_t>& key, CompletionHandler<v
completionHandler(succeeded, WTFMove(wrappedKey));
}

void WebPageProxy::unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, CompletionHandler<void(bool, Vector<uint8_t>&&)>&& completionHandler)
void WebPageProxy::unwrapCryptoKey(const struct WebCore::WrappedCryptoKey& wrappedKey, CompletionHandler<void(bool, Vector<uint8_t>&&)>&& completionHandler)
{
PageClientProtector protector(pageClient());

Expand All @@ -11039,9 +11040,12 @@ void WebPageProxy::unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Completion
if (auto keyData = m_navigationClient->webCryptoMasterKey(*this))
masterKey = Vector(keyData->dataReference());

Vector<uint8_t> key;
bool succeeded = unwrapSerializedCryptoKey(masterKey, wrappedKey, key);
completionHandler(succeeded, WTFMove(key));
auto key = WebCore::unwrapCryptoKey(masterKey, wrappedKey);
if (!key) {
completionHandler(false, { });
return;
}
completionHandler(true, WTFMove(*key));
}

void WebPageProxy::addMIMETypeWithCustomContentProvider(const String& mimeType)
Expand Down
3 changes: 2 additions & 1 deletion Source/WebKit/UIProcess/WebPageProxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ struct ViewportAttributes;
struct ViewportArguments;
struct WheelEventHandlingResult;
struct WindowFeatures;
struct WrappedCryptoKey;

template<typename> class ProcessQualified;
template<typename> class RectEdges;
Expand Down Expand Up @@ -1689,7 +1690,7 @@ class WebPageProxy final : public API::ObjectImpl<API::Object::Type::Page>, publ
#endif

void wrapCryptoKey(const Vector<uint8_t>&, CompletionHandler<void(bool, Vector<uint8_t>&&)>&&);
void unwrapCryptoKey(const Vector<uint8_t>&, CompletionHandler<void(bool, Vector<uint8_t>&&)>&&);
void unwrapCryptoKey(const WebCore::WrappedCryptoKey&, CompletionHandler<void(bool, Vector<uint8_t>&&)>&&);

void takeSnapshot(WebCore::IntRect, WebCore::IntSize bitmapSize, SnapshotOptions, CompletionHandler<void(std::optional<ShareableBitmapHandle>&&)>&&);

Expand Down
2 changes: 1 addition & 1 deletion Source/WebKit/UIProcess/WebPageProxy.messages.in
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ messages -> WebPageProxy {
DidUpdateActivityState() CanDispatchOutOfOrder

WrapCryptoKey(Vector<uint8_t> key) -> (bool succeeded, Vector<uint8_t> wrappedKey) Synchronous
UnwrapCryptoKey(Vector<uint8_t> wrappedKey) -> (bool succeeded, Vector<uint8_t> key) Synchronous
UnwrapCryptoKey(struct WebCore::WrappedCryptoKey wrappedKey) -> (bool succeeded, Vector<uint8_t> key) Synchronous


#if ENABLE(TELEPHONE_NUMBER_DETECTION)
Expand Down
7 changes: 6 additions & 1 deletion Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
#include <WebCore/ColorChooser.h>
#include <WebCore/ContentRuleListResults.h>
#include <WebCore/CookieConsentDecisionResult.h>
#include <WebCore/CryptoKey.h>
#include <WebCore/DataListSuggestionPicker.h>
#include <WebCore/DatabaseTracker.h>
#include <WebCore/DocumentLoader.h>
Expand All @@ -99,6 +100,7 @@
#include <WebCore/ScriptController.h>
#include <WebCore/SecurityOrigin.h>
#include <WebCore/SecurityOriginData.h>
#include <WebCore/SerializedCryptoKeyWrap.h>
#include <WebCore/Settings.h>
#include <WebCore/TextIndicator.h>
#include <WebCore/TextRecognitionOptions.h>
Expand Down Expand Up @@ -1481,7 +1483,10 @@ bool WebChromeClient::wrapCryptoKey(const Vector<uint8_t>& key, Vector<uint8_t>&

bool WebChromeClient::unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key) const
{
auto sendResult = WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPageProxy::UnwrapCryptoKey(wrappedKey), page().identifier());
auto ipcCryptoKey = WebCore::readSerializedCryptoKey(wrappedKey);
if (!ipcCryptoKey)
return false;
auto sendResult = WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPageProxy::UnwrapCryptoKey(*ipcCryptoKey), page().identifier());
if (!sendResult.succeeded())
return false;

Expand Down

0 comments on commit b4edd8f

Please sign in to comment.