Browse Source

Added documentation and reorganized

Brandon Toms 3 năm trước cách đây
mục cha
commit
64968dd07f
1 tập tin đã thay đổi với 179 bổ sung188 xóa
  1. 179 188
      Tests/CryptoSwiftTests/RSASecKeyTests.swift

+ 179 - 188
Tests/CryptoSwiftTests/RSASecKeyTests.swift

@@ -15,19 +15,24 @@
 
     // MARK: SecKey <-> RSA Interoperability
 
-    // From CryptoSwift RSA -> External Representation -> SecKey
-    func testRSAExternalRepresentationPrivate() throws {
+    /// 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.privateKeyDER()
+      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: kSecAttrKeyClassPrivate,
+        kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
         kSecAttrKeySizeInBits as String: 1024,
         kSecAttrIsPermanent as String: false
       ]
@@ -46,22 +51,27 @@
 
       // Ensure both the CryptoSwift Ext Rep and the SecKey Ext Rep match
       XCTAssertEqual(rsaSecKeyRawRep, Data(rsaCryptoSwiftRawRep))
-      XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.externalRepresentation())
+      XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.publicKeyExternalRepresentation())
     }
-
+    
     /// From CryptoSwift RSA -> External Representation -> SecKey
-    func testRSAExternalRepresentationPublic() throws {
+    ///
+    /// 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.publicKeyDER()
+      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: kSecAttrKeyClassPublic,
+        kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
         kSecAttrKeySizeInBits as String: 1024,
         kSecAttrIsPermanent as String: false
       ]
@@ -80,11 +90,16 @@
 
       // Ensure both the CryptoSwift Ext Rep and the SecKey Ext Rep match
       XCTAssertEqual(rsaSecKeyRawRep, Data(rsaCryptoSwiftRawRep))
-      XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.publicKeyExternalRepresentation())
+      XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.externalRepresentation())
     }
 
     /// From SecKey -> External Representation -> CryptoSwift RSA
-    func testSecKeyExternalRepresentationPrivate() throws {
+    ///
+    /// 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,
@@ -99,9 +114,15 @@
         return
       }
 
-      // Lets grab the external representation
+      // Extract the public key from the private RSA SecKey
+      guard let rsaSecKeyPublic = SecKeyCopyPublicKey(rsaSecKey) else {
+        XCTFail("Public Key Extraction Error")
+        return
+      }
+
+      // Lets grab the external representation of the public key
       var externalRepError: Unmanaged<CFError>?
-      guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) as? Data else {
+      guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKeyPublic, &externalRepError) as? Data else {
         XCTFail("Failed to copy external representation for RSA SecKey")
         return
       }
@@ -109,12 +130,17 @@
       // Ensure we can import the private RSA key into CryptoSwift
       let rsaCryptoSwift = try RSA(rawRepresentation: rsaSecKeyRawRep)
 
-      XCTAssertNotNil(rsaCryptoSwift.d)
+      XCTAssertNil(rsaCryptoSwift.d)
       XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.externalRepresentation())
     }
-
+    
     /// From SecKey -> External Representation -> CryptoSwift RSA
-    func testSecKeyExternalRepresentationPublic() throws {
+    ///
+    /// 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,
@@ -129,15 +155,9 @@
         return
       }
 
-      // Extract the public key from the private RSA SecKey
-      guard let rsaSecKeyPublic = SecKeyCopyPublicKey(rsaSecKey) else {
-        XCTFail("Public Key Extraction Error")
-        return
-      }
-
-      // Lets grab the external representation of the public key
+      // Lets grab the external representation
       var externalRepError: Unmanaged<CFError>?
-      guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKeyPublic, &externalRepError) as? Data else {
+      guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) as? Data else {
         XCTFail("Failed to copy external representation for RSA SecKey")
         return
       }
@@ -145,13 +165,20 @@
       // Ensure we can import the private RSA key into CryptoSwift
       let rsaCryptoSwift = try RSA(rawRepresentation: rsaSecKeyRawRep)
 
-      XCTAssertNil(rsaCryptoSwift.d)
+      XCTAssertNotNil(rsaCryptoSwift.d)
       XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.externalRepresentation())
     }
 
-    func testRSASecKey() throws {
+    /// 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 = 1
+      let tests = 3
       let messageToSign: String = "RSA Keys!"
 
       for _ in 0..<tests {
@@ -200,8 +227,6 @@
         // Decrypt with SecKey
         XCTAssertEqual(try self.secKeyDecrypt(csEncryption, usingVariant: .rsaEncryptionRaw, withKey: rsaSecKey).bytes, messageToSign.bytes, "SecKey Decryption of CryptoSwift Encryption Failed")
 
-        //print(csEncryption)
-        //print(skEncryption.bytes)
         XCTAssertEqual(csEncryption, skEncryption.bytes, "Encrypted Data Does Not Match")
 
         // Encrypt with SecKey
@@ -213,9 +238,6 @@
         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")
-
-        print(csEncryption2.count)
-        print(skEncryption2.bytes.count)
       }
     }
 
@@ -278,10 +300,31 @@
       }
       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 = 1056
+      let keySize = 1024
       let messages = [
         "",
         "👋",
@@ -325,14 +368,14 @@
         return
       }
 
-      var template = FixtureTemplate2
+      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 = MessageTemplate
+        var messageTemplate = RSASecKeyTests.MessageTemplate
         messageTemplate = messageTemplate.replacingOccurrences(of: "{{PLAINTEXT_MESSAGE}}", with: message)
 
         let encryptedMessages = try encrypt(data: message.data(using: .utf8)!, with: rsaSecKeyPublic)
@@ -346,182 +389,130 @@
 
       template = template.replacingOccurrences(of: "{{MESSAGE_TEMPLATES}}", with: "\(messageEntries.joined(separator: ",\n\t"))")
 
+      print("\n**************************")
+      print("   Test Fixture Output")
+      print("**************************\n")
       print(template)
+      print("\n**************************")
     }
-  }
-
-  extension RSASecKeyTests {
-    static func allTests() -> [(String, (RSASecKeyTests) -> () throws -> Void)] {
-      let tests = [
-        ("testRSAExternalRepresentationPrivate", testRSAExternalRepresentationPrivate),
-        ("testRSAExternalRepresentationPublic", testRSAExternalRepresentationPublic),
-        ("testSecKeyExternalRepresentationPrivate", testSecKeyExternalRepresentationPrivate),
-        ("testSecKeyExternalRepresentationPublic", testSecKeyExternalRepresentationPublic)
-      ]
 
-      return tests
-    }
-  }
+    private static let FixtureTemplate = """
+      static let RSA_{{KEY_SIZE}} = Fixture(
+        keySize: {{KEY_SIZE}},
+        publicDER: \"\"\"
+    {{PUBLIC_DER}}
+    \"\"\",
+        privateDER: \"\"\"
+    {{PRIVATE_DER}}
+    \"\"\",
+        messages: [
+          {{MESSAGE_TEMPLATES}}
+        ]
+      )
+    """
 
-  private let FixtureTemplate = """
-    static let RSA_{{KEY_SIZE}} = Fixture(
-      keySize: {{KEY_SIZE}},
-      publicDER: \"\"\"
-  {{PUBLIC_DER}}
-  \"\"\",
-      privateDER: \"\"\"
-  {{PRIVATE_DER}}
-  \"\"\",
-      rawMessage: [
-        {{PLAINTEXT_MESSAGE}}
-      ],
+    private static let MessageTemplate = """
+    "{{PLAINTEXT_MESSAGE}}": (
       encryptedMessage: [
         {{ENCRYPTED_MESSAGES}}
       ],
-      signedMessages: [
+      signedMessage: [
         {{SIGNED_MESSAGES}}
       ]
     )
-  """
-
-  private let FixtureTemplate2 = """
-    static let RSA_{{KEY_SIZE}} = Fixture(
-      keySize: {{KEY_SIZE}},
-      publicDER: \"\"\"
-  {{PUBLIC_DER}}
-  \"\"\",
-      privateDER: \"\"\"
-  {{PRIVATE_DER}}
-  \"\"\",
-      messages: [
-        {{MESSAGE_TEMPLATES}}
-      ]
-    )
-  """
-
-  private let MessageTemplate = """
-  "{{PLAINTEXT_MESSAGE}}": (
-    encryptedMessage: [
-      {{ENCRYPTED_MESSAGES}}
-    ],
-    signedMessage: [
-      {{SIGNED_MESSAGES}}
-    ]
-  )
-  """
-
-  //encryptedMessage: [
-//  "algid:encrypt:RSA:raw": "{{ENCRYPTED_RAW}}",
-//  "algid:encrypt:RSA:PKCS1": "{{ENCRYPTED_PKCS1}}"
-  //],
-  //signedMessages: [
-//  "algid:sign:RSA:raw"                    : "{{SIGNATURE_RAW}}",
-//  "algid:sign:RSA:digest-PKCS1v15"        : "{{SIGNATURE_DIGEST_PKCS1v15}}",
-//  "algid:sign:RSA:digest-PKCS1v15:SHA1"   : "{{SIGNATURE_DIGEST_PKCS1v15_SHA1}}",
-//  "algid:sign:RSA:digest-PKCS1v15:SHA224" : "{{SIGNATURE_DIGEST_PKCS1v15_SHA224}}",
-//  "algid:sign:RSA:digest-PKCS1v15:SHA256" : "{{SIGNATURE_DIGEST_PKCS1v15_SHA256}}",
-//  "algid:sign:RSA:digest-PKCS1v15:SHA384" : "{{SIGNATURE_DIGEST_PKCS1v15_SHA384}}",
-//  "algid:sign:RSA:digest-PKCS1v15:SHA512" : "{{SIGNATURE_DIGEST_PKCS1v15_SHA512}}",
-//  "algid:sign:RSA:message-PKCS1v15:SHA1"  : "{{SIGNATURE_MESSAGE_PKCS1v15_SHA1}}",
-//  "algid:sign:RSA:message-PKCS1v15:SHA224": "{{SIGNATURE_MESSAGE_PKCS1v15_SHA224}}",
-//  "algid:sign:RSA:message-PKCS1v15:SHA256": "{{SIGNATURE_MESSAGE_PKCS1v15_SHA256}}",
-//  "algid:sign:RSA:message-PKCS1v15:SHA384": "{{SIGNATURE_MESSAGE_PKCS1v15_SHA384}}",
-//  "algid:sign:RSA:message-PKCS1v15:SHA512": "{{SIGNATURE_MESSAGE_PKCS1v15_SHA512}}"
-  //]
-
-  //private func printHexData16BytesWide(_ bytes:[UInt8]) {
-//    print(bytes.toHexString().split(intoChunksOfLength: 32).map { $0.split(intoChunksOfLength: 2).map { "0x\($0.uppercased())" }.joined(separator: ", ") }.joined(separator: ",\n"))
-  //}
-
-  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
-  }
+    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
+      ]
 
-  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
+      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)
       }
 
-      // 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 secKey
     }
 
-    return sigs
-  }
+    // 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,
+      ]
 
-  private func encrypt(data: Data, with key: SecKey) throws -> [String] {
-    let algorithms: [SecKeyAlgorithm] = [
-      .rsaEncryptionRaw,
-      .rsaEncryptionPKCS1
-    ]
+      var sigs: [String] = []
 
-    var encryptions: [String] = []
+      for algo in algorithms {
+        var error: Unmanaged<CFError>?
 
-    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
+        // 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())\"")
       }
-      encryptions.append("\"\(algo.rawValue)\": \"\(encryptedData.base64EncodedString())\"")
+
+      return sigs
     }
 
-    return encryptions
+    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