// Copyright © 2019 IBM. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // import Foundation #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) import CommonCrypto #elseif os(Linux) import OpenSSL #endif /// A protocol for signing an instance of some object to generate an `ECSignature`. @available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *) protocol ECSignable { /// Sign the object using ECDSA and produce an `ECSignature`. func sign(with: ECPrivateKey) throws -> ECSignature } @available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *) extension String: ECSignable { /// UTF8 encode the String to Data and sign it using the `ECPrivateKey`. /// The Data is signed using ECDSA with either SHA256, SHA384 or SHA512, depending on the key's curve. /// - Parameter with key: The elliptic curve private key. /// - Returns: An ECSignature on failure. /// - Throws: An ECError if a valid signature is unable to be created. public func sign(with key: ECPrivateKey) throws -> ECSignature { return try Data(self.utf8).sign(with: key) } } @available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *) extension Data: ECSignable { /// Sign the plaintext data using the provided `ECPrivateKey`. /// The Data is signed using ECDSA with either SHA256, SHA384 or SHA512, depending on the key's curve. /// - Parameter with key: The elliptic curve private key. /// - Returns: An ECSignature on failure. /// - Throws: An ECError if a valid signature is unable to be created. public func sign(with key: ECPrivateKey) throws -> ECSignature { #if os(Linux) let md_ctx = EVP_MD_CTX_new_wrapper() let evp_key = EVP_PKEY_new() defer { EVP_PKEY_free(evp_key) EVP_MD_CTX_free_wrapper(md_ctx) } guard EVP_PKEY_set1_EC_KEY(evp_key, .make(optional: key.nativeKey)) == 1 else { throw ECError.failedNativeKeyCreation } guard EVP_DigestSignInit(md_ctx, nil, .make(optional: key.curve.signingAlgorithm), nil, evp_key) == 1 else { throw ECError.failedEvpInit } guard self.withUnsafeBytes({ (message: UnsafeRawBufferPointer) -> Int32 in return EVP_DigestUpdate(md_ctx, message.baseAddress?.assumingMemoryBound(to: UInt8.self), self.count) }) == 1 else { throw ECError.failedSigningAlgorithm } var sig_len: Int = 0 EVP_DigestSignFinal(md_ctx, nil, &sig_len) let sig = UnsafeMutablePointer.allocate(capacity: sig_len) defer { #if swift(>=4.1) sig.deallocate() #else sig.deallocate(capacity: sig_len) #endif } guard EVP_DigestSignFinal(md_ctx, sig, &sig_len) == 1 else { throw ECError.failedSigningAlgorithm } return try ECSignature(asn1: Data(bytes: sig, count: sig_len)) #else let hash = key.curve.digest(data: self) // Memory storage for error from SecKeyCreateSignature var error: Unmanaged? = nil // cfSignature is CFData that is ANS1 encoded as a sequence of two UInts (r and s) guard let cfSignature = SecKeyCreateSignature(key.nativeKey, key.curve.signingAlgorithm, hash as CFData, &error) else { if let thrownError = error?.takeRetainedValue() { throw thrownError } else { throw ECError.failedSigningAlgorithm } } return try ECSignature(asn1: cfSignature as Data) #endif } }