Skip to content

Commit 133a347

Browse files
josephnoirLukasa
andauthored
Add validation function for raw signatures (#284)
### Motivation Signatures can be performed on any kind of data, e.g., to prove ownership of the private key for a given certificate. This data usually comes as a blob of bytes. ### Modification Add a signature verification function that operates on bytes represented by the `DataProtocol`. The bytes are converted to the signature type matching the key and share an implementation with the verification function for `Certificate.Signature`. ### Result Easy validation of signatures represented as bytes. --------- Co-authored-by: Cory Benfield <[email protected]>
1 parent 48d7c83 commit 133a347

File tree

3 files changed

+470
-14
lines changed

3 files changed

+470
-14
lines changed

Sources/X509/CertificatePublicKey.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,37 @@ extension Certificate.PublicKey {
156156
return ed25519.isValidSignature(signature, for: bytes, signatureAlgorithm: signatureAlgorithm)
157157
}
158158
}
159+
160+
/// Confirms that `signature` is a valid signature for `bytes`, created by the
161+
/// private key associated with this public key.
162+
///
163+
/// This function accepts raw signature bytes (such as those from a TLS handshake)
164+
/// and validates them directly against the data.
165+
///
166+
/// - Parameters:
167+
/// - signature: The raw signature bytes to validate.
168+
/// - bytes: The data that was signed.
169+
/// - signatureAlgorithm: The algorithm used to create the signature.
170+
/// - Returns: Whether the signature was produced by signing `bytes` with the private key corresponding to this public key.
171+
@inlinable
172+
public func isValidSignature<SignatureBytes: DataProtocol, Bytes: DataProtocol>(
173+
_ signature: SignatureBytes,
174+
for bytes: Bytes,
175+
signatureAlgorithm: Certificate.SignatureAlgorithm
176+
) -> Bool {
177+
switch self.backing {
178+
case .p256(let p256):
179+
return p256.isValidSignature(signature, for: bytes, signatureAlgorithm: signatureAlgorithm)
180+
case .p384(let p384):
181+
return p384.isValidSignature(signature, for: bytes, signatureAlgorithm: signatureAlgorithm)
182+
case .p521(let p521):
183+
return p521.isValidSignature(signature, for: bytes, signatureAlgorithm: signatureAlgorithm)
184+
case .rsa(let rsa):
185+
return rsa.isValidSignature(signature, for: bytes, signatureAlgorithm: signatureAlgorithm)
186+
case .ed25519(let ed25519):
187+
return ed25519.isValidSignature(signature, for: bytes, signatureAlgorithm: signatureAlgorithm)
188+
}
189+
}
159190
}
160191

161192
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *)

Sources/X509/Signature.swift

Lines changed: 116 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -188,13 +188,37 @@ extension P256.Signing.PublicKey {
188188
return false
189189
}
190190

191+
return self.isValidSignature(innerSignature, for: bytes, signatureAlgorithm: signatureAlgorithm)
192+
}
193+
194+
@inlinable
195+
internal func isValidSignature<SignatureBytes: DataProtocol, Bytes: DataProtocol>(
196+
_ signature: SignatureBytes,
197+
for bytes: Bytes,
198+
signatureAlgorithm: Certificate.SignatureAlgorithm
199+
) -> Bool {
200+
guard let ecdsaSignature = try? ECDSASignature(derEncoded: Array(signature)),
201+
let innerSignature = P256.Signing.ECDSASignature(ecdsaSignature)
202+
else {
203+
return false
204+
}
205+
206+
return self.isValidSignature(innerSignature, for: bytes, signatureAlgorithm: signatureAlgorithm)
207+
}
208+
209+
@inlinable
210+
internal func isValidSignature<Bytes: DataProtocol>(
211+
_ signature: P256.Signing.ECDSASignature,
212+
for bytes: Bytes,
213+
signatureAlgorithm: Certificate.SignatureAlgorithm
214+
) -> Bool {
191215
switch signatureAlgorithm {
192216
case .ecdsaWithSHA256:
193-
return self.isValidSignature(innerSignature, for: SHA256.hash(data: bytes))
217+
return self.isValidSignature(signature, for: SHA256.hash(data: bytes))
194218
case .ecdsaWithSHA384:
195-
return self.isValidSignature(innerSignature, for: SHA384.hash(data: bytes))
219+
return self.isValidSignature(signature, for: SHA384.hash(data: bytes))
196220
case .ecdsaWithSHA512:
197-
return self.isValidSignature(innerSignature, for: SHA512.hash(data: bytes))
221+
return self.isValidSignature(signature, for: SHA512.hash(data: bytes))
198222
default:
199223
return false
200224
}
@@ -216,13 +240,37 @@ extension P384.Signing.PublicKey {
216240
return false
217241
}
218242

243+
return self.isValidSignature(innerSignature, for: bytes, signatureAlgorithm: signatureAlgorithm)
244+
}
245+
246+
@inlinable
247+
internal func isValidSignature<SignatureBytes: DataProtocol, Bytes: DataProtocol>(
248+
_ signature: SignatureBytes,
249+
for bytes: Bytes,
250+
signatureAlgorithm: Certificate.SignatureAlgorithm
251+
) -> Bool {
252+
guard let ecdsaSignature = try? ECDSASignature(derEncoded: Array(signature)),
253+
let innerSignature = P384.Signing.ECDSASignature(ecdsaSignature)
254+
else {
255+
return false
256+
}
257+
258+
return self.isValidSignature(innerSignature, for: bytes, signatureAlgorithm: signatureAlgorithm)
259+
}
260+
261+
@inlinable
262+
internal func isValidSignature<Bytes: DataProtocol>(
263+
_ signature: P384.Signing.ECDSASignature,
264+
for bytes: Bytes,
265+
signatureAlgorithm: Certificate.SignatureAlgorithm
266+
) -> Bool {
219267
switch signatureAlgorithm {
220268
case .ecdsaWithSHA256:
221-
return self.isValidSignature(innerSignature, for: SHA256.hash(data: bytes))
269+
return self.isValidSignature(signature, for: SHA256.hash(data: bytes))
222270
case .ecdsaWithSHA384:
223-
return self.isValidSignature(innerSignature, for: SHA384.hash(data: bytes))
271+
return self.isValidSignature(signature, for: SHA384.hash(data: bytes))
224272
case .ecdsaWithSHA512:
225-
return self.isValidSignature(innerSignature, for: SHA512.hash(data: bytes))
273+
return self.isValidSignature(signature, for: SHA512.hash(data: bytes))
226274
default:
227275
return false
228276
}
@@ -244,13 +292,37 @@ extension P521.Signing.PublicKey {
244292
return false
245293
}
246294

295+
return self.isValidSignature(innerSignature, for: bytes, signatureAlgorithm: signatureAlgorithm)
296+
}
297+
298+
@inlinable
299+
internal func isValidSignature<SignatureBytes: DataProtocol, Bytes: DataProtocol>(
300+
_ signature: SignatureBytes,
301+
for bytes: Bytes,
302+
signatureAlgorithm: Certificate.SignatureAlgorithm
303+
) -> Bool {
304+
guard let ecdsaSignature = try? ECDSASignature(derEncoded: Array(signature)),
305+
let innerSignature = P521.Signing.ECDSASignature(ecdsaSignature)
306+
else {
307+
return false
308+
}
309+
310+
return self.isValidSignature(innerSignature, for: bytes, signatureAlgorithm: signatureAlgorithm)
311+
}
312+
313+
@inlinable
314+
internal func isValidSignature<Bytes: DataProtocol>(
315+
_ signature: P521.Signing.ECDSASignature,
316+
for bytes: Bytes,
317+
signatureAlgorithm: Certificate.SignatureAlgorithm
318+
) -> Bool {
247319
switch signatureAlgorithm {
248320
case .ecdsaWithSHA256:
249-
return self.isValidSignature(innerSignature, for: SHA256.hash(data: bytes))
321+
return self.isValidSignature(signature, for: SHA256.hash(data: bytes))
250322
case .ecdsaWithSHA384:
251-
return self.isValidSignature(innerSignature, for: SHA384.hash(data: bytes))
323+
return self.isValidSignature(signature, for: SHA384.hash(data: bytes))
252324
case .ecdsaWithSHA512:
253-
return self.isValidSignature(innerSignature, for: SHA512.hash(data: bytes))
325+
return self.isValidSignature(signature, for: SHA512.hash(data: bytes))
254326
default:
255327
return false
256328
}
@@ -269,18 +341,39 @@ extension _RSA.Signing.PublicKey {
269341
// Signature mismatch
270342
return false
271343
}
344+
345+
return self.isValidSignature(innerSignature, for: bytes, signatureAlgorithm: signatureAlgorithm)
346+
}
347+
348+
@inlinable
349+
internal func isValidSignature<SignatureBytes: DataProtocol, Bytes: DataProtocol>(
350+
_ signature: SignatureBytes,
351+
for bytes: Bytes,
352+
signatureAlgorithm: Certificate.SignatureAlgorithm
353+
) -> Bool {
354+
let rsaSignature = _RSA.Signing.RSASignature(rawRepresentation: signature)
355+
356+
return self.isValidSignature(rsaSignature, for: bytes, signatureAlgorithm: signatureAlgorithm)
357+
}
358+
359+
@inlinable
360+
internal func isValidSignature<Bytes: DataProtocol>(
361+
_ signature: _RSA.Signing.RSASignature,
362+
for bytes: Bytes,
363+
signatureAlgorithm: Certificate.SignatureAlgorithm
364+
) -> Bool {
272365
// For now we don't support RSA PSS, as it's not deployed in the WebPKI.
273366
let padding = _RSA.Signing.Padding.insecurePKCS1v1_5
274367

275368
switch signatureAlgorithm {
276369
case .sha1WithRSAEncryption:
277-
return self.isValidSignature(innerSignature, for: Insecure.SHA1.hash(data: bytes), padding: padding)
370+
return self.isValidSignature(signature, for: Insecure.SHA1.hash(data: bytes), padding: padding)
278371
case .sha256WithRSAEncryption:
279-
return self.isValidSignature(innerSignature, for: SHA256.hash(data: bytes), padding: padding)
372+
return self.isValidSignature(signature, for: SHA256.hash(data: bytes), padding: padding)
280373
case .sha384WithRSAEncryption:
281-
return self.isValidSignature(innerSignature, for: SHA384.hash(data: bytes), padding: padding)
374+
return self.isValidSignature(signature, for: SHA384.hash(data: bytes), padding: padding)
282375
case .sha512WithRSAEncryption:
283-
return self.isValidSignature(innerSignature, for: SHA512.hash(data: bytes), padding: padding)
376+
return self.isValidSignature(signature, for: SHA512.hash(data: bytes), padding: padding)
284377
default:
285378
return false
286379
}
@@ -300,9 +393,18 @@ extension Curve25519.Signing.PublicKey {
300393
return false
301394
}
302395

396+
return self.isValidSignature(rawInnerSignature, for: bytes, signatureAlgorithm: signatureAlgorithm)
397+
}
398+
399+
@inlinable
400+
internal func isValidSignature<SignatureBytes: DataProtocol, Bytes: DataProtocol>(
401+
_ signature: SignatureBytes,
402+
for bytes: Bytes,
403+
signatureAlgorithm: Certificate.SignatureAlgorithm
404+
) -> Bool {
303405
switch signatureAlgorithm {
304406
case .ed25519:
305-
return self.isValidSignature(rawInnerSignature, for: bytes)
407+
return self.isValidSignature(signature, for: bytes)
306408
default:
307409
return false
308410
}

0 commit comments

Comments
 (0)