Skip to content

Commit a4c08bf

Browse files
Enabled the test that checked for shared secret failures
1 parent 8be059b commit a4c08bf

File tree

2 files changed

+36
-31
lines changed

2 files changed

+36
-31
lines changed

Sources/WebPush/WebPushManager.swift

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -260,12 +260,12 @@ public actor WebPushManager: Sendable {
260260
logger: Logger? = nil
261261
) async throws {
262262
switch executor {
263-
case .httpClient(let httpClient, let privateKey):
263+
case .httpClient(let httpClient, let privateKeyProvider):
264264
var logger = logger ?? backgroundActivityLogger
265265
logger[metadataKey: "message"] = ".data(\(message.base64URLEncodedString()))"
266266
try await execute(
267267
httpClient: httpClient,
268-
applicationServerECDHPrivateKey: privateKey,
268+
privateKeyProvider: privateKeyProvider,
269269
data: message,
270270
subscriber: subscriber,
271271
expiration: expiration,
@@ -346,10 +346,10 @@ public actor WebPushManager: Sendable {
346346
var logger = logger
347347
logger[metadataKey: "message"] = "\(message)"
348348
switch executor {
349-
case .httpClient(let httpClient, let privateKey):
349+
case .httpClient(let httpClient, let privateKeyProvider):
350350
try await execute(
351351
httpClient: httpClient,
352-
applicationServerECDHPrivateKey: privateKey,
352+
privateKeyProvider: privateKeyProvider,
353353
data: message.data,
354354
subscriber: subscriber,
355355
expiration: expiration,
@@ -377,7 +377,7 @@ public actor WebPushManager: Sendable {
377377
/// - logger: The logger to use for status updates.
378378
func execute(
379379
httpClient: some HTTPClientProtocol,
380-
applicationServerECDHPrivateKey: P256.KeyAgreement.PrivateKey?,
380+
privateKeyProvider: Executor.KeyProvider,
381381
data message: some DataProtocol,
382382
subscriber: some SubscriberProtocol,
383383
expiration: Expiration,
@@ -402,13 +402,13 @@ public actor WebPushManager: Sendable {
402402

403403
/// Prepare authorization, private keys, and payload ahead of time to bail early if they can't be created.
404404
let authorization = try loadCurrentVAPIDAuthorizationHeader(endpoint: subscriber.endpoint, signingKey: signingKey)
405-
let applicationServerECDHPrivateKey = applicationServerECDHPrivateKey ?? P256.KeyAgreement.PrivateKey()
405+
let applicationServerECDHPrivateKey: P256.KeyAgreement.PrivateKey
406406

407407
/// Perform key exchange between the user agent's public key and our private key, deriving a shared secret.
408408
let userAgent = subscriber.userAgentKeyMaterial
409409
let sharedSecret: SharedSecret
410410
do {
411-
sharedSecret = try applicationServerECDHPrivateKey.sharedSecretFromKeyAgreement(with: userAgent.publicKey)
411+
(applicationServerECDHPrivateKey, sharedSecret) = try privateKeyProvider.sharedSecretFromKeyAgreement(with: userAgent.publicKey)
412412
} catch {
413413
logger.debug("A shared secret could not be derived from the subscriber's public key and the newly-generated private key.", metadata: ["error" : "\(error)"])
414414
throw BadSubscriberError()
@@ -689,6 +689,7 @@ extension WebPushManager {
689689

690690
/// A message originally sent via ``WebPushManager/send(string:to:expiration:urgency:)``
691691
case string(String)
692+
692693
/// A message originally sent via ``WebPushManager/send(json:to:expiration:urgency:)``
693694
case json(any Encodable&Sendable)
694695

@@ -737,16 +738,37 @@ extension WebPushManager {
737738

738739
/// An internal type representing the executor for a push message.
739740
package enum Executor: Sendable {
741+
/// A Private Key and Shared Secret provider.
742+
package enum KeyProvider: Sendable {
743+
/// Generate a new Private Key and Shared Secret when asked.
744+
case generateNew
745+
746+
/// Used a shared generator to provide a Private Key and Shared Secret when asked.
747+
case shared(@Sendable (P256.KeyAgreement.PublicKey) throws -> (P256.KeyAgreement.PrivateKey, SharedSecret))
748+
749+
/// Generate the Private Key and Shared Secret against a provided Public Key.
750+
func sharedSecretFromKeyAgreement(with publicKeyShare: P256.KeyAgreement.PublicKey) throws -> (P256.KeyAgreement.PrivateKey, SharedSecret) {
751+
switch self {
752+
case .generateNew:
753+
let privateKey = P256.KeyAgreement.PrivateKey()
754+
let sharedSecret = try privateKey.sharedSecretFromKeyAgreement(with: publicKeyShare)
755+
return (privateKey, sharedSecret)
756+
case .shared(let handler):
757+
return try handler(publicKeyShare)
758+
}
759+
}
760+
}
761+
740762
/// Use an HTTP client and optional private key to send an encrypted payload to a subscriber.
741763
///
742764
/// This is used in tests to capture the encrypted request and make sure it is well-formed.
743-
case httpClient(any HTTPClientProtocol, P256.KeyAgreement.PrivateKey?)
765+
case httpClient(any HTTPClientProtocol, KeyProvider)
744766

745767
/// Use an HTTP client to send an encrypted payload to a subscriber.
746768
///
747769
/// This is used in tests to capture the encrypted request and make sure it is well-formed.
748770
package static func httpClient(_ httpClient: any HTTPClientProtocol) -> Self {
749-
.httpClient(httpClient, nil)
771+
.httpClient(httpClient, .generateNew)
750772
}
751773

752774
/// Use a handler to capture the original message.

Tests/WebPushTests/WebPushManagerTests.swift

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -364,39 +364,22 @@ struct WebPushManagerTests {
364364
}
365365
}
366366

367-
@Test(.disabled("Fails because we need a public/private key pair that fails to make a shared secret"))
368-
func sendMessageToSubscriberWithInvalidUserAgentKey() async throws {
369-
try await confirmation(expectedCount: 0) { requestWasMade in
370-
let vapidConfiguration = VAPID.Configuration.mockedConfiguration
371-
372-
let privateKey = try P256.KeyAgreement.PrivateKey(rawRepresentation: Data(base64Encoded: "fkqlT3FL8B34XFAmm+o6hnIfhK/nT3tB6lirzzR06I0=")!)
373-
let publicKey = try P256.KeyAgreement.PublicKey(compressedRepresentation: Data(base64Encoded: "Ahj5uud0fNhE6YUlt8zQ2vbh0gqBiyF1qakeTq5TQ7yY")!)
374-
var authenticationSecret: [UInt8] = Array(repeating: 0, count: 16)
375-
for index in authenticationSecret.indices { authenticationSecret[index] = .random(in: .min ... .max) }
376-
377-
let subscriber = Subscriber(
378-
endpoint: URL(string: "https://example.com/subscriber")!,
379-
userAgentKeyMaterial: UserAgentKeyMaterial(
380-
publicKey: publicKey,
381-
authenticationSecret: Data(authenticationSecret)
382-
),
383-
vapidKeyID: .mockedKeyID1
384-
)
385-
367+
@Test func sendMessageToSubscriberWithInvalidUserAgentKey() async throws {
368+
await confirmation(expectedCount: 0) { requestWasMade in
386369
let manager = WebPushManager(
387-
vapidConfiguration: vapidConfiguration,
370+
vapidConfiguration: .mockedConfiguration,
388371
backgroundActivityLogger: Logger(label: "WebPushManagerTests", factory: { PrintLogHandler(label: $0, metadataProvider: $1) }),
389372
executor: .httpClient(
390373
MockHTTPClient({ request in
391374
requestWasMade()
392375
return HTTPClientResponse(status: .created)
393376
}),
394-
privateKey
377+
.shared({ _ in throw CancellationError() })
395378
)
396379
)
397380

398381
await #expect(throws: BadSubscriberError()) {
399-
try await manager.send(string: "hello", to: subscriber)
382+
try await manager.send(string: "hello", to: .mockedSubscriber())
400383
}
401384
}
402385
}

0 commit comments

Comments
 (0)