File: RSA.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (462 lines) | stat: -rw-r--r-- 18,790 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftCrypto open source project
//
// Copyright (c) 2021 Apple Inc. and the SwiftCrypto project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.md for the list of SwiftCrypto project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import Foundation
import Crypto

#if canImport(Security)
fileprivate typealias BackingPublicKey = SecurityRSAPublicKey
fileprivate typealias BackingPrivateKey = SecurityRSAPrivateKey
#else
fileprivate typealias BackingPublicKey = BoringSSLRSAPublicKey
fileprivate typealias BackingPrivateKey = BoringSSLRSAPrivateKey
#endif

/// Types associated with the RSA algorithm
///
/// RSA is an asymmetric algorithm. In comparison to elliptic-curve equivalents, RSA requires relatively larger
/// key sizes to achieve equivalent security guarantees. These keys are inefficient to transmit and are often slow to
/// compute with, meaning that RSA-based cryptosystems perform poorly in comparison to elliptic-curve based systems.
/// Additionally, several common operating modes of RSA are insecure and unsafe to use.
///
/// When rolling out new cryptosystems, users should avoid RSA and use ECDSA or edDSA instead. RSA
/// support is provided for interoperability with legacy systems.
#if swift(>=5.8)
@_documentation(visibility: public)
public enum _RSA { }
#else
public enum _RSA { }
#endif

extension _RSA {
    public enum Signing { }
    public enum Encryption { }
}

extension _RSA.Signing {
    public struct PublicKey {
        private var backing: BackingPublicKey

        /// Construct an RSA public key from a PEM representation.
        ///
        /// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
        /// for their use-case.
        public init(pemRepresentation: String) throws {
            self.backing = try BackingPublicKey(pemRepresentation: pemRepresentation)

            guard self.keySizeInBits >= 1024 else {
                throw CryptoKitError.incorrectParameterSize
            }
        }

        /// Construct an RSA public key from a DER representation.
        ///
        /// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
        /// for their use-case.
        public init<Bytes: DataProtocol>(derRepresentation: Bytes) throws {
            self.backing = try BackingPublicKey(derRepresentation: derRepresentation)

            guard self.keySizeInBits >= 1024 else {
                throw CryptoKitError.incorrectParameterSize
            }
        }

        public var pkcs1DERRepresentation: Data {
            self.backing.pkcs1DERRepresentation
        }

        public var pkcs1PEMRepresentation: String {
            self.backing.pkcs1PEMRepresentation
        }

        public var derRepresentation: Data {
            self.backing.derRepresentation
        }

        public var pemRepresentation: String {
            self.backing.pemRepresentation
        }

        public var keySizeInBits: Int {
            self.backing.keySizeInBits
        }

        fileprivate init(_ backing: BackingPublicKey) {
            self.backing = backing
        }
    }
}

extension _RSA.Signing {
    public struct PrivateKey {
        private var backing: BackingPrivateKey

        /// Construct an RSA private key from a PEM representation.
        ///
        /// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
        /// for their use-case.
        public init(pemRepresentation: String) throws {
            self.backing = try BackingPrivateKey(pemRepresentation: pemRepresentation)

            guard self.keySizeInBits >= 1024 else {
                throw CryptoKitError.incorrectParameterSize
            }
        }

        /// Construct an RSA private key from a DER representation.
        ///
        /// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
        /// for their use-case.
        public init<Bytes: DataProtocol>(derRepresentation: Bytes) throws {
            self.backing = try BackingPrivateKey(derRepresentation: derRepresentation)

            guard self.keySizeInBits >= 1024 else {
                throw CryptoKitError.incorrectParameterSize
            }
        }

        /// Randomly generate a new RSA private key of a given size.
        ///
        /// This constructor will refuse to generate keys smaller than 1024 bits. Callers that want to enforce minimum
        /// key size requirements should validate `keySize` before use.
        public init(keySize: _RSA.Signing.KeySize) throws {
            guard keySize.bitCount >= 1024 else {
                throw CryptoKitError.incorrectParameterSize
            }
            self.backing = try BackingPrivateKey(keySize: keySize)
        }

        public var derRepresentation: Data {
            self.backing.derRepresentation
        }

        public var pemRepresentation: String {
            self.backing.pemRepresentation
        }

        public var keySizeInBits: Int {
            self.backing.keySizeInBits
        }

        public var publicKey: _RSA.Signing.PublicKey {
            _RSA.Signing.PublicKey(self.backing.publicKey)
        }
    }
}

extension _RSA.Signing {
    public struct RSASignature: ContiguousBytes {
        public var rawRepresentation: Data

        public init<D: DataProtocol>(rawRepresentation: D) {
            self.rawRepresentation = Data(rawRepresentation)
        }
        
        internal init(signatureBytes: [UInt8]) {
            self.rawRepresentation = Data(signatureBytes)
        }

        public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
            try self.rawRepresentation.withUnsafeBytes(body)
        }
    }
}

extension _RSA.Signing {
    public struct Padding {
        internal enum Backing {
            case pkcs1v1_5
            case pss
        }

        internal var backing: Backing

        private init(_ backing: Backing) {
            self.backing = backing
        }

        /// PKCS#1 v1.5 padding as used in signing.
        ///
        /// As a note, PKCS#1 v1.5 padding is not known to be insecure in the signing operation at this time,
        /// merely in encryption. However, it's substantially less secure than PSS, and becoming comfortable with
        /// it in the signing context opens the door to the possibility of using it in the encryption context,
        /// where it is definitely known to be weak. So here we label it "insecure".
        public static let insecurePKCS1v1_5 = Self(.pkcs1v1_5)

        /// PSS padding using MGF1.
        ///
        /// MGF1 is parameterised with a hash function. The salt length will be the size of the digest from the given hash function.
        public static let PSS = Self(.pss)
    }
}

extension _RSA.Signing.PrivateKey {
    ///  Generates an RSA signature with the given key using the default padding.
    ///
    ///  The default padding is PSS using MGF1 with same hash function as produced the digest being
    ///  signed, and a salt that is as long as the digest. Note that this API will not select any
    ///  known-insecure digests.
    ///
    /// - Parameter digest: The digest to sign.
    /// - Returns: The RSA Signature.
    /// - Throws: If there is a failure producing the signature
    public func signature<D: Digest>(for digest: D) throws -> _RSA.Signing.RSASignature {
        return try self.signature(for: digest, padding: .PSS)
    }

    /// Generates an RSA signature with the given key using the default padding.
    ///
    /// SHA256 is used as the hash function. The default padding is PSS using MGF1 with SHA256
    /// and a 32-byte salt.
    ///
    /// - Parameter data: The data to sign.
    /// - Returns: The RSA Signature.
    /// - Throws: If there is a failure producing the signature.
    public func signature<D: DataProtocol>(for data: D) throws -> _RSA.Signing.RSASignature {
        return try self.signature(for: SHA256.hash(data: data), padding: .PSS)
    }

    /// Generates an RSA signature with the given key.
    ///
    /// - Parameter digest: The digest to sign.
    /// - Parameter padding: The padding to use.
    /// - Returns: The RSA Signature.
    /// - Throws: If there is a failure producing the signature
    public func signature<D: Digest>(for digest: D, padding: _RSA.Signing.Padding) throws -> _RSA.Signing.RSASignature {
        return try self.backing.signature(for: digest, padding: padding)
    }

    /// Generates an RSA signature with the given key.
    /// SHA256 is used as the hash function.
    ///
    /// - Parameter data: The data to sign.
    /// - Parameter padding: The padding to use.
    /// - Returns: The RSA Signature.
    /// - Throws: If there is a failure producing the signature.
    public func signature<D: DataProtocol>(for data: D, padding: _RSA.Signing.Padding) throws -> _RSA.Signing.RSASignature {
        return try self.signature(for: SHA256.hash(data: data), padding: padding)
    }
 }

extension _RSA.Signing.PublicKey {
    /// Verifies an RSA signature with the given padding over a given digest using the default padding.
    ///
    /// The default padding is PSS using MGF1 with same hash function as produced the digest being
    /// signed, and a salt that is as long as the digest. Note that this API will not select any
    /// known-insecure digests.
    ///
    /// - Parameters:
    ///   - signature: The signature to verify
    ///   - digest: The digest that was signed.
    /// - Returns: True if the signature is valid, false otherwise.
    public func isValidSignature<D: Digest>(_ signature: _RSA.Signing.RSASignature, for digest: D) -> Bool {
        return self.isValidSignature(signature, for: digest, padding: .PSS)
    }

    /// Verifies an RSA signature with the given padding over a message with the default padding.
    ///
    /// SHA256 is used as the hash function. The default padding is PSS using MGF1 with SHA256
    /// and a 32-byte salt.
    ///
    /// - Parameters:
    ///   - signature: The signature to verify
    ///   - data: The data that was signed.
    /// - Returns: True if the signature is valid, false otherwise.
    public func isValidSignature<D: DataProtocol>(_ signature: _RSA.Signing.RSASignature, for data: D) -> Bool {
        return self.isValidSignature(signature, for: SHA256.hash(data: data), padding: .PSS)
    }

    /// Verifies an RSA signature with the given padding over a given digest.
    ///
    /// - Parameters:
    ///   - signature: The signature to verify
    ///   - digest: The digest that was signed.
    ///   - padding: The padding to use.
    /// - Returns: True if the signature is valid, false otherwise.
    public func isValidSignature<D: Digest>(_ signature: _RSA.Signing.RSASignature, for digest: D, padding: _RSA.Signing.Padding) -> Bool {
        return self.backing.isValidSignature(signature, for: digest, padding: padding)
    }

    /// Verifies an RSA signature with the given padding over a message.
    /// SHA256 is used as the hash function.
    ///
    /// - Parameters:
    ///   - signature: The signature to verify
    ///   - data: The data that was signed.
    ///   - padding: The padding to use.
    /// - Returns: True if the signature is valid, false otherwise.
    public func isValidSignature<D: DataProtocol>(_ signature: _RSA.Signing.RSASignature, for data: D, padding: _RSA.Signing.Padding) -> Bool {
        return self.isValidSignature(signature, for: SHA256.hash(data: data), padding: padding)
    }
}

extension _RSA.Signing {
    public struct KeySize {
        public let bitCount: Int

        /// RSA key size of 2048 bits
        public static let bits2048 = _RSA.Signing.KeySize(bitCount: 2048)

        /// RSA key size of 3072 bits
        public static let bits3072 = _RSA.Signing.KeySize(bitCount: 3072)

        /// RSA key size of 4096 bits
        public static let bits4096 = _RSA.Signing.KeySize(bitCount: 4096)

        /// RSA key size with a custom number of bits.
        ///
        /// Params:
        ///     - bitsCount: Positive integer that is a multiple of 8.
        public init(bitCount: Int) {
            precondition(bitCount % 8 == 0 && bitCount > 0)
            self.bitCount = bitCount
        }
    }
}

extension _RSA.Encryption {
    /// Identical to ``_RSA/Signing/PublicKey``.
    public struct PublicKey {
        private var backing: BackingPublicKey
        
        /// Construct an RSA public key from a PEM representation.
        ///
        /// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
        /// for their use-case.
        public init(pemRepresentation: String) throws {
            self.backing = try BackingPublicKey(pemRepresentation: pemRepresentation)
            guard self.keySizeInBits >= 1024, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
        }

        /// Construct an RSA public key from a DER representation.
        ///
        /// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
        /// for their use-case.
        public init<Bytes: DataProtocol>(derRepresentation: Bytes) throws {
            self.backing = try BackingPublicKey(derRepresentation: derRepresentation)
            guard self.keySizeInBits >= 1024, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
        }

        public var pkcs1DERRepresentation: Data { self.backing.pkcs1DERRepresentation }
        public var pkcs1PEMRepresentation: String { self.backing.pkcs1PEMRepresentation }
        public var derRepresentation: Data { self.backing.derRepresentation }
        public var pemRepresentation: String { self.backing.pemRepresentation }
        public var keySizeInBits: Int { self.backing.keySizeInBits }
        fileprivate init(_ backing: BackingPublicKey) { self.backing = backing }
    }
    
    /// Identical to ``_RSA/Signing/PrivateKey``.
    public struct PrivateKey {
        private var backing: BackingPrivateKey

        /// Construct an RSA private key from a PEM representation.
        ///
        /// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
        /// for their use-case.
        public init(pemRepresentation: String) throws {
            self.backing = try BackingPrivateKey(pemRepresentation: pemRepresentation)
            guard self.keySizeInBits >= 1024, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
        }

        /// Construct an RSA private key from a DER representation.
        ///
        /// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
        /// for their use-case.
        public init<Bytes: DataProtocol>(derRepresentation: Bytes) throws {
            self.backing = try BackingPrivateKey(derRepresentation: derRepresentation)
            guard self.keySizeInBits >= 1024, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
        }

        /// Randomly generate a new RSA private key of a given size.
        ///
        /// This constructor will refuse to generate keys smaller than 1024 bits. Callers that want to enforce minimum
        /// key size requirements should validate `keySize` before use.
        public init(keySize: _RSA.Signing.KeySize) throws {
            guard keySize.bitCount >= 1024 else { throw CryptoKitError.incorrectParameterSize }
            self.backing = try BackingPrivateKey(keySize: keySize)
        }
        
        public var derRepresentation: Data { self.backing.derRepresentation }
        public var pemRepresentation: String { self.backing.pemRepresentation }
        public var keySizeInBits: Int { self.backing.keySizeInBits }
        public var publicKey: _RSA.Encryption.PublicKey { .init(self.backing.publicKey) }
    }
}

extension _RSA.Encryption {
    public struct Padding {
        internal enum Backing {
            case pkcs1_oaep
        }
        
        internal var backing: Backing
        
        private init(_ backing: Backing) {
            self.backing = backing
        }
        
        /// PKCS#1 OAEP padding
        ///
        /// As defined by [RFC 8017 ยง 7.1](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1).
        public static let PKCS1_OAEP = Self(.pkcs1_oaep)
    }
}

extension _RSA.Encryption.PrivateKey {
    /// Decrypt a message encrypted with this key's public key and using the specified padding mode.
    ///
    /// > Important: The size of the data to decrypt must be equal to the block size of the key (e.g.
    ///   `keySizeInBits / 8`). Attempting to decrypt data of the wrong size will fail.
    public func decrypt<D: DataProtocol>(_ data: D, padding: _RSA.Encryption.Padding) throws -> Data {
        return try self.backing.decrypt(data, padding: padding)
    }
}

extension _RSA.Encryption.PublicKey {
    /// Return the maximum amount of data in bytes this key can encrypt in a single operation when using
    /// the specified padding mode.
    ///
    /// ## Common values:
    ///
    /// Key size|Padding|Max length
    /// -|-|-
    /// 2048|PKCS-OAEP|214 bytes
    /// 3072|PKCS-OAEP|342 bytes
    /// 4096|PKCS-OAEP|470 bytes
    public func maximumEncryptSize(with padding: _RSA.Encryption.Padding) -> Int {
        switch padding.backing {
        case .pkcs1_oaep:
            return (self.keySizeInBits / 8) - 42
        }
    }
    
    /// Encrypt a message with this key, using the specified padding mode.
    ///
    /// > Important: The size of the data to encrypt _must_ not exceed the modulus of the key (e.g.
    ///   `keySizeInBits / 8`), minus any additional space required by the padding mode. Attempting to
    ///   encrypt data larger than this will fail. Use ``maximumEncryptSize(with:)`` to determine
    ///   exactly how many bytes can be encrypted by the key.
    public func encrypt<D: DataProtocol>(_ data: D, padding: _RSA.Encryption.Padding) throws -> Data {
        return try self.backing.encrypt(data, padding: padding)
    }
}

extension _RSA {
    static let PKCS1KeyType = "RSA PRIVATE KEY"

    static let PKCS8KeyType = "PRIVATE KEY"

    static let PKCS1PublicKeyType = "RSA PUBLIC KEY"

    static let SPKIPublicKeyType = "PUBLIC KEY"
}