Diff
Modified: trunk/Source/WebCore/ChangeLog (287014 => 287015)
--- trunk/Source/WebCore/ChangeLog 2021-12-14 08:12:55 UTC (rev 287014)
+++ trunk/Source/WebCore/ChangeLog 2021-12-14 08:18:02 UTC (rev 287015)
@@ -1,3 +1,44 @@
+2021-12-14 Ben Nham <[email protected]>
+
+ Add web push message decryption routines
+ https://bugs.webkit.org/show_bug.cgi?id=233903
+
+ Reviewed by Brady Eidson.
+
+ This adds routines to decrypt push payloads encoded with the aes128gcm and aesgcm schemes.
+ The crypto primitives that we depend are implemented in PushCrypto, while the logic to use
+ those primitives to decrypt message payloads is in PushMessageCrypto.
+
+ Test: TestWebKitAPI.PushMessageCrypto
+
+ * Headers.cmake:
+ * Modules/push-api/PushCrypto.cpp: Added.
+ (WebCore::PushCrypto::P256DHKeyPair::generate):
+ (WebCore::PushCrypto::validateP256DHPublicKey):
+ (WebCore::PushCrypto::computeP256DHSharedSecret):
+ (WebCore::PushCrypto::hmacSHA256):
+ (WebCore::PushCrypto::decryptAES128GCM):
+ * Modules/push-api/PushCrypto.h: Added.
+ * Modules/push-api/PushManager.cpp:
+ (WebCore::PushManager::subscribe):
+ * Modules/push-api/PushMessageCrypto.cpp: Added.
+ (WebCore::PushCrypto::ClientKeys::generate):
+ (WebCore::PushCrypto::areClientKeyLengthsValid):
+ (WebCore::PushCrypto::computeAES128GCMPaddingLength):
+ (WebCore::PushCrypto::decryptAES128GCMPayload):
+ (WebCore::PushCrypto::computeAESGCMPaddingLength):
+ (WebCore::PushCrypto::decryptAESGCMPayload):
+ * Modules/push-api/PushMessageCrypto.h: Added.
+ * Modules/push-api/cocoa/PushCryptoCocoa.cpp: Added.
+ (WebCore::PushCrypto::P256DHKeyPair::generate):
+ (WebCore::PushCrypto::validateP256DHPublicKey):
+ (WebCore::PushCrypto::computeP256DHSharedSecret):
+ (WebCore::PushCrypto::hmacSHA256):
+ (WebCore::PushCrypto::decryptAES128GCM):
+ * Sources.txt:
+ * SourcesCocoa.txt:
+ * WebCore.xcodeproj/project.pbxproj:
+
2021-12-13 Carlos Garcia Campos <[email protected]>
[GTK][a11y] Handle the Embedded method sent by AtkSocket from AccessibilityRootAtspi
Modified: trunk/Source/WebCore/Headers.cmake (287014 => 287015)
--- trunk/Source/WebCore/Headers.cmake 2021-12-14 08:12:55 UTC (rev 287014)
+++ trunk/Source/WebCore/Headers.cmake 2021-12-14 08:18:02 UTC (rev 287015)
@@ -290,6 +290,8 @@
Modules/permissions/PermissionObserver.h
Modules/permissions/PermissionState.h
+ Modules/push-api/PushCrypto.h
+ Modules/push-api/PushMessageCrypto.h
Modules/push-api/PushPermissionState.h
Modules/push-api/PushSubscriptionData.h
Added: trunk/Source/WebCore/Modules/push-api/PushCrypto.cpp (0 => 287015)
--- trunk/Source/WebCore/Modules/push-api/PushCrypto.cpp (rev 0)
+++ trunk/Source/WebCore/Modules/push-api/PushCrypto.cpp 2021-12-14 08:18:02 UTC (rev 287015)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include "config.h"
+#include "PushCrypto.h"
+
+#if ENABLE(SERVICE_WORKER)
+
+#include <wtf/Scope.h>
+
+namespace WebCore::PushCrypto {
+
+#if !PLATFORM(COCOA)
+
+P256DHKeyPair P256DHKeyPair::generate(void)
+{
+ return { };
+}
+
+bool validateP256DHPublicKey(Span<const uint8_t>)
+{
+ return false;
+}
+
+std::optional<Vector<uint8_t>> computeP256DHSharedSecret(Span<const uint8_t> publicKey, const P256DHKeyPair& keyPair)
+{
+ return std::nullopt;
+}
+
+Vector<uint8_t> hmacSHA256(Span<const uint8_t>, Span<const uint8_t>)
+{
+ return { };
+}
+
+std::optional<Vector<uint8_t>> decryptAES128GCM(Span<const uint8_t>, Span<const uint8_t>, Span<const uint8_t>)
+{
+ return std::nullopt;
+}
+
+#endif // !PLATFORM(COCOA)
+
+} // namespace WebCore::PushCrypto
+
+#endif // ENABLE(SERVICE_WORKER)
Added: trunk/Source/WebCore/Modules/push-api/PushCrypto.h (0 => 287015)
--- trunk/Source/WebCore/Modules/push-api/PushCrypto.h (rev 0)
+++ trunk/Source/WebCore/Modules/push-api/PushCrypto.h 2021-12-14 08:18:02 UTC (rev 287015)
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#if ENABLE(SERVICE_WORKER)
+
+#include <wtf/Forward.h>
+#include <wtf/Span.h>
+#include <wtf/Vector.h>
+
+namespace WebCore::PushCrypto {
+
+static constexpr size_t p256dhPublicKeyLength = 65;
+static constexpr size_t p256dhPrivateKeyLength = 32;
+static constexpr size_t p256dhSharedSecretLength = 32;
+static constexpr size_t sha256DigestLength = 32;
+static constexpr size_t aes128GCMTagLength = 16;
+
+struct P256DHKeyPair {
+ Vector<uint8_t> publicKey;
+ Vector<uint8_t> privateKey;
+
+ static P256DHKeyPair generate(void);
+};
+
+bool validateP256DHPublicKey(Span<const uint8_t> publicKey);
+
+WEBCORE_EXPORT std::optional<Vector<uint8_t>> computeP256DHSharedSecret(Span<const uint8_t> publicKey, const P256DHKeyPair&);
+
+WEBCORE_EXPORT Vector<uint8_t> hmacSHA256(Span<const uint8_t> key, Span<const uint8_t> message);
+
+WEBCORE_EXPORT std::optional<Vector<uint8_t>> decryptAES128GCM(Span<const uint8_t> key, Span<const uint8_t> iv, Span<const uint8_t> cipherTextWithTag);
+
+} // namespace WebCore::PushCrypto
+
+#endif // ENABLE(SERVICE_WORKER)
Modified: trunk/Source/WebCore/Modules/push-api/PushManager.cpp (287014 => 287015)
--- trunk/Source/WebCore/Modules/push-api/PushManager.cpp 2021-12-14 08:12:55 UTC (rev 287014)
+++ trunk/Source/WebCore/Modules/push-api/PushManager.cpp 2021-12-14 08:18:02 UTC (rev 287015)
@@ -28,11 +28,11 @@
#if ENABLE(SERVICE_WORKER)
-#include "CryptoKeyEC.h"
#include "EventLoop.h"
#include "Exception.h"
#include "JSPushPermissionState.h"
#include "JSPushSubscription.h"
+#include "PushCrypto.h"
#include "ScriptExecutionContext.h"
#include "ServiceWorkerRegistration.h"
#include <wtf/IsoMallocInlines.h>
@@ -101,14 +101,7 @@
return;
}
-#if ENABLE(WEB_CRYPTO)
- auto keyData = keyDataResult.returnValue();
- auto key = CryptoKeyEC::importRaw(CryptoAlgorithmIdentifier::ECDSA, "P-256"_s, WTFMove(keyData), false, CryptoKeyUsageVerify);
-#else
- auto key = nullptr;
-#endif
-
- if (!key) {
+ if (!PushCrypto::validateP256DHPublicKey(keyDataResult.returnValue())) {
promise.reject(Exception { InvalidAccessError, "applicationServerKey must contain a valid P-256 public key"_s });
return;
}
Added: trunk/Source/WebCore/Modules/push-api/PushMessageCrypto.cpp (0 => 287015)
--- trunk/Source/WebCore/Modules/push-api/PushMessageCrypto.cpp (rev 0)
+++ trunk/Source/WebCore/Modules/push-api/PushMessageCrypto.cpp 2021-12-14 08:18:02 UTC (rev 287015)
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include "config.h"
+#include "PushMessageCrypto.h"
+
+#if ENABLE(SERVICE_WORKER)
+
+#include "PushCrypto.h"
+#include <wtf/ByteOrder.h>
+#include <wtf/CryptographicallyRandomNumber.h>
+
+namespace WebCore::PushCrypto {
+
+// Arbitrary limit that's larger than the largest payload APNS should ever give us.
+static constexpr size_t maxPushPayloadLength = 65535;
+
+// From RFC8291.
+static constexpr size_t saltLength = 16;
+static constexpr size_t sharedAuthSecretLength = 16;
+
+ClientKeys ClientKeys::generate()
+{
+ uint8_t sharedAuthSecret[sharedAuthSecretLength];
+ cryptographicallyRandomValues(sharedAuthSecret, sizeof(sharedAuthSecret));
+
+ return ClientKeys {
+ P256DHKeyPair::generate(),
+ Vector<uint8_t> { sharedAuthSecret, sizeof(sharedAuthSecret) }
+ };
+}
+
+static bool areClientKeyLengthsValid(const ClientKeys& clientKeys)
+{
+ return clientKeys.clientP256DHKeyPair.publicKey.size() == p256dhPublicKeyLength && clientKeys.clientP256DHKeyPair.privateKey.size() == p256dhPrivateKeyLength && clientKeys.sharedAuthSecret.size() == sharedAuthSecretLength;
+}
+
+static size_t computeAES128GCMPaddingLength(const uint8_t *begin, size_t length)
+{
+ /*
+ * Compute padding length as defined in RFC8188 Section 2:
+ *
+ * +-----------+-----+
+ * | data | pad |
+ * +-----------+-----+
+ *
+ * pad must be of non-zero length and is a delimiter octet (0x02) followed by any number of 0x00 octets.
+ */
+ if (!length)
+ return SIZE_MAX;
+
+ const uint8_t* end = begin + length;
+ const uint8_t* cur = end - 1;
+ while (cur > begin && (*cur == 0x00))
+ --cur;
+ if (*cur != 0x02)
+ return SIZE_MAX;
+
+ return end - cur;
+}
+
+std::optional<Vector<uint8_t>> decryptAES128GCMPayload(const ClientKeys& clientKeys, Span<const uint8_t> payload)
+{
+ if (!areClientKeyLengthsValid(clientKeys))
+ return std::nullopt;
+
+ // Extract encryption parameters from header as described in RFC8188.
+ struct PayloadHeader {
+ uint8_t salt[saltLength];
+ uint8_t ignored[4];
+ uint8_t keyLength;
+ uint8_t serverPublicKey[p256dhPublicKeyLength];
+ };
+ static_assert(sizeof(PayloadHeader) == 86);
+ static constexpr size_t minPushPayloadLength = sizeof(PayloadHeader) + 1 /* minPaddingLength */ + aes128GCMTagLength;
+
+ if (payload.size() < minPushPayloadLength || payload.size() > maxPushPayloadLength)
+ return std::nullopt;
+
+ PayloadHeader header;
+ memcpy(&header, payload.data(), sizeof(header));
+
+ if (header.keyLength != p256dhPublicKeyLength)
+ return std::nullopt;
+
+ /*
+ * The rest of the comments are snippets from RFC8291 3.4.
+ *
+ * -- For a user agent:
+ * ecdh_secret = ECDH(ua_private, as_public)
+ */
+ auto ecdhSecretResult = computeP256DHSharedSecret(header.serverPublicKey, clientKeys.clientP256DHKeyPair);
+ if (!ecdhSecretResult)
+ return std::nullopt;
+
+ /*
+ * # HKDF-Extract(salt=auth_secret, IKM=ecdh_secret)
+ * PRK_key = HMAC-SHA-256(auth_secret, ecdh_secret)
+ */
+ auto prkKey = hmacSHA256(clientKeys.sharedAuthSecret, *ecdhSecretResult);
+
+ /*
+ * # HKDF-Expand(PRK_key, key_info, L_key=32)
+ * key_info = "WebPush: info" || 0x00 || ua_public || as_public
+ * IKM = HMAC-SHA-256(PRK_key, key_info || 0x01)
+ */
+ struct KeyInfo {
+ uint8_t label[14] = { "WebPush: info" };
+ uint8_t clientKey[p256dhPublicKeyLength];
+ uint8_t serverKey[p256dhPublicKeyLength];
+ uint8_t end = 0x01;
+ };
+ static_assert(sizeof(KeyInfo) == 145);
+
+ KeyInfo keyInfo;
+ memcpy(keyInfo.clientKey, clientKeys.clientP256DHKeyPair.publicKey.data(), p256dhPublicKeyLength);
+ memcpy(keyInfo.serverKey, header.serverPublicKey, p256dhPublicKeyLength);
+
+ auto ikm = hmacSHA256(prkKey, Span(reinterpret_cast<uint8_t*>(&keyInfo), sizeof(keyInfo)));
+
+ /*
+ * # HKDF-Extract(salt, IKM)
+ * PRK = HMAC-SHA-256(salt, IKM)
+ */
+ auto prk = hmacSHA256(header.salt, ikm);
+
+ /*
+ * # HKDF-Expand(PRK, cek_info, L_cek=16)
+ * cek_info = "Content-Encoding: aes128gcm" || 0x00
+ * CEK = HMAC-SHA-256(PRK, cek_info || 0x01)[0..15]
+ */
+ static const uint8_t cekInfo[] = "Content-Encoding: aes128gcm\x00\x01";
+ auto cek = hmacSHA256(prk, Span(cekInfo, sizeof(cekInfo) - 1));
+ cek.shrink(16);
+
+ /*
+ * # HKDF-Expand(PRK, nonce_info, L_nonce=12)
+ * nonce_info = "Content-Encoding: nonce" || 0x00
+ * NONCE = HMAC-SHA-256(PRK, nonce_info || 0x01)[0..11]
+ */
+ static const uint8_t nonceInfo[] = "Content-Encoding: nonce\x00\x01";
+ auto nonce = hmacSHA256(prk, Span(nonceInfo, sizeof(nonceInfo) - 1));
+ nonce.shrink(12);
+
+ // Finally, decrypt with AES128GCM and return the unpadded plaintext.
+ auto cipherText = Span(payload.data() + sizeof(header), payload.size() - sizeof(header));
+ auto plainTextResult = decryptAES128GCM(cek, nonce, cipherText);
+ if (!plainTextResult)
+ return std::nullopt;
+
+ auto plainText = WTFMove(plainTextResult.value());
+ size_t paddingLength = computeAES128GCMPaddingLength(plainText.data(), plainText.size());
+ if (paddingLength == SIZE_MAX)
+ return std::nullopt;
+
+ plainText.shrink(plainText.size() - paddingLength);
+ return plainText;
+}
+
+static size_t computeAESGCMPaddingLength(const uint8_t *begin, size_t length)
+{
+ /*
+ * Compute padding length as defined in draft-ietf-httpbis-encryption-encoding-03:
+ *
+ * +-----+-----------+
+ * | pad | data |
+ * +-----+-----------+
+ *
+ * Padding consists of a two octet unsigned integer in network byte order, followed by that
+ * number of 0x00 octets. The minimum padding size is 2 bytes.
+ */
+ if (length < 2)
+ return SIZE_MAX;
+
+ uint16_t paddingLength;
+ memcpy(&paddingLength, begin, 2);
+ paddingLength = ntohs(paddingLength);
+
+ const uint8_t* cur = begin + 2;
+ const uint8_t* end = begin + length;
+ uint16_t paddingLeft = paddingLength;
+ while (cur < end && (*cur == 0x0) && paddingLeft) {
+ ++cur;
+ --paddingLeft;
+ }
+
+ if (paddingLeft)
+ return SIZE_MAX;
+
+ return cur - begin;
+}
+
+std::optional<Vector<uint8_t>> decryptAESGCMPayload(const ClientKeys& clientKeys, Span<const uint8_t> serverP256DHPublicKey, Span<const uint8_t> salt, Span<const uint8_t> payload)
+{
+ if (!areClientKeyLengthsValid(clientKeys) || serverP256DHPublicKey.size() != p256dhPublicKeyLength || salt.size() != saltLength)
+ return std::nullopt;
+
+ // Padding must be at least the size of the two octet unsigned integer used in the padding scheme plus the size of the AES128GCM tag.
+ if (payload.size() < 2 + aes128GCMTagLength || payload.size() > maxPushPayloadLength)
+ return std::nullopt;
+
+ /*
+ * These comments are snippets from draft-ietf-webpush-encryption-04.
+ *
+ * -- For a User Agent:
+ * ecdh_secret = ECDH(ua_private, as_public)
+ */
+ auto ecdhSecretResult = computeP256DHSharedSecret(serverP256DHPublicKey, clientKeys.clientP256DHKeyPair);
+ if (!ecdhSecretResult)
+ return std::nullopt;
+
+ /*
+ * auth_info = "Content-Encoding: auth" || 0x00
+ * PRK_combine = HMAC-SHA-256(auth_secret, ecdh_secret)
+ * IKM = HMAC-SHA-256(PRK_combine, auth_info || 0x01)
+ * PRK = HMAC-SHA-256(salt, IKM)
+ */
+ static const uint8_t authInfo[] = "Content-Encoding: auth\x00\x01";
+ auto prkCombine = hmacSHA256(clientKeys.sharedAuthSecret, *ecdhSecretResult);
+ auto ikm = hmacSHA256(prkCombine, Span(authInfo, sizeof(authInfo) - 1));
+ auto prk = hmacSHA256(salt, ikm);
+
+ /*
+ * context = "P-256" || 0x00 ||
+ * 0x00 || 0x41 || ua_public ||
+ * 0x00 || 0x41 || as_public
+ *
+ * Note that we also append a 0x01 byte at the end here since the cek and nonce
+ * derivation functions below require that trailing 0x01 byte.
+ */
+ struct KeyDerivationContext {
+ uint8_t label[6] = { "P-256" };
+ uint8_t clientPublicKeyLength[2] = { 0, 0x41 };
+ uint8_t clientPublicKey[p256dhPublicKeyLength];
+ uint8_t serverPublicKeyLength[2] = { 0, 0x41 };
+ uint8_t serverPublicKey[p256dhPublicKeyLength];
+ uint8_t end = 0x01;
+ };
+ static_assert(sizeof(KeyDerivationContext) == 141);
+ KeyDerivationContext context;
+ memcpy(context.clientPublicKey, clientKeys.clientP256DHKeyPair.publicKey.data(), p256dhPublicKeyLength);
+ memcpy(context.serverPublicKey, serverP256DHPublicKey.data(), p256dhPublicKeyLength);
+
+ /*
+ * cek_info = "Content-Encoding: aesgcm" || 0x00 || context
+ * CEK = HMAC-SHA-256(PRK, cek_info || 0x01)[0..15]
+ */
+ static const uint8_t cekInfoHeader[] = "Content-Encoding: aesgcm";
+ uint8_t cekInfo[sizeof(cekInfoHeader) + sizeof(context)];
+ memcpy(cekInfo, cekInfoHeader, sizeof(cekInfoHeader));
+ memcpy(cekInfo + sizeof(cekInfoHeader), &context, sizeof(context));
+
+ auto cek = hmacSHA256(prk, cekInfo);
+ cek.shrink(16);
+
+ /*
+ * nonce_info = "Content-Encoding: nonce" || 0x00 || context
+ * NONCE = HMAC-SHA-256(PRK, nonce_info || 0x01)[0..11]
+ */
+ static const uint8_t nonceInfoHeader[] = "Content-Encoding: nonce";
+ uint8_t nonceInfo[sizeof(nonceInfoHeader) + sizeof(context)];
+ memcpy(nonceInfo, nonceInfoHeader, sizeof(nonceInfoHeader));
+ memcpy(nonceInfo + sizeof(nonceInfoHeader), &context, sizeof(context));
+
+ auto nonce = hmacSHA256(prk, nonceInfo);
+ nonce.shrink(12);
+
+ // Finally, decrypt with AES128GCM and return the unpadded plaintext.
+ auto plainTextResult = decryptAES128GCM(cek, nonce, payload);
+ if (!plainTextResult)
+ return std::nullopt;
+
+ auto plainText = WTFMove(plainTextResult.value());
+ size_t paddingLength = computeAESGCMPaddingLength(plainText.data(), plainText.size());
+ if (paddingLength == SIZE_MAX)
+ return std::nullopt;
+
+ return Vector<uint8_t> { plainText.data() + paddingLength, plainText.size() - paddingLength };
+}
+
+} // namespace WebCore::PushCrypto
+
+#endif // ENABLE(SERVICE_WORKER)
Added: trunk/Source/WebCore/Modules/push-api/PushMessageCrypto.h (0 => 287015)
--- trunk/Source/WebCore/Modules/push-api/PushMessageCrypto.h (rev 0)
+++ trunk/Source/WebCore/Modules/push-api/PushMessageCrypto.h 2021-12-14 08:18:02 UTC (rev 287015)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#if ENABLE(SERVICE_WORKER)
+
+#include "PushCrypto.h"
+#include <wtf/Forward.h>
+#include <wtf/Span.h>
+#include <wtf/Vector.h>
+
+namespace WebCore::PushCrypto {
+
+struct ClientKeys {
+ P256DHKeyPair clientP256DHKeyPair;
+ Vector<uint8_t> sharedAuthSecret;
+
+ WEBCORE_EXPORT static ClientKeys generate();
+};
+
+// Decrypts a push payload encoded with the aes128gcm Content-Encoding as described in RFC8291.
+WEBCORE_EXPORT std::optional<Vector<uint8_t>> decryptAES128GCMPayload(const ClientKeys&, Span<const uint8_t> payload);
+
+// Decrypts a push payload encoded with the aesgcm Content-Encoding as described in draft-ietf-webpush-encryption-04.
+WEBCORE_EXPORT std::optional<Vector<uint8_t>> decryptAESGCMPayload(const ClientKeys&, Span<const uint8_t> serverP256DHPublicKey, Span<const uint8_t> salt, Span<const uint8_t> payload);
+
+} // namespace WebCore::PushCrypto
+
+#endif // ENABLE(SERVICE_WORKER)
Added: trunk/Source/WebCore/Modules/push-api/cocoa/PushCryptoCocoa.cpp (0 => 287015)
--- trunk/Source/WebCore/Modules/push-api/cocoa/PushCryptoCocoa.cpp (rev 0)
+++ trunk/Source/WebCore/Modules/push-api/cocoa/PushCryptoCocoa.cpp 2021-12-14 08:18:02 UTC (rev 287015)
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include "config.h"
+#include "PushCrypto.h"
+
+#if ENABLE(SERVICE_WORKER)
+
+#include <CommonCrypto/CommonHMAC.h>
+#include <pal/spi/cocoa/CommonCryptoSPI.h>
+#include <wtf/Scope.h>
+
+namespace WebCore::PushCrypto {
+
+P256DHKeyPair P256DHKeyPair::generate(void)
+{
+ CCECCryptorRef ccPublicKey = nullptr;
+ CCECCryptorRef ccPrivateKey = nullptr;
+ auto releaser = WTF::makeScopeExit([&ccPublicKey, &ccPrivateKey]() {
+ if (ccPublicKey)
+ CCECCryptorRelease(ccPublicKey);
+ if (ccPrivateKey)
+ CCECCryptorRelease(ccPrivateKey);
+ });
+
+ CCCryptorStatus status = CCECCryptorGeneratePair(256, &ccPublicKey, &ccPrivateKey);
+ RELEASE_ASSERT(status == kCCSuccess);
+
+ uint8_t publicKey[p256dhPublicKeyLength];
+ size_t publicKeyLength = sizeof(publicKey);
+ status = CCECCryptorExportKey(kCCImportKeyBinary, publicKey, &publicKeyLength, ccECKeyPublic, ccPublicKey);
+ RELEASE_ASSERT(status == kCCSuccess && publicKeyLength == sizeof(publicKey));
+
+ // CommonCrypto expects the binary format to be 65 byte public key followed by the 32 byte private key, so we want to extract the last 32 bytes from the buffer.
+ uint8_t key[p256dhPublicKeyLength + p256dhPrivateKeyLength];
+ size_t keyLength = sizeof(key);
+ status = CCECCryptorExportKey(kCCImportKeyBinary, key, &keyLength, ccECKeyPrivate, ccPrivateKey);
+ RELEASE_ASSERT(status == kCCSuccess && keyLength == sizeof(key));
+
+ return P256DHKeyPair {
+ Vector<uint8_t> { publicKey, p256dhPublicKeyLength },
+ Vector<uint8_t> { key + p256dhPublicKeyLength, p256dhPrivateKeyLength }
+ };
+}
+
+bool validateP256DHPublicKey(Span<const uint8_t> publicKey)
+{
+ CCECCryptorRef ccPublicKey = nullptr;
+ CCCryptorStatus status = CCECCryptorImportKey(kCCImportKeyBinary, publicKey.data(), publicKey.size(), ccECKeyPublic, &ccPublicKey);
+ if (!ccPublicKey)
+ return false;
+ CCECCryptorRelease(ccPublicKey);
+ return status == kCCSuccess;
+}
+
+std::optional<Vector<uint8_t>> computeP256DHSharedSecret(Span<const uint8_t> publicKey, const P256DHKeyPair& keyPair)
+{
+ if (publicKey.size() != p256dhPublicKeyLength || keyPair.publicKey.size() != p256dhPublicKeyLength || keyPair.privateKey.size() != p256dhPrivateKeyLength)
+ return std::nullopt;
+
+ CCECCryptorRef ccPublicKey = nullptr;
+ CCECCryptorRef ccPrivateKey = nullptr;
+ auto releaser = WTF::makeScopeExit([&ccPublicKey, &ccPrivateKey]() {
+ if (ccPublicKey)
+ CCECCryptorRelease(ccPublicKey);
+ if (ccPrivateKey)
+ CCECCryptorRelease(ccPrivateKey);
+ });
+
+ if (CCECCryptorImportKey(kCCImportKeyBinary, publicKey.data(), p256dhPublicKeyLength, ccECKeyPublic, &ccPublicKey) != kCCSuccess)
+ return std::nullopt;
+
+ // CommonCrypto expects the binary format to be 65 byte public key followed by the 32 byte private key.
+ uint8_t keyBuf[p256dhPublicKeyLength + p256dhPrivateKeyLength];
+ memcpy(keyBuf, keyPair.publicKey.data(), p256dhPublicKeyLength);
+ memcpy(keyBuf + p256dhPublicKeyLength, keyPair.privateKey.data(), p256dhPrivateKeyLength);
+ if (CCECCryptorImportKey(kCCImportKeyBinary, keyBuf, sizeof(keyBuf), ccECKeyPrivate, &ccPrivateKey) != kCCSuccess)
+ return std::nullopt;
+
+ Vector<uint8_t> sharedSecret(p256dhSharedSecretLength);
+ size_t sharedSecretLength = sharedSecret.size();
+ if (CCECCryptorComputeSharedSecret(ccPrivateKey, ccPublicKey, sharedSecret.begin(), &sharedSecretLength) != kCCSuccess || sharedSecretLength != p256dhSharedSecretLength)
+ return std::nullopt;
+
+ return sharedSecret;
+}
+
+Vector<uint8_t> hmacSHA256(Span<const uint8_t> key, Span<const uint8_t> message)
+{
+ Vector<uint8_t> result(sha256DigestLength);
+ CCHmac(kCCHmacAlgSHA256, key.data(), key.size(), message.data(), message.size(), result.begin());
+ return result;
+}
+
+std::optional<Vector<uint8_t>> decryptAES128GCM(Span<const uint8_t> key, Span<const uint8_t> iv, Span<const uint8_t> cipherTextWithTag)
+{
+ if (cipherTextWithTag.size() < aes128GCMTagLength)
+ return std::nullopt;
+
+ Vector<uint8_t> plainText(cipherTextWithTag.size() - aes128GCMTagLength);
+ auto result = CCCryptorGCMOneshotDecrypt(kCCAlgorithmAES, key.data(), key.size(), iv.data(), iv.size(), nullptr /* additionalData */, 0 /* additionalDataLength */, cipherTextWithTag.data(), cipherTextWithTag.size() - aes128GCMTagLength, plainText.data(), cipherTextWithTag.end() - aes128GCMTagLength, aes128GCMTagLength);
+ if (result != kCCSuccess)
+ return std::nullopt;
+
+ return plainText;
+}
+
+} // namespace WebCore::PushCrypto
+
+#endif // ENABLE(SERVICE_WORKER)
Modified: trunk/Source/WebCore/PAL/ChangeLog (287014 => 287015)
--- trunk/Source/WebCore/PAL/ChangeLog 2021-12-14 08:12:55 UTC (rev 287014)
+++ trunk/Source/WebCore/PAL/ChangeLog 2021-12-14 08:18:02 UTC (rev 287015)
@@ -1,3 +1,12 @@
+2021-12-14 Ben Nham <[email protected]>
+
+ Add web push message decryption routines
+ https://bugs.webkit.org/show_bug.cgi?id=233903
+
+ Reviewed by Brady Eidson.
+
+ * pal/spi/cocoa/CommonCryptoSPI.h: Add AES-GCM decryption SPI.
+
2021-12-11 Commit Queue <[email protected]>
Unreviewed, reverting r286893.
Modified: trunk/Source/WebCore/PAL/pal/spi/cocoa/CommonCryptoSPI.h (287014 => 287015)
--- trunk/Source/WebCore/PAL/pal/spi/cocoa/CommonCryptoSPI.h 2021-12-14 08:12:55 UTC (rev 287014)
+++ trunk/Source/WebCore/PAL/pal/spi/cocoa/CommonCryptoSPI.h 2021-12-14 08:18:02 UTC (rev 287015)
@@ -25,6 +25,8 @@
#pragma once
+#include <CommonCrypto/CommonCrypto.h>
+
#if USE(APPLE_INTERNAL_SDK)
#include <CommonCrypto/CommonCryptorSPI.h>
@@ -118,6 +120,7 @@
extern "C" CCStatus CCKDFParametersCreateHkdf(CCKDFParametersRef *params, const void *salt, size_t saltLen, const void *context, size_t contextLen);
extern "C" void CCKDFParametersDestroy(CCKDFParametersRef params);
extern "C" CCCryptorStatus CCCryptorGCM(CCOperation op, CCAlgorithm alg, const void* key, size_t keyLength, const void* iv, size_t ivLen, const void* aData, size_t aDataLen, const void* dataIn, size_t dataInLength, void* dataOut, void* tag, size_t* tagLength);
+extern "C" CCCryptorStatus CCCryptorGCMOneshotDecrypt(CCAlgorithm alg, const void *key, size_t keyLength, const void *iv, size_t ivLen, const void *aData, size_t aDataLen, const void *dataIn, size_t dataInLength, void *dataOut, const void *tagIn, size_t tagLength);
extern "C" CCCryptorStatus CCRSACryptorCreateFromData(CCRSAKeyType keyType, const uint8_t *modulus, size_t modulusLength, const uint8_t *exponent, size_t exponentLength, const uint8_t *p, size_t pLength, const uint8_t *q, size_t qLength, CCRSACryptorRef *ref);
#endif // !USE(APPLE_INTERNAL_SDK)
Modified: trunk/Source/WebCore/Sources.txt (287014 => 287015)
--- trunk/Source/WebCore/Sources.txt 2021-12-14 08:12:55 UTC (rev 287014)
+++ trunk/Source/WebCore/Sources.txt 2021-12-14 08:18:02 UTC (rev 287015)
@@ -257,7 +257,9 @@
Modules/pictureinpicture/EnterPictureInPictureEvent.cpp
Modules/pictureinpicture/HTMLVideoElementPictureInPicture.cpp
Modules/pictureinpicture/PictureInPictureWindow.cpp
+Modules/push-api/PushCrypto.cpp
Modules/push-api/PushEvent.cpp
+Modules/push-api/PushMessageCrypto.cpp
Modules/push-api/PushMessageData.cpp
Modules/push-api/PushSubscription.cpp
Modules/push-api/PushSubscriptionChangeEvent.cpp
Modified: trunk/Source/WebCore/SourcesCocoa.txt (287014 => 287015)
--- trunk/Source/WebCore/SourcesCocoa.txt 2021-12-14 08:12:55 UTC (rev 287014)
+++ trunk/Source/WebCore/SourcesCocoa.txt 2021-12-14 08:18:02 UTC (rev 287015)
@@ -106,6 +106,7 @@
Modules/model-element/scenekit/SceneKitModelLoaderClient.mm
Modules/model-element/scenekit/SceneKitModelLoaderUSD.mm
Modules/model-element/scenekit/SceneKitModelPlayer.mm
+Modules/push-api/cocoa/PushCryptoCocoa.cpp
Modules/speech/cocoa/SpeechRecognizerCocoa.mm
Modules/speech/cocoa/WebSpeechRecognizerTask.mm
Modules/speech/cocoa/WebSpeechRecognizerTaskMock.mm
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (287014 => 287015)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2021-12-14 08:12:55 UTC (rev 287014)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2021-12-14 08:18:02 UTC (rev 287015)
@@ -5436,6 +5436,8 @@
EB28ED442717AC11005F0393 /* PushSubscriptionData.h in Headers */ = {isa = PBXBuildFile; fileRef = EB28ED412717ABA9005F0393 /* PushSubscriptionData.h */; settings = {ATTRIBUTES = (Private, ); }; };
EB3C29B62729C38800FB65B4 /* PushSubscriptionChangeEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = EB3C29B42729C36E00FB65B4 /* PushSubscriptionChangeEvent.h */; };
EB3C29B72729C38C00FB65B4 /* PushSubscriptionChangeEventInit.h in Headers */ = {isa = PBXBuildFile; fileRef = EB3C29B12729C36D00FB65B4 /* PushSubscriptionChangeEventInit.h */; };
+ EBA75C46275EC61A00D6D31C /* PushCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = EBA75C42275EBDAB00D6D31C /* PushCrypto.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ EBA75C47275EC62100D6D31C /* PushMessageCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = EBA75C40275EBDAA00D6D31C /* PushMessageCrypto.h */; settings = {ATTRIBUTES = (Private, ); }; };
EBB9738127100671007732EF /* PushManager.h in Headers */ = {isa = PBXBuildFile; fileRef = EBB9738027100654007732EF /* PushManager.h */; };
EBB9738227100676007732EF /* PushPermissionState.h in Headers */ = {isa = PBXBuildFile; fileRef = EBB9737827100651007732EF /* PushPermissionState.h */; settings = {ATTRIBUTES = (Private, ); }; };
EBB9738327100684007732EF /* ServiceWorkerRegistrationPushAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = EBB9737A27100652007732EF /* ServiceWorkerRegistrationPushAPI.h */; };
@@ -17581,6 +17583,11 @@
EB3C29B22729C36E00FB65B4 /* PushSubscriptionChangeEventInit.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = PushSubscriptionChangeEventInit.idl; sourceTree = "<group>"; };
EB3C29B32729C36E00FB65B4 /* PushSubscriptionChangeEvent.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = PushSubscriptionChangeEvent.idl; sourceTree = "<group>"; };
EB3C29B42729C36E00FB65B4 /* PushSubscriptionChangeEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PushSubscriptionChangeEvent.h; sourceTree = "<group>"; };
+ EBA75C3E275EBDAA00D6D31C /* PushMessageCrypto.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PushMessageCrypto.cpp; sourceTree = "<group>"; };
+ EBA75C40275EBDAA00D6D31C /* PushMessageCrypto.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PushMessageCrypto.h; sourceTree = "<group>"; };
+ EBA75C41275EBDAB00D6D31C /* PushCrypto.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PushCrypto.cpp; sourceTree = "<group>"; };
+ EBA75C42275EBDAB00D6D31C /* PushCrypto.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PushCrypto.h; sourceTree = "<group>"; };
+ EBA75C45275EC58E00D6D31C /* PushCryptoCocoa.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PushCryptoCocoa.cpp; sourceTree = "<group>"; };
EBB9737827100651007732EF /* PushPermissionState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PushPermissionState.h; sourceTree = "<group>"; };
EBB9737A27100652007732EF /* ServiceWorkerRegistrationPushAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ServiceWorkerRegistrationPushAPI.h; sourceTree = "<group>"; };
EBB9737B27100652007732EF /* ServiceWorkerRegistration+PushAPI.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = "ServiceWorkerRegistration+PushAPI.idl"; sourceTree = "<group>"; };
@@ -21034,6 +21041,9 @@
418FCBB82706E4BD00F96ECA /* push-api */ = {
isa = PBXGroup;
children = (
+ EBA75C44275EC56000D6D31C /* cocoa */,
+ EBA75C41275EBDAB00D6D31C /* PushCrypto.cpp */,
+ EBA75C42275EBDAB00D6D31C /* PushCrypto.h */,
EB0FB6FD270D0AE800F7810D /* PushEncryptionKeyName.h */,
EB0FB700270D0AEA00F7810D /* PushEncryptionKeyName.idl */,
418FCBBB2706E4F600F96ECA /* PushEvent.cpp */,
@@ -21044,6 +21054,8 @@
EBB9737C27100652007732EF /* PushManager.cpp */,
EBB9738027100654007732EF /* PushManager.h */,
EBB9737E27100653007732EF /* PushManager.idl */,
+ EBA75C3E275EBDAA00D6D31C /* PushMessageCrypto.cpp */,
+ EBA75C40275EBDAA00D6D31C /* PushMessageCrypto.h */,
418FCBBE2706E4F700F96ECA /* PushMessageData.cpp */,
418FCBBC2706E4F600F96ECA /* PushMessageData.h */,
418FCBBA2706E4F500F96ECA /* PushMessageData.idl */,
@@ -31231,6 +31243,14 @@
path = "contact-picker";
sourceTree = "<group>";
};
+ EBA75C44275EC56000D6D31C /* cocoa */ = {
+ isa = PBXGroup;
+ children = (
+ EBA75C45275EC58E00D6D31C /* PushCryptoCocoa.cpp */,
+ );
+ path = cocoa;
+ sourceTree = "<group>";
+ };
ED501DC90B249F3900AE18D9 /* mac */ = {
isa = PBXGroup;
children = (
@@ -36424,10 +36444,12 @@
57303BEB20097F4000355965 /* PublicKeyCredentialType.h in Headers */,
0081FF0016B0A2D3008AAA7A /* PublicSuffix.h in Headers */,
10FB084B14E15C7E00A3DB98 /* PublicURLManager.h in Headers */,
+ EBA75C46275EC61A00D6D31C /* PushCrypto.h in Headers */,
EB0FB708270D0B1000F7810D /* PushEncryptionKeyName.h in Headers */,
418FCBC12706E4FB00F96ECA /* PushEvent.h in Headers */,
418FCBC22706E50100F96ECA /* PushEventInit.h in Headers */,
EBB9738127100671007732EF /* PushManager.h in Headers */,
+ EBA75C47275EC62100D6D31C /* PushMessageCrypto.h in Headers */,
EBB9738227100676007732EF /* PushPermissionState.h in Headers */,
83D511F6250C1CBF002EDC51 /* PushPullFIFO.h in Headers */,
EB0FB709270D0B1800F7810D /* PushSubscription.h in Headers */,