diff --git a/Package.swift b/Package.swift index cdac8f17..03aec1e2 100644 --- a/Package.swift +++ b/Package.swift @@ -1,22 +1,26 @@ -// swift-tools-version:5.2 +// swift-tools-version:5.3 + import PackageDescription let package = Package( - name: "SwiftyRSA", - products: [ - .library( - name: "SwiftyRSA", - type: .dynamic, - targets: ["SwiftyRSA"]), - ], - dependencies: [ - ], - targets: [ - .target( - name: "SwiftyRSA", - dependencies: [], - path: "Source", - swiftSettings: [.unsafeFlags(["-enable-library-evolution"])] - ) - ] + name: "SwiftyRSA", + platforms: [ + .iOS(.v11), .macOS(.v10_13), .tvOS(.v11), .watchOS(.v4) + ], + products: [ + .library( + name: "SwiftyRSA", + // removed 'type: .dynamic' because the XCFramework binary determines its own linking type. + targets: ["SwiftyRSA"] + ), + ], + dependencies: [], + targets: [ + // Switch to Binary Target to enforce Library Evolution (LE) support + .binaryTarget( + name: "SwiftyRSA", + url: "https://github.com/bangnguyengrab/SwiftyRSA/releases/download/1.8.8/SwiftyRSA.xcframework.zip", + checksum: "9122380b2b0832a6d9d9ed890c910964d85bf87e0d0bda63cd9f2579eafcc5cc" + ) + ] ) diff --git a/Source/Asn1Parser.swift b/Source/Asn1Parser.swift deleted file mode 100644 index d4993b7f..00000000 --- a/Source/Asn1Parser.swift +++ /dev/null @@ -1,220 +0,0 @@ -// -// Asn1Parser.swift -// SwiftyRSA -// -// Created by Lois Di Qual on 5/9/17. -// Copyright © 2017 Scoop. All rights reserved. -// - -import Foundation - -/// Simple data scanner that consumes bytes from a raw data and keeps an updated position. -private class Scanner { - - enum ScannerError: Error { - case outOfBounds - } - - let data: Data - var index: Int = 0 - - /// Returns whether there is no more data to consume - var isComplete: Bool { - return index >= data.count - } - - /// Creates a scanner with provided data - /// - /// - Parameter data: Data to consume - init(data: Data) { - self.data = data - } - - /// Consumes data of provided length and returns it - /// - /// - Parameter length: length of the data to consume - /// - Returns: data consumed - /// - Throws: ScannerError.outOfBounds error if asked to consume too many bytes - func consume(length: Int) throws -> Data { - - guard length > 0 else { - return Data() - } - - guard index + length <= data.count else { - throw ScannerError.outOfBounds - } - - let subdata = data.subdata(in: index.. Int { - - let lengthByte = try consume(length: 1).firstByte - - // If the first byte's value is less than 0x80, it directly contains the length - // so we can return it - guard lengthByte >= 0x80 else { - return Int(lengthByte) - } - - // If the first byte's value is more than 0x80, it indicates how many following bytes - // will describe the length. For instance, 0x85 indicates that 0x85 - 0x80 = 0x05 = 5 - // bytes will describe the length, so we need to read the 5 next bytes and get their integer - // value to determine the length. - let nextByteCount = lengthByte - 0x80 - let length = try consume(length: Int(nextByteCount)) - - return length.integer - } -} - -private extension Data { - - /// Returns the first byte of the current data - var firstByte: UInt8 { - var byte: UInt8 = 0 - copyBytes(to: &byte, count: MemoryLayout.size) - return byte - } - - /// Returns the integer value of the current data. - /// @warning: this only supports data up to 4 bytes, as we can only extract 32-bit integers. - var integer: Int { - - guard count > 0 else { - return 0 - } - - var int: UInt32 = 0 - var offset: Int32 = Int32(count - 1) - forEach { byte in - let byte32 = UInt32(byte) - let shifted = byte32 << (UInt32(offset) * 8) - int = int | shifted - offset -= 1 - } - - return Int(int) - } -} - -/// A simple ASN1 parser that will recursively iterate over a root node and return a Node tree. -/// The root node can be any of the supported nodes described in `Node`. If the parser encounters a sequence -/// it will recursively parse its children. -enum Asn1Parser { - - /// An ASN1 node - enum Node { - case sequence(nodes: [Node]) - case integer(data: Data) - case objectIdentifier(data: Data) - case null - case bitString(data: Data) - case octetString(data: Data) - } - - enum ParserError: Error { - case noType - case invalidType(value: UInt8) - } - - /// Parses ASN1 data and returns its root node. - /// - /// - Parameter data: ASN1 data to parse - /// - Returns: Root ASN1 Node - /// - Throws: A ParserError if anything goes wrong, or if an unknown node was encountered - static func parse(data: Data) throws -> Node { - let scanner = Scanner(data: data) - let node = try parseNode(scanner: scanner) - return node - } - - /// Parses an ASN1 given an existing scanne. - /// @warning: this will modify the state (ie: position) of the provided scanner. - /// - /// - Parameter scanner: Scanner to use to consume the data - /// - Returns: Parsed node - /// - Throws: A ParserError if anything goes wrong, or if an unknown node was encountered - private static func parseNode(scanner: Scanner) throws -> Node { - - let firstByte = try scanner.consume(length: 1).firstByte - - // Sequence - if firstByte == 0x30 { - let length = try scanner.consumeLength() - let data = try scanner.consume(length: length) - let nodes = try parseSequence(data: data) - return .sequence(nodes: nodes) - } - - // Integer - if firstByte == 0x02 { - let length = try scanner.consumeLength() - let data = try scanner.consume(length: length) - return .integer(data: data) - } - - // Object identifier - if firstByte == 0x06 { - let length = try scanner.consumeLength() - let data = try scanner.consume(length: length) - return .objectIdentifier(data: data) - } - - // Null - if firstByte == 0x05 { - _ = try scanner.consume(length: 1) - return .null - } - - // Bit String - if firstByte == 0x03 { - let length = try scanner.consumeLength() - - // There's an extra byte (0x00) after the bit string length in all the keys I've encountered. - // I couldn't find a specification that referenced this extra byte, but let's consume it and discard it. - _ = try scanner.consume(length: 1) - - let data = try scanner.consume(length: length - 1) - return .bitString(data: data) - } - - // Octet String - if firstByte == 0x04 { - let length = try scanner.consumeLength() - let data = try scanner.consume(length: length) - return .octetString(data: data) - } - - throw ParserError.invalidType(value: firstByte) - } - - /// Parses an ASN1 sequence and returns its child nodes - /// - /// - Parameter data: ASN1 data - /// - Returns: A list of ASN1 nodes - /// - Throws: A ParserError if anything goes wrong, or if an unknown node was encountered - private static func parseSequence(data: Data) throws -> [Node] { - let scanner = Scanner(data: data) - var nodes: [Node] = [] - while !scanner.isComplete { - let node = try parseNode(scanner: scanner) - nodes.append(node) - } - return nodes - } -} diff --git a/Source/ClearMessage.swift b/Source/ClearMessage.swift deleted file mode 100644 index 4206caa9..00000000 --- a/Source/ClearMessage.swift +++ /dev/null @@ -1,180 +0,0 @@ -// -// ClearMessage.swift -// SwiftyRSA -// -// Created by Lois Di Qual on 5/18/17. -// Copyright © 2017 Scoop. All rights reserved. -// - -import Foundation - -public class ClearMessage: Message { - - /// Data of the message - public let data: Data - - /// Creates a clear message with data. - /// - /// - Parameter data: Data of the clear message - public required init(data: Data) { - self.data = data - } - - /// Creates a clear message from a string, with the specified encoding. - /// - /// - Parameters: - /// - string: String value of the clear message - /// - encoding: Encoding to use to generate the clear data - /// - Throws: SwiftyRSAError - public convenience init(string: String, using encoding: String.Encoding) throws { - guard let data = string.data(using: encoding) else { - throw SwiftyRSAError.stringToDataConversionFailed - } - self.init(data: data) - } - - /// Returns the string representation of the clear message using the specified - /// string encoding. - /// - /// - Parameter encoding: Encoding to use during the string conversion - /// - Returns: String representation of the clear message - /// - Throws: SwiftyRSAError - public func string(encoding: String.Encoding) throws -> String { - guard let str = String(data: data, encoding: encoding) else { - throw SwiftyRSAError.dataToStringConversionFailed - } - return str - } - - /// Encrypts a clear message with a public key and returns an encrypted message. - /// - /// - Parameters: - /// - key: Public key to encrypt the clear message with - /// - padding: Padding to use during the encryption - /// - Returns: Encrypted message - /// - Throws: SwiftyRSAError - public func encrypted(with key: PublicKey, padding: Padding) throws -> EncryptedMessage { - - let blockSize = SecKeyGetBlockSize(key.reference) - - var maxChunkSize: Int - switch padding { - case []: - maxChunkSize = blockSize - case .OAEP: - maxChunkSize = blockSize - 42 - default: - maxChunkSize = blockSize - 11 - } - - var decryptedDataAsArray = [UInt8](repeating: 0, count: data.count) - (data as NSData).getBytes(&decryptedDataAsArray, length: data.count) - - var encryptedDataBytes = [UInt8](repeating: 0, count: 0) - var idx = 0 - while idx < decryptedDataAsArray.count { - - let idxEnd = min(idx + maxChunkSize, decryptedDataAsArray.count) - let chunkData = [UInt8](decryptedDataAsArray[idx.. Signature { - - let digest = self.digest(digestType: digestType) - let blockSize = SecKeyGetBlockSize(key.reference) - let maxChunkSize = blockSize - 11 - - guard digest.count <= maxChunkSize else { - throw SwiftyRSAError.invalidDigestSize(digestSize: digest.count, maxChunkSize: maxChunkSize) - } - - var digestBytes = [UInt8](repeating: 0, count: digest.count) - (digest as NSData).getBytes(&digestBytes, length: digest.count) - - var signatureBytes = [UInt8](repeating: 0, count: blockSize) - var signatureDataLength = blockSize - - let status = SecKeyRawSign(key.reference, digestType.padding, digestBytes, digestBytes.count, &signatureBytes, &signatureDataLength) - - guard status == noErr else { - throw SwiftyRSAError.signatureCreateFailed(status: status) - } - - let signatureData = Data(bytes: signatureBytes, count: signatureBytes.count) - return Signature(data: signatureData) - } - - /// Verifies the signature of a clear message. - /// - /// - Parameters: - /// - key: Public key to verify the signature with - /// - signature: Signature to verify - /// - digestType: Digest type used for the signature - /// - Returns: Result of the verification - /// - Throws: SwiftyRSAError - public func verify(with key: PublicKey, signature: Signature, digestType: Signature.DigestType) throws -> Bool { - - let digest = self.digest(digestType: digestType) - var digestBytes = [UInt8](repeating: 0, count: digest.count) - (digest as NSData).getBytes(&digestBytes, length: digest.count) - - var signatureBytes = [UInt8](repeating: 0, count: signature.data.count) - (signature.data as NSData).getBytes(&signatureBytes, length: signature.data.count) - - let status = SecKeyRawVerify(key.reference, digestType.padding, digestBytes, digestBytes.count, signatureBytes, signatureBytes.count) - - if status == errSecSuccess { - return true - } else if status == -9809 { - return false - } else { - throw SwiftyRSAError.signatureVerifyFailed(status: status) - } - } - - func digest(digestType: Signature.DigestType) -> Data { - - let digest: Data - - switch digestType { - case .sha1: - digest = data.swiftyRSASHA1() - case .sha224: - digest = data.swiftyRSASHA224() - case .sha256: - digest = data.swiftyRSASHA256() - case .sha384: - digest = data.swiftyRSASHA384() - case .sha512: - digest = data.swiftyRSASHA512() - } - - return digest - } -} diff --git a/Source/Data+SHA.swift b/Source/Data+SHA.swift deleted file mode 100644 index 74968eb8..00000000 --- a/Source/Data+SHA.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// Data+SHA.swift -// -// -// Created by Joanna Bednarz on 02/10/2020. -// - -import Foundation -import CommonCrypto - -extension Data { - - func swiftyRSASHA1() -> Data { - var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH)) - withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in - _ = CC_SHA1(buffer.baseAddress, CC_LONG(count), &digest) - } - return Data(digest) - } - - func swiftyRSASHA224() -> Data { - var digest = [UInt8](repeating: 0, count: Int(CC_SHA224_DIGEST_LENGTH)) - withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in - _ = CC_SHA224(buffer.baseAddress, CC_LONG(count), &digest) - } - return Data(digest) - } - - func swiftyRSASHA256() -> Data { - var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) - withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in - _ = CC_SHA256(buffer.baseAddress, CC_LONG(count), &digest) - } - return Data(digest) - } - - func swiftyRSASHA384() -> Data { - var digest = [UInt8](repeating: 0, count: Int(CC_SHA384_DIGEST_LENGTH)) - withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in - _ = CC_SHA384(buffer.baseAddress, CC_LONG(count), &digest) - } - return Data(digest) - } - - func swiftyRSASHA512() -> Data { - var digest = [UInt8](repeating: 0, count: Int(CC_SHA512_DIGEST_LENGTH)) - withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in - _ = CC_SHA512(buffer.baseAddress, CC_LONG(count), &digest) - } - return Data(digest) - } - -} diff --git a/Source/EncryptedMessage.swift b/Source/EncryptedMessage.swift deleted file mode 100644 index 9e67a8a4..00000000 --- a/Source/EncryptedMessage.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// EncryptedMessage.swift -// SwiftyRSA -// -// Created by Lois Di Qual on 5/18/17. -// Copyright © 2017 Scoop. All rights reserved. -// - -import Foundation - -public class EncryptedMessage: Message { - - /// Data of the message - public let data: Data - - /// Creates an encrypted message with data. - /// - /// - Parameter data: Data of the encrypted message. - public required init(data: Data) { - self.data = data - } - - /// Decrypts an encrypted message with a private key and returns a clear message. - /// - /// - Parameters: - /// - key: Private key to decrypt the mssage with - /// - padding: Padding to use during the decryption - /// - Returns: Clear message - /// - Throws: SwiftyRSAError - public func decrypted(with key: PrivateKey, padding: Padding) throws -> ClearMessage { - let blockSize = SecKeyGetBlockSize(key.reference) - - var encryptedDataAsArray = [UInt8](repeating: 0, count: data.count) - (data as NSData).getBytes(&encryptedDataAsArray, length: data.count) - - var decryptedDataBytes = [UInt8](repeating: 0, count: 0) - var idx = 0 - while idx < encryptedDataAsArray.count { - - let idxEnd = min(idx + blockSize, encryptedDataAsArray.count) - let chunkData = [UInt8](encryptedDataAsArray[idx.. - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.7.0 - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - UIRequiredDeviceCapabilities - - arm64 - - - diff --git a/Source/Info.plist b/Source/Info.plist deleted file mode 100644 index 01a700fd..00000000 --- a/Source/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.7.0 - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/Source/Key.swift b/Source/Key.swift deleted file mode 100644 index 2220cd27..00000000 --- a/Source/Key.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// Key.swift -// SwiftyRSA -// -// Created by Loïs Di Qual on 9/19/16. -// Copyright © 2016 Scoop. All rights reserved. -// - -import Foundation -import Security - -public protocol Key: AnyObject { - - var reference: SecKey { get } - var originalData: Data? { get } - - init(data: Data) throws - init(reference: SecKey) throws - init(base64Encoded base64String: String) throws - init(pemEncoded pemString: String) throws - init(pemNamed pemName: String, in bundle: Bundle) throws - init(derNamed derName: String, in bundle: Bundle) throws - - func pemString() throws -> String - func data() throws -> Data - func base64String() throws -> String -} - -public extension Key { - - /// Returns a Base64 representation of the public key. - /// - /// - Returns: Data of the key, Base64-encoded - /// - Throws: SwiftyRSAError - func base64String() throws -> String { - return try data().base64EncodedString() - } - - func data() throws -> Data { - return try SwiftyRSA.data(forKeyReference: reference) - } - - /// Creates a public key with a base64-encoded string. - /// - /// - Parameter base64String: Base64-encoded public key data - /// - Throws: SwiftyRSAError - init(base64Encoded base64String: String) throws { - guard let data = Data(base64Encoded: base64String, options: [.ignoreUnknownCharacters]) else { - throw SwiftyRSAError.invalidBase64String - } - try self.init(data: data) - } - - /// Creates a public key with a PEM string. - /// - /// - Parameter pemString: PEM-encoded public key string - /// - Throws: SwiftyRSAError - init(pemEncoded pemString: String) throws { - let base64String = try SwiftyRSA.base64String(pemEncoded: pemString) - try self.init(base64Encoded: base64String) - } - - /// Creates a public key with a PEM file. - /// - /// - Parameters: - /// - pemName: Name of the PEM file - /// - bundle: Bundle in which to look for the PEM file. Defaults to the main bundle. - /// - Throws: SwiftyRSAError - init(pemNamed pemName: String, in bundle: Bundle = Bundle.main) throws { - guard let path = bundle.path(forResource: pemName, ofType: "pem") else { - throw SwiftyRSAError.pemFileNotFound(name: pemName) - } - let keyString = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8) - try self.init(pemEncoded: keyString) - } - - /// Creates a private key with a DER file. - /// - /// - Parameters: - /// - derName: Name of the DER file - /// - bundle: Bundle in which to look for the DER file. Defaults to the main bundle. - /// - Throws: SwiftyRSAError - init(derNamed derName: String, in bundle: Bundle = Bundle.main) throws { - guard let path = bundle.path(forResource: derName, ofType: "der") else { - throw SwiftyRSAError.derFileNotFound(name: derName) - } - let data = try Data(contentsOf: URL(fileURLWithPath: path)) - try self.init(data: data) - } -} diff --git a/Source/Message.swift b/Source/Message.swift deleted file mode 100644 index cb164fa5..00000000 --- a/Source/Message.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// Message.swift -// SwiftyRSA -// -// Created by Loïs Di Qual on 9/19/16. -// Copyright © 2016 Scoop. All rights reserved. -// - -import Foundation - -public protocol Message { - var data: Data { get } - var base64String: String { get } - init(data: Data) - init(base64Encoded base64String: String) throws -} - -public extension Message { - - /// Base64-encoded string of the message data - var base64String: String { - return data.base64EncodedString() - } - - /// Creates an encrypted message with a base64-encoded string. - /// - /// - Parameter base64String: Base64-encoded data of the encrypted message - /// - Throws: SwiftyRSAError - init(base64Encoded base64String: String) throws { - guard let data = Data(base64Encoded: base64String) else { - throw SwiftyRSAError.invalidBase64String - } - self.init(data: data) - } -} diff --git a/Source/PrivateKey.swift b/Source/PrivateKey.swift deleted file mode 100644 index 84b43763..00000000 --- a/Source/PrivateKey.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// PrivateKey.swift -// SwiftyRSA -// -// Created by Lois Di Qual on 5/17/17. -// Copyright © 2017 Scoop. All rights reserved. -// - -import Foundation - -public class PrivateKey: Key { - - /// Reference to the key within the keychain - public let reference: SecKey - - /// Original data of the private key. - /// Note that it does not contain PEM headers and holds data as bytes, not as a base 64 string. - public let originalData: Data? - - let tag: String? - - /// Returns a PEM representation of the private key. - /// - /// - Returns: Data of the key, PEM-encoded - /// - Throws: SwiftyRSAError - public func pemString() throws -> String { - let data = try self.data() - let pem = SwiftyRSA.format(keyData: data, withPemType: "RSA PRIVATE KEY") - return pem - } - - /// Creates a private key with a keychain key reference. - /// This initializer will throw if the provided key reference is not a private RSA key. - /// - /// - Parameter reference: Reference to the key within the keychain. - /// - Throws: SwiftyRSAError - public required init(reference: SecKey) throws { - - guard SwiftyRSA.isValidKeyReference(reference, forClass: kSecAttrKeyClassPrivate) else { - throw SwiftyRSAError.notAPrivateKey - } - - self.reference = reference - self.tag = nil - self.originalData = nil - } - - /// Creates a private key with a RSA public key data. - /// - /// - Parameter data: Private key data - /// - Throws: SwiftyRSAError - required public init(data: Data) throws { - self.originalData = data - let tag = UUID().uuidString - self.tag = tag - let dataWithoutHeader = try SwiftyRSA.stripKeyHeader(keyData: data) - reference = try SwiftyRSA.addKey(dataWithoutHeader, isPublic: false, tag: tag) - } - - deinit { - if let tag = tag { - SwiftyRSA.removeKey(tag: tag) - } - } -} diff --git a/Source/PublicKey.swift b/Source/PublicKey.swift deleted file mode 100644 index a5dd6267..00000000 --- a/Source/PublicKey.swift +++ /dev/null @@ -1,116 +0,0 @@ -// -// PublicKey.swift -// SwiftyRSA -// -// Created by Lois Di Qual on 5/17/17. -// Copyright © 2017 Scoop. All rights reserved. -// - -import Foundation - -public class PublicKey: Key { - - /// Reference to the key within the keychain - public let reference: SecKey - - /// Data of the public key as provided when creating the key. - /// Note that if the key was created from a base64string / DER string / PEM file / DER file, - /// the data holds the actual bytes of the key, not any textual representation like PEM headers - /// or base64 characters. - public let originalData: Data? - - let tag: String? // Only used on iOS 8/9 - - /// Returns a PEM representation of the public key. - /// - /// - Returns: Data of the key, PEM-encoded - /// - Throws: SwiftyRSAError - public func pemString() throws -> String { - let data = try self.data() - let pem = SwiftyRSA.format(keyData: data, withPemType: "RSA PUBLIC KEY") - return pem - } - - /// Creates a public key with a keychain key reference. - /// This initializer will throw if the provided key reference is not a public RSA key. - /// - /// - Parameter reference: Reference to the key within the keychain. - /// - Throws: SwiftyRSAError - public required init(reference: SecKey) throws { - - guard SwiftyRSA.isValidKeyReference(reference, forClass: kSecAttrKeyClassPublic) else { - throw SwiftyRSAError.notAPublicKey - } - - self.reference = reference - self.tag = nil - self.originalData = nil - } - - /// Data of the public key as returned by the keychain. - /// This method throws if SwiftyRSA cannot extract data from the key. - /// - /// - Returns: Data of the public key as returned by the keychain. - /// - Throws: SwiftyRSAError - required public init(data: Data) throws { - - let tag = UUID().uuidString - self.tag = tag - - self.originalData = data - let dataWithoutHeader = try SwiftyRSA.stripKeyHeader(keyData: data) - - reference = try SwiftyRSA.addKey(dataWithoutHeader, isPublic: true, tag: tag) - } - - static let publicKeyRegex: NSRegularExpression? = { - let publicKeyRegex = "(-----BEGIN PUBLIC KEY-----.+?-----END PUBLIC KEY-----)" - return try? NSRegularExpression(pattern: publicKeyRegex, options: .dotMatchesLineSeparators) - }() - - /// Takes an input string, scans for public key sections, and then returns a PublicKey for any valid keys found - /// - This method scans the file for public key armor - if no keys are found, an empty array is returned - /// - Each public key block found is "parsed" by `publicKeyFromPEMString()` - /// - should that method throw, the error is _swallowed_ and not rethrown - /// - /// - parameter pemString: The string to use to parse out values - /// - /// - returns: An array of `PublicKey` objects - public static func publicKeys(pemEncoded pemString: String) -> [PublicKey] { - - // If our regexp isn't valid, or the input string is empty, we can't move forward… - guard let publicKeyRegexp = publicKeyRegex, pemString.count > 0 else { - return [] - } - - let all = NSRange( - location: 0, - length: pemString.count - ) - - let matches = publicKeyRegexp.matches( - in: pemString, - options: NSRegularExpression.MatchingOptions(rawValue: 0), - range: all - ) - - let keys = matches.compactMap { result -> PublicKey? in - - let match = result.range(at: 1) - let start = pemString.index(pemString.startIndex, offsetBy: match.location) - let end = pemString.index(start, offsetBy: match.length) - - let thisKey = pemString[start.. _objc_KeyPair { - let (privateKey, publicKey) = try SwiftyRSA.generateRSAKeyPair(sizeInBits: size) - return _objc_KeyPair( - privateKey: _objc_PrivateKey(swiftValue: privateKey), - publicKey: _objc_PublicKey(swiftValue: publicKey) - ) - } -} - -// MARK: - PublicKey - -@objc(PublicKey) -public class _objc_PublicKey: NSObject, Key, ObjcBridgeable { // swiftlint:disable:this type_name - - fileprivate let swiftValue: PublicKey - - @objc public var reference: SecKey { - return swiftValue.reference - } - - @objc public var originalData: Data? { - return swiftValue.originalData - } - - @objc public func pemString() throws -> String { - return try swiftValue.pemString() - } - - @objc public func data() throws -> Data { - return try swiftValue.data() - } - - @objc public func base64String() throws -> String { - return try swiftValue.base64String() - } - - required public init(swiftValue: PublicKey) { - self.swiftValue = swiftValue - } - - @objc required public init(data: Data) throws { - self.swiftValue = try PublicKey(data: data) - } - - @objc public required init(reference: SecKey) throws { - self.swiftValue = try PublicKey(reference: reference) - } - - @objc public required init(base64Encoded base64String: String) throws { - self.swiftValue = try PublicKey(base64Encoded: base64String) - } - - @objc public required init(pemEncoded pemString: String) throws { - self.swiftValue = try PublicKey(pemEncoded: pemString) - } - - @objc public required init(pemNamed pemName: String, in bundle: Bundle) throws { - self.swiftValue = try PublicKey(pemNamed: pemName, in: bundle) - } - - @objc public required init(derNamed derName: String, in bundle: Bundle) throws { - self.swiftValue = try PublicKey(derNamed: derName, in: bundle) - } - - @objc public static func publicKeys(pemEncoded pemString: String) -> [_objc_PublicKey] { - return PublicKey.publicKeys(pemEncoded: pemString).map { _objc_PublicKey(swiftValue: $0) } - } -} - -// MARK: - PrivateKey - -@objc(PrivateKey) -public class _objc_PrivateKey: NSObject, Key, ObjcBridgeable { // swiftlint:disable:this type_name - - fileprivate let swiftValue: PrivateKey - - @objc public var reference: SecKey { - return swiftValue.reference - } - - @objc public var originalData: Data? { - return swiftValue.originalData - } - - @objc public func pemString() throws -> String { - return try swiftValue.pemString() - } - - @objc public func data() throws -> Data { - return try swiftValue.data() - } - - @objc public func base64String() throws -> String { - return try swiftValue.base64String() - } - - public required init(swiftValue: PrivateKey) { - self.swiftValue = swiftValue - } - - @objc public required init(data: Data) throws { - self.swiftValue = try PrivateKey(data: data) - } - - @objc public required init(reference: SecKey) throws { - self.swiftValue = try PrivateKey(reference: reference) - } - - @objc public required init(base64Encoded base64String: String) throws { - self.swiftValue = try PrivateKey(base64Encoded: base64String) - } - - @objc public required init(pemEncoded pemString: String) throws { - self.swiftValue = try PrivateKey(pemEncoded: pemString) - } - - @objc public required init(pemNamed pemName: String, in bundle: Bundle) throws { - self.swiftValue = try PrivateKey(pemNamed: pemName, in: bundle) - } - - @objc public required init(derNamed derName: String, in bundle: Bundle) throws { - self.swiftValue = try PrivateKey(derNamed: derName, in: bundle) - } -} - -// MARK: - VerificationResult - -@objc(VerificationResult) -public class _objc_VerificationResult: NSObject { // swiftlint:disable:this type_name - @objc public let isSuccessful: Bool - init(isSuccessful: Bool) { - self.isSuccessful = isSuccessful - } -} - -// MARK: - ClearMessage - -@objc(ClearMessage) -public class _objc_ClearMessage: NSObject, Message, ObjcBridgeable { // swiftlint:disable:this type_name - - fileprivate let swiftValue: ClearMessage - - @objc public var base64String: String { - return swiftValue.base64String - } - - @objc public var data: Data { - return swiftValue.data - } - - public required init(swiftValue: ClearMessage) { - self.swiftValue = swiftValue - } - - @objc public required init(data: Data) { - self.swiftValue = ClearMessage(data: data) - } - - @objc public required init(string: String, using rawEncoding: UInt) throws { - let encoding = String.Encoding(rawValue: rawEncoding) - self.swiftValue = try ClearMessage(string: string, using: encoding) - } - - @objc public required init(base64Encoded base64String: String) throws { - self.swiftValue = try ClearMessage(base64Encoded: base64String) - } - - @objc public func string(encoding rawEncoding: UInt) throws -> String { - let encoding = String.Encoding(rawValue: rawEncoding) - return try swiftValue.string(encoding: encoding) - } - - @objc public func encrypted(with key: _objc_PublicKey, padding: Padding) throws -> _objc_EncryptedMessage { - let encryptedMessage = try swiftValue.encrypted(with: key.swiftValue, padding: padding) - return _objc_EncryptedMessage(swiftValue: encryptedMessage) - } - - @objc public func signed(with key: _objc_PrivateKey, digestType: _objc_Signature.DigestType) throws -> _objc_Signature { - let signature = try swiftValue.signed(with: key.swiftValue, digestType: digestType.swiftValue) - return _objc_Signature(swiftValue: signature) - } - - @objc public func verify(with key: _objc_PublicKey, signature: _objc_Signature, digestType: _objc_Signature.DigestType) throws -> _objc_VerificationResult { - let isSuccessful = try swiftValue.verify(with: key.swiftValue, signature: signature.swiftValue, digestType: digestType.swiftValue) - return _objc_VerificationResult(isSuccessful: isSuccessful) - } -} - -// MARK: - EncryptedMessage - -@objc(EncryptedMessage) -public class _objc_EncryptedMessage: NSObject, Message, ObjcBridgeable { // swiftlint:disable:this type_name - - fileprivate let swiftValue: EncryptedMessage - - @objc public var base64String: String { - return swiftValue.base64String - } - - @objc public var data: Data { - return swiftValue.data - } - - public required init(swiftValue: EncryptedMessage) { - self.swiftValue = swiftValue - } - - @objc public required init(data: Data) { - self.swiftValue = EncryptedMessage(data: data) - } - - @objc public required init(base64Encoded base64String: String) throws { - self.swiftValue = try EncryptedMessage(base64Encoded: base64String) - } - - @objc public func decrypted(with key: _objc_PrivateKey, padding: Padding) throws -> _objc_ClearMessage { - let clearMessage = try swiftValue.decrypted(with: key.swiftValue, padding: padding) - return _objc_ClearMessage(swiftValue: clearMessage) - } -} - -// MARK: - Signature - -@objc(Signature) -public class _objc_Signature: NSObject, ObjcBridgeable { // swiftlint:disable:this type_name - - @objc - public enum DigestType: Int { - case sha1 - case sha224 - case sha256 - case sha384 - case sha512 - - fileprivate var swiftValue: Signature.DigestType { - switch self { - case .sha1: return .sha1 - case .sha224: return .sha224 - case .sha256: return .sha256 - case .sha384: return .sha384 - case .sha512: return .sha512 - } - } - } - - fileprivate let swiftValue: Signature - - @objc public var base64String: String { - return swiftValue.base64String - } - - @objc public var data: Data { - return swiftValue.data - } - - public required init(swiftValue: Signature) { - self.swiftValue = swiftValue - } - - @objc public init(data: Data) { - self.swiftValue = Signature(data: data) - } - - @objc public required init(base64Encoded base64String: String) throws { - self.swiftValue = try Signature(base64Encoded: base64String) - } -} diff --git a/Source/SwiftyRSA.swift b/Source/SwiftyRSA.swift deleted file mode 100644 index 80f490c7..00000000 --- a/Source/SwiftyRSA.swift +++ /dev/null @@ -1,349 +0,0 @@ -// -// SwiftyRSA.swift -// SwiftyRSA -// -// Created by Loïs Di Qual on 7/2/15. -// -// Copyright (c) 2015 Scoop Technologies, Inc. All rights reserved. -// - -import Foundation -import Security - -public typealias Padding = SecPadding - -public enum SwiftyRSA { - - static func base64String(pemEncoded pemString: String) throws -> String { - let lines = pemString.components(separatedBy: "\n").filter { line in - return !line.hasPrefix("-----BEGIN") && !line.hasPrefix("-----END") - } - - guard lines.count != 0 else { - throw SwiftyRSAError.pemDoesNotContainKey - } - - return lines.joined(separator: "") - } - - static func isValidKeyReference(_ reference: SecKey, forClass requiredClass: CFString) -> Bool { - - guard #available(iOS 10.0, *), #available(watchOS 3.0, *), #available(tvOS 10.0, *) else { - return true - } - - let attributes = SecKeyCopyAttributes(reference) as? [CFString: Any] - guard let keyType = attributes?[kSecAttrKeyType] as? String, let keyClass = attributes?[kSecAttrKeyClass] as? String else { - return false - } - - let isRSA = keyType == (kSecAttrKeyTypeRSA as String) - let isValidClass = keyClass == (requiredClass as String) - return isRSA && isValidClass - } - - static func format(keyData: Data, withPemType pemType: String) -> String { - - func split(_ str: String, byChunksOfLength length: Int) -> [String] { - return stride(from: 0, to: str.count, by: length).map { index -> String in - let startIndex = str.index(str.startIndex, offsetBy: index) - let endIndex = str.index(startIndex, offsetBy: length, limitedBy: str.endIndex) ?? str.endIndex - return String(str[startIndex.. Data { - - // On iOS+, we can use `SecKeyCopyExternalRepresentation` directly - if #available(iOS 10.0, *), #available(watchOS 3.0, *), #available(tvOS 10.0, *) { - - var error: Unmanaged? - let data = SecKeyCopyExternalRepresentation(reference, &error) - guard let unwrappedData = data as Data? else { - throw SwiftyRSAError.keyRepresentationFailed(error: error?.takeRetainedValue()) - } - return unwrappedData - - // On iOS 8/9, we need to add the key again to the keychain with a temporary tag, grab the data, - // and delete the key again. - } else { - - let temporaryTag = UUID().uuidString - let addParams: [CFString: Any] = [ - kSecValueRef: reference, - kSecReturnData: true, - kSecClass: kSecClassKey, - kSecAttrApplicationTag: temporaryTag - ] - - var data: AnyObject? - let addStatus = SecItemAdd(addParams as CFDictionary, &data) - guard let unwrappedData = data as? Data else { - throw SwiftyRSAError.keyAddFailed(status: addStatus) - } - - let deleteParams: [CFString: Any] = [ - kSecClass: kSecClassKey, - kSecAttrApplicationTag: temporaryTag - ] - - _ = SecItemDelete(deleteParams as CFDictionary) - - return unwrappedData - } - } - - /// Will generate a new private and public key - /// - /// - Parameters: - /// - size: Indicates the total number of bits in this cryptographic key - /// - Returns: A touple of a private and public key - /// - Throws: Throws and error if the tag cant be parsed or if keygeneration fails - @available(iOS 10.0, watchOS 3.0, tvOS 10.0, *) - public static func generateRSAKeyPair(sizeInBits size: Int) throws -> (privateKey: PrivateKey, publicKey: PublicKey) { - return try generateRSAKeyPair(sizeInBits: size, applyUnitTestWorkaround: false) - } - - @available(iOS 10.0, watchOS 3.0, tvOS 10.0, *) - static func generateRSAKeyPair(sizeInBits size: Int, applyUnitTestWorkaround: Bool = false) throws -> (privateKey: PrivateKey, publicKey: PublicKey) { - - guard let tagData = UUID().uuidString.data(using: .utf8) else { - throw SwiftyRSAError.stringToDataConversionFailed - } - - // @hack Don't store permanently when running unit tests, otherwise we'll get a key creation error (NSOSStatusErrorDomain -50) - // @see http://www.openradar.me/36809637 - // @see https://stackoverflow.com/q/48414685/646960 - let isPermanent = applyUnitTestWorkaround ? false : true - - let attributes: [CFString: Any] = [ - kSecAttrKeyType: kSecAttrKeyTypeRSA, - kSecAttrKeySizeInBits: size, - kSecPrivateKeyAttrs: [ - kSecAttrIsPermanent: isPermanent, - kSecAttrApplicationTag: tagData - ] - ] - - var error: Unmanaged? - guard let privKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error), - let pubKey = SecKeyCopyPublicKey(privKey) else { - throw SwiftyRSAError.keyGenerationFailed(error: error?.takeRetainedValue()) - } - let privateKey = try PrivateKey(reference: privKey) - let publicKey = try PublicKey(reference: pubKey) - - return (privateKey: privateKey, publicKey: publicKey) - } - - static func addKey(_ keyData: Data, isPublic: Bool, tag: String) throws -> SecKey { - - let keyData = keyData - - guard let tagData = tag.data(using: .utf8) else { - throw SwiftyRSAError.tagEncodingFailed - } - - let keyClass = isPublic ? kSecAttrKeyClassPublic : kSecAttrKeyClassPrivate - - // On iOS 10+, we can use SecKeyCreateWithData without going through the keychain - if #available(iOS 10.0, *), #available(watchOS 3.0, *), #available(tvOS 10.0, *) { - - let sizeInBits = keyData.count * 8 - let keyDict: [CFString: Any] = [ - kSecAttrKeyType: kSecAttrKeyTypeRSA, - kSecAttrKeyClass: keyClass, - kSecAttrKeySizeInBits: NSNumber(value: sizeInBits), - kSecReturnPersistentRef: true - ] - - var error: Unmanaged? - guard let key = SecKeyCreateWithData(keyData as CFData, keyDict as CFDictionary, &error) else { - throw SwiftyRSAError.keyCreateFailed(error: error?.takeRetainedValue()) - } - return key - - // On iOS 9 and earlier, add a persistent version of the key to the system keychain - } else { - - let persistKey = UnsafeMutablePointer(mutating: nil) - - let keyAddDict: [CFString: Any] = [ - kSecClass: kSecClassKey, - kSecAttrApplicationTag: tagData, - kSecAttrKeyType: kSecAttrKeyTypeRSA, - kSecValueData: keyData, - kSecAttrKeyClass: keyClass, - kSecReturnPersistentRef: true, - kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock - ] - - let addStatus = SecItemAdd(keyAddDict as CFDictionary, persistKey) - guard addStatus == errSecSuccess || addStatus == errSecDuplicateItem else { - throw SwiftyRSAError.keyAddFailed(status: addStatus) - } - - let keyCopyDict: [CFString: Any] = [ - kSecClass: kSecClassKey, - kSecAttrApplicationTag: tagData, - kSecAttrKeyType: kSecAttrKeyTypeRSA, - kSecAttrKeyClass: keyClass, - kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock, - kSecReturnRef: true, - ] - - // Now fetch the SecKeyRef version of the key - var keyRef: AnyObject? - let copyStatus = SecItemCopyMatching(keyCopyDict as CFDictionary, &keyRef) - - guard let unwrappedKeyRef = keyRef else { - throw SwiftyRSAError.keyCopyFailed(status: copyStatus) - } - - return unwrappedKeyRef as! SecKey // swiftlint:disable:this force_cast - } - } - - /** - This method strips the x509 header from a provided ASN.1 DER key. - If the key doesn't contain a header, the DER data is returned as is. - - Supported formats are: - - Headerless: - SEQUENCE - INTEGER (1024 or 2048 bit) -- modulo - INTEGER -- public exponent - - With x509 header: - SEQUENCE - SEQUENCE - OBJECT IDENTIFIER 1.2.840.113549.1.1.1 - NULL - BIT STRING - SEQUENCE - INTEGER (1024 or 2048 bit) -- modulo - INTEGER -- public exponent - - Example of headerless key: - https://lapo.it/asn1js/#3082010A0282010100C1A0DFA367FBC2A5FD6ED5A071E02A4B0617E19C6B5AD11BB61192E78D212F10A7620084A3CED660894134D4E475BAD7786FA1D40878683FD1B7A1AD9C0542B7A666457A270159DAC40CE25B2EAE7CCD807D31AE725CA394F90FBB5C5BA500545B99C545A9FE08EFF00A5F23457633E1DB84ED5E908EF748A90F8DFCCAFF319CB0334705EA012AF15AA090D17A9330159C9AFC9275C610BB9B7C61317876DC7386C723885C100F774C19830F475AD1E9A9925F9CA9A69CE0181A214DF2EB75FD13E6A546B8C8ED699E33A8521242B7E42711066AEC22D25DD45D56F94D3170D6F2C25164D2DACED31C73963BA885ADCB706F40866B8266433ED5161DC50E4B3B0203010001 - - Example of key with X509 header (notice the additional ASN.1 sequence): - https://lapo.it/asn1js/#30819F300D06092A864886F70D010101050003818D0030818902818100D0674615A252ED3D75D2A3073A0A8A445F3188FD3BEB8BA8584F7299E391BDEC3427F287327414174997D147DD8CA62647427D73C9DA5504E0A3EED5274A1D50A1237D688486FADB8B82061675ABFA5E55B624095DB8790C6DBCAE83D6A8588C9A6635D7CF257ED1EDE18F04217D37908FD0CBB86B2C58D5F762E6207FF7B92D0203010001 - */ - static func stripKeyHeader(keyData: Data) throws -> Data { - - let node: Asn1Parser.Node - do { - node = try Asn1Parser.parse(data: keyData) - } catch { - throw SwiftyRSAError.asn1ParsingFailed - } - - // Ensure the raw data is an ASN1 sequence - guard case .sequence(let nodes) = node else { - throw SwiftyRSAError.invalidAsn1RootNode - } - - // Detect whether the sequence only has integers, in which case it's a headerless key - let onlyHasIntegers = nodes.filter { node -> Bool in - if case .integer = node { - return false - } - return true - }.isEmpty - - // Headerless key - if onlyHasIntegers { - return keyData - } - - // If last element of the sequence is a bit string, return its data - if let last = nodes.last, case .bitString(let data) = last { - return data - } - - // If last element of the sequence is an octet string, return its data - if let last = nodes.last, case .octetString(let data) = last { - return data - } - - // Unable to extract bit/octet string or raw integer sequence - throw SwiftyRSAError.invalidAsn1Structure - } - - /** - This method prepend the x509 header to the given PublicKey data. - If the key already contain a x509 header, the given data is returned as is. - It letterally does the opposite of the previous method : - From a given headerless key : - SEQUENCE - INTEGER (1024 or 2048 bit) -- modulo - INTEGER -- public exponent - the key is returned following the X509 header : - SEQUENCE - SEQUENCE - OBJECT IDENTIFIER 1.2.840.113549.1.1.1 - NULL - BIT STRING - SEQUENCE - INTEGER (1024 or 2048 bit) -- modulo - INTEGER -- public exponent - */ - - static func prependX509KeyHeader(keyData: Data) throws -> Data { - if try keyData.isAnHeaderlessKey() { - let x509certificate: Data = keyData.prependx509Header() - return x509certificate - } else if try keyData.hasX509Header() { - return keyData - } else { // invalideHeader - throw SwiftyRSAError.x509CertificateFailed - } - } - - static func removeKey(tag: String) { - - guard let tagData = tag.data(using: .utf8) else { - return - } - - let keyRemoveDict: [CFString: Any] = [ - kSecClass: kSecClassKey, - kSecAttrKeyType: kSecAttrKeyTypeRSA, - kSecAttrApplicationTag: tagData, - ] - - SecItemDelete(keyRemoveDict as CFDictionary) - } -} - -#if !swift(>=4.1) -extension Array { - func compactMap(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult] { - return try self.flatMap(transform) - } -} -#endif - -#if !swift(>=4.0) -extension NSTextCheckingResult { - func range(at idx: Int) -> NSRange { - return self.rangeAt(1) - } -} -#endif diff --git a/Source/SwiftyRSAError.swift b/Source/SwiftyRSAError.swift deleted file mode 100644 index a747afdc..00000000 --- a/Source/SwiftyRSAError.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// SwiftyRSAError.swift -// SwiftyRSA -// -// Created by Lois Di Qual on 5/15/17. -// Contributions by Stchepinsky Nathan on 24/06/2021 -// Copyright © 2017 Scoop. All rights reserved. -// - -import Foundation - -public enum SwiftyRSAError: Error { - - case pemDoesNotContainKey - case keyRepresentationFailed(error: CFError?) - case keyGenerationFailed(error: CFError?) - case keyCreateFailed(error: CFError?) - case keyAddFailed(status: OSStatus) - case keyCopyFailed(status: OSStatus) - case tagEncodingFailed - case asn1ParsingFailed - case invalidAsn1RootNode - case invalidAsn1Structure - case invalidBase64String - case chunkDecryptFailed(index: Int) - case chunkEncryptFailed(index: Int) - case stringToDataConversionFailed - case dataToStringConversionFailed - case invalidDigestSize(digestSize: Int, maxChunkSize: Int) - case signatureCreateFailed(status: OSStatus) - case signatureVerifyFailed(status: OSStatus) - case pemFileNotFound(name: String) - case derFileNotFound(name: String) - case notAPublicKey - case notAPrivateKey - case x509CertificateFailed -} - -extension SwiftyRSAError: LocalizedError { - public var errorDescription: String? { - switch self { - case .pemDoesNotContainKey: - return "Couldn't get data from PEM key: no data available after stripping headers" - case .keyRepresentationFailed(let error): - return "Couldn't retrieve key data from the keychain: CFError \(String(describing: error))" - case .keyGenerationFailed(let error): - return "Couldn't generate key pair: CFError: \(String(describing: error))" - case .keyCreateFailed(let error): - return "Couldn't create key reference from key data: CFError \(String(describing: error))" - case .keyAddFailed(let status): - return "Couldn't retrieve key data from the keychain: OSStatus \(status)" - case .keyCopyFailed(let status): - return "Couldn't copy and retrieve key reference from the keychain: OSStatus \(status)" - case .tagEncodingFailed: - return "Couldn't create tag data for key" - case .asn1ParsingFailed: - return "Couldn't parse the ASN1 key data. Please file a bug at https://goo.gl/y67MW6" - case .invalidAsn1RootNode: - return "Couldn't parse the provided key because its root ASN1 node is not a sequence. The key is probably corrupt" - case .invalidAsn1Structure: - return "Couldn't parse the provided key because it has an unexpected ASN1 structure" - case .invalidBase64String: - return "The provided string is not a valid Base 64 string" - case .chunkDecryptFailed(let index): - return "Couldn't decrypt chunk at index \(index)" - case .chunkEncryptFailed(let index): - return "Couldn't encrypt chunk at index \(index)" - case .stringToDataConversionFailed: - return "Couldn't convert string to data using specified encoding" - case .dataToStringConversionFailed: - return "Couldn't convert data to string representation" - case .invalidDigestSize(let digestSize, let maxChunkSize): - return "Provided digest type produces a size (\(digestSize)) that is bigger than the maximum chunk size \(maxChunkSize) of the RSA key" - case .signatureCreateFailed(let status): - return "Couldn't sign provided data: OSStatus \(status)" - case .signatureVerifyFailed(let status): - return "Couldn't verify signature of the provided data: OSStatus \(status)" - case .pemFileNotFound(let name): - return "Couldn't find a PEM file named '\(name)'" - case .derFileNotFound(let name): - return "Couldn't find a DER file named '\(name)'" - case .notAPublicKey: - return "Provided key is not a valid RSA public key" - case .notAPrivateKey: - return "Provided key is not a valid RSA pivate key" - case .x509CertificateFailed : - return "Couldn't prepend the provided key because it has an unexpected structure" - } - } -} diff --git a/Source/X509Certificate.swift b/Source/X509Certificate.swift deleted file mode 100644 index 2cb4f865..00000000 --- a/Source/X509Certificate.swift +++ /dev/null @@ -1,163 +0,0 @@ -// -// X509Certificate.swift -// SwiftyRSA -// -// Created by Stchepinsky Nathan on 24/06/2021. -// Copyright © 2021 Scoop. All rights reserved. -// - -import Foundation - -/// Encoding/Decoding lengths as octets -private extension NSInteger { - func encodedOctets() -> [CUnsignedChar] { - // Short form - if self < 128 { - return [CUnsignedChar(self)] - } - - // Long form - let long = Int(log2(Double(self)) / 8 + 1) - var len = self - var result: [CUnsignedChar] = [CUnsignedChar(long + 0x80)] - - for _ in 0..> 8 - } - - return result - } - - init?(octetBytes: [CUnsignedChar], startIdx: inout NSInteger) { - if octetBytes[startIdx] < 128 { - // Short form - self.init(octetBytes[startIdx]) - startIdx += 1 - } else { - // Long form - let octets = NSInteger(octetBytes[startIdx] as UInt8 - 128) - - if octets > octetBytes.count - startIdx { - self.init(0) - return nil - } - - var result = UInt64(0) - - for octet in 1...octets { - result = (result << 8) - result = result + UInt64(octetBytes[startIdx + octet]) - } - - startIdx += 1 + octets - self.init(result) - } - } -} - -public extension Data { - // This code source come from Heimdall project https://github.com/henrinormak/Heimdall published under MIT Licence - - /// This method prepend the X509 header to a given public key - func prependx509Header() -> Data { - let result = NSMutableData() - - let encodingLength: Int = (self.count + 1).encodedOctets().count - let OID: [CUnsignedChar] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00] - - var builder: [CUnsignedChar] = [] - - // ASN.1 SEQUENCE - builder.append(0x30) - - // Overall size, made of OID + bitstring encoding + actual key - let size = OID.count + 2 + encodingLength + self.count - let encodedSize = size.encodedOctets() - builder.append(contentsOf: encodedSize) - result.append(builder, length: builder.count) - result.append(OID, length: OID.count) - builder.removeAll(keepingCapacity: false) - - builder.append(0x03) - builder.append(contentsOf: (self.count + 1).encodedOctets()) - builder.append(0x00) - result.append(builder, length: builder.count) - - // Actual key bytes - result.append(self) - - return result as Data - } - - func hasX509Header() throws -> Bool { - let node: Asn1Parser.Node - do { - node = try Asn1Parser.parse(data: self) - } catch { - throw SwiftyRSAError.asn1ParsingFailed - } - - // Ensure the raw data is an ASN1 sequence - guard case .sequence(let nodes) = node else { - return false - } - - // Must contain 2 elements, a sequence and a bit string - if nodes.count != 2 { - return false - } - - // Ensure the first node is an ASN1 sequence - guard case .sequence(let firstNode) = nodes[0] else { - return false - } - - // Must contain 2 elements, an object id and NULL - if firstNode.count != 2 { - return false - } - - guard case .objectIdentifier(_) = firstNode[0] else { - return false - } - - guard case .null = firstNode[1] else { - return false - } - - // The 2sd child has to be a bit string containing a sequence of 2 int - let last = nodes[1] - if case .bitString(let secondChildSequence) = last { - return try secondChildSequence.isAnHeaderlessKey() - } else { - return false - } - } - - func isAnHeaderlessKey() throws -> Bool { - let node: Asn1Parser.Node - do { - node = try Asn1Parser.parse(data: self) - } catch { - throw SwiftyRSAError.asn1ParsingFailed - } - - // Ensure the raw data is an ASN1 sequence - guard case .sequence(let nodes) = node else { - return false - } - - // Detect whether the sequence only has integers, in which case it's a headerless key - let onlyHasIntegers = nodes.filter { node -> Bool in - if case .integer = node { - return false - } - return true - }.isEmpty - - // Headerless key - return onlyHasIntegers - } -} diff --git a/SwiftyRSA.podspec b/SwiftyRSA.podspec index e0edbee7..e99f86ac 100644 --- a/SwiftyRSA.podspec +++ b/SwiftyRSA.podspec @@ -17,7 +17,10 @@ Pod::Spec.new do |s| s.exclude_files = "Source/SwiftyRSA+ObjC.swift" s.framework = "Security" s.requires_arc = true - s.pod_target_xcconfig = { 'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES' } + s.pod_target_xcconfig = { + 'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES', + 'OTHER_SWIFT_FLAGS' => '$(inherited) -Xfrontend -enable-library-evolution' +} s.swift_version = "5.0" s.ios.deployment_target = "10.0"