RSASecKeyTests.swift 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. //
  2. // CryptoSwift
  3. //
  4. // Copyright (C) 2014-2021 Marcin Krzyżanowski <marcin@krzyzanowskim.com>
  5. // This software is provided 'as-is', without any express or implied warranty.
  6. //
  7. // In no event will the authors be held liable for any damages arising from the use of this software.
  8. //
  9. // 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:
  10. //
  11. // - 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.
  12. // - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
  13. // - This notice may not be removed or altered from any source or binary distribution.
  14. //
  15. #if canImport(Security)
  16. import Security
  17. import XCTest
  18. @testable import CryptoSwift
  19. final class RSASecKeyTests: XCTestCase {
  20. // MARK: SecKey <-> RSA Interoperability
  21. /// From CryptoSwift RSA -> External Representation -> SecKey
  22. ///
  23. /// This test enforces that
  24. /// 1) We can export the raw external representation of a CryptoSwift RSA Public Key
  25. /// 2) And that we can import / create an RSA SecKey from that raw external representation
  26. /// 3) Proves interoperability between Apple's `Security` Framework and `CryptoSwift`
  27. func testRSAExternalRepresentationPublic() throws {
  28. // Generate a CryptoSwift RSA Key
  29. let rsaCryptoSwift = try RSA(keySize: 1024)
  30. // Get the key's rawExternalRepresentation
  31. let rsaCryptoSwiftRawRep = try rsaCryptoSwift.publicKeyDER()
  32. // We should be able to instantiate an RSA SecKey from this data
  33. let attributes: [String: Any] = [
  34. kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
  35. kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
  36. kSecAttrKeySizeInBits as String: 1024,
  37. kSecAttrIsPermanent as String: false
  38. ]
  39. var error: Unmanaged<CFError>?
  40. guard let rsaSecKey = SecKeyCreateWithData(Data(rsaCryptoSwiftRawRep) as CFData, attributes as CFDictionary, &error) else {
  41. XCTFail("Error constructing SecKey from raw key data: \(error.debugDescription)")
  42. return
  43. }
  44. // Get the SecKey's external representation
  45. var externalRepError: Unmanaged<CFError>?
  46. guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) as? Data else {
  47. XCTFail("Failed to copy external representation for RSA SecKey")
  48. return
  49. }
  50. // Ensure both the CryptoSwift Ext Rep and the SecKey Ext Rep match
  51. XCTAssertEqual(rsaSecKeyRawRep, Data(rsaCryptoSwiftRawRep))
  52. XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.publicKeyExternalRepresentation())
  53. }
  54. /// From CryptoSwift RSA -> External Representation -> SecKey
  55. ///
  56. /// This test enforces that
  57. /// 1) We can export the raw external representation of a 1 byte CryptoSwift RSA Public Key
  58. /// 2) And that we can import / create an RSA SecKey from that raw external representation
  59. /// 3) Proves interoperability between Apple's `Security` Framework and `CryptoSwift`
  60. func testRSAExternalRepresentationPublic_1ByteExponent() throws {
  61. // Generate a CryptoSwift RSA Key
  62. let n: Array<UInt8> = [
  63. 0xDF, 0x74, 0xEA, 0xA8, 0xB4, 0x77, 0x79, 0xE5, 0x26, 0x0E, 0xD1, 0xD2, 0xF3, 0xDA, 0x85, 0xDE,
  64. 0x9E, 0x0C, 0x3F, 0xB5, 0x9F, 0xAA, 0xDD, 0x99, 0x8E, 0x3D, 0xE5, 0x66, 0x32, 0xE3, 0x8C, 0x21,
  65. 0x83, 0xBF, 0x0A, 0xFD, 0x70, 0x93, 0x65, 0x34, 0x38, 0xAE, 0x29, 0xBA, 0x0E, 0x0E, 0x05, 0x22,
  66. 0x4F, 0x86, 0x5D, 0x3D, 0x66, 0xCE, 0x88, 0xC0, 0x1F, 0x0D, 0xC0, 0xF3, 0x94, 0xEC, 0x40, 0xD3,
  67. 0x80, 0xC3, 0x9C, 0x41, 0x46, 0x01, 0xFF, 0xA4, 0x0D, 0x08, 0x8D, 0x49, 0xED, 0x99, 0xB6, 0xA0,
  68. 0xBB, 0x6E, 0x72, 0x1C, 0xF3, 0xE2, 0x92, 0x1B, 0xFA, 0x8E, 0x19, 0x02, 0x15, 0x69, 0xAC, 0xB8,
  69. 0x51, 0xCA, 0xFA, 0xA2, 0xF0, 0xE4, 0xCA, 0x43, 0xB9, 0x1F, 0x19, 0xFB, 0x39, 0x4E, 0xEC, 0x26,
  70. 0x9F, 0x53, 0x3F, 0xD1, 0xF4, 0x24, 0x7F, 0x33, 0x31, 0x00, 0x3A, 0x63, 0x38, 0xA3, 0x60, 0x7F,
  71. 0x4B, 0xB2, 0xEA, 0x53, 0x7A, 0xBA, 0xD8, 0xDD, 0x4E, 0xB3, 0xCA, 0x28, 0x55, 0x10, 0x35, 0xD7,
  72. 0x3A, 0xFD, 0x3C, 0xBC, 0x11, 0xE4, 0xE7, 0x40, 0x4C, 0x14, 0x96, 0xC4, 0xF6, 0x2C, 0x05, 0xC2,
  73. 0x7C, 0x42, 0xB1, 0x4E, 0x7E, 0x51, 0x95, 0x19, 0x63, 0x0F, 0x3B, 0x52, 0xED, 0x27, 0xE0, 0xB3,
  74. 0xC5, 0x4B, 0xD0, 0x53, 0xF2, 0xD5, 0x08, 0xBC, 0x07, 0xE7, 0x45, 0x72, 0x04, 0x58, 0x28, 0x5A,
  75. 0xDC, 0xA6, 0xC5, 0x5A, 0x67, 0x59, 0x41, 0x99, 0x72, 0xAC, 0x5C, 0x34, 0x6E, 0xE5, 0x04, 0x7F,
  76. 0x46, 0x0D, 0xDF, 0xF4, 0x34, 0x6A, 0xE0, 0x64, 0x75, 0xB0, 0xD1, 0xFC, 0x44, 0xB6, 0x3D, 0xB8,
  77. 0x0A, 0x4C, 0x97, 0x64, 0x8D, 0xCF, 0xEB, 0x11, 0xE3, 0x34, 0x4F, 0x1B, 0xCE, 0x51, 0xFE, 0xC7,
  78. 0xB9, 0x43, 0x4E, 0x86, 0xBB, 0x40, 0x54, 0x43, 0x75, 0x0F, 0x9B, 0xAE, 0xC4, 0x4D, 0x56, 0x4B
  79. ]
  80. let e: Array<UInt8> = [
  81. 0x03
  82. ]
  83. let rsaCryptoSwift = RSA(n: n, e: e)
  84. // Get the key's rawExternalRepresentation
  85. let rsaCryptoSwiftRawRep = try rsaCryptoSwift.publicKeyDER()
  86. // We should be able to instantiate an RSA SecKey from this data
  87. let attributes: [String: Any] = [
  88. kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
  89. kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
  90. kSecAttrKeySizeInBits as String: 2048,
  91. kSecAttrIsPermanent as String: false
  92. ]
  93. var error: Unmanaged<CFError>?
  94. guard let rsaSecKey = SecKeyCreateWithData(Data(rsaCryptoSwiftRawRep) as CFData, attributes as CFDictionary, &error) else {
  95. XCTFail("Error constructing SecKey from raw key data: \(error.debugDescription)")
  96. return
  97. }
  98. // Get the SecKey's external representation
  99. var externalRepError: Unmanaged<CFError>?
  100. guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) as? Data else {
  101. XCTFail("Failed to copy external representation for RSA SecKey")
  102. return
  103. }
  104. // Ensure both the CryptoSwift Ext Rep and the SecKey Ext Rep match
  105. XCTAssertEqual(rsaSecKeyRawRep, Data(rsaCryptoSwiftRawRep))
  106. XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.publicKeyExternalRepresentation())
  107. }
  108. /// From CryptoSwift RSA -> External Representation -> SecKey
  109. ///
  110. /// This test enforces that
  111. /// 1) We can export the raw external representation of a CryptoSwift RSA Private Key
  112. /// 2) And that we can import / create an RSA SecKey from that raw external representation
  113. /// 3) Proves interoperability between Apple's `Security` Framework and `CryptoSwift`
  114. func testRSAExternalRepresentationPrivate() throws {
  115. // Generate a CryptoSwift RSA Key
  116. let rsaCryptoSwift = try RSA(keySize: 1024)
  117. // Get the key's rawExternalRepresentation
  118. let rsaCryptoSwiftRawRep = try rsaCryptoSwift.privateKeyDER()
  119. // We should be able to instantiate an RSA SecKey from this data
  120. let attributes: [String: Any] = [
  121. kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
  122. kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
  123. kSecAttrKeySizeInBits as String: 1024,
  124. kSecAttrIsPermanent as String: false
  125. ]
  126. var error: Unmanaged<CFError>?
  127. guard let rsaSecKey = SecKeyCreateWithData(Data(rsaCryptoSwiftRawRep) as CFData, attributes as CFDictionary, &error) else {
  128. XCTFail("Error constructing SecKey from raw key data: \(error.debugDescription)")
  129. return
  130. }
  131. // Get the SecKey's external representation
  132. var externalRepError: Unmanaged<CFError>?
  133. guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) as? Data else {
  134. XCTFail("Failed to copy external representation for RSA SecKey")
  135. return
  136. }
  137. // Ensure both the CryptoSwift Ext Rep and the SecKey Ext Rep match
  138. XCTAssertEqual(rsaSecKeyRawRep, Data(rsaCryptoSwiftRawRep))
  139. XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.externalRepresentation())
  140. }
  141. /// From SecKey -> External Representation -> CryptoSwift RSA
  142. ///
  143. /// This test enforces that
  144. /// 1) Given the raw external representation of a Public RSA SecKey, we can import that same key into CryptoSwift
  145. /// 2) When we export the raw external representation of the RSA Key we get the exact same data
  146. /// 3) Proves interoperability between Apple's `Security` Framework and `CryptoSwift`
  147. func testSecKeyExternalRepresentationPublic() throws {
  148. // Generate a SecKey RSA Key
  149. let parameters: [CFString: Any] = [
  150. kSecAttrKeyType: kSecAttrKeyTypeRSA,
  151. kSecAttrKeySizeInBits: 1024
  152. ]
  153. var error: Unmanaged<CFError>?
  154. // Generate the RSA SecKey
  155. guard let rsaSecKey = SecKeyCreateRandomKey(parameters as CFDictionary, &error) else {
  156. XCTFail("Key Generation Error: \(error.debugDescription)")
  157. return
  158. }
  159. // Extract the public key from the private RSA SecKey
  160. guard let rsaSecKeyPublic = SecKeyCopyPublicKey(rsaSecKey) else {
  161. XCTFail("Public Key Extraction Error")
  162. return
  163. }
  164. // Let's grab the external representation of the public key
  165. var externalRepError: Unmanaged<CFError>?
  166. guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKeyPublic, &externalRepError) as? Data else {
  167. XCTFail("Failed to copy external representation for RSA SecKey")
  168. return
  169. }
  170. // Ensure we can import the private RSA key into CryptoSwift
  171. let rsaCryptoSwift = try RSA(rawRepresentation: rsaSecKeyRawRep)
  172. XCTAssertNil(rsaCryptoSwift.d)
  173. XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.externalRepresentation())
  174. }
  175. /// From SecKey -> External Representation -> CryptoSwift RSA
  176. ///
  177. /// This test enforces that
  178. /// 1) Given the raw external representation of a Private RSA SecKey, we can import that same key into CryptoSwift
  179. /// 2) When we export the raw external representation of the RSA Key we get the exact same data
  180. /// 3) Proves interoperability between Apple's `Security` Framework and `CryptoSwift`
  181. func testSecKeyExternalRepresentationPrivate() throws {
  182. // Generate a SecKey RSA Key
  183. let parameters: [CFString: Any] = [
  184. kSecAttrKeyType: kSecAttrKeyTypeRSA,
  185. kSecAttrKeySizeInBits: 1024
  186. ]
  187. var error: Unmanaged<CFError>?
  188. // Generate the RSA SecKey
  189. guard let rsaSecKey = SecKeyCreateRandomKey(parameters as CFDictionary, &error) else {
  190. XCTFail("Key Generation Error: \(error.debugDescription)")
  191. return
  192. }
  193. // Let's grab the external representation
  194. var externalRepError: Unmanaged<CFError>?
  195. guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) as? Data else {
  196. XCTFail("Failed to copy external representation for RSA SecKey")
  197. return
  198. }
  199. // Ensure we can import the private RSA key into CryptoSwift
  200. let rsaCryptoSwift = try RSA(rawRepresentation: rsaSecKeyRawRep)
  201. XCTAssertNotNil(rsaCryptoSwift.d)
  202. XCTAssertEqual(rsaSecKeyRawRep, try rsaCryptoSwift.externalRepresentation())
  203. }
  204. /// This test generates X RSA keys and tests them between `Security` and `CryptoSwift` for interoperability
  205. ///
  206. /// For each key generated, this test enforces that
  207. /// 1) We can import the raw external representation (generated by the `Security` framework) of the RSA Key into `CryptoSwift`
  208. /// 2) When signing messages using a deterministic variant, we get the same output from both `Security` and `CryptoSwift`
  209. /// 3) We can verify a signature generated from `CryptoSwift` with `Security` and vice versa
  210. /// 4) We can encrypt and decrypt a message generated from `CryptoSwift` with `Security` and vice versa
  211. func testRSASecKeys() throws {
  212. let tests = 3
  213. let messageToSign: String = "RSA Keys!"
  214. for _ in 0..<tests {
  215. // Generate a SecKey RSA Key
  216. let parameters: [CFString: Any] = [
  217. kSecAttrKeyType: kSecAttrKeyTypeRSA,
  218. kSecAttrKeySizeInBits: 1024
  219. ]
  220. var error: Unmanaged<CFError>?
  221. // Generate the RSA SecKey
  222. guard let rsaSecKey = SecKeyCreateRandomKey(parameters as CFDictionary, &error) else {
  223. XCTFail("Key Generation Error: \(error.debugDescription)")
  224. break
  225. }
  226. // Let's grab the external representation
  227. var externalRepError: Unmanaged<CFError>?
  228. guard let rsaSecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) as? Data else {
  229. XCTFail("Failed to copy external representation for RSA SecKey")
  230. break
  231. }
  232. // Ensure we can import the private RSA key into CryptoSwift
  233. let rsaCryptoSwift = try RSA(rawRepresentation: rsaSecKeyRawRep)
  234. // Sign the message with both keys and ensure they're the same (the pkcs1v15 signature variant is deterministic)
  235. let csSignature = try rsaCryptoSwift.sign(messageToSign.bytes, variant: .message_pkcs1v15_SHA256)
  236. let skSignature = try secKeySign(messageToSign.bytes, variant: .rsaSignatureMessagePKCS1v15SHA256, withKey: rsaSecKey)
  237. XCTAssertEqual(csSignature, skSignature.byteArray, "Signatures don't match!")
  238. // Ensure we can verify each signature using the opposite library
  239. XCTAssertTrue(try rsaCryptoSwift.verify(signature: skSignature.byteArray, for: messageToSign.bytes, variant: .message_pkcs1v15_SHA256))
  240. XCTAssertTrue(try self.secKeyVerify(csSignature, forBytes: messageToSign.bytes, usingVariant: .rsaSignatureMessagePKCS1v15SHA256, withKey: rsaSecKey))
  241. // Encrypt with SecKey
  242. let skEncryption = try secKeyEncrypt(messageToSign.bytes, usingVariant: .rsaEncryptionRaw, withKey: rsaSecKey)
  243. // Decrypt with CryptoSwift Key
  244. XCTAssertEqual(try rsaCryptoSwift.decrypt(skEncryption.byteArray, variant: .raw), messageToSign.bytes, "CryptoSwift Decryption of SecKey Encryption Failed")
  245. // Encrypt with CryptoSwift
  246. let csEncryption = try rsaCryptoSwift.encrypt(messageToSign.bytes, variant: .raw)
  247. // Decrypt with SecKey
  248. XCTAssertEqual(try self.secKeyDecrypt(csEncryption, usingVariant: .rsaEncryptionRaw, withKey: rsaSecKey).byteArray, messageToSign.bytes, "SecKey Decryption of CryptoSwift Encryption Failed")
  249. XCTAssertEqual(csEncryption, skEncryption.byteArray, "Encrypted Data Does Not Match")
  250. // Encrypt with SecKey
  251. let skEncryption2 = try secKeyEncrypt(messageToSign.bytes, usingVariant: .rsaEncryptionPKCS1, withKey: rsaSecKey)
  252. // Decrypt with CryptoSwift Key
  253. XCTAssertEqual(try rsaCryptoSwift.decrypt(skEncryption2.byteArray, variant: .pksc1v15), messageToSign.bytes, "CryptoSwift Decryption of SecKey Encryption Failed")
  254. // Encrypt with CryptoSwift
  255. let csEncryption2 = try rsaCryptoSwift.encrypt(messageToSign.bytes, variant: .pksc1v15)
  256. // Decrypt with SecKey
  257. XCTAssertEqual(try self.secKeyDecrypt(csEncryption2, usingVariant: .rsaEncryptionPKCS1, withKey: rsaSecKey).byteArray, messageToSign.bytes, "SecKey Decryption of CryptoSwift Encryption Failed")
  258. }
  259. }
  260. private func secKeySign(_ bytes: Array<UInt8>, variant: SecKeyAlgorithm, withKey key: SecKey) throws -> Data {
  261. var error: Unmanaged<CFError>?
  262. // Sign the data
  263. guard let signature = SecKeyCreateSignature(
  264. key,
  265. variant,
  266. Data(bytes) as CFData,
  267. &error
  268. ) as Data?
  269. else { throw NSError(domain: "Failed to sign bytes: \(bytes)", code: 0) }
  270. return signature
  271. }
  272. private func secKeyVerify(_ signature: Array<UInt8>, forBytes bytes: Array<UInt8>, usingVariant variant: SecKeyAlgorithm, withKey key: SecKey) throws -> Bool {
  273. let pubKey = SecKeyCopyPublicKey(key)!
  274. var error: Unmanaged<CFError>?
  275. // Perform the signature verification
  276. let result = SecKeyVerifySignature(
  277. pubKey,
  278. variant,
  279. Data(bytes) as CFData,
  280. Data(signature) as CFData,
  281. &error
  282. )
  283. // Throw the error if we encountered one...
  284. if let error = error { throw error.takeRetainedValue() as Error }
  285. // return the result of the verification
  286. return result
  287. }
  288. private func secKeyEncrypt(_ bytes: Array<UInt8>, usingVariant variant: SecKeyAlgorithm, withKey key: SecKey) throws -> Data {
  289. let pubKey = SecKeyCopyPublicKey(key)!
  290. var error: Unmanaged<CFError>?
  291. guard let encryptedData = SecKeyCreateEncryptedData(pubKey, variant, Data(bytes) as CFData, &error) else {
  292. throw NSError(domain: "Error Encrypting Data: \(error.debugDescription)", code: 0, userInfo: nil)
  293. }
  294. // Throw the error if we encountered one...
  295. if let error = error { throw error.takeRetainedValue() as Error }
  296. // return the result of the encryption
  297. return encryptedData as Data
  298. }
  299. private func secKeyDecrypt(_ bytes: Array<UInt8>, usingVariant variant: SecKeyAlgorithm, withKey key: SecKey) throws -> Data {
  300. var error: Unmanaged<CFError>?
  301. guard let decryptedData = SecKeyCreateDecryptedData(key, variant, Data(bytes) as CFData, &error) else {
  302. throw NSError(domain: "Error Decrypting Data: \(error.debugDescription)", code: 0, userInfo: nil)
  303. }
  304. return (decryptedData as Data).drop { $0 == 0x00 }
  305. }
  306. }
  307. extension RSASecKeyTests {
  308. static func allTests() -> [(String, (RSASecKeyTests) -> () throws -> Void)] {
  309. let tests = [
  310. ("testRSAExternalRepresentationPublic", testRSAExternalRepresentationPublic),
  311. ("testRSAExternalRepresentationPrivate", testRSAExternalRepresentationPrivate),
  312. ("testSecKeyExternalRepresentationPublic", testSecKeyExternalRepresentationPublic),
  313. ("testSecKeyExternalRepresentationPrivate", testSecKeyExternalRepresentationPrivate),
  314. ("testRSASecKeys", testRSASecKeys)
  315. ]
  316. return tests
  317. }
  318. }
  319. // - MARK: Test Fixture Generation Code
  320. extension RSASecKeyTests {
  321. /// This 'Test' generates an RSA Key and uses that key to sign and encrypt a series of messages that we can test against.
  322. ///
  323. /// It prints a `Fixture` object that can be copy and pasted / used in other tests.
  324. func testCreateTestFixture() throws {
  325. let keySize = 1024
  326. let messages = [
  327. "",
  328. "👋",
  329. "RSA Keys",
  330. "CryptoSwift RSA Keys!",
  331. "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 🔐"
  332. ]
  333. print(messages.map { $0.bytes.count })
  334. /// Generate a SecKey RSA Key
  335. let parameters: [CFString: Any] = [
  336. kSecAttrKeyType: kSecAttrKeyTypeRSA,
  337. kSecAttrKeySizeInBits: keySize
  338. ]
  339. var error: Unmanaged<CFError>?
  340. // Generate the RSA SecKey
  341. guard let rsaSecKey = SecKeyCreateRandomKey(parameters as CFDictionary, &error) else {
  342. XCTFail("Key Generation Error: \(error.debugDescription)")
  343. return
  344. }
  345. // Extract the public key from the private RSA SecKey
  346. guard let rsaSecKeyPublic = SecKeyCopyPublicKey(rsaSecKey) else {
  347. XCTFail("Public Key Extraction Error")
  348. return
  349. }
  350. /// Let's grab the external representation of the public key
  351. var publicExternalRepError: Unmanaged<CFError>?
  352. guard let publicRSASecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKeyPublic, &publicExternalRepError) as? Data else {
  353. XCTFail("Failed to copy external representation for RSA SecKey")
  354. return
  355. }
  356. /// Let's grab the external representation of the private key
  357. var privateExternalRepError: Unmanaged<CFError>?
  358. guard let privateRSASecKeyRawRep = SecKeyCopyExternalRepresentation(rsaSecKey, &privateExternalRepError) as? Data else {
  359. XCTFail("Failed to copy external representation for RSA SecKey")
  360. return
  361. }
  362. var template = RSASecKeyTests.FixtureTemplate
  363. template = template.replacingOccurrences(of: "{{KEY_SIZE}}", with: "\(keySize)")
  364. template = template.replacingOccurrences(of: "{{PUBLIC_DER}}", with: "\(publicRSASecKeyRawRep.base64EncodedString())")
  365. template = template.replacingOccurrences(of: "{{PRIVATE_DER}}", with: "\(privateRSASecKeyRawRep.base64EncodedString())")
  366. var messageEntries: [String] = []
  367. for message in messages {
  368. var messageTemplate = RSASecKeyTests.MessageTemplate
  369. messageTemplate = messageTemplate.replacingOccurrences(of: "{{PLAINTEXT_MESSAGE}}", with: message)
  370. let encryptedMessages = try encrypt(data: message.data(using: .utf8)!, with: rsaSecKeyPublic)
  371. messageTemplate = messageTemplate.replacingOccurrences(of: "{{ENCRYPTED_MESSAGES}}", with: encryptedMessages.joined(separator: ",\n\t\t "))
  372. let signedMessages = try sign(message: message.data(using: .utf8)!, using: rsaSecKey)
  373. messageTemplate = messageTemplate.replacingOccurrences(of: "{{SIGNED_MESSAGES}}", with: signedMessages.joined(separator: ",\n\t\t "))
  374. messageEntries.append(messageTemplate)
  375. }
  376. template = template.replacingOccurrences(of: "{{MESSAGE_TEMPLATES}}", with: "\(messageEntries.joined(separator: ",\n\t"))")
  377. print("\n**************************")
  378. print(" Test Fixture Output ")
  379. print("**************************\n")
  380. print(template)
  381. print("\n**************************")
  382. }
  383. private static let FixtureTemplate = """
  384. static let RSA_{{KEY_SIZE}} = Fixture(
  385. keySize: {{KEY_SIZE}},
  386. publicDER: \"\"\"
  387. {{PUBLIC_DER}}
  388. \"\"\",
  389. privateDER: \"\"\"
  390. {{PRIVATE_DER}}
  391. \"\"\",
  392. messages: [
  393. {{MESSAGE_TEMPLATES}}
  394. ]
  395. )
  396. """
  397. private static let MessageTemplate = """
  398. "{{PLAINTEXT_MESSAGE}}": (
  399. encryptedMessage: [
  400. {{ENCRYPTED_MESSAGES}}
  401. ],
  402. signedMessage: [
  403. {{SIGNED_MESSAGES}}
  404. ]
  405. )
  406. """
  407. private func initSecKey(rawRepresentation unsafe: Data) throws -> SecKey {
  408. let attributes: [String: Any] = [
  409. kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
  410. kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
  411. kSecAttrKeySizeInBits as String: 1024,
  412. kSecAttrIsPermanent as String: false
  413. ]
  414. var error: Unmanaged<CFError>?
  415. guard let secKey = SecKeyCreateWithData(unsafe as CFData, attributes as CFDictionary, &error) else {
  416. throw NSError(domain: "Error constructing SecKey from raw key data: \(error.debugDescription)", code: 0, userInfo: nil)
  417. }
  418. return secKey
  419. }
  420. // We don't support PSS yet so we skip these variants
  421. private func sign(message: Data, using key: SecKey) throws -> [String] {
  422. let algorithms: [SecKeyAlgorithm] = [
  423. .rsaSignatureRaw,
  424. //.rsaSignatureDigestPSSSHA1,
  425. //.rsaSignatureDigestPSSSHA224,
  426. //.rsaSignatureDigestPSSSHA256,
  427. //.rsaSignatureDigestPSSSHA384,
  428. //.rsaSignatureDigestPSSSHA512,
  429. .rsaSignatureDigestPKCS1v15Raw,
  430. .rsaSignatureDigestPKCS1v15SHA1,
  431. .rsaSignatureDigestPKCS1v15SHA224,
  432. .rsaSignatureDigestPKCS1v15SHA256,
  433. .rsaSignatureDigestPKCS1v15SHA384,
  434. .rsaSignatureDigestPKCS1v15SHA512,
  435. //.rsaSignatureMessagePSSSHA1,
  436. //.rsaSignatureMessagePSSSHA224,
  437. //.rsaSignatureMessagePSSSHA256,
  438. //.rsaSignatureMessagePSSSHA384,
  439. //.rsaSignatureMessagePSSSHA512,
  440. .rsaSignatureMessagePKCS1v15SHA1,
  441. .rsaSignatureMessagePKCS1v15SHA224,
  442. .rsaSignatureMessagePKCS1v15SHA256,
  443. .rsaSignatureMessagePKCS1v15SHA384,
  444. .rsaSignatureMessagePKCS1v15SHA512,
  445. ]
  446. var sigs: [String] = []
  447. for algo in algorithms {
  448. var error: Unmanaged<CFError>?
  449. // Sign the data
  450. guard let signature = SecKeyCreateSignature(
  451. key,
  452. algo,
  453. message as CFData,
  454. &error
  455. ) as Data?
  456. else {
  457. print("\"\(algo.rawValue)\": \"nil\",")
  458. sigs.append("\"\(algo.rawValue)\": \"\"")
  459. continue
  460. }
  461. // Throw the error if we encountered one
  462. if let error = error { print("\"\(algo.rawValue)\": \"\(error.takeRetainedValue())\","); continue }
  463. // Append the signature
  464. sigs.append("\"\(algo.rawValue)\": \"\(signature.base64EncodedString())\"")
  465. }
  466. return sigs
  467. }
  468. private func encrypt(data: Data, with key: SecKey) throws -> [String] {
  469. let algorithms: [SecKeyAlgorithm] = [
  470. .rsaEncryptionRaw,
  471. .rsaEncryptionPKCS1
  472. ]
  473. var encryptions: [String] = []
  474. for algo in algorithms {
  475. var error: Unmanaged<CFError>?
  476. guard let encryptedData = SecKeyCreateEncryptedData(key, algo, data as CFData, &error) as? Data else {
  477. print("\"\(algo.rawValue)\": \"\(error?.takeRetainedValue().localizedDescription ?? "nil")\",")
  478. encryptions.append("\"\(algo.rawValue)\": \"\"")
  479. continue
  480. }
  481. encryptions.append("\"\(algo.rawValue)\": \"\(encryptedData.base64EncodedString())\"")
  482. }
  483. return encryptions
  484. }
  485. }
  486. #endif