ArgumentCodersCF.cpp   [plain text]


/*
 * Copyright (C) 2010-2018 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

// FIXME: This is a .cpp but has ObjC in it?

#include "config.h"
#include "ArgumentCodersCF.h"

#include "DataReference.h"
#include "Decoder.h"
#include "Encoder.h"
#include <wtf/HashSet.h>
#include <wtf/ProcessPrivilege.h>
#include <wtf/Vector.h>
#include <wtf/cf/CFURLExtras.h>
#include <wtf/spi/cocoa/SecuritySPI.h>

#if USE(FOUNDATION)
#import <Foundation/Foundation.h>
#endif

namespace IPC {
using namespace WebCore;

CFTypeRef tokenNullTypeRef()
{
    static CFStringRef tokenNullType = CFSTR("WKNull");
    return tokenNullType;
}

enum CFType {
    CFArray,
    CFBoolean,
    CFData,
    CFDate,
    CFDictionary,
    CFNull,
    CFNumber,
    CFString,
    CFURL,
    SecCertificate,
    SecIdentity,
#if HAVE(SEC_KEYCHAIN)
    SecKeychainItem,
#endif
#if HAVE(SEC_ACCESS_CONTROL)
    SecAccessControl,
#endif
#if HAVE(SEC_TRUST_SERIALIZATION)
    SecTrust,
#endif
    Null,
    Unknown,
};

static CFType typeFromCFTypeRef(CFTypeRef type)
{
    ASSERT(type);

    if (type == tokenNullTypeRef())
        return Null;

    CFTypeID typeID = CFGetTypeID(type);
    if (typeID == CFArrayGetTypeID())
        return CFArray;
    if (typeID == CFBooleanGetTypeID())
        return CFBoolean;
    if (typeID == CFDataGetTypeID())
        return CFData;
    if (typeID == CFDateGetTypeID())
        return CFDate;
    if (typeID == CFDictionaryGetTypeID())
        return CFDictionary;
    if (typeID == CFNullGetTypeID())
        return CFNull;
    if (typeID == CFNumberGetTypeID())
        return CFNumber;
    if (typeID == CFStringGetTypeID())
        return CFString;
    if (typeID == CFURLGetTypeID())
        return CFURL;
    if (typeID == SecCertificateGetTypeID())
        return SecCertificate;
    if (typeID == SecIdentityGetTypeID())
        return SecIdentity;
#if HAVE(SEC_KEYCHAIN)
    if (typeID == SecKeychainItemGetTypeID())
        return SecKeychainItem;
#endif
#if HAVE(SEC_ACCESS_CONTROL)
    if (typeID == SecAccessControlGetTypeID())
        return SecAccessControl;
#endif
#if HAVE(SEC_TRUST_SERIALIZATION)
    if (typeID == SecTrustGetTypeID())
        return SecTrust;
#endif

    ASSERT_NOT_REACHED();
    return Unknown;
}

void encode(Encoder& encoder, CFTypeRef typeRef)
{
    CFType type = typeFromCFTypeRef(typeRef);
    encoder.encodeEnum(type);

    switch (type) {
    case CFArray:
        encode(encoder, static_cast<CFArrayRef>(typeRef));
        return;
    case CFBoolean:
        encode(encoder, static_cast<CFBooleanRef>(typeRef));
        return;
    case CFData:
        encode(encoder, static_cast<CFDataRef>(typeRef));
        return;
    case CFDate:
        encode(encoder, static_cast<CFDateRef>(typeRef));
        return;
    case CFDictionary:
        encode(encoder, static_cast<CFDictionaryRef>(typeRef));
        return;
    case CFNull:
        return;
    case CFNumber:
        encode(encoder, static_cast<CFNumberRef>(typeRef));
        return;
    case CFString:
        encode(encoder, static_cast<CFStringRef>(typeRef));
        return;
    case CFURL:
        encode(encoder, static_cast<CFURLRef>(typeRef));
        return;
    case SecCertificate:
        encode(encoder, static_cast<SecCertificateRef>(const_cast<void*>(typeRef)));
        return;
    case SecIdentity:
        encode(encoder, static_cast<SecIdentityRef>(const_cast<void*>(typeRef)));
        return;
#if HAVE(SEC_KEYCHAIN)
    case SecKeychainItem:
        encode(encoder, static_cast<SecKeychainItemRef>(const_cast<void*>(typeRef)));
        return;
#endif
#if HAVE(SEC_ACCESS_CONTROL)
    case SecAccessControl:
        encode(encoder, static_cast<SecAccessControlRef>(const_cast<void*>(typeRef)));
        return;
#endif
#if HAVE(SEC_TRUST_SERIALIZATION)
    case SecTrust:
        encode(encoder, static_cast<SecTrustRef>(const_cast<void*>(typeRef)));
        return;
#endif
    case Null:
        return;
    case Unknown:
        break;
    }

    ASSERT_NOT_REACHED();
}

bool decode(Decoder& decoder, RetainPtr<CFTypeRef>& result)
{
    CFType type;
    if (!decoder.decodeEnum(type))
        return false;

    switch (type) {
    case CFArray: {
        RetainPtr<CFArrayRef> array;
        if (!decode(decoder, array))
            return false;
        result = adoptCF(array.leakRef());
        return true;
    }
    case CFBoolean: {
        RetainPtr<CFBooleanRef> boolean;
        if (!decode(decoder, boolean))
            return false;
        result = adoptCF(boolean.leakRef());
        return true;
    }
    case CFData: {
        RetainPtr<CFDataRef> data;
        if (!decode(decoder, data))
            return false;
        result = adoptCF(data.leakRef());
        return true;
    }
    case CFDate: {
        RetainPtr<CFDateRef> date;
        if (!decode(decoder, date))
            return false;
        result = adoptCF(date.leakRef());
        return true;
    }
    case CFDictionary: {
        RetainPtr<CFDictionaryRef> dictionary;
        if (!decode(decoder, dictionary))
            return false;
        result = adoptCF(dictionary.leakRef());
        return true;
    }
    case CFNull:
        result = adoptCF(kCFNull);
        return true;
    case CFNumber: {
        RetainPtr<CFNumberRef> number;
        if (!decode(decoder, number))
            return false;
        result = adoptCF(number.leakRef());
        return true;
    }
    case CFString: {
        RetainPtr<CFStringRef> string;
        if (!decode(decoder, string))
            return false;
        result = adoptCF(string.leakRef());
        return true;
    }
    case CFURL: {
        RetainPtr<CFURLRef> url;
        if (!decode(decoder, url))
            return false;
        result = adoptCF(url.leakRef());
        return true;
    }
    case SecCertificate: {
        RetainPtr<SecCertificateRef> certificate;
        if (!decode(decoder, certificate))
            return false;
        result = adoptCF(certificate.leakRef());
        return true;
    }
    case SecIdentity: {
        RetainPtr<SecIdentityRef> identity;
        if (!decode(decoder, identity))
            return false;
        result = adoptCF(identity.leakRef());
        return true;
    }
#if HAVE(SEC_KEYCHAIN)
    case SecKeychainItem: {
        RetainPtr<SecKeychainItemRef> keychainItem;
        if (!decode(decoder, keychainItem))
            return false;
        result = adoptCF(keychainItem.leakRef());
        return true;
    }
#endif
#if HAVE(SEC_ACCESS_CONTROL)
    case SecAccessControl: {
        RetainPtr<SecAccessControlRef> accessControl;
        if (!decode(decoder, accessControl))
            return false;
        result = adoptCF(accessControl.leakRef());
        return true;
    }
#endif
#if HAVE(SEC_TRUST_SERIALIZATION)
    case SecTrust: {
        RetainPtr<SecTrustRef> trust;
        if (!decode(decoder, trust))
            return false;
        result = adoptCF(trust.leakRef());
        return true;
    }
#endif
    case Null:
        result = tokenNullTypeRef();
        return true;
    case Unknown:
        ASSERT_NOT_REACHED();
        return false;
    }

    return false;
}

void encode(Encoder& encoder, CFArrayRef array)
{
    if (!array) {
        encoder << true;
        return;
    }

    encoder << false;

    CFIndex size = CFArrayGetCount(array);
    Vector<CFTypeRef, 32> values(size);

    CFArrayGetValues(array, CFRangeMake(0, size), values.data());

    HashSet<CFIndex> invalidIndicies;
    for (CFIndex i = 0; i < size; ++i) {
        // Ignore values we don't support.
        ASSERT(typeFromCFTypeRef(values[i]) != Unknown);
        if (typeFromCFTypeRef(values[i]) == Unknown)
            invalidIndicies.add(i);
    }

    encoder << static_cast<uint64_t>(size - invalidIndicies.size());

    for (CFIndex i = 0; i < size; ++i) {
        if (invalidIndicies.contains(i))
            continue;
        encode(encoder, values[i]);
    }
}

bool decode(Decoder& decoder, RetainPtr<CFArrayRef>& result)
{
    bool isNull = false;
    if (!decoder.decode(isNull))
        return false;

    if (isNull) {
        result = nullptr;
        return true;
    }

    uint64_t size;
    if (!decoder.decode(size))
        return false;

    auto array = adoptCF(CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks));

    for (size_t i = 0; i < size; ++i) {
        RetainPtr<CFTypeRef> element;
        if (!decode(decoder, element))
            return false;

        if (!element)
            continue;
        
        CFArrayAppendValue(array.get(), element.get());
    }

    result = adoptCF(array.leakRef());
    return true;
}

void encode(Encoder& encoder, CFBooleanRef boolean)
{
    encoder << static_cast<bool>(CFBooleanGetValue(boolean));
}

bool decode(Decoder& decoder, RetainPtr<CFBooleanRef>& result)
{
    bool boolean;
    if (!decoder.decode(boolean))
        return false;

    result = adoptCF(boolean ? kCFBooleanTrue : kCFBooleanFalse);
    return true;
}

void encode(Encoder& encoder, CFDataRef data)
{
    CFIndex length = CFDataGetLength(data);
    const UInt8* bytePtr = CFDataGetBytePtr(data);

    encoder << IPC::DataReference(bytePtr, length);
}

bool decode(Decoder& decoder, RetainPtr<CFDataRef>& result)
{
    IPC::DataReference dataReference;
    if (!decoder.decode(dataReference))
        return false;

    result = adoptCF(CFDataCreate(0, dataReference.data(), dataReference.size()));
    return true;
}

void encode(Encoder& encoder, CFDateRef date)
{
    encoder << static_cast<double>(CFDateGetAbsoluteTime(date));
}

bool decode(Decoder& decoder, RetainPtr<CFDateRef>& result)
{
    double absoluteTime;
    if (!decoder.decode(absoluteTime))
        return false;

    result = adoptCF(CFDateCreate(0, absoluteTime));
    return true;
}

void encode(Encoder& encoder, CFDictionaryRef dictionary)
{
    if (!dictionary) {
        encoder << true;
        return;
    }

    encoder << false;

    CFIndex size = CFDictionaryGetCount(dictionary);
    Vector<CFTypeRef, 32> keys(size);
    Vector<CFTypeRef, 32> values(size);
    
    CFDictionaryGetKeysAndValues(dictionary, keys.data(), values.data());

    HashSet<CFTypeRef> invalidKeys;
    for (CFIndex i = 0; i < size; ++i) {
        ASSERT(keys[i]);
        ASSERT(values[i]);

        // Ignore keys/values we don't support.
        ASSERT(typeFromCFTypeRef(keys[i]) != Unknown);
        ASSERT(typeFromCFTypeRef(values[i]) != Unknown);
        if (typeFromCFTypeRef(keys[i]) == Unknown || typeFromCFTypeRef(values[i]) == Unknown)
            invalidKeys.add(keys[i]);
    }

    encoder << static_cast<uint64_t>(size - invalidKeys.size());

    for (CFIndex i = 0; i < size; ++i) {
        if (invalidKeys.contains(keys[i]))
            continue;

        encode(encoder, keys[i]);
        encode(encoder, values[i]);
    }
}

bool decode(Decoder& decoder, RetainPtr<CFDictionaryRef>& result)
{
    bool isNull = false;
    if (!decoder.decode(isNull))
        return false;

    if (isNull) {
        result = nullptr;
        return true;
    }

    uint64_t size;
    if (!decoder.decode(size))
        return false;

    RetainPtr<CFMutableDictionaryRef> dictionary = adoptCF(CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
    for (uint64_t i = 0; i < size; ++i) {
        RetainPtr<CFTypeRef> key;
        if (!decode(decoder, key))
            return false;

        RetainPtr<CFTypeRef> value;
        if (!decode(decoder, value))
            return false;

        CFDictionarySetValue(dictionary.get(), key.get(), value.get());
    }

    result = adoptCF(dictionary.leakRef());
    return true;
}

void encode(Encoder& encoder, CFNumberRef number)
{
    CFNumberType numberType = CFNumberGetType(number);

    Vector<uint8_t> buffer(CFNumberGetByteSize(number));
    bool result = CFNumberGetValue(number, numberType, buffer.data());
    ASSERT_UNUSED(result, result);

    encoder.encodeEnum(numberType);
    encoder << IPC::DataReference(buffer);
}

static size_t sizeForNumberType(CFNumberType numberType)
{
    switch (numberType) {
    case kCFNumberSInt8Type:
        return sizeof(SInt8);
    case kCFNumberSInt16Type:
        return sizeof(SInt16);
    case kCFNumberSInt32Type:
        return sizeof(SInt32);
    case kCFNumberSInt64Type:
        return sizeof(SInt64);
    case kCFNumberFloat32Type:
        return sizeof(Float32);
    case kCFNumberFloat64Type:
        return sizeof(Float64);
    case kCFNumberCharType:
        return sizeof(char);
    case kCFNumberShortType:
        return sizeof(short);
    case kCFNumberIntType:
        return sizeof(int);
    case kCFNumberLongType:
        return sizeof(long);
    case kCFNumberLongLongType:
        return sizeof(long long);
    case kCFNumberFloatType:
        return sizeof(float);
    case kCFNumberDoubleType:
        return sizeof(double);
    case kCFNumberCFIndexType:
        return sizeof(CFIndex);
    case kCFNumberNSIntegerType:
        return sizeof(long);
    case kCFNumberCGFloatType:
        return sizeof(double);
    }

    return 0;
}

bool decode(Decoder& decoder, RetainPtr<CFNumberRef>& result)
{
    CFNumberType numberType;
    if (!decoder.decodeEnum(numberType))
        return false;

    IPC::DataReference dataReference;
    if (!decoder.decode(dataReference))
        return false;

    size_t neededBufferSize = sizeForNumberType(numberType);
    if (!neededBufferSize || dataReference.size() != neededBufferSize)
        return false;

    ASSERT(dataReference.data());
    CFNumberRef number = CFNumberCreate(0, numberType, dataReference.data());
    result = adoptCF(number);

    return true;
}

void encode(Encoder& encoder, CFStringRef string)
{
    CFIndex length = CFStringGetLength(string);
    CFStringEncoding encoding = CFStringGetFastestEncoding(string);

    CFRange range = CFRangeMake(0, length);
    CFIndex bufferLength = 0;

    CFIndex numConvertedBytes = CFStringGetBytes(string, range, encoding, 0, false, 0, 0, &bufferLength);
    ASSERT(numConvertedBytes == length);

    Vector<UInt8, 128> buffer(bufferLength);
    numConvertedBytes = CFStringGetBytes(string, range, encoding, 0, false, buffer.data(), buffer.size(), &bufferLength);
    ASSERT(numConvertedBytes == length);

    encoder.encodeEnum(encoding);
    encoder << IPC::DataReference(buffer);
}

bool decode(Decoder& decoder, RetainPtr<CFStringRef>& result)
{
    CFStringEncoding encoding;
    if (!decoder.decodeEnum(encoding))
        return false;

    if (!CFStringIsEncodingAvailable(encoding))
        return false;
    
    IPC::DataReference dataReference;
    if (!decoder.decode(dataReference))
        return false;

    CFStringRef string = CFStringCreateWithBytes(0, dataReference.data(), dataReference.size(), encoding, false);
    if (!string)
        return false;

    result = adoptCF(string);
    return true;
}

void encode(Encoder& encoder, CFURLRef url)
{
    CFURLRef baseURL = CFURLGetBaseURL(url);
    encoder << static_cast<bool>(baseURL);
    if (baseURL)
        encode(encoder, baseURL);

    WTF::URLCharBuffer urlBytes;
    WTF::getURLBytes(url, urlBytes);
    IPC::DataReference dataReference(reinterpret_cast<const uint8_t*>(urlBytes.data()), urlBytes.size());
    encoder << dataReference;
}

bool decode(Decoder& decoder, RetainPtr<CFURLRef>& result)
{
    RetainPtr<CFURLRef> baseURL;
    bool hasBaseURL;
    if (!decoder.decode(hasBaseURL))
        return false;
    if (hasBaseURL) {
        if (!decode(decoder, baseURL))
            return false;
    }

    IPC::DataReference urlBytes;
    if (!decoder.decode(urlBytes))
        return false;

#if USE(FOUNDATION)
    // FIXME: Move this to ArgumentCodersCFMac.mm and change this file back to be C++
    // instead of Objective-C++.
    if (urlBytes.isEmpty()) {
        // CFURL can't hold an empty URL, unlike NSURL.
        // FIXME: This discards base URL, which seems incorrect.
        result = (__bridge CFURLRef)[NSURL URLWithString:@""];
        return true;
    }
#endif

    result = WTF::createCFURLFromBuffer(reinterpret_cast<const char*>(urlBytes.data()), urlBytes.size(), baseURL.get());
    return result;
}

void encode(Encoder& encoder, SecCertificateRef certificate)
{
    RetainPtr<CFDataRef> data = adoptCF(SecCertificateCopyData(certificate));
    encode(encoder, data.get());
}

bool decode(Decoder& decoder, RetainPtr<SecCertificateRef>& result)
{
    RetainPtr<CFDataRef> data;
    if (!decode(decoder, data))
        return false;

    result = adoptCF(SecCertificateCreateWithData(0, data.get()));
    return true;
}

#if PLATFORM(IOS_FAMILY)
static bool secKeyRefDecodingAllowed;

void setAllowsDecodingSecKeyRef(bool allowsDecodingSecKeyRef)
{
    secKeyRefDecodingAllowed = allowsDecodingSecKeyRef;
}

static CFDataRef copyPersistentRef(SecKeyRef key)
{
    RELEASE_ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessCredentials));
    // This function differs from SecItemCopyPersistentRef in that it specifies an access group.
    // This is necessary in case there are multiple copies of the key in the keychain, because we
    // need a reference to the one that the Networking process will be able to access.
    CFDataRef persistentRef = nullptr;
    SecItemCopyMatching((CFDictionaryRef)@{
        (id)kSecReturnPersistentRef: @YES,
        (id)kSecValueRef: (id)key,
        (id)kSecAttrSynchronizable: (id)kSecAttrSynchronizableAny,
        (id)kSecAttrAccessGroup: @"com.apple.identities",
    }, (CFTypeRef*)&persistentRef);

    return persistentRef;
}
#endif

void encode(Encoder& encoder, SecIdentityRef identity)
{
    SecCertificateRef certificate = nullptr;
    SecIdentityCopyCertificate(identity, &certificate);
    encode(encoder, certificate);
    CFRelease(certificate);

    SecKeyRef key = nullptr;
    SecIdentityCopyPrivateKey(identity, &key);

    CFDataRef keyData = nullptr;
#if PLATFORM(IOS_FAMILY)
    keyData = copyPersistentRef(key);
#endif
#if PLATFORM(MAC)
    RELEASE_ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessCredentials));
    SecKeychainItemCreatePersistentReference((SecKeychainItemRef)key, &keyData);
#endif
    CFRelease(key);

    encoder << !!keyData;
    if (keyData) {
        encode(encoder, keyData);
        CFRelease(keyData);
    }
}

bool decode(Decoder& decoder, RetainPtr<SecIdentityRef>& result)
{
    RetainPtr<SecCertificateRef> certificate;
    if (!decode(decoder, certificate))
        return false;

    bool hasKey;
    if (!decoder.decode(hasKey))
        return false;

    if (!hasKey)
        return true;

    RetainPtr<CFDataRef> keyData;
    if (!decode(decoder, keyData))
        return false;

#if PLATFORM(COCOA)
    if (!hasProcessPrivilege(ProcessPrivilege::CanAccessCredentials))
        return true;
#endif

    SecKeyRef key = nullptr;
#if PLATFORM(IOS_FAMILY)
    if (secKeyRefDecodingAllowed)
        SecKeyFindWithPersistentRef(keyData.get(), &key);
#endif
#if PLATFORM(MAC)
    SecKeychainItemCopyFromPersistentReference(keyData.get(), (SecKeychainItemRef*)&key);
#endif
    if (key) {
        result = adoptCF(SecIdentityCreate(kCFAllocatorDefault, certificate.get(), key));
        CFRelease(key);
    }

    return true;
}

#if HAVE(SEC_KEYCHAIN)
void encode(Encoder& encoder, SecKeychainItemRef keychainItem)
{
    RELEASE_ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessCredentials));

    CFDataRef data;
    if (SecKeychainItemCreatePersistentReference(keychainItem, &data) == errSecSuccess) {
        encode(encoder, data);
        CFRelease(data);
    }
}

bool decode(Decoder& decoder, RetainPtr<SecKeychainItemRef>& result)
{
    RELEASE_ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessCredentials));

    RetainPtr<CFDataRef> data;
    if (!IPC::decode(decoder, data))
        return false;

    SecKeychainItemRef item;
    if (SecKeychainItemCopyFromPersistentReference(data.get(), &item) != errSecSuccess || !item)
        return false;
    
    result = adoptCF(item);
    return true;
}
#endif

#if HAVE(SEC_ACCESS_CONTROL)
void encode(Encoder& encoder, SecAccessControlRef accessControl)
{
    RetainPtr<CFDataRef> data = adoptCF(SecAccessControlCopyData(accessControl));
    if (data)
        encode(encoder, data.get());
}

bool decode(Decoder& decoder, RetainPtr<SecAccessControlRef>& result)
{
    RetainPtr<CFDataRef> data;
    if (!decode(decoder, data))
        return false;

    result = adoptCF(SecAccessControlCreateFromData(kCFAllocatorDefault, data.get(), nullptr));
    if (!result)
        return false;

    return true;
}
#endif

#if HAVE(SEC_TRUST_SERIALIZATION)
void encode(Encoder& encoder, SecTrustRef trust)
{
    auto data = adoptCF(SecTrustSerialize(trust, nullptr));
    if (!data) {
        encoder << false;
        return;
    }

    encoder << true;
    IPC::encode(encoder, data.get());
}

bool decode(Decoder& decoder, RetainPtr<SecTrustRef>& result)
{
    bool hasTrust;
    if (!decoder.decode(hasTrust))
        return false;

    if (!hasTrust)
        return true;

    RetainPtr<CFDataRef> trustData;
    if (!IPC::decode(decoder, trustData))
        return false;

    auto trust = adoptCF(SecTrustDeserialize(trustData.get(), nullptr));
    if (!trust)
        return false;

    result = WTFMove(trust);
    return true;
}
#endif

} // namespace IPC