Explorar o código

Merge pull request #976 from btoms20/feature/rsa+der

Nathan Fallet %!s(int64=3) %!d(string=hai) anos
pai
achega
aaea7adc5e

+ 94 - 0
README.md

@@ -549,6 +549,100 @@ RSA key generation
 let rsa = try RSA(keySize: 2048) // This generates a modulus, public exponent and private exponent with the given size
 ```
 
+RSA Encryption & Decryption Example
+``` swift
+// Alice Generates a Private Key
+let alicesPrivateKey = try RSA(keySize: 1024)
+    
+// Alice shares her **public** key with Bob
+let alicesPublicKeyData = try alicesPrivateKey.publicKeyDER()
+    
+// Bob receives the raw external representation of Alices public key and imports it
+let bobsImportOfAlicesPublicKey = try RSA(rawRepresentation: Data(alicesPublicKeyData))
+    
+// Bob can now encrypt a message for Alice using her public key
+let message = "Hi Alice! This is Bob!"
+let privateMessage = try bobsImportOfAlicesPublicKey.encrypt(message.bytes)
+    
+// This results in some encrypted output like this
+// URcRwG6LfH63zOQf2w+HIllPri9Rb6hFlXbi/bh03zPl2MIIiSTjbAPqbVFmoF3RmDzFjIarIS7ZpT57a1F+OFOJjx50WYlng7dioKFS/rsuGHYnMn4csjCRF6TAqvRQcRnBueeINRRA8SLaLHX6sZuQkjIE5AoHJwgavmiv8PY=
+      
+// Bob can now send this encrypted message to Alice without worrying about people being able to read the original contents
+    
+// Alice receives the encrypted message and uses her private key to decrypt the data and recover the original message
+let originalDecryptedMessage = try alicesPrivateKey.decrypt(privateMessage)
+    
+print(String(data: Data(originalDecryptedMessage), encoding: .utf8))
+// "Hi Alice! This is Bob!"
+```
+
+RSA Signature & Verification Example
+``` swift
+// Alice Generates a Private Key
+let alicesPrivateKey = try RSA(keySize: 1024)
+    
+// Alice wants to sign a message that she agrees with
+let messageAliceSupports = "Hi my name is Alice!"
+let alicesSignature = try alicesPrivateKey.sign(messageAliceSupports.bytes)
+    
+// Alice shares her Public key and the signature with Bob
+let alicesPublicKeyData = try alicesPrivateKey.publicKeyDER()
+    
+// Bob receives the raw external representation of Alices Public key and imports it!
+let bobsImportOfAlicesPublicKey = try RSA(rawRepresentation: Data(alicesPublicKeyData))
+        
+// Bob can now verify that Alice signed the message using the Private key associated with her shared Public key.
+let verifiedSignature = try bobsImportOfAlicesPublicKey.verify(signature: alicesSignature, for: "Hi my name is Alice!".bytes)
+    
+if verifiedSignature == true {
+  // Bob knows that the signature Alice provided is valid for the message and was signed using the Private key associated with Alices shared Public key.
+} else {
+  // The signature was invalid, so either
+  // - the message Alice signed was different then what we expected.
+  // - or Alice used a Private key that isn't associated with the shared Public key that Bob has.
+}
+```
+
+CryptoSwift RSA Key -> Apple's Security Framework SecKey Example
+``` swift
+/// Starting with a CryptoSwift RSA Key
+let rsaKey = try RSA(keySize: 1024)
+
+/// Define your Keys attributes
+let attributes: [String:Any] = [
+  kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
+  kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, // or kSecAttrKeyClassPublic
+  kSecAttrKeySizeInBits as String: 1024, // The appropriate bits
+  kSecAttrIsPermanent as String: false
+]
+var error:Unmanaged<CFError>? = nil
+guard let rsaSecKey = try SecKeyCreateWithData(rsaKey.externalRepresentation() as CFData, attributes as CFDictionary, &error) else {
+  /// Error constructing SecKey from raw key data
+  return
+}
+
+/// You now have an RSA SecKey for use with Apple's Security framework
+```
+
+Apple's Security Framework SecKey -> CryptoSwift RSA Key Example
+``` swift
+/// Starting with a SecKey RSA Key
+let rsaSecKey:SecKey
+
+/// Copy External Representation
+var externalRepError:Unmanaged<CFError>?
+guard let cfdata = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) else {
+  /// Failed to copy external representation for RSA SecKey
+  return
+}
+
+/// Instantiate the RSA Key from the raw external representation
+let rsaKey = try RSA(rawRepresentation: cfdata as Data)
+
+/// You now have a CryptoSwift RSA Key
+```
+
+
 ## Author
 
 CryptoSwift is owned and maintained by [Marcin Krzyżanowski](http://www.krzyzanowskim.com)

+ 5 - 10
Sources/CryptoSwift/PEM/DER.swift

@@ -20,11 +20,6 @@ internal protocol DERCodable: DERDecodable, DEREncodable { }
 
 /// Conform to this protocol if your type can be instantiated from a ASN1 DER representation
 internal protocol DERDecodable {
-  /// The keys primary ASN1 object identifier (ex: for RSA Keys --> 'rsaEncryption' --> [0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01])
-  static var primaryObjectIdentifier: Array<UInt8> { get }
-  /// The keys secondary ASN1 object identifier (ex: for RSA Keys --> 'null' --> nil)
-  static var secondaryObjectIdentifier: Array<UInt8>? { get }
-
   /// Attempts to instantiate an instance of your Public Key when given a DER representation of your Public Key
   init(publicDER: Array<UInt8>) throws
   /// Attempts to instantiate an instance of your Private Key when given a DER representation of your Private Key
@@ -67,11 +62,6 @@ extension DERDecodable {
 
 /// Conform to this protocol if your type can be described in an ASN1 DER representation
 internal protocol DEREncodable {
-  /// The keys primary ASN1 object identifier (ex: for RSA Keys --> 'rsaEncryption' --> [0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01])
-  static var primaryObjectIdentifier: Array<UInt8> { get }
-  /// The keys secondary ASN1 object identifier (ex: for RSA Keys --> 'null' --> nil)
-  static var secondaryObjectIdentifier: Array<UInt8>? { get }
-
   /// Returns the DER encoded representation of the Public Key
   func publicKeyDER() throws -> Array<UInt8>
   /// Returns the DER encoded representation of the Private Key
@@ -101,6 +91,11 @@ extension DEREncodable {
 }
 
 struct DER {
+  internal enum Error:Swift.Error {
+    /// We were provided invalid DER data
+    case invalidDERFormat
+  }
+  
   /// Integer to Octet String Primitive
   /// - Parameters:
   ///   - x: nonnegative integer to be converted

+ 96 - 0
Sources/CryptoSwift/PKCS/PKCS1v15.swift

@@ -0,0 +1,96 @@
+//
+//  CryptoSwift
+//
+//  Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+//  This software is provided 'as-is', without any express or implied warranty.
+//
+//  In no event will the authors be held liable for any damages arising from the use of this software.
+//
+//  Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+//  - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+//  - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+//  - This notice may not be removed or altered from any source or binary distribution.
+//
+
+//  PKCS is a group of public-key cryptography standards devised
+//  and published by RSA Security Inc, starting in the early 1990s.
+//
+
+/// EMSA PKCS1 v1.5 Padding Scheme
+///
+/// The EMSA Version of the PKCS1 v1.5 padding scheme is **deterministic** (it pads the messages contents with 255 value bytes)
+/// ```
+/// // The returned structure
+/// // - PS is the applied padding
+/// // - M is your original Message
+/// EM = 0x00 || 0x01 || PS || 0x00 || M.
+/// ```
+/// - Note: This Padding scheme is intended to be used for encoding RSA Signatures
+///
+/// [EMSA-PKCS1v1_5 IETF Spec](https://datatracker.ietf.org/doc/html/rfc8017#section-9.2)
+struct EMSAPKCS1v15Padding: PaddingProtocol {
+
+  init() {
+  }
+
+  @inlinable
+  func add(to bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+    var r = blockSize - ((bytes.count + 3) % blockSize)
+    if r <= 0 { r = blockSize - 3 }
+
+    return [0x00, 0x01] + Array<UInt8>(repeating: 0xFF, count: r) + [0x00] + bytes
+  }
+
+  @inlinable
+  func remove(from bytes: Array<UInt8>, blockSize _: Int?) -> Array<UInt8> {
+    assert(!bytes.isEmpty, "Need bytes to remove padding")
+
+    assert(bytes.prefix(2) == [0x00, 0x01], "Invalid padding prefix")
+
+    guard let paddingLength = bytes.dropFirst(2).firstIndex(of: 0x00) else { return bytes }
+
+    guard (paddingLength + 1) <= bytes.count else { return bytes }
+
+    return Array(bytes[(paddingLength + 1)...])
+  }
+}
+
+/// EME PKCS1 v1.5 Padding Scheme
+///
+/// The EME Version of the PKCS1 v1.5 padding scheme is **non deterministic** (it pads the messages contents with psuedo-random bytes)
+/// ```
+/// // The returned structure
+/// // - PS is the applied padding
+/// // - M is your original Message
+/// EM = 0x00 || 0x02 || PS || 0x00 || M.
+/// ```
+/// - Note: This Padding scheme is intended to be used for encoding messages before RSA Encryption
+///
+/// [EME-PKCS1v1_5 IETF Spec](https://datatracker.ietf.org/doc/html/rfc8017#section-7.2.1)
+struct EMEPKCS1v15Padding: PaddingProtocol {
+
+  init() {
+  }
+
+  @inlinable
+  func add(to bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+    var r = blockSize - ((bytes.count + 3) % blockSize)
+    if r <= 0 { r = blockSize - 3 }
+
+    return [0x00, 0x02] + (0..<r).map { _ in UInt8.random(in: 1...UInt8.max) } + [0x00] + bytes
+  }
+
+  @inlinable
+  func remove(from bytes: Array<UInt8>, blockSize _: Int?) -> Array<UInt8> {
+    assert(!bytes.isEmpty, "Need bytes to remove padding")
+
+    assert(bytes.prefix(2) == [0x00, 0x02], "Invalid padding prefix")
+
+    guard let paddingLength = bytes.dropFirst(2).firstIndex(of: 0x00) else { return bytes }
+
+    guard (paddingLength + 1) <= bytes.count else { return bytes }
+
+    return Array(bytes[(paddingLength + 1)...])
+  }
+}

+ 9 - 1
Sources/CryptoSwift/Padding.swift

@@ -19,7 +19,7 @@ public protocol PaddingProtocol {
 }
 
 public enum Padding: PaddingProtocol {
-  case noPadding, zeroPadding, pkcs7, pkcs5, iso78164, iso10126
+  case noPadding, zeroPadding, pkcs7, pkcs5, eme_pkcs1v15, emsa_pkcs1v15, iso78164, iso10126
 
   public func add(to: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
     switch self {
@@ -31,6 +31,10 @@ public enum Padding: PaddingProtocol {
         return PKCS7.Padding().add(to: to, blockSize: blockSize)
       case .pkcs5:
         return PKCS5.Padding().add(to: to, blockSize: blockSize)
+      case .eme_pkcs1v15:
+        return EMEPKCS1v15Padding().add(to: to, blockSize: blockSize)
+      case .emsa_pkcs1v15:
+        return EMSAPKCS1v15Padding().add(to: to, blockSize: blockSize)
       case .iso78164:
         return ISO78164Padding().add(to: to, blockSize: blockSize)
       case .iso10126:
@@ -48,6 +52,10 @@ public enum Padding: PaddingProtocol {
         return PKCS7.Padding().remove(from: from, blockSize: blockSize)
       case .pkcs5:
         return PKCS5.Padding().remove(from: from, blockSize: blockSize)
+      case .eme_pkcs1v15:
+        return EMEPKCS1v15Padding().remove(from: from, blockSize: blockSize)
+      case .emsa_pkcs1v15:
+        return EMSAPKCS1v15Padding().remove(from: from, blockSize: blockSize)
       case .iso78164:
         return ISO78164Padding().remove(from: from, blockSize: blockSize)
       case .iso10126:

+ 0 - 152
Sources/CryptoSwift/RSA.swift

@@ -1,152 +0,0 @@
-//
-//  CryptoSwift
-//
-//  Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
-//  This software is provided 'as-is', without any express or implied warranty.
-//
-//  In no event will the authors be held liable for any damages arising from the use of this software.
-//
-//  Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
-//
-//  - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
-//  - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
-//  - This notice may not be removed or altered from any source or binary distribution.
-//
-
-// Foundation is required for `Data` to be found
-import Foundation
-
-// Note: The `BigUInt` struct was copied from:
-// https://github.com/attaswift/BigInt
-// It allows fast calculation for RSA big numbers
-
-public final class RSA {
-
-  public enum Error: Swift.Error {
-    /// No private key specified
-    case noPrivateKey
-    /// Failed to calculate the inverse e and phi
-    case invalidInverseNotCoprimes
-  }
-
-  /// RSA Modulus
-  public let n: BigUInteger
-
-  /// RSA Public Exponent
-  public let e: BigUInteger
-
-  /// RSA Private Exponent
-  public let d: BigUInteger?
-
-  /// The size of the modulus, in bits
-  public let keySize: Int
-
-  /// The underlying primes used to generate the Private Exponent
-  private let primes: (p: BigUInteger, q: BigUInteger)?
-
-  /// Initialize with RSA parameters
-  /// - Parameters:
-  ///   - n: The RSA Modulus
-  ///   - e: The RSA Public Exponent
-  ///   - d: The RSA Private Exponent (or nil if unknown, e.g. if only public key is known)
-  public init(n: BigUInteger, e: BigUInteger, d: BigUInteger? = nil) {
-    self.n = n
-    self.e = e
-    self.d = d
-    self.primes = nil
-
-    self.keySize = n.bitWidth
-  }
-
-  /// Initialize with RSA parameters
-  /// - Parameters:
-  ///   - n: The RSA Modulus
-  ///   - e: The RSA Public Exponent
-  ///   - d: The RSA Private Exponent (or nil if unknown, e.g. if only public key is known)
-  public convenience init(n: Array<UInt8>, e: Array<UInt8>, d: Array<UInt8>? = nil) {
-    if let d = d {
-      self.init(n: BigUInteger(Data(n)), e: BigUInteger(Data(e)), d: BigUInteger(Data(d)))
-    } else {
-      self.init(n: BigUInteger(Data(n)), e: BigUInteger(Data(e)))
-    }
-  }
-
-  /// Initialize with a generated key pair
-  /// - Parameter keySize: The size of the modulus
-  public convenience init(keySize: Int) throws {
-    // Generate prime numbers
-    let p = BigUInteger.generatePrime(keySize / 2)
-    let q = BigUInteger.generatePrime(keySize / 2)
-
-    // Calculate modulus
-    let n = p * q
-
-    // Calculate public and private exponent
-    let e: BigUInteger = 65537
-    let phi = (p - 1) * (q - 1)
-    guard let d = e.inverse(phi) else {
-      throw RSA.Error.invalidInverseNotCoprimes
-    }
-
-    // Initialize
-    self.init(n: n, e: e, d: d, p: p, q: q)
-  }
-
-  /// Initialize with RSA parameters
-  /// - Parameters:
-  ///   - n: The RSA Modulus
-  ///   - e: The RSA Public Exponent
-  ///   - d: The RSA Private Exponent
-  ///   - p: The 1st Prime used to generate the Private Exponent
-  ///   - q: The 2nd Prime used to generate the Private Exponent
-  private init(n: BigUInteger, e: BigUInteger, d: BigUInteger, p: BigUInteger, q: BigUInteger) {
-    self.n = n
-    self.e = e
-    self.d = d
-    self.primes = (p, q)
-
-    self.keySize = n.bitWidth
-  }
-
-  // TODO: Add initializer from PEM (ASN.1 with DER header) (See #892)
-
-  // TODO: Add export to PEM (ASN.1 with DER header) (See #892)
-}
-
-// MARK: Cipher
-
-extension RSA: Cipher {
-
-  @inlinable
-  public func encrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
-    // Calculate encrypted data
-    return BigUInteger(Data(bytes)).power(self.e, modulus: self.n).serialize().bytes
-  }
-
-  @inlinable
-  public func decrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
-    // Check for Private Exponent presence
-    guard let d = d else {
-      throw RSA.Error.noPrivateKey
-    }
-
-    // Calculate decrypted data
-    return BigUInteger(Data(bytes)).power(d, modulus: self.n).serialize().bytes
-  }
-}
-
-// MARK: CS.BigUInt extension
-
-extension BigUInteger {
-
-  public static func generatePrime(_ width: Int) -> BigUInteger {
-    // Note: Need to find a better way to generate prime numbers
-    while true {
-      var random = BigUInteger.randomInteger(withExactWidth: width)
-      random |= BigUInteger(1)
-      if random.isPrime() {
-        return random
-      }
-    }
-  }
-}

+ 138 - 0
Sources/CryptoSwift/RSA/RSA+Cipher.swift

@@ -0,0 +1,138 @@
+//
+//  CryptoSwift
+//
+//  Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+//  This software is provided 'as-is', without any express or implied warranty.
+//
+//  In no event will the authors be held liable for any damages arising from the use of this software.
+//
+//  Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+//  - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+//  - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+//  - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+// MARK: Cipher
+
+extension RSA: Cipher {
+
+  @inlinable
+  public func encrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
+    return try self.encrypt(Array<UInt8>(bytes), variant: .pksc1v15)
+  }
+
+  @inlinable
+  public func encrypt(_ bytes: Array<UInt8>, variant: RSAEncryptionVariant) throws -> Array<UInt8> {
+    // Prepare the data for the specified variant
+    let preparedData = try variant.prepare(bytes, blockSize: self.keySize / 8)
+
+    // Encrypt the prepared data
+    return try variant.formatEncryptedBytes(self.encryptPreparedBytes(preparedData), blockSize: self.keySize / 8)
+  }
+
+  @inlinable
+  internal func encryptPreparedBytes(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
+    // Ensure our Key is large enough to safely encrypt the data
+    //guard (self.keySize / 8) >= bytes.count else { throw RSA.Error.invalidMessageLengthForEncryption }
+
+    // Calculate encrypted data
+    return BigUInteger(Data(bytes)).power(self.e, modulus: self.n).serialize().bytes
+  }
+
+  @inlinable
+  public func decrypt(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
+    return try self.decrypt(Array<UInt8>(bytes), variant: .pksc1v15)
+  }
+
+  @inlinable
+  public func decrypt(_ bytes: Array<UInt8>, variant: RSAEncryptionVariant) throws -> Array<UInt8> {
+    // Decrypt the data
+    let decrypted = try self.decryptPreparedBytes(bytes)
+
+    // Remove padding / unstructure data and return the raw plaintext
+    return variant.removePadding(decrypted, blockSize: self.keySize / 8)
+  }
+
+  @inlinable
+  internal func decryptPreparedBytes(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
+    // Check for Private Exponent presence
+    guard let d = d else { throw RSA.Error.noPrivateKey }
+
+    // Calculate decrypted data
+    return BigUInteger(Data(bytes)).power(d, modulus: self.n).serialize().bytes
+  }
+}
+
+extension RSA {
+  /// RSA Encryption Block Types
+  /// - [RFC2313 8.1 - Encryption block formatting](https://datatracker.ietf.org/doc/html/rfc2313#section-8.1)
+  public enum RSAEncryptionVariant {
+    /// The `unsafe` encryption variant, is fully deterministic and doesn't format the inbound data in any way.
+    ///
+    /// - Warning: This is considered an unsafe method of encryption.
+    case unsafe
+    /// The `raw` encryption variant formats the inbound data with a deterministic padding scheme.
+    ///
+    /// - Warning: This is also considered to be an unsafe method of encryption, but matches the `Security` frameworks functionality.
+    case raw
+    /// The `pkcs1v15` encryption variant formats the inbound data with a non deterministic pseudo random padding scheme.
+    ///
+    /// [EME PKCS1v1.5 Padding Scheme Spec](https://datatracker.ietf.org/doc/html/rfc2313#section-8.1)
+    case pksc1v15
+
+    @inlinable
+    internal func prepare(_ bytes: Array<UInt8>, blockSize: Int) throws -> Array<UInt8> {
+      switch self {
+        case .unsafe:
+          return bytes
+        case .raw:
+          // We need at least 11 bytes of padding in order to safely encrypt messages
+          // - block types 1 and 2 have this minimum padding requirement, block type 0 isn't specified, but we enforce the minimum padding length here to be safe.
+          guard blockSize >= bytes.count + 11 else { throw RSA.Error.invalidMessageLengthForEncryption }
+          return Array(repeating: 0x00, count: blockSize - bytes.count) + bytes
+        case .pksc1v15:
+          // The `Security` framework refuses to encrypt a zero byte message using the pkcs1v15 padding scheme, so we do the same
+          guard !bytes.isEmpty else { throw RSA.Error.invalidMessageLengthForEncryption }
+          // We need at least 11 bytes of random padding in order to safely encrypt messages (RFC2313 Section 8.1 - Note 6)
+          guard blockSize >= bytes.count + 11 else { throw RSA.Error.invalidMessageLengthForEncryption }
+          return Padding.eme_pkcs1v15.add(to: bytes, blockSize: blockSize)
+      }
+    }
+
+    @inlinable
+    internal func formatEncryptedBytes(_ bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+      switch self {
+        case .unsafe:
+          return bytes
+        case .raw, .pksc1v15:
+          // Format the encrypted bytes before returning
+          var bytes = bytes
+          if bytes.isEmpty {
+            // Instead of returning an empty byte array, we return an array of zero's of length keySize bytes
+            // This functionality matches that of Apple's `Security` framework
+            return Array<UInt8>(repeating: 0, count: blockSize)
+          } else {
+            while bytes.count % 4 != 0 { bytes.insert(0x00, at: 0) }
+            return bytes
+          }
+      }
+    }
+
+    @inlinable
+    internal func removePadding(_ bytes: Array<UInt8>, blockSize: Int) -> Array<UInt8> {
+      switch self {
+        case .unsafe:
+          return bytes
+        case .raw:
+          return bytes
+        case .pksc1v15:
+          // Convert the Octet String into an Integer Primitive using the BigInteger `serialize` method
+          // (this effectively just prefixes the data with a 0x00 byte indicating that its a positive integer)
+          return Padding.eme_pkcs1v15.remove(from: [0x00] + bytes, blockSize: blockSize)
+      }
+    }
+  }
+}

+ 228 - 0
Sources/CryptoSwift/RSA/RSA+Signature.swift

@@ -0,0 +1,228 @@
+//
+//  CryptoSwift
+//
+//  Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+//  This software is provided 'as-is', without any express or implied warranty.
+//
+//  In no event will the authors be held liable for any damages arising from the use of this software.
+//
+//  Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+//  - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+//  - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+//  - This notice may not be removed or altered from any source or binary distribution.
+//
+
+import Foundation
+
+// MARK: Signatures & Verification
+
+extension RSA: Signature {
+  public func sign(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8> {
+    try self.sign(Array(bytes), variant: .message_pkcs1v15_SHA256)
+  }
+
+  /// Signs the data using the Private key and the specified signature variant
+  /// - Parameters:
+  ///   - bytes: The data to be signed
+  ///   - variant: The variant to use (`digest` variants expect a pre-hashed digest matching that of the specified hash function, `message` variants will hash the data using the specified hash function before signing it)
+  /// - Returns: The signature of the data
+  public func sign(_ bytes: Array<UInt8>, variant: SignatureVariant) throws -> Array<UInt8> {
+    // Check for Private Exponent presence
+    guard let d = d else { throw RSA.Error.noPrivateKey }
+
+    // Hash & Encode Message
+    let hashedAndEncoded = try RSA.hashedAndEncoded(bytes, variant: variant, keySizeInBytes: self.keySize / 8)
+
+    /// Calculate the Signature
+    let signedData = BigUInteger(Data(hashedAndEncoded)).power(d, modulus: self.n).serialize().bytes
+
+    return signedData
+  }
+
+  public func verify(signature: ArraySlice<UInt8>, for expectedData: ArraySlice<UInt8>) throws -> Bool {
+    try self.verify(signature: Array(signature), for: Array(expectedData), variant: .message_pkcs1v15_SHA256)
+  }
+
+  /// Verifies whether a Signature is valid for the provided data
+  /// - Parameters:
+  ///   - signature: The signature to verify
+  ///   - expectedData: The original data that you expected to have been signed
+  ///   - variant: The variant used to sign the data
+  /// - Returns: `True` when the signature is valid for the expected data, `False` otherwise.
+  ///
+  /// [IETF Verification Spec](https://datatracker.ietf.org/doc/html/rfc8017#section-8.2.2)
+  public func verify(signature: Array<UInt8>, for bytes: Array<UInt8>, variant: SignatureVariant) throws -> Bool {
+    /// Step 1: Ensure the signature is the same length as the key's modulus
+    guard signature.count == (self.keySize / 8) || (signature.count - 1) == (self.keySize / 8) else { throw Error.invalidSignatureLength }
+
+    let expectedData = try Array<UInt8>(RSA.hashedAndEncoded(bytes, variant: variant, keySizeInBytes: self.keySize / 8).dropFirst())
+
+    /// Step 2: 'Decrypt' the signature
+    let signatureResult = BigUInteger(Data(signature)).power(self.e, modulus: self.n).serialize().bytes
+
+    /// Step 3: Compare the 'decrypted' signature with the prepared / encoded expected message....
+    guard signatureResult == expectedData else { return false }
+
+    return true
+  }
+
+  /// Hashes and Encodes a message for signing and verifying
+  ///
+  /// - Note: [EMSA-PKCS1-v1_5](https://datatracker.ietf.org/doc/html/rfc8017#section-9.2)
+  fileprivate static func hashedAndEncoded(_ bytes: [UInt8], variant: SignatureVariant, keySizeInBytes: Int) throws -> Array<UInt8> {
+    /// 1.  Apply the hash function to the message M to produce a hash
+    let hashedMessage = variant.calculateHash(bytes)
+
+    guard variant.enforceLength(hashedMessage) else { throw RSA.Error.invalidMessageLengthForSigning }
+
+    /// 2. Encode the algorithm ID for the hash function and the hash value into an ASN.1 value of type DigestInfo
+    /// PKCS#1_15 DER Structure (OID == sha256WithRSAEncryption)
+    let asn: ASN1.Node = .sequence(nodes: [
+      .sequence(nodes: [
+        .objectIdentifier(data: Data(variant.identifier)),
+        .null
+      ]),
+      .octetString(data: Data(hashedMessage))
+    ])
+
+    let t = ASN1.Encoder.encode(asn)
+
+    /// 3.  If emLen < tLen + 11, output "intended encoded message length too short" and stop
+    //print("Checking Key Size: \(keySizeInBytes) < \(t.count + 11)")
+    if keySizeInBytes < t.count + 11 { throw RSA.Error.invalidMessageLengthForSigning }
+
+    /// 4.  Generate an octet string PS consisting of emLen - tLen - 3
+    /// octets with hexadecimal value 0xff. The length of PS will be
+    /// at least 8 octets.
+    /// 5.  Concatenate PS, the DER encoding T, and other padding to form
+    /// the encoded message EM as EM = 0x00 || 0x01 || PS || 0x00 || T.
+    let padded = variant.pad(bytes: t, to: keySizeInBytes)
+
+    /// Ensure the signature is of the correct length
+    guard padded.count == keySizeInBytes else { throw RSA.Error.invalidMessageLengthForSigning }
+
+    return padded
+  }
+}
+
+extension RSA {
+  public enum SignatureVariant {
+    /// Hashes the raw message using MD5 before signing the data
+    case message_pkcs1v15_MD5
+    /// Hashes the raw message using SHA1 before signing the data
+    case message_pkcs1v15_SHA1
+    /// Hashes the raw message using SHA224 before signing the data
+    case message_pkcs1v15_SHA224
+    /// Hashes the raw message using SHA256 before signing the data
+    case message_pkcs1v15_SHA256
+    /// Hashes the raw message using SHA384 before signing the data
+    case message_pkcs1v15_SHA384
+    /// Hashes the raw message using SHA512 before signing the data
+    case message_pkcs1v15_SHA512
+    /// Hashes the raw message using SHA512-224 before signing the data
+    case message_pkcs1v15_SHA512_224
+    /// Hashes the raw message using SHA512-256 before signing the data
+    case message_pkcs1v15_SHA512_256
+    /// This variant isn't supported yet
+    case digest_pkcs1v15_RAW
+    /// This variant expects that the data to be signed is a valid MD5 Hash Digest
+    case digest_pkcs1v15_MD5
+    /// This variant expects that the data to be signed is a valid SHA1 Hash Digest
+    case digest_pkcs1v15_SHA1
+    /// This variant expects that the data to be signed is a valid SHA224 Hash Digest
+    case digest_pkcs1v15_SHA224
+    /// This variant expects that the data to be signed is a valid SHA256 Hash Digest
+    case digest_pkcs1v15_SHA256
+    /// This variant expects that the data to be signed is a valid SHA384 Hash Digest
+    case digest_pkcs1v15_SHA384
+    /// This variant expects that the data to be signed is a valid SHA512 Hash Digest
+    case digest_pkcs1v15_SHA512
+    /// This variant expects that the data to be signed is a valid SHA512-224 Hash Digest
+    case digest_pkcs1v15_SHA512_224
+    /// This variant expects that the data to be signed is a valid SHA512-256 Hash Digest
+    case digest_pkcs1v15_SHA512_256
+
+    internal var identifier: Array<UInt8> {
+      switch self {
+        case .digest_pkcs1v15_RAW: return []
+        case .message_pkcs1v15_MD5, .digest_pkcs1v15_MD5: return Array<UInt8>(arrayLiteral: 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05)
+        case .message_pkcs1v15_SHA1, .digest_pkcs1v15_SHA1: return Array<UInt8>(arrayLiteral: 0x2b, 0x0e, 0x03, 0x02, 0x1a)
+        case .message_pkcs1v15_SHA256, .digest_pkcs1v15_SHA256: return Array<UInt8>(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01)
+        case .message_pkcs1v15_SHA384, .digest_pkcs1v15_SHA384: return Array<UInt8>(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02)
+        case .message_pkcs1v15_SHA512, .digest_pkcs1v15_SHA512: return Array<UInt8>(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03)
+        case .message_pkcs1v15_SHA224, .digest_pkcs1v15_SHA224: return Array<UInt8>(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04)
+        case .message_pkcs1v15_SHA512_224, .digest_pkcs1v15_SHA512_224: return Array<UInt8>(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x05)
+        case .message_pkcs1v15_SHA512_256, .digest_pkcs1v15_SHA512_256: return Array<UInt8>(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x06)
+      }
+    }
+
+    internal func calculateHash(_ bytes: Array<UInt8>) -> Array<UInt8> {
+      switch self {
+        case .message_pkcs1v15_MD5:
+          return Digest.md5(bytes)
+        case .message_pkcs1v15_SHA1:
+          return Digest.sha1(bytes)
+        case .message_pkcs1v15_SHA224:
+          return Digest.sha224(bytes)
+        case .message_pkcs1v15_SHA256:
+          return Digest.sha256(bytes)
+        case .message_pkcs1v15_SHA384:
+          return Digest.sha384(bytes)
+        case .message_pkcs1v15_SHA512:
+          return Digest.sha512(bytes)
+        case .message_pkcs1v15_SHA512_224:
+          return Digest.sha2(bytes, variant: .sha224)
+        case .message_pkcs1v15_SHA512_256:
+          return Digest.sha2(bytes, variant: .sha256)
+        case .digest_pkcs1v15_RAW,
+             .digest_pkcs1v15_MD5,
+             .digest_pkcs1v15_SHA1,
+             .digest_pkcs1v15_SHA224,
+             .digest_pkcs1v15_SHA256,
+             .digest_pkcs1v15_SHA384,
+             .digest_pkcs1v15_SHA512,
+             .digest_pkcs1v15_SHA512_224,
+             .digest_pkcs1v15_SHA512_256:
+          return bytes
+      }
+    }
+
+    internal func enforceLength(_ bytes: Array<UInt8>) -> Bool {
+      switch self {
+        case .digest_pkcs1v15_MD5:
+          return bytes.count <= 16
+        case .digest_pkcs1v15_SHA1:
+          return bytes.count <= 20
+        case .digest_pkcs1v15_SHA224:
+          return bytes.count <= 28
+        case .digest_pkcs1v15_SHA256:
+          return bytes.count <= 32
+        case .digest_pkcs1v15_SHA384:
+          return bytes.count <= 48
+        case .digest_pkcs1v15_SHA512:
+          return bytes.count <= 64
+        case .digest_pkcs1v15_SHA512_224:
+          return bytes.count <= 28
+        case .digest_pkcs1v15_SHA512_256:
+          return bytes.count <= 32
+        case .message_pkcs1v15_MD5,
+             .message_pkcs1v15_SHA1,
+             .message_pkcs1v15_SHA224,
+             .message_pkcs1v15_SHA256,
+             .message_pkcs1v15_SHA384,
+             .message_pkcs1v15_SHA512,
+             .message_pkcs1v15_SHA512_224,
+             .message_pkcs1v15_SHA512_256:
+          return true
+        default:
+          return false
+      }
+    }
+
+    /// Right now the only Padding Scheme supported is [EMCS-PKCS1v15](https://www.rfc-editor.org/rfc/rfc8017#section-9.2) (others include [EMSA-PSS](https://www.rfc-editor.org/rfc/rfc8017#section-9.1))
+    internal func pad(bytes: Array<UInt8>, to blockSize: Int) -> Array<UInt8> {
+      return Padding.emsa_pkcs1v15.add(to: bytes, blockSize: blockSize)
+    }
+  }
+}

+ 376 - 0
Sources/CryptoSwift/RSA/RSA.swift

@@ -0,0 +1,376 @@
+//
+//  CryptoSwift
+//
+//  Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+//  This software is provided 'as-is', without any express or implied warranty.
+//
+//  In no event will the authors be held liable for any damages arising from the use of this software.
+//
+//  Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+//  - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+//  - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+//  - This notice may not be removed or altered from any source or binary distribution.
+//
+
+// Foundation is required for `Data` to be found
+import Foundation
+
+// Note: The `BigUInt` struct was copied from:
+// https://github.com/attaswift/BigInt
+// It allows fast calculation for RSA big numbers
+
+public final class RSA: DERCodable {
+  /// RSA Key Errors
+  public enum Error: Swift.Error {
+    /// No private key specified
+    case noPrivateKey
+    /// Failed to calculate the inverse e and phi
+    case invalidInverseNotCoprimes
+    /// We only support Version 0 RSA keys (we don't support Version 1 introduced in RFC 3447)
+    case unsupportedRSAVersion
+    /// Failed to verify primes during initialiization (the provided primes don't reproduce the provided private exponent)
+    case invalidPrimes
+    /// We attempted to export a private key without our underlying primes
+    case noPrimes
+    /// Unable to calculate the coefficient during a private key import / export
+    case unableToCalculateCoefficient
+    /// The signature to verify is of an invalid length
+    case invalidSignatureLength
+    /// The message to be signed is of an invalid length
+    case invalidMessageLengthForSigning
+    /// The message to be encrypted is of an invalid length
+    case invalidMessageLengthForEncryption
+    /// The error thrown when Decryption fails
+    case invalidDecryption
+  }
+
+  /// RSA Modulus
+  public let n: BigUInteger
+
+  /// RSA Public Exponent
+  public let e: BigUInteger
+
+  /// RSA Private Exponent
+  public let d: BigUInteger?
+
+  /// The size of the modulus, in bits
+  public let keySize: Int
+
+  /// The underlying primes used to generate the Private Exponent
+  private let primes: (p: BigUInteger, q: BigUInteger)?
+
+  /// Initialize with RSA parameters
+  /// - Parameters:
+  ///   - n: The RSA Modulus
+  ///   - e: The RSA Public Exponent
+  ///   - d: The RSA Private Exponent (or nil if unknown, e.g. if only public key is known)
+  public init(n: BigUInteger, e: BigUInteger, d: BigUInteger? = nil) {
+    self.n = n
+    self.e = e
+    self.d = d
+    self.primes = nil
+
+    self.keySize = n.bitWidth
+  }
+
+  /// Initialize with RSA parameters
+  /// - Parameters:
+  ///   - n: The RSA Modulus
+  ///   - e: The RSA Public Exponent
+  ///   - d: The RSA Private Exponent (or nil if unknown, e.g. if only public key is known)
+  public convenience init(n: Array<UInt8>, e: Array<UInt8>, d: Array<UInt8>? = nil) {
+    if let d = d {
+      self.init(n: BigUInteger(Data(n)), e: BigUInteger(Data(e)), d: BigUInteger(Data(d)))
+    } else {
+      self.init(n: BigUInteger(Data(n)), e: BigUInteger(Data(e)))
+    }
+  }
+
+  /// Initialize with a generated key pair
+  /// - Parameter keySize: The size of the modulus
+  public convenience init(keySize: Int) throws {
+    // Generate prime numbers
+    let p = BigUInteger.generatePrime(keySize / 2)
+    let q = BigUInteger.generatePrime(keySize / 2)
+
+    // Calculate modulus
+    let n = p * q
+
+    // Calculate public and private exponent
+    let e: BigUInteger = 65537
+    let phi = (p - 1) * (q - 1)
+    guard let d = e.inverse(phi) else {
+      throw RSA.Error.invalidInverseNotCoprimes
+    }
+
+    // Initialize
+    self.init(n: n, e: e, d: d, p: p, q: q)
+  }
+
+  /// Initialize with RSA parameters
+  /// - Parameters:
+  ///   - n: The RSA Modulus
+  ///   - e: The RSA Public Exponent
+  ///   - d: The RSA Private Exponent
+  ///   - p: The 1st Prime used to generate the Private Exponent
+  ///   - q: The 2nd Prime used to generate the Private Exponent
+  private init(n: BigUInteger, e: BigUInteger, d: BigUInteger, p: BigUInteger, q: BigUInteger) {
+    self.n = n
+    self.e = e
+    self.d = d
+    self.primes = (p, q)
+
+    self.keySize = n.bitWidth
+  }
+}
+
+// MARK: DER Initializers (See #892)
+
+extension RSA {
+  /// Decodes the provided data into a Public RSA Key
+  ///
+  /// [IETF Spec RFC2313](https://datatracker.ietf.org/doc/html/rfc2313#section-7.1)
+  /// ```
+  /// =========================
+  ///  RSA PublicKey Structure
+  /// =========================
+  ///
+  /// RSAPublicKey ::= SEQUENCE {
+  ///   modulus           INTEGER,  -- n
+  ///   publicExponent    INTEGER,  -- e
+  /// }
+  /// ```
+  internal convenience init(publicDER der: Array<UInt8>) throws {
+    let asn = try ASN1.Decoder.decode(data: Data(der))
+
+    // Enforce the above ASN Structure
+    guard case .sequence(let params) = asn else { throw DER.Error.invalidDERFormat }
+    guard params.count == 2 else { throw DER.Error.invalidDERFormat }
+
+    guard case .integer(let modulus) = params[0] else { throw DER.Error.invalidDERFormat }
+    guard case .integer(let publicExponent) = params[1] else { throw DER.Error.invalidDERFormat }
+
+    self.init(n: BigUInteger(modulus), e: BigUInteger(publicExponent))
+  }
+
+  /// Decodes the provided data into a Private RSA Key
+  ///
+  /// [IETF Spec RFC2313](https://datatracker.ietf.org/doc/html/rfc2313#section-7.2)
+  /// ```
+  /// ==========================
+  ///  RSA PrivateKey Structure
+  /// ==========================
+  ///
+  /// RSAPrivateKey ::= SEQUENCE {
+  ///   version           Version,
+  ///   modulus           INTEGER,  -- n
+  ///   publicExponent    INTEGER,  -- e
+  ///   privateExponent   INTEGER,  -- d
+  ///   prime1            INTEGER,  -- p
+  ///   prime2            INTEGER,  -- q
+  ///   exponent1         INTEGER,  -- d mod (p-1)
+  ///   exponent2         INTEGER,  -- d mod (q-1)
+  ///   coefficient       INTEGER,  -- (inverse of q) mod p
+  /// }
+  /// ```
+  internal convenience init(privateDER der: Array<UInt8>) throws {
+    let asn = try ASN1.Decoder.decode(data: Data(der))
+
+    // Enforce the above ASN Structure (do we need to extract and verify the eponents and coefficients?)
+    guard case .sequence(let params) = asn else { throw DER.Error.invalidDERFormat }
+    guard params.count == 9 else { throw DER.Error.invalidDERFormat }
+    guard case .integer(let version) = params[0] else { throw DER.Error.invalidDERFormat }
+    guard case .integer(let modulus) = params[1] else { throw DER.Error.invalidDERFormat }
+    guard case .integer(let publicExponent) = params[2] else { throw DER.Error.invalidDERFormat }
+    guard case .integer(let privateExponent) = params[3] else { throw DER.Error.invalidDERFormat }
+    guard case .integer(let prime1) = params[4] else { throw DER.Error.invalidDERFormat }
+    guard case .integer(let prime2) = params[5] else { throw DER.Error.invalidDERFormat }
+    guard case .integer(let exponent1) = params[6] else { throw DER.Error.invalidDERFormat }
+    guard case .integer(let exponent2) = params[7] else { throw DER.Error.invalidDERFormat }
+    guard case .integer(let coefficient) = params[8] else { throw DER.Error.invalidDERFormat }
+
+    // We only support version 0x00 == RFC2313 at the moment
+    // - TODO: Support multiple primes 0x01 version defined in [RFC3447](https://www.rfc-editor.org/rfc/rfc3447#appendix-A.1.2)
+    guard version == Data(hex: "0x00") else { throw Error.unsupportedRSAVersion }
+
+    // Ensure the supplied parameters are correct...
+    // Calculate modulus
+    guard BigUInteger(modulus) == BigUInteger(prime1) * BigUInteger(prime2) else { throw Error.invalidPrimes }
+
+    // Calculate public and private exponent
+    let phi = (BigUInteger(prime1) - 1) * (BigUInteger(prime2) - 1)
+    guard let d = BigUInteger(publicExponent).inverse(phi) else { throw Error.invalidPrimes }
+    guard BigUInteger(privateExponent) == d else { throw Error.invalidPrimes }
+
+    // Ensure the provided coefficient is correct (derived from the primes)
+    guard let calculatedCoefficient = BigUInteger(prime2).inverse(BigUInteger(prime1)) else { throw RSA.Error.unableToCalculateCoefficient }
+    guard calculatedCoefficient == BigUInteger(coefficient) else { throw RSA.Error.invalidPrimes }
+
+    // Ensure the provided exponents are correct as well
+    guard (d % (BigUInteger(prime1) - 1)) == BigUInteger(exponent1) else { throw RSA.Error.invalidPrimes }
+    guard (d % (BigUInteger(prime2) - 1)) == BigUInteger(exponent2) else { throw RSA.Error.invalidPrimes }
+
+    // Proceed with regular initialization
+    self.init(n: BigUInteger(modulus), e: BigUInteger(publicExponent), d: BigUInteger(privateExponent), p: BigUInteger(prime1), q: BigUInteger(prime2))
+  }
+}
+
+// MARK: DER Exports (See #892)
+
+extension RSA {
+  /// The DER representation of this public key
+  ///
+  /// [IETF Spec RFC2313](https://datatracker.ietf.org/doc/html/rfc2313#section-7.1)
+  /// ```
+  /// =========================
+  ///  RSA PublicKey Structure
+  /// =========================
+  ///
+  /// RSAPublicKey ::= SEQUENCE {
+  ///   modulus           INTEGER,  -- n
+  ///   publicExponent    INTEGER   -- e
+  /// }
+  /// ```
+  func publicKeyDER() throws -> Array<UInt8> {
+    let mod = self.n.serialize()
+    let pubKeyAsnNode: ASN1.Node =
+      .sequence(nodes: [
+        .integer(data: DER.i2ospData(x: mod.bytes, size: self.keySize / 8)),
+        .integer(data: DER.i2ospData(x: self.e.serialize().bytes, size: 3))
+      ])
+    return ASN1.Encoder.encode(pubKeyAsnNode)
+  }
+
+  /// The DER representation of this private key
+  ///
+  /// [IETF Spec RFC2313](https://datatracker.ietf.org/doc/html/rfc2313#section-7.2)
+  /// ```
+  /// ==========================
+  ///  RSA PrivateKey Structure
+  /// ==========================
+  ///
+  /// RSAPrivateKey ::= SEQUENCE {
+  ///   version           Version,
+  ///   modulus           INTEGER,  -- n
+  ///   publicExponent    INTEGER,  -- e
+  ///   privateExponent   INTEGER,  -- d
+  ///   prime1            INTEGER,  -- p
+  ///   prime2            INTEGER,  -- q
+  ///   exponent1         INTEGER,  -- d mod (p-1)
+  ///   exponent2         INTEGER,  -- d mod (q-1)
+  ///   coefficient       INTEGER,  -- (inverse of q) mod p
+  /// }
+  /// ```
+  func privateKeyDER() throws -> Array<UInt8> {
+    // Make sure we have a private key
+    guard let d = d else { throw RSA.Error.noPrivateKey }
+    // Make sure we have access to our primes
+    guard let primes = primes else { throw RSA.Error.noPrimes }
+    // Make sure we can calculate our coefficient (inverse of q mod p)
+    guard let coefficient = primes.q.inverse(primes.p) else { throw RSA.Error.unableToCalculateCoefficient }
+
+    let bitWidth = self.keySize / 8
+    let paramWidth = bitWidth / 2
+    // Structure the data (according to RFC2313, version 0x00 RSA Private Key Syntax)
+    let mod = self.n.serialize()
+    let privateKeyAsnNode: ASN1.Node =
+      .sequence(nodes: [
+        .integer(data: Data(hex: "0x00")),
+        .integer(data: DER.i2ospData(x: mod.bytes, size: bitWidth)),
+        .integer(data: DER.i2ospData(x: self.e.serialize().bytes, size: 3)),
+        .integer(data: DER.i2ospData(x: d.serialize().bytes, size: bitWidth)),
+        .integer(data: DER.i2ospData(x: primes.p.serialize().bytes, size: paramWidth)),
+        .integer(data: DER.i2ospData(x: primes.q.serialize().bytes, size: paramWidth)),
+        .integer(data: DER.i2ospData(x: (d % (primes.p - 1)).serialize().bytes, size: paramWidth)),
+        .integer(data: DER.i2ospData(x: (d % (primes.q - 1)).serialize().bytes, size: paramWidth)),
+        .integer(data: DER.i2ospData(x: coefficient.serialize().bytes, size: paramWidth))
+      ])
+
+    // Encode and return the data
+    return ASN1.Encoder.encode(privateKeyAsnNode)
+  }
+
+  /// This method returns the DER encoding of the RSA Key.
+  ///
+  /// - Returns: The ASN1 DER Encoding of the Public or Private RSA Key
+  /// - Note: If the RSA Key is a private key, the private key representation is returned
+  /// - Note: If the RSA Key is a public key, the public key representation is returned
+  /// - Note: If you'd like to only export the public DER of an RSA Key call the `publicKeyExternalRepresentation()` method
+  /// - Note: This method returns the same data as Apple's `SecKeyCopyExternalRepresentation` method.
+  ///
+  /// An example of converting a CryptoSwift RSA key to a SecKey RSA key
+  /// ```
+  /// /// Starting with a CryptoSwift RSA Key
+  /// let rsaKey = try RSA(keySize: 1024)
+  ///
+  /// /// Define your Keys attributes
+  /// let attributes: [String:Any] = [
+  ///   kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
+  ///   kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, // or kSecAttrKeyClassPublic
+  ///   kSecAttrKeySizeInBits as String: 1024, // The appropriate bits
+  ///   kSecAttrIsPermanent as String: false
+  /// ]
+  /// var error:Unmanaged<CFError>? = nil
+  /// guard let rsaSecKey = try SecKeyCreateWithData(rsaKey.externalRepresentation() as CFData, attributes as CFDictionary, &error) else {
+  ///   /// Error constructing SecKey from raw key data
+  ///   return
+  /// }
+  ///
+  /// /// You now have an RSA SecKey for use with Apple's Security framework
+  /// ```
+  ///
+  /// An example of converting a SecKey RSA key to a CryptoSwift RSA key
+  /// ```
+  /// /// Starting with a SecKey RSA Key
+  /// let rsaSecKey:SecKey
+  ///
+  /// /// Copy External Representation
+  /// var externalRepError:Unmanaged<CFError>?
+  /// guard let cfdata = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) else {
+  ///     /// Failed to copy external representation for RSA SecKey
+  ///     return
+  /// }
+  ///
+  /// /// Instantiate the RSA Key from the raw external representation
+  /// let rsaKey = try RSA(rawRepresentation: cfdata as Data)
+  ///
+  /// /// You now have a CryptoSwift RSA Key
+  /// ```
+  ///
+  func externalRepresentation() throws -> Data {
+    if self.primes != nil {
+      return try Data(self.privateKeyDER())
+    } else {
+      return try Data(self.publicKeyDER())
+    }
+  }
+}
+
+// MARK: CS.BigUInt extension
+
+extension BigUInteger {
+
+  public static func generatePrime(_ width: Int) -> BigUInteger {
+    // Note: Need to find a better way to generate prime numbers
+    while true {
+      var random = BigUInteger.randomInteger(withExactWidth: width)
+      random |= BigUInteger(1)
+      if random.isPrime() {
+        return random
+      }
+    }
+  }
+}
+
+// MARK: CustomStringConvertible Conformance
+
+extension RSA: CustomStringConvertible {
+  public var description: String {
+    if self.d != nil {
+      return "CryptoSwift.RSA.PrivateKey<\(self.keySize)>"
+    } else {
+      return "CryptoSwift.RSA.PublicKey<\(self.keySize)>"
+    }
+  }
+}

+ 66 - 0
Sources/CryptoSwift/Signature.swift

@@ -0,0 +1,66 @@
+//
+//  CryptoSwift
+//
+//  Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+//  This software is provided 'as-is', without any express or implied warranty.
+//
+//  In no event will the authors be held liable for any damages arising from the use of this software.
+//
+//  Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+//  - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+//  - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+//  - This notice may not be removed or altered from any source or binary distribution.
+//
+
+public enum SignatureError: Error {
+  case sign
+  case verify
+}
+
+public protocol Signature: AnyObject {
+  var keySize: Int { get }
+
+  /// Sign the given bytes at once
+  ///
+  /// - parameter bytes: Plaintext data to be signed
+  /// - returns: The signed data
+  func sign(_ bytes: ArraySlice<UInt8>) throws -> Array<UInt8>
+  /// Sign the given bytes at once
+  ///
+  /// - parameter bytes: Plaintext data to be signed
+  /// - returns: The signed data
+  func sign(_ bytes: Array<UInt8>) throws -> Array<UInt8>
+
+  /// Verify the given bytes against the expected data
+  ///
+  /// - parameter signature: Signature data
+  /// - parameter expectedData: The original data that you expected to be signed
+  /// - returns: `True` when the signature is valid for the expected data, `False` otherwise.
+  func verify(signature: ArraySlice<UInt8>, for expectedData: ArraySlice<UInt8>) throws -> Bool
+  /// Verify the given bytes against the expected data
+  ///
+  /// - parameter signature: Signature data
+  /// - parameter expectedData: The original data that you expected to be signed
+  /// - returns: `True` when the signature is valid for the expected data, `False` otherwise.
+  func verify(signature: Array<UInt8>, for expectedData: Array<UInt8>) throws -> Bool
+}
+
+extension Signature {
+  /// Sign the given bytes at once
+  ///
+  /// - parameter bytes: Plaintext data to be signed
+  /// - returns: The signed data
+  public func sign(_ bytes: Array<UInt8>) throws -> Array<UInt8> {
+    try self.sign(bytes.slice)
+  }
+
+  /// Verify the given bytes against the expected data
+  ///
+  /// - parameter signature: Signature data
+  /// - parameter expectedData: The original data that you expected to be signed
+  /// - returns: `True` when the signature is valid for the expected data, `False` otherwise.
+  public func verify(signature: Array<UInt8>, for expectedData: Array<UInt8>) throws -> Bool {
+    try self.verify(signature: signature.slice, for: expectedData.slice)
+  }
+}

+ 116 - 6
Tests/CryptoSwiftTests/PaddingTests.swift

@@ -65,25 +65,25 @@ final class PaddingTests: XCTestCase {
     let expected: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 0x80, 0x80]
     let padded = ISO78164Padding().add(to: input, blockSize: 16)
     XCTAssertEqual(padded, expected, "ISO78164 failed")
-    let clean =  ISO78164Padding().remove(from: padded, blockSize: nil)
+    let clean = ISO78164Padding().remove(from: padded, blockSize: nil)
     XCTAssertEqual(clean, input, "ISO78164 failed")
   }
 
   func testISO78164_1() {
     let input: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 0]
     let expected: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 0, 0x80] + [UInt8](repeating: UInt8(0), count: 15)
-    let padded =  ISO78164Padding().add(to: input, blockSize: 16)
+    let padded = ISO78164Padding().add(to: input, blockSize: 16)
     XCTAssertEqual(padded, expected, "ISO78164 failed")
-    let clean =  ISO78164Padding().remove(from: padded, blockSize: nil)
+    let clean = ISO78164Padding().remove(from: padded, blockSize: nil)
     XCTAssertEqual(clean, input, "ISO78164 failed")
   }
 
   func testISO78164_2() {
     let input: Array<UInt8> = []
     let expected: Array<UInt8> = [0x80] + [UInt8](repeating: UInt8(0), count: 15)
-    let padded =  ISO78164Padding().add(to: input, blockSize: 16)
+    let padded = ISO78164Padding().add(to: input, blockSize: 16)
     XCTAssertEqual(padded, expected, "ISO78164 failed")
-    let clean =  ISO78164Padding().remove(from: padded, blockSize: nil)
+    let clean = ISO78164Padding().remove(from: padded, blockSize: nil)
     XCTAssertEqual(clean, input, "ISO78164 failed")
   }
 
@@ -117,6 +117,106 @@ final class PaddingTests: XCTestCase {
     XCTAssertEqual(clean, input, "ISO10126 failed")
   }
 
+  func testEMSAPKCS1v15_1() {
+    let input: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]
+    let padded = EMSAPKCS1v15Padding().add(to: input, blockSize: 32)
+    XCTAssertEqual(padded.prefix(2), [0, 1], "EMSAPKCS1v15 failed")
+    XCTAssertTrue(padded.suffix(16) == input, "EMSAPKCS1v15 failed")
+    XCTAssertEqual(padded.count, 32)
+    let clean = EMSAPKCS1v15Padding().remove(from: padded, blockSize: nil)
+    XCTAssertEqual(clean, input, "EMSAPKCS1v15 failed")
+  }
+
+  func testEMSAPKCS1v15_2() {
+    let input: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]
+    let padded = EMSAPKCS1v15Padding().add(to: input, blockSize: 16)
+    XCTAssertEqual(padded.prefix(2), [0, 1], "EMSAPKCS1v15 failed")
+    XCTAssertTrue(padded.suffix(16) == input, "EMSAPKCS1v15 failed")
+    XCTAssertEqual(padded.count, 32)
+    let clean = EMSAPKCS1v15Padding().remove(from: padded, blockSize: nil)
+    XCTAssertEqual(clean, input, "EMSAPKCS1v15 failed")
+  }
+
+  func testEMSAPKCS1v15_3() {
+    let input: Array<UInt8> = []
+    let padded = EMSAPKCS1v15Padding().add(to: input, blockSize: 16)
+    XCTAssertTrue(padded.starts(with: input), "EMSAPKCS1v15 failed")
+    XCTAssertEqual(padded.last, 0, "EMSAPKCS1v15 failed")
+    XCTAssertEqual(padded.count, 16)
+    let clean = EMSAPKCS1v15Padding().remove(from: padded, blockSize: nil)
+    XCTAssertEqual(clean, input, "EMSAPKCS1v15 failed")
+  }
+
+  func testEMSAPKCS1v15_4() {
+    let input: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3]
+    let padded = EMSAPKCS1v15Padding().add(to: input, blockSize: 16)
+    XCTAssertEqual(padded.prefix(2), [0, 1], "EMSAPKCS1v15 failed")
+    XCTAssertTrue(padded.suffix(13) == input, "EMSAPKCS1v15 failed")
+    XCTAssertEqual(padded.count, 32)
+    let clean = EMSAPKCS1v15Padding().remove(from: padded, blockSize: nil)
+    XCTAssertEqual(clean, input, "EMSAPKCS1v15 failed")
+  }
+
+  func testEMSAPKCS1v15_5() {
+    let input: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2]
+    let padded = EMSAPKCS1v15Padding().add(to: input, blockSize: 16)
+    XCTAssertEqual(padded.prefix(2), [0, 1], "EMSAPKCS1v15 failed")
+    XCTAssertTrue(padded.suffix(12) == input, "EMSAPKCS1v15 failed")
+    XCTAssertEqual(padded.count, 16)
+    let clean = EMSAPKCS1v15Padding().remove(from: padded, blockSize: nil)
+    XCTAssertEqual(clean, input, "EMSAPKCS1v15 failed")
+  }
+
+  func testEMEPKCS1v15_1() {
+    let input: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]
+    let padded = EMEPKCS1v15Padding().add(to: input, blockSize: 32)
+    XCTAssertEqual(padded.prefix(2), [0, 2], "EMEPKCS1v15 failed")
+    XCTAssertTrue(padded.suffix(16) == input, "EMEPKCS1v15 failed")
+    XCTAssertEqual(padded.count, 32)
+    let clean = EMEPKCS1v15Padding().remove(from: padded, blockSize: nil)
+    XCTAssertEqual(clean, input, "EMEPKCS1v15 failed")
+  }
+
+  func testEMEPKCS1v15_2() {
+    let input: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]
+    let padded = EMEPKCS1v15Padding().add(to: input, blockSize: 16)
+    XCTAssertEqual(padded.prefix(2), [0, 2], "EMEPKCS1v15 failed")
+    XCTAssertTrue(padded.suffix(16) == input, "EMEPKCS1v15 failed")
+    XCTAssertEqual(padded.count, 32)
+    let clean = EMEPKCS1v15Padding().remove(from: padded, blockSize: nil)
+    XCTAssertEqual(clean, input, "EMEPKCS1v15 failed")
+  }
+
+  func testEMEPKCS1v15_3() {
+    let input: Array<UInt8> = []
+    let padded = EMEPKCS1v15Padding().add(to: input, blockSize: 16)
+    XCTAssertTrue(padded.starts(with: input), "EMEPKCS1v15 failed")
+    XCTAssertEqual(padded.last, 0, "EMEPKCS1v15 failed")
+    XCTAssertEqual(padded.count, 16)
+    let clean = EMEPKCS1v15Padding().remove(from: padded, blockSize: nil)
+    XCTAssertEqual(clean, input, "EMEPKCS1v15 failed")
+  }
+
+  func testEMEPKCS1v15_4() {
+    let input: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3]
+    let padded = EMEPKCS1v15Padding().add(to: input, blockSize: 16)
+    XCTAssertEqual(padded.prefix(2), [0, 2], "EMEPKCS1v15 failed")
+    XCTAssertTrue(padded.suffix(13) == input, "EMEPKCS1v15 failed")
+    XCTAssertEqual(padded.count, 32)
+    let clean = EMEPKCS1v15Padding().remove(from: padded, blockSize: nil)
+    XCTAssertEqual(clean, input, "EMEPKCS1v15 failed")
+  }
+
+  func testEMEPKCS1v15_5() {
+    let input: Array<UInt8> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2]
+    let padded = EMEPKCS1v15Padding().add(to: input, blockSize: 16)
+    XCTAssertEqual(padded.prefix(2), [0, 2], "EMEPKCS1v15 failed")
+    XCTAssertTrue(padded.suffix(12) == input, "EMEPKCS1v15 failed")
+    XCTAssertEqual(padded.count, 16)
+    let clean = EMEPKCS1v15Padding().remove(from: padded, blockSize: nil)
+    XCTAssertEqual(clean, input, "EMEPKCS1v15 failed")
+  }
+
   static let allTests = [
     ("testPKCS7_0", testPKCS7_0),
     ("testPKCS7_1", testPKCS7_1),
@@ -128,6 +228,16 @@ final class PaddingTests: XCTestCase {
     ("testISO78164_2", testISO78164_2),
     ("testISO10126_0", testISO10126_0),
     ("testISO10126_1", testISO10126_1),
-    ("testISO10126_2", testISO10126_2)
+    ("testISO10126_2", testISO10126_2),
+    ("testEMSAPKCS1v15_1", testEMSAPKCS1v15_1),
+    ("testEMSAPKCS1v15_2", testEMSAPKCS1v15_2),
+    ("testEMSAPKCS1v15_3", testEMSAPKCS1v15_3),
+    ("testEMSAPKCS1v15_4", testEMSAPKCS1v15_4),
+    ("testEMSAPKCS1v15_5", testEMSAPKCS1v15_5),
+    ("testEMEPKCS1v15_1", testEMEPKCS1v15_1),
+    ("testEMEPKCS1v15_2", testEMEPKCS1v15_2),
+    ("testEMEPKCS1v15_3", testEMEPKCS1v15_3),
+    ("testEMEPKCS1v15_4", testEMEPKCS1v15_4),
+    ("testEMEPKCS1v15_5", testEMEPKCS1v15_5),
   ]
 }

+ 527 - 0
Tests/CryptoSwiftTests/RSASecKeyTests.swift

@@ -0,0 +1,527 @@
+//
+//  CryptoSwift
+//
+//  Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
+//  This software is provided 'as-is', without any express or implied warranty.
+//
+//  In no event will the authors be held liable for any damages arising from the use of this software.
+//
+//  Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+//
+//  - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
+//  - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+//  - This notice may not be removed or altered from any source or binary distribution.
+//
+
+#if canImport(Security)
+
+  import Security
+  import XCTest
+  @testable import CryptoSwift
+
+  final class RSASecKeyTests: XCTestCase {
+
+    // MARK: SecKey <-> RSA Interoperability
+
+    /// From CryptoSwift RSA -> External Representation -> SecKey
+    ///
+    /// This test enforces that
+    /// 1) We can export the raw external representation of a CryptoSwift RSA Public Key
+    /// 2) And that we can import / create an RSA SecKey from that raw external representation
+    /// 3) Proves interoperability between Apple's `Security` Framework and `CryptoSwift`
+    func testRSAExternalRepresentationPublic() throws {
+
+      // Generate a CryptoSwift RSA Key
+      let rsaCryptoSwift = try RSA(keySize: 1024)
+
+      // Get the key's rawExternalRepresentation
+      let rsaCryptoSwiftRawRep = try rsaCryptoSwift.publicKeyDER()
+
+      // We should be able to instantiate an RSA SecKey from this data
+      let attributes: [String: Any] = [
+        kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
+        kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
+        kSecAttrKeySizeInBits as String: 1024,
+        kSecAttrIsPermanent as String: false
+      ]
+      var error: Unmanaged<CFError>?
+      guard let rsaSecKey = SecKeyCreateWithData(Data(rsaCryptoSwiftRawRep) as CFData, attributes as CFDictionary, &error) else {
+        XCTFail("Error constructing SecKey from raw key data: \(error.debugDescription)")
+        return
+      }
+
+      // Get the SecKey's external representation
+      var externalRepError: Unmanaged<CFError>?
+      guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) as? Data else {
+        XCTFail("Failed to copy external representation for RSA SecKey")
+        return
+      }
+
+      // Ensure both the CryptoSwift Ext Rep and the SecKey Ext Rep match
+      XCTAssertEqual(rsaSecKeyRawRep, Data(rsaCryptoSwiftRawRep))
+      XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.publicKeyExternalRepresentation())
+    }
+
+    /// From CryptoSwift RSA -> External Representation -> SecKey
+    ///
+    /// This test enforces that
+    /// 1) We can export the raw external representation of a CryptoSwift RSA Private Key
+    /// 2) And that we can import / create an RSA SecKey from that raw external representation
+    /// 3) Proves interoperability between Apple's `Security` Framework and `CryptoSwift`
+    func testRSAExternalRepresentationPrivate() throws {
+
+      // Generate a CryptoSwift RSA Key
+      let rsaCryptoSwift = try RSA(keySize: 1024)
+
+      // Get the key's rawExternalRepresentation
+      let rsaCryptoSwiftRawRep = try rsaCryptoSwift.privateKeyDER()
+
+      // We should be able to instantiate an RSA SecKey from this data
+      let attributes: [String: Any] = [
+        kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
+        kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
+        kSecAttrKeySizeInBits as String: 1024,
+        kSecAttrIsPermanent as String: false
+      ]
+      var error: Unmanaged<CFError>?
+      guard let rsaSecKey = SecKeyCreateWithData(Data(rsaCryptoSwiftRawRep) as CFData, attributes as CFDictionary, &error) else {
+        XCTFail("Error constructing SecKey from raw key data: \(error.debugDescription)")
+        return
+      }
+
+      // Get the SecKey's external representation
+      var externalRepError: Unmanaged<CFError>?
+      guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) as? Data else {
+        XCTFail("Failed to copy external representation for RSA SecKey")
+        return
+      }
+
+      // Ensure both the CryptoSwift Ext Rep and the SecKey Ext Rep match
+      XCTAssertEqual(rsaSecKeyRawRep, Data(rsaCryptoSwiftRawRep))
+      XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.externalRepresentation())
+    }
+
+    /// From SecKey -> External Representation -> CryptoSwift RSA
+    ///
+    /// This test enforces that
+    /// 1) Given the raw external representation of a Public RSA SecKey, we can import that same key into CryptoSwift
+    /// 2) When we export the raw external representation of the RSA Key we get the exact same data
+    /// 3) Proves interoperability between Apple's `Security` Framework and `CryptoSwift`
+    func testSecKeyExternalRepresentationPublic() throws {
+      // Generate a SecKey RSA Key
+      let parameters: [CFString: Any] = [
+        kSecAttrKeyType: kSecAttrKeyTypeRSA,
+        kSecAttrKeySizeInBits: 1024
+      ]
+
+      var error: Unmanaged<CFError>?
+
+      // Generate the RSA SecKey
+      guard let rsaSecKey = SecKeyCreateRandomKey(parameters as CFDictionary, &error) else {
+        XCTFail("Key Generation Error: \(error.debugDescription)")
+        return
+      }
+
+      // Extract the public key from the private RSA SecKey
+      guard let rsaSecKeyPublic = SecKeyCopyPublicKey(rsaSecKey) else {
+        XCTFail("Public Key Extraction Error")
+        return
+      }
+
+      // Let's grab the external representation of the public key
+      var externalRepError: Unmanaged<CFError>?
+      guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKeyPublic, &externalRepError) as? Data else {
+        XCTFail("Failed to copy external representation for RSA SecKey")
+        return
+      }
+
+      // Ensure we can import the private RSA key into CryptoSwift
+      let rsaCryptoSwift = try RSA(rawRepresentation: rsaSecKeyRawRep)
+
+      XCTAssertNil(rsaCryptoSwift.d)
+      XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.externalRepresentation())
+    }
+
+    /// From SecKey -> External Representation -> CryptoSwift RSA
+    ///
+    /// This test enforces that
+    /// 1) Given the raw external representation of a Private RSA SecKey, we can import that same key into CryptoSwift
+    /// 2) When we export the raw external representation of the RSA Key we get the exact same data
+    /// 3) Proves interoperability between Apple's `Security` Framework and `CryptoSwift`
+    func testSecKeyExternalRepresentationPrivate() throws {
+      // Generate a SecKey RSA Key
+      let parameters: [CFString: Any] = [
+        kSecAttrKeyType: kSecAttrKeyTypeRSA,
+        kSecAttrKeySizeInBits: 1024
+      ]
+
+      var error: Unmanaged<CFError>?
+
+      // Generate the RSA SecKey
+      guard let rsaSecKey = SecKeyCreateRandomKey(parameters as CFDictionary, &error) else {
+        XCTFail("Key Generation Error: \(error.debugDescription)")
+        return
+      }
+
+      // Let's grab the external representation
+      var externalRepError: Unmanaged<CFError>?
+      guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) as? Data else {
+        XCTFail("Failed to copy external representation for RSA SecKey")
+        return
+      }
+
+      // Ensure we can import the private RSA key into CryptoSwift
+      let rsaCryptoSwift = try RSA(rawRepresentation: rsaSecKeyRawRep)
+
+      XCTAssertNotNil(rsaCryptoSwift.d)
+      XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.externalRepresentation())
+    }
+
+    /// This test generates X RSA keys and tests them between `Security` and `CryptoSwift` for interoperability
+    ///
+    /// For each key generated, this test enforces that
+    /// 1) We can import the raw external representation (generated by the `Security` framework) of the RSA Key into `CryptoSwift`
+    /// 2) When signing messages using a deterministic variant, we get the same output from both `Security` and `CryptoSwift`
+    /// 3) We can verify a signature generated from `CryptoSwift` with `Security` and vice versa
+    /// 4) We can encrypt and decrypt a message generated from `CryptoSwift` with `Security` and vice versa
+    func testRSASecKeys() throws {
+
+      let tests = 3
+      let messageToSign: String = "RSA Keys!"
+
+      for _ in 0..<tests {
+
+        // Generate a SecKey RSA Key
+        let parameters: [CFString: Any] = [
+          kSecAttrKeyType: kSecAttrKeyTypeRSA,
+          kSecAttrKeySizeInBits: 1024
+        ]
+
+        var error: Unmanaged<CFError>?
+
+        // Generate the RSA SecKey
+        guard let rsaSecKey = SecKeyCreateRandomKey(parameters as CFDictionary, &error) else {
+          XCTFail("Key Generation Error: \(error.debugDescription)")
+          break
+        }
+
+        // Let's grab the external representation
+        var externalRepError: Unmanaged<CFError>?
+        guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) as? Data else {
+          XCTFail("Failed to copy external representation for RSA SecKey")
+          break
+        }
+
+        // Ensure we can import the private RSA key into CryptoSwift
+        let rsaCryptoSwift = try RSA(rawRepresentation: rsaSecKeyRawRep)
+
+        // Sign the message with both keys and ensure they're the same (the pkcs1v15 signature variant is deterministic)
+        let csSignature = try rsaCryptoSwift.sign(messageToSign.bytes, variant: .message_pkcs1v15_SHA256)
+
+        let skSignature = try secKeySign(messageToSign.bytes, variant: .rsaSignatureMessagePKCS1v15SHA256, withKey: rsaSecKey)
+
+        XCTAssertEqual(csSignature, skSignature.bytes, "Signatures don't match!")
+
+        // Ensure we can verify each signature using the opposite library
+        XCTAssertTrue(try rsaCryptoSwift.verify(signature: skSignature.bytes, for: messageToSign.bytes, variant: .message_pkcs1v15_SHA256))
+        XCTAssertTrue(try self.secKeyVerify(csSignature, forBytes: messageToSign.bytes, usingVariant: .rsaSignatureMessagePKCS1v15SHA256, withKey: rsaSecKey))
+
+        // Encrypt with SecKey
+        let skEncryption = try secKeyEncrypt(messageToSign.bytes, usingVariant: .rsaEncryptionRaw, withKey: rsaSecKey)
+        // Decrypt with CryptoSwift Key
+        XCTAssertEqual(try rsaCryptoSwift.decrypt(skEncryption.bytes, variant: .raw), messageToSign.bytes, "CryptoSwift Decryption of SecKey Encryption Failed")
+
+        // Encrypt with CryptoSwift
+        let csEncryption = try rsaCryptoSwift.encrypt(messageToSign.bytes, variant: .raw)
+        // Decrypt with SecKey
+        XCTAssertEqual(try self.secKeyDecrypt(csEncryption, usingVariant: .rsaEncryptionRaw, withKey: rsaSecKey).bytes, messageToSign.bytes, "SecKey Decryption of CryptoSwift Encryption Failed")
+
+        XCTAssertEqual(csEncryption, skEncryption.bytes, "Encrypted Data Does Not Match")
+
+        // Encrypt with SecKey
+        let skEncryption2 = try secKeyEncrypt(messageToSign.bytes, usingVariant: .rsaEncryptionPKCS1, withKey: rsaSecKey)
+        // Decrypt with CryptoSwift Key
+        XCTAssertEqual(try rsaCryptoSwift.decrypt(skEncryption2.bytes, variant: .pksc1v15), messageToSign.bytes, "CryptoSwift Decryption of SecKey Encryption Failed")
+
+        // Encrypt with CryptoSwift
+        let csEncryption2 = try rsaCryptoSwift.encrypt(messageToSign.bytes, variant: .pksc1v15)
+        // Decrypt with SecKey
+        XCTAssertEqual(try self.secKeyDecrypt(csEncryption2, usingVariant: .rsaEncryptionPKCS1, withKey: rsaSecKey).bytes, messageToSign.bytes, "SecKey Decryption of CryptoSwift Encryption Failed")
+      }
+    }
+
+    private func secKeySign(_ bytes: Array<UInt8>, variant: SecKeyAlgorithm, withKey key: SecKey) throws -> Data {
+      var error: Unmanaged<CFError>?
+
+      // Sign the data
+      guard let signature = SecKeyCreateSignature(
+        key,
+        variant,
+        Data(bytes) as CFData,
+        &error
+      ) as Data?
+      else { throw NSError(domain: "Failed to sign bytes: \(bytes)", code: 0) }
+
+      return signature
+    }
+
+    private func secKeyVerify(_ signature: Array<UInt8>, forBytes bytes: Array<UInt8>, usingVariant variant: SecKeyAlgorithm, withKey key: SecKey) throws -> Bool {
+      let pubKey = SecKeyCopyPublicKey(key)!
+
+      var error: Unmanaged<CFError>?
+
+      // Perform the signature verification
+      let result = SecKeyVerifySignature(
+        pubKey,
+        variant,
+        Data(bytes) as CFData,
+        Data(signature) as CFData,
+        &error
+      )
+
+      // Throw the error if we encountered one...
+      if let error = error { throw error.takeRetainedValue() as Error }
+
+      // return the result of the verification
+      return result
+    }
+
+    private func secKeyEncrypt(_ bytes: Array<UInt8>, usingVariant variant: SecKeyAlgorithm, withKey key: SecKey) throws -> Data {
+      let pubKey = SecKeyCopyPublicKey(key)!
+
+      var error: Unmanaged<CFError>?
+
+      guard let encryptedData = SecKeyCreateEncryptedData(pubKey, variant, Data(bytes) as CFData, &error) else {
+        throw NSError(domain: "Error Encrypting Data: \(error.debugDescription)", code: 0, userInfo: nil)
+      }
+
+      // Throw the error if we encountered one...
+      if let error = error { throw error.takeRetainedValue() as Error }
+
+      // return the result of the encryption
+      return encryptedData as Data
+    }
+
+    private func secKeyDecrypt(_ bytes: Array<UInt8>, usingVariant variant: SecKeyAlgorithm, withKey key: SecKey) throws -> Data {
+      var error: Unmanaged<CFError>?
+      guard let decryptedData = SecKeyCreateDecryptedData(key, variant, Data(bytes) as CFData, &error) else {
+        throw NSError(domain: "Error Decrypting Data: \(error.debugDescription)", code: 0, userInfo: nil)
+      }
+      return (decryptedData as Data).drop { $0 == 0x00 }
+    }
+  }
+
+  extension RSASecKeyTests {
+    static func allTests() -> [(String, (RSASecKeyTests) -> () throws -> Void)] {
+      let tests = [
+        ("testRSAExternalRepresentationPublic", testRSAExternalRepresentationPublic),
+        ("testRSAExternalRepresentationPrivate", testRSAExternalRepresentationPrivate),
+        ("testSecKeyExternalRepresentationPublic", testSecKeyExternalRepresentationPublic),
+        ("testSecKeyExternalRepresentationPrivate", testSecKeyExternalRepresentationPrivate),
+        ("testRSASecKeys", testRSASecKeys)
+      ]
+
+      return tests
+    }
+  }
+
+  // - MARK: Test Fixture Generation Code
+  extension RSASecKeyTests {
+
+    /// This 'Test' generates an RSA Key and uses that key to sign and encrypt a series of messages that we can test against.
+    ///
+    /// It prints a `Fixture` object that can be copy and pasted / used in other tests.
+    func testCreateTestFixture() throws {
+
+      let keySize = 1024
+      let messages = [
+        "",
+        "👋",
+        "RSA Keys",
+        "CryptoSwift RSA Keys!",
+        "CryptoSwift RSA Keys are really cool! They support encrypting / decrypting messages, signing and verifying signed messages, and importing and exporting encrypted keys for use between sessions 🔐"
+      ]
+      print(messages.map { $0.bytes.count })
+
+      /// Generate a SecKey RSA Key
+      let parameters: [CFString: Any] = [
+        kSecAttrKeyType: kSecAttrKeyTypeRSA,
+        kSecAttrKeySizeInBits: keySize
+      ]
+
+      var error: Unmanaged<CFError>?
+
+      // Generate the RSA SecKey
+      guard let rsaSecKey = SecKeyCreateRandomKey(parameters as CFDictionary, &error) else {
+        XCTFail("Key Generation Error: \(error.debugDescription)")
+        return
+      }
+
+      // Extract the public key from the private RSA SecKey
+      guard let rsaSecKeyPublic = SecKeyCopyPublicKey(rsaSecKey) else {
+        XCTFail("Public Key Extraction Error")
+        return
+      }
+
+      /// Let's grab the external representation of the public key
+      var publicExternalRepError: Unmanaged<CFError>?
+      guard let publicRSASecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKeyPublic, &publicExternalRepError) as? Data else {
+        XCTFail("Failed to copy external representation for RSA SecKey")
+        return
+      }
+
+      /// Let's grab the external representation of the private key
+      var privateExternalRepError: Unmanaged<CFError>?
+      guard let privateRSASecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKey, &privateExternalRepError) as? Data else {
+        XCTFail("Failed to copy external representation for RSA SecKey")
+        return
+      }
+
+      var template = RSASecKeyTests.FixtureTemplate
+      template = template.replacingOccurrences(of: "{{KEY_SIZE}}", with: "\(keySize)")
+      template = template.replacingOccurrences(of: "{{PUBLIC_DER}}", with: "\(publicRSASecKeyRawRep.base64EncodedString())")
+      template = template.replacingOccurrences(of: "{{PRIVATE_DER}}", with: "\(privateRSASecKeyRawRep.base64EncodedString())")
+
+      var messageEntries: [String] = []
+      for message in messages {
+        var messageTemplate = RSASecKeyTests.MessageTemplate
+        messageTemplate = messageTemplate.replacingOccurrences(of: "{{PLAINTEXT_MESSAGE}}", with: message)
+
+        let encryptedMessages = try encrypt(data: message.data(using: .utf8)!, with: rsaSecKeyPublic)
+        messageTemplate = messageTemplate.replacingOccurrences(of: "{{ENCRYPTED_MESSAGES}}", with: encryptedMessages.joined(separator: ",\n\t\t  "))
+
+        let signedMessages = try sign(message: message.data(using: .utf8)!, using: rsaSecKey)
+        messageTemplate = messageTemplate.replacingOccurrences(of: "{{SIGNED_MESSAGES}}", with: signedMessages.joined(separator: ",\n\t\t  "))
+
+        messageEntries.append(messageTemplate)
+      }
+
+      template = template.replacingOccurrences(of: "{{MESSAGE_TEMPLATES}}", with: "\(messageEntries.joined(separator: ",\n\t"))")
+
+      print("\n**************************")
+      print("   Test Fixture Output      ")
+      print("**************************\n")
+      print(template)
+      print("\n**************************")
+    }
+
+    private static let FixtureTemplate = """
+      static let RSA_{{KEY_SIZE}} = Fixture(
+        keySize: {{KEY_SIZE}},
+        publicDER: \"\"\"
+    {{PUBLIC_DER}}
+    \"\"\",
+        privateDER: \"\"\"
+    {{PRIVATE_DER}}
+    \"\"\",
+        messages: [
+          {{MESSAGE_TEMPLATES}}
+        ]
+      )
+    """
+
+    private static let MessageTemplate = """
+    "{{PLAINTEXT_MESSAGE}}": (
+      encryptedMessage: [
+        {{ENCRYPTED_MESSAGES}}
+      ],
+      signedMessage: [
+        {{SIGNED_MESSAGES}}
+      ]
+    )
+    """
+
+    private func initSecKey(rawRepresentation unsafe: Data) throws -> SecKey {
+      let attributes: [String: Any] = [
+        kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
+        kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
+        kSecAttrKeySizeInBits as String: 1024,
+        kSecAttrIsPermanent as String: false
+      ]
+
+      var error: Unmanaged<CFError>?
+      guard let secKey = SecKeyCreateWithData(unsafe as CFData, attributes as CFDictionary, &error) else {
+        throw NSError(domain: "Error constructing SecKey from raw key data: \(error.debugDescription)", code: 0, userInfo: nil)
+      }
+
+      return secKey
+    }
+
+    // We don't support PSS yet so we skip these variants
+    private func sign(message: Data, using key: SecKey) throws -> [String] {
+      let algorithms: [SecKeyAlgorithm] = [
+        .rsaSignatureRaw,
+        //.rsaSignatureDigestPSSSHA1,
+        //.rsaSignatureDigestPSSSHA224,
+        //.rsaSignatureDigestPSSSHA256,
+        //.rsaSignatureDigestPSSSHA384,
+        //.rsaSignatureDigestPSSSHA512,
+        .rsaSignatureDigestPKCS1v15Raw,
+        .rsaSignatureDigestPKCS1v15SHA1,
+        .rsaSignatureDigestPKCS1v15SHA224,
+        .rsaSignatureDigestPKCS1v15SHA256,
+        .rsaSignatureDigestPKCS1v15SHA384,
+        .rsaSignatureDigestPKCS1v15SHA512,
+        //.rsaSignatureMessagePSSSHA1,
+        //.rsaSignatureMessagePSSSHA224,
+        //.rsaSignatureMessagePSSSHA256,
+        //.rsaSignatureMessagePSSSHA384,
+        //.rsaSignatureMessagePSSSHA512,
+        .rsaSignatureMessagePKCS1v15SHA1,
+        .rsaSignatureMessagePKCS1v15SHA224,
+        .rsaSignatureMessagePKCS1v15SHA256,
+        .rsaSignatureMessagePKCS1v15SHA384,
+        .rsaSignatureMessagePKCS1v15SHA512,
+      ]
+
+      var sigs: [String] = []
+
+      for algo in algorithms {
+        var error: Unmanaged<CFError>?
+
+        // Sign the data
+        guard let signature = SecKeyCreateSignature(
+          key,
+          algo,
+          message as CFData,
+          &error
+        ) as Data?
+        else {
+          print("\"\(algo.rawValue)\": \"nil\",")
+          sigs.append("\"\(algo.rawValue)\": \"\"")
+          continue
+        }
+
+        // Throw the error if we encountered one
+        if let error = error { print("\"\(algo.rawValue)\": \"\(error.takeRetainedValue())\","); continue }
+
+        // Append the signature
+        sigs.append("\"\(algo.rawValue)\": \"\(signature.base64EncodedString())\"")
+      }
+
+      return sigs
+    }
+
+    private func encrypt(data: Data, with key: SecKey) throws -> [String] {
+      let algorithms: [SecKeyAlgorithm] = [
+        .rsaEncryptionRaw,
+        .rsaEncryptionPKCS1
+      ]
+
+      var encryptions: [String] = []
+
+      for algo in algorithms {
+        var error: Unmanaged<CFError>?
+        guard let encryptedData = SecKeyCreateEncryptedData(key, algo, data as CFData, &error) as? Data else {
+          print("\"\(algo.rawValue)\": \"\(error?.takeRetainedValue().localizedDescription ?? "nil")\",")
+          encryptions.append("\"\(algo.rawValue)\": \"\"")
+          continue
+        }
+        encryptions.append("\"\(algo.rawValue)\": \"\(encryptedData.base64EncodedString())\"")
+      }
+
+      return encryptions
+    }
+  }
+
+#endif

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 772 - 9
Tests/CryptoSwiftTests/RSATests.swift


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio