blob: 327c0e8afe15df64733cdc110d54bf05e585c6b2 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h"
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
#include "third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom-blink.h"
#include "third_party/blink/public/mojom/filesystem/file_system.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_crypto.h"
#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
#include "third_party/blink/renderer/bindings/modules/v8/serialization/web_crypto_sub_tags.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/modules/crypto/crypto_key.h"
#include "third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.h"
#include "third_party/blink/renderer/modules/file_system_access/file_system_file_handle.h"
#include "third_party/blink/renderer/modules/filesystem/dom_file_system.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_certificate.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_certificate_generator.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.h"
#include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.h"
#include "third_party/blink/renderer/modules/webcodecs/video_frame.h"
#include "third_party/blink/renderer/modules/webcodecs/video_frame_attachment.h"
namespace blink {
ScriptWrappable* V8ScriptValueDeserializerForModules::ReadDOMObject(
SerializationTag tag,
ExceptionState& exception_state) {
// Give the core/ implementation a chance to try first.
// If it didn't recognize the kind of wrapper, try the modules types.
if (ScriptWrappable* wrappable =
V8ScriptValueDeserializer::ReadDOMObject(tag, exception_state))
return wrappable;
switch (tag) {
case kCryptoKeyTag:
return ReadCryptoKey();
case kDOMFileSystemTag: {
uint32_t raw_type;
String name;
String root_url;
if (!ReadUint32(&raw_type) ||
raw_type >
static_cast<int32_t>(mojom::blink::FileSystemType::kMaxValue) ||
!ReadUTF8String(&name) || !ReadUTF8String(&root_url))
return nullptr;
return MakeGarbageCollected<DOMFileSystem>(
ExecutionContext::From(GetScriptState()), name,
static_cast<mojom::blink::FileSystemType>(raw_type), KURL(root_url));
}
case kFileSystemFileHandleTag:
case kFileSystemDirectoryHandleTag:
return ReadFileSystemHandle(tag);
case kRTCCertificateTag: {
String pem_private_key;
String pem_certificate;
if (!ReadUTF8String(&pem_private_key) ||
!ReadUTF8String(&pem_certificate))
return nullptr;
std::unique_ptr<RTCCertificateGenerator> certificate_generator =
std::make_unique<RTCCertificateGenerator>();
if (!certificate_generator)
return nullptr;
rtc::scoped_refptr<rtc::RTCCertificate> certificate =
certificate_generator->FromPEM(pem_private_key, pem_certificate);
if (!certificate)
return nullptr;
return MakeGarbageCollected<RTCCertificate>(std::move(certificate));
}
case kRTCEncodedAudioFrameTag:
return ReadRTCEncodedAudioFrame();
case kRTCEncodedVideoFrameTag:
return ReadRTCEncodedVideoFrame();
case kVideoFrameTag:
return ReadVideoFrame();
default:
break;
}
return nullptr;
}
namespace {
bool AlgorithmIdFromWireFormat(uint32_t raw_id, WebCryptoAlgorithmId* id) {
switch (static_cast<CryptoKeyAlgorithmTag>(raw_id)) {
case kAesCbcTag:
*id = kWebCryptoAlgorithmIdAesCbc;
return true;
case kHmacTag:
*id = kWebCryptoAlgorithmIdHmac;
return true;
case kRsaSsaPkcs1v1_5Tag:
*id = kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5;
return true;
case kSha1Tag:
*id = kWebCryptoAlgorithmIdSha1;
return true;
case kSha256Tag:
*id = kWebCryptoAlgorithmIdSha256;
return true;
case kSha384Tag:
*id = kWebCryptoAlgorithmIdSha384;
return true;
case kSha512Tag:
*id = kWebCryptoAlgorithmIdSha512;
return true;
case kAesGcmTag:
*id = kWebCryptoAlgorithmIdAesGcm;
return true;
case kRsaOaepTag:
*id = kWebCryptoAlgorithmIdRsaOaep;
return true;
case kAesCtrTag:
*id = kWebCryptoAlgorithmIdAesCtr;
return true;
case kAesKwTag:
*id = kWebCryptoAlgorithmIdAesKw;
return true;
case kRsaPssTag:
*id = kWebCryptoAlgorithmIdRsaPss;
return true;
case kEcdsaTag:
*id = kWebCryptoAlgorithmIdEcdsa;
return true;
case kEcdhTag:
*id = kWebCryptoAlgorithmIdEcdh;
return true;
case kHkdfTag:
*id = kWebCryptoAlgorithmIdHkdf;
return true;
case kPbkdf2Tag:
*id = kWebCryptoAlgorithmIdPbkdf2;
return true;
}
return false;
}
bool AsymmetricKeyTypeFromWireFormat(uint32_t raw_key_type,
WebCryptoKeyType* key_type) {
switch (static_cast<AsymmetricCryptoKeyType>(raw_key_type)) {
case kPublicKeyType:
*key_type = kWebCryptoKeyTypePublic;
return true;
case kPrivateKeyType:
*key_type = kWebCryptoKeyTypePrivate;
return true;
}
return false;
}
bool NamedCurveFromWireFormat(uint32_t raw_named_curve,
WebCryptoNamedCurve* named_curve) {
switch (static_cast<NamedCurveTag>(raw_named_curve)) {
case kP256Tag:
*named_curve = kWebCryptoNamedCurveP256;
return true;
case kP384Tag:
*named_curve = kWebCryptoNamedCurveP384;
return true;
case kP521Tag:
*named_curve = kWebCryptoNamedCurveP521;
return true;
}
return false;
}
bool KeyUsagesFromWireFormat(uint32_t raw_usages,
WebCryptoKeyUsageMask* usages,
bool* extractable) {
// Reminder to update this when adding new key usages.
static_assert(kEndOfWebCryptoKeyUsage == (1 << 7) + 1,
"update required when adding new key usages");
const uint32_t kAllPossibleUsages =
kExtractableUsage | kEncryptUsage | kDecryptUsage | kSignUsage |
kVerifyUsage | kDeriveKeyUsage | kWrapKeyUsage | kUnwrapKeyUsage |
kDeriveBitsUsage;
if (raw_usages & ~kAllPossibleUsages)
return false;
*usages = 0;
*extractable = raw_usages & kExtractableUsage;
if (raw_usages & kEncryptUsage)
*usages |= kWebCryptoKeyUsageEncrypt;
if (raw_usages & kDecryptUsage)
*usages |= kWebCryptoKeyUsageDecrypt;
if (raw_usages & kSignUsage)
*usages |= kWebCryptoKeyUsageSign;
if (raw_usages & kVerifyUsage)
*usages |= kWebCryptoKeyUsageVerify;
if (raw_usages & kDeriveKeyUsage)
*usages |= kWebCryptoKeyUsageDeriveKey;
if (raw_usages & kWrapKeyUsage)
*usages |= kWebCryptoKeyUsageWrapKey;
if (raw_usages & kUnwrapKeyUsage)
*usages |= kWebCryptoKeyUsageUnwrapKey;
if (raw_usages & kDeriveBitsUsage)
*usages |= kWebCryptoKeyUsageDeriveBits;
return true;
}
} // namespace
CryptoKey* V8ScriptValueDeserializerForModules::ReadCryptoKey() {
// Read params.
uint8_t raw_key_type;
if (!ReadOneByte(&raw_key_type))
return nullptr;
WebCryptoKeyAlgorithm algorithm;
WebCryptoKeyType key_type = kWebCryptoKeyTypeSecret;
switch (raw_key_type) {
case kAesKeyTag: {
uint32_t raw_id;
WebCryptoAlgorithmId id;
uint32_t length_bytes;
if (!ReadUint32(&raw_id) || !AlgorithmIdFromWireFormat(raw_id, &id) ||
!ReadUint32(&length_bytes) ||
length_bytes > std::numeric_limits<uint16_t>::max() / 8u)
return nullptr;
algorithm = WebCryptoKeyAlgorithm::CreateAes(id, length_bytes * 8);
key_type = kWebCryptoKeyTypeSecret;
break;
}
case kHmacKeyTag: {
uint32_t length_bytes;
uint32_t raw_hash;
WebCryptoAlgorithmId hash;
if (!ReadUint32(&length_bytes) ||
length_bytes > std::numeric_limits<unsigned>::max() / 8 ||
!ReadUint32(&raw_hash) || !AlgorithmIdFromWireFormat(raw_hash, &hash))
return nullptr;
algorithm = WebCryptoKeyAlgorithm::CreateHmac(hash, length_bytes * 8);
key_type = kWebCryptoKeyTypeSecret;
break;
}
case kRsaHashedKeyTag: {
uint32_t raw_id;
WebCryptoAlgorithmId id;
uint32_t raw_key_type;
uint32_t modulus_length_bits;
uint32_t public_exponent_size;
const void* public_exponent_bytes;
uint32_t raw_hash;
WebCryptoAlgorithmId hash;
if (!ReadUint32(&raw_id) || !AlgorithmIdFromWireFormat(raw_id, &id) ||
!ReadUint32(&raw_key_type) ||
!AsymmetricKeyTypeFromWireFormat(raw_key_type, &key_type) ||
!ReadUint32(&modulus_length_bits) ||
!ReadUint32(&public_exponent_size) ||
!ReadRawBytes(public_exponent_size, &public_exponent_bytes) ||
!ReadUint32(&raw_hash) || !AlgorithmIdFromWireFormat(raw_hash, &hash))
return nullptr;
algorithm = WebCryptoKeyAlgorithm::CreateRsaHashed(
id, modulus_length_bits,
reinterpret_cast<const unsigned char*>(public_exponent_bytes),
public_exponent_size, hash);
break;
}
case kEcKeyTag: {
uint32_t raw_id;
WebCryptoAlgorithmId id;
uint32_t raw_key_type;
uint32_t raw_named_curve;
WebCryptoNamedCurve named_curve;
if (!ReadUint32(&raw_id) || !AlgorithmIdFromWireFormat(raw_id, &id) ||
!ReadUint32(&raw_key_type) ||
!AsymmetricKeyTypeFromWireFormat(raw_key_type, &key_type) ||
!ReadUint32(&raw_named_curve) ||
!NamedCurveFromWireFormat(raw_named_curve, &named_curve))
return nullptr;
algorithm = WebCryptoKeyAlgorithm::CreateEc(id, named_curve);
break;
}
case kNoParamsKeyTag: {
uint32_t raw_id;
WebCryptoAlgorithmId id;
if (!ReadUint32(&raw_id) || !AlgorithmIdFromWireFormat(raw_id, &id))
return nullptr;
algorithm = WebCryptoKeyAlgorithm::CreateWithoutParams(id);
break;
}
}
if (algorithm.IsNull())
return nullptr;
// Read key usages.
uint32_t raw_usages;
WebCryptoKeyUsageMask usages;
bool extractable;
if (!ReadUint32(&raw_usages) ||
!KeyUsagesFromWireFormat(raw_usages, &usages, &extractable))
return nullptr;
// Read key data.
uint32_t key_data_length;
const void* key_data;
if (!ReadUint32(&key_data_length) ||
!ReadRawBytes(key_data_length, &key_data))
return nullptr;
WebCryptoKey key = WebCryptoKey::CreateNull();
if (!Platform::Current()->Crypto()->DeserializeKeyForClone(
algorithm, key_type, extractable, usages,
reinterpret_cast<const unsigned char*>(key_data), key_data_length,
key))
return nullptr;
return MakeGarbageCollected<CryptoKey>(key);
}
FileSystemHandle* V8ScriptValueDeserializerForModules::ReadFileSystemHandle(
SerializationTag tag) {
if (!RuntimeEnabledFeatures::FileSystemAccessEnabled(
ExecutionContext::From(GetScriptState()))) {
return nullptr;
}
String name;
uint32_t token_index;
if (!ReadUTF8String(&name) || !ReadUint32(&token_index)) {
return nullptr;
}
// Find the FileSystemHandle's token.
SerializedScriptValue::FileSystemAccessTokensArray& tokens_array =
GetSerializedScriptValue()->FileSystemAccessTokens();
if (token_index >= tokens_array.size()) {
return nullptr;
}
// IndexedDB code assumes that deserializing a SSV is non-destructive. So
// rather than consuming the token here instead we clone it.
mojo::Remote<mojom::blink::FileSystemAccessTransferToken> token(
std::move(tokens_array[token_index]));
if (!token) {
return nullptr;
}
mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken> token_clone;
token->Clone(token_clone.InitWithNewPipeAndPassReceiver());
tokens_array[token_index] = std::move(token_clone);
// Use the FileSystemAccessManager to redeem the token to clone the
// FileSystemHandle.
ExecutionContext* execution_context =
ExecutionContext::From(GetScriptState());
mojo::Remote<mojom::blink::FileSystemAccessManager>
file_system_access_manager;
execution_context->GetBrowserInterfaceBroker().GetInterface(
file_system_access_manager.BindNewPipeAndPassReceiver());
// Clone the FileSystemHandle object.
switch (tag) {
case kFileSystemFileHandleTag: {
mojo::PendingRemote<mojom::blink::FileSystemAccessFileHandle> file_handle;
file_system_access_manager->GetFileHandleFromToken(
token.Unbind(), file_handle.InitWithNewPipeAndPassReceiver());
return MakeGarbageCollected<FileSystemFileHandle>(execution_context, name,
std::move(file_handle));
}
case kFileSystemDirectoryHandleTag: {
mojo::PendingRemote<mojom::blink::FileSystemAccessDirectoryHandle>
directory_handle;
file_system_access_manager->GetDirectoryHandleFromToken(
token.Unbind(), directory_handle.InitWithNewPipeAndPassReceiver());
return MakeGarbageCollected<FileSystemDirectoryHandle>(
execution_context, name, std::move(directory_handle));
}
default: {
NOTREACHED();
return nullptr;
}
}
}
RTCEncodedAudioFrame*
V8ScriptValueDeserializerForModules::ReadRTCEncodedAudioFrame() {
uint32_t index;
if (!ReadUint32(&index))
return nullptr;
const auto* attachment =
GetSerializedScriptValue()
->GetAttachmentIfExists<RTCEncodedAudioFramesAttachment>();
if (!attachment)
return nullptr;
const auto& frames = attachment->EncodedAudioFrames();
if (index >= frames.size())
return nullptr;
return MakeGarbageCollected<RTCEncodedAudioFrame>(frames[index]);
}
RTCEncodedVideoFrame*
V8ScriptValueDeserializerForModules::ReadRTCEncodedVideoFrame() {
uint32_t index;
if (!ReadUint32(&index))
return nullptr;
const auto* attachment =
GetSerializedScriptValue()
->GetAttachmentIfExists<RTCEncodedVideoFramesAttachment>();
if (!attachment)
return nullptr;
const auto& frames = attachment->EncodedVideoFrames();
if (index >= frames.size())
return nullptr;
return MakeGarbageCollected<RTCEncodedVideoFrame>(frames[index]);
}
VideoFrame* V8ScriptValueDeserializerForModules::ReadVideoFrame() {
if (!RuntimeEnabledFeatures::WebCodecsEnabled(
ExecutionContext::From(GetScriptState()))) {
return nullptr;
}
uint32_t index;
if (!ReadUint32(&index))
return nullptr;
const auto* attachment =
GetSerializedScriptValue()->GetAttachmentIfExists<VideoFrameAttachment>();
if (!attachment)
return nullptr;
const auto& handles = attachment->Handles();
if (index >= attachment->size())
return nullptr;
return MakeGarbageCollected<VideoFrame>(handles[index]);
}
} // namespace blink