Initial commit
This commit is contained in:
92
Pods/BlueECC/Sources/CryptorECC/ASN1.swift
generated
Normal file
92
Pods/BlueECC/Sources/CryptorECC/ASN1.swift
generated
Normal file
@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Copyright IBM Corporation 2019
|
||||
*
|
||||
* 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
|
||||
|
||||
// Private and public keys are stores in ASN1 format.
|
||||
// The following code is used to parse the data and retrieve the required elements.
|
||||
struct ASN1 {
|
||||
|
||||
indirect enum ASN1Element {
|
||||
case seq(elements: [ASN1Element])
|
||||
case integer(int: Int)
|
||||
case bytes(data: Data)
|
||||
case constructed(tag: Int, elem: ASN1Element)
|
||||
case unknown
|
||||
}
|
||||
|
||||
static func toASN1Element(data: Data) -> (ASN1Element, Int) {
|
||||
guard data.count >= 2 else {
|
||||
// format error
|
||||
return (.unknown, data.count)
|
||||
}
|
||||
|
||||
switch data[0] {
|
||||
case 0x30: // sequence
|
||||
let (length, lengthOfLength) = readLength(data: data.advanced(by: 1))
|
||||
var result: [ASN1Element] = []
|
||||
var subdata = data.advanced(by: 1 + lengthOfLength)
|
||||
var alreadyRead = 0
|
||||
|
||||
while alreadyRead < length {
|
||||
let (e, l) = toASN1Element(data: subdata)
|
||||
result.append(e)
|
||||
subdata = subdata.count > l ? subdata.advanced(by: l) : Data()
|
||||
alreadyRead += l
|
||||
}
|
||||
return (.seq(elements: result), 1 + lengthOfLength + length)
|
||||
|
||||
case 0x02: // integer
|
||||
let (length, lengthOfLength) = readLength(data: data.advanced(by: 1))
|
||||
if length < 8 {
|
||||
var result: Int = 0
|
||||
let subdata = data.advanced(by: 1 + lengthOfLength)
|
||||
// ignore negative case
|
||||
for i in 0..<length {
|
||||
result = 256 * result + Int(subdata[i])
|
||||
}
|
||||
return (.integer(int: result), 1 + lengthOfLength + length)
|
||||
}
|
||||
// number is too large to fit in Int; return the bytes
|
||||
return (.bytes(data: data.subdata(in: (1 + lengthOfLength) ..< (1 + lengthOfLength + length))), 1 + lengthOfLength + length)
|
||||
|
||||
|
||||
case let s where (s & 0xe0) == 0xa0: // constructed
|
||||
let tag = Int(s & 0x1f)
|
||||
let (length, lengthOfLength) = readLength(data: data.advanced(by: 1))
|
||||
let subdata = data.advanced(by: 1 + lengthOfLength)
|
||||
let (e, _) = toASN1Element(data: subdata)
|
||||
return (.constructed(tag: tag, elem: e), 1 + lengthOfLength + length)
|
||||
|
||||
default: // octet string
|
||||
let (length, lengthOfLength) = readLength(data: data.advanced(by: 1))
|
||||
return (.bytes(data: data.subdata(in: (1 + lengthOfLength) ..< (1 + lengthOfLength + length))), 1 + lengthOfLength + length)
|
||||
}
|
||||
}
|
||||
|
||||
static private func readLength(data: Data) -> (Int, Int) {
|
||||
if data[0] & 0x80 == 0x00 { // short form
|
||||
return (Int(data[0]), 1)
|
||||
} else {
|
||||
let lenghOfLength = Int(data[0] & 0x7F)
|
||||
var result: Int = 0
|
||||
for i in 1..<(1 + lenghOfLength) {
|
||||
result = 256 * result + Int(data[i])
|
||||
}
|
||||
return (result, 1 + lenghOfLength)
|
||||
}
|
||||
}
|
||||
}
|
||||
39
Pods/BlueECC/Sources/CryptorECC/Data+Extensions.swift
generated
Normal file
39
Pods/BlueECC/Sources/CryptorECC/Data+Extensions.swift
generated
Normal file
@ -0,0 +1,39 @@
|
||||
// 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 !swift(>=5.0)
|
||||
// Extension to allow Swift 5 `withUnsafeBytes` API for earlier versions
|
||||
internal extension Data {
|
||||
func withUnsafeBytes<T>(_ body: (UnsafeRawBufferPointer) throws -> T) rethrows -> T {
|
||||
let c = count
|
||||
return try withUnsafeBytes { (p: UnsafePointer<UInt8>) throws -> T in
|
||||
try body(UnsafeRawBufferPointer(start: p, count: c))
|
||||
}
|
||||
}
|
||||
|
||||
mutating func withUnsafeMutableBytes<T>(_ body: (UnsafeMutableRawBufferPointer) throws -> T) rethrows -> T {
|
||||
let c = count
|
||||
return try withUnsafeMutableBytes { (p: UnsafeMutablePointer<UInt8>) throws -> T in
|
||||
try body(UnsafeMutableRawBufferPointer(start: p, count: c))
|
||||
}
|
||||
}
|
||||
|
||||
init(_ bytes: [UInt8]) {
|
||||
self.init(bytes: bytes)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
146
Pods/BlueECC/Sources/CryptorECC/ECDecryptable.swift
generated
Normal file
146
Pods/BlueECC/Sources/CryptorECC/ECDecryptable.swift
generated
Normal file
@ -0,0 +1,146 @@
|
||||
// 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
|
||||
|
||||
/// Extensions for encrypting, decrypting or signing `Data` using the appropriate algorithm determined by the key's curve with the provided `ECPrivateKey` or `ECPublicKey`.
|
||||
@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *)
|
||||
extension Data {
|
||||
|
||||
/// Decrypt the encrypted data using the provided `ECPrivateKey`.
|
||||
/// The signing algorithm used is determined based on the private key's elliptic curve.
|
||||
/// - Parameter ecPrivateKey: The elliptic curve private key.
|
||||
/// - Returns: The plaintext Data.
|
||||
/// - Throws: An ECError if the Encrypted data fails to be decrypted.
|
||||
public func decrypt(with key: ECPrivateKey) throws -> Data {
|
||||
#if os(Linux)
|
||||
// Initialize the decryption context.
|
||||
let rsaDecryptCtx = EVP_CIPHER_CTX_new()
|
||||
EVP_CIPHER_CTX_init_wrapper(rsaDecryptCtx)
|
||||
|
||||
let tagLength = 16
|
||||
let encKeyLength = key.curve.keySize
|
||||
let encryptedDataLength = Int(self.count) - encKeyLength - tagLength
|
||||
// Extract encryptedAESKey, encryptedData, GCM tag from data
|
||||
let encryptedKey = self.subdata(in: 0..<encKeyLength)
|
||||
let encryptedData = self.subdata(in: encKeyLength..<encKeyLength+encryptedDataLength)
|
||||
var tagData = self.subdata(in: encKeyLength+encryptedDataLength..<self.count)
|
||||
// Allocate memory for decryption
|
||||
let ec_group = EC_KEY_get0_group(key.nativeKey)
|
||||
let skey_len = Int((EC_GROUP_get_degree(ec_group) + 7) / 8)
|
||||
let symKey = UnsafeMutablePointer<UInt8>.allocate(capacity: skey_len)
|
||||
let decrypted = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(encryptedData.count + 16))
|
||||
defer {
|
||||
// On completion deallocate the memory
|
||||
EVP_CIPHER_CTX_reset_wrapper(rsaDecryptCtx)
|
||||
EVP_CIPHER_CTX_free_wrapper(rsaDecryptCtx)
|
||||
#if swift(>=4.1)
|
||||
symKey.deallocate()
|
||||
decrypted.deallocate()
|
||||
#else
|
||||
symKey.deallocate(capacity: skey_len)
|
||||
decrypted.deallocate(capacity: Int(encryptedData.count + 16))
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get public key point from key
|
||||
let pubk_point = EC_POINT_new(ec_group)
|
||||
defer {
|
||||
EC_POINT_free(pubk_point)
|
||||
}
|
||||
encryptedKey.withUnsafeBytes({ (pubk: UnsafeRawBufferPointer) in
|
||||
let pubk_bn = BN_bin2bn(pubk.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(encryptedKey.count), nil)
|
||||
let pubk_bn_ctx = BN_CTX_new()
|
||||
BN_CTX_start(pubk_bn_ctx)
|
||||
EC_POINT_bn2point(ec_group, pubk_bn, pubk_point, pubk_bn_ctx)
|
||||
BN_CTX_end(pubk_bn_ctx)
|
||||
BN_CTX_free(pubk_bn_ctx)
|
||||
BN_clear_free(pubk_bn)
|
||||
})
|
||||
|
||||
// calculate symmetric key
|
||||
ECDH_compute_key(symKey, skey_len, pubk_point, key.nativeKey, nil)
|
||||
// processedLen is the number of bytes that each EVP_DecryptUpdate/EVP_DecryptFinal decrypts.
|
||||
// The sum of processedLen is the total size of the decrypted message (decMsgLen)
|
||||
var processedLen: Int32 = 0
|
||||
var decMsgLen: Int32 = 0
|
||||
|
||||
// get aes key and iv using ANSI x9.63 Key Derivation Function
|
||||
let symKeyData = Data(bytes: symKey, count: skey_len)
|
||||
let counterData = Data([0x00, 0x00, 0x00, 0x01])
|
||||
let preHashKey = symKeyData + counterData + encryptedKey
|
||||
let hashedKey = key.curve.digest(data: preHashKey)
|
||||
let aesKey = [UInt8](hashedKey.subdata(in: 0 ..< 16))
|
||||
let iv = [UInt8](hashedKey.subdata(in: 16 ..< 32))
|
||||
|
||||
// Set the IV length to be 16 bytes.
|
||||
// Set the envelope decryption algorithm as 128 bit AES-GCM.
|
||||
guard EVP_DecryptInit_ex(rsaDecryptCtx, EVP_aes_128_gcm(), nil, nil, nil) == 1 else {
|
||||
throw ECError.failedEvpInit
|
||||
}
|
||||
guard EVP_CIPHER_CTX_ctrl(rsaDecryptCtx, EVP_CTRL_GCM_SET_IVLEN, 16, nil) == 1,
|
||||
// Set the AES key to be 16 bytes.
|
||||
EVP_CIPHER_CTX_set_key_length(rsaDecryptCtx, 16) == 1
|
||||
else {
|
||||
throw ECError.failedDecryptionAlgorithm
|
||||
}
|
||||
|
||||
// Set the envelope decryption context AES key and IV.
|
||||
guard EVP_DecryptInit_ex(rsaDecryptCtx, nil, nil, aesKey, iv) == 1 else {
|
||||
throw ECError.failedDecryptionAlgorithm
|
||||
}
|
||||
|
||||
// Decrypt the encrypted data using the symmetric key.
|
||||
guard encryptedData.withUnsafeBytes({ (enc: UnsafeRawBufferPointer) -> Int32 in
|
||||
return EVP_DecryptUpdate(rsaDecryptCtx, decrypted, &processedLen, enc.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(encryptedData.count))
|
||||
}) != 0 else {
|
||||
throw ECError.failedDecryptionAlgorithm
|
||||
}
|
||||
decMsgLen += processedLen
|
||||
// Verify the provided GCM tag.
|
||||
guard tagData.withUnsafeMutableBytes({ (tag: UnsafeMutableRawBufferPointer) -> Int32 in
|
||||
return EVP_CIPHER_CTX_ctrl(rsaDecryptCtx, EVP_CTRL_GCM_SET_TAG, 16, tag.baseAddress)
|
||||
}) == 1
|
||||
else {
|
||||
throw ECError.failedDecryptionAlgorithm
|
||||
}
|
||||
guard EVP_DecryptFinal_ex(rsaDecryptCtx, decrypted.advanced(by: Int(decMsgLen)), &processedLen) == 1 else {
|
||||
throw ECError.failedDecryptionAlgorithm
|
||||
}
|
||||
decMsgLen += processedLen
|
||||
// return the decrypted plaintext.
|
||||
return Data(bytes: decrypted, count: Int(decMsgLen))
|
||||
#else
|
||||
var error: Unmanaged<CFError>? = nil
|
||||
guard let eData = SecKeyCreateDecryptedData(key.nativeKey,
|
||||
key.curve.encryptionAlgorithm,
|
||||
self as CFData,
|
||||
&error)
|
||||
else {
|
||||
guard let error = error?.takeRetainedValue() else {
|
||||
throw ECError.failedEncryptionAlgorithm
|
||||
}
|
||||
throw error
|
||||
}
|
||||
return eData as Data
|
||||
#endif
|
||||
}
|
||||
}
|
||||
177
Pods/BlueECC/Sources/CryptorECC/ECEncryptable.swift
generated
Normal file
177
Pods/BlueECC/Sources/CryptorECC/ECEncryptable.swift
generated
Normal file
@ -0,0 +1,177 @@
|
||||
// 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 encrypting an instance of some object to generate some encrypted data.
|
||||
@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *)
|
||||
protocol ECEncryptable {
|
||||
/// Encrypt the object using ECIES and produce some encrypted `Data`.
|
||||
func encrypt(with: ECPublicKey) throws -> Data
|
||||
}
|
||||
|
||||
/// Extensions for encrypting or signing a `String` by converting it to UTF8 Data, then using the appropriate algorithm determined by the key's curve with the provided `ECPrivateKey` or `ECPublicKey`.
|
||||
@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *)
|
||||
extension String: ECEncryptable {
|
||||
|
||||
/// UTF8 encode the String to Data and encrypt it using the `ECPublicKey`.
|
||||
/// This either uses the `SecKeyAlgorithm`: `eciesEncryptionStandardVariableIVX963SHA256AESGCM`,
|
||||
/// or the equivalent OpenSSL implementation.
|
||||
/// - Parameter ecPrivateKey: The elliptic curve private key.
|
||||
/// - Returns: The encrypted Data.
|
||||
/// - Throws: An ECError is the plaintext fails to be encrypted.
|
||||
public func encrypt(with key: ECPublicKey) throws -> Data {
|
||||
return try Data(self.utf8).encrypt(with: key)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension for signing `Data` with an `ECPrivateKey` and the algorithm determined by the key's curve.
|
||||
@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *)
|
||||
extension Data: ECEncryptable {
|
||||
|
||||
/// Encrypt the data using the `ECPublicKey`.
|
||||
/// This either uses the `SecKeyAlgorithm`: `eciesEncryptionStandardVariableIVX963SHA256AESGCM`,
|
||||
/// or the equivalent OpenSSL implementation.
|
||||
/// - Parameter ecPrivateKey: The elliptic curve private key.
|
||||
/// - Returns: The encrypted Data.
|
||||
/// - Throws: An ECError is the plaintext fails to be encrypted.
|
||||
public func encrypt(with key: ECPublicKey) throws -> Data {
|
||||
#if os(Linux)
|
||||
// Compute symmetric key
|
||||
let ec_key = EC_KEY_new_by_curve_name(key.curve.nativeCurve)
|
||||
defer {
|
||||
EC_KEY_free(ec_key)
|
||||
}
|
||||
EC_KEY_generate_key(ec_key)
|
||||
let ec_group = EC_KEY_get0_group(ec_key)
|
||||
let symKey_len = Int((EC_GROUP_get_degree(ec_group) + 7) / 8)
|
||||
let symKey = UnsafeMutablePointer<UInt8>.allocate(capacity: symKey_len)
|
||||
ECDH_compute_key(symKey, symKey_len, EC_KEY_get0_public_key(key.nativeKey), ec_key, nil)
|
||||
|
||||
// get temp public key data
|
||||
let pub_bn_ctx = BN_CTX_new()
|
||||
BN_CTX_start(pub_bn_ctx)
|
||||
let pub = EC_KEY_get0_public_key(ec_key)
|
||||
let pub_bn = BN_new()
|
||||
EC_POINT_point2bn(ec_group, pub, POINT_CONVERSION_UNCOMPRESSED, pub_bn, pub_bn_ctx)
|
||||
let pubk = UnsafeMutablePointer<UInt8>.allocate(capacity: key.curve.keySize)
|
||||
BN_bn2bin(pub_bn, pubk)
|
||||
defer {
|
||||
BN_CTX_end(pub_bn_ctx)
|
||||
BN_CTX_free(pub_bn_ctx)
|
||||
BN_clear_free(pub_bn)
|
||||
#if swift(>=4.1)
|
||||
pubk.deallocate()
|
||||
symKey.deallocate()
|
||||
#else
|
||||
pubk.deallocate(capacity: key.curve.keySize)
|
||||
symKey.deallocate(capacity: symKey_len)
|
||||
#endif
|
||||
}
|
||||
|
||||
// get aes key and iv using ANSI x9.63 Key Derivation Function
|
||||
let symKeyData = Data(bytes: symKey, count: symKey_len)
|
||||
let counterData = Data([0x00, 0x00, 0x00, 0x01])
|
||||
let sharedInfo = Data(bytes: pubk, count: key.curve.keySize)
|
||||
let preHashKey = symKeyData + counterData + sharedInfo
|
||||
let hashedKey = key.curve.digest(data: preHashKey)
|
||||
let aesKey = [UInt8](hashedKey.subdata(in: 0 ..< (hashedKey.count - 16)))
|
||||
let iv = [UInt8](hashedKey.subdata(in: (hashedKey.count - 16) ..< hashedKey.count))
|
||||
|
||||
|
||||
// AES encrypt data
|
||||
// Initialize encryption context
|
||||
let rsaEncryptCtx = EVP_CIPHER_CTX_new_wrapper()
|
||||
EVP_CIPHER_CTX_init_wrapper(rsaEncryptCtx)
|
||||
|
||||
// Allocate encryption memory
|
||||
let tag = UnsafeMutablePointer<UInt8>.allocate(capacity: 16)
|
||||
let encrypted = UnsafeMutablePointer<UInt8>.allocate(capacity: self.count + 16)
|
||||
defer {
|
||||
// On completion deallocate the memory
|
||||
EVP_CIPHER_CTX_reset_wrapper(rsaEncryptCtx)
|
||||
EVP_CIPHER_CTX_free_wrapper(rsaEncryptCtx)
|
||||
|
||||
#if swift(>=4.1)
|
||||
tag.deallocate()
|
||||
encrypted.deallocate()
|
||||
#else
|
||||
tag.deallocate(capacity: 16)
|
||||
encrypted.deallocate(capacity: self.count + 16)
|
||||
#endif
|
||||
}
|
||||
|
||||
var processedLength: Int32 = 0
|
||||
var encLength: Int32 = 0
|
||||
guard EVP_EncryptInit_ex(rsaEncryptCtx, EVP_aes_128_gcm(), nil, nil, nil) == 1 else {
|
||||
throw ECError.failedEvpInit
|
||||
}
|
||||
// Set the IV length to be 16 to match Apple.
|
||||
guard EVP_CIPHER_CTX_ctrl(rsaEncryptCtx, EVP_CTRL_GCM_SET_IVLEN, 16, nil) == 1
|
||||
// Add the aad to the encryption context.
|
||||
// This is used in generating the GCM tag. We don't use this processedLength.
|
||||
else {
|
||||
throw ECError.failedEncryptionAlgorithm
|
||||
}
|
||||
guard EVP_EncryptInit_ex(rsaEncryptCtx, nil, nil, aesKey, iv) == 1 else {
|
||||
throw ECError.failedDecryptionAlgorithm
|
||||
}
|
||||
// Encrypt the plaintext into encrypted using gcmAlgorithm with the random aes key and all 0 iv.
|
||||
guard(self.withUnsafeBytes({ (plaintext: UnsafeRawBufferPointer) -> Int32 in
|
||||
return EVP_EncryptUpdate(rsaEncryptCtx, encrypted, &processedLength, plaintext.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(self.count))
|
||||
})) == 1 else {
|
||||
throw ECError.failedEncryptionAlgorithm
|
||||
}
|
||||
|
||||
encLength += processedLength
|
||||
// Finalize the encryption so no more data will be added and generate the GCM tag.
|
||||
guard EVP_EncryptFinal_ex(rsaEncryptCtx, encrypted.advanced(by: Int(encLength)), &processedLength) == 1 else {
|
||||
throw ECError.failedEncryptionAlgorithm
|
||||
}
|
||||
|
||||
encLength += processedLength
|
||||
// Get the 16 byte GCM tag.
|
||||
guard EVP_CIPHER_CTX_ctrl(rsaEncryptCtx, EVP_CTRL_GCM_GET_TAG, 16, tag) == 1 else {
|
||||
throw ECError.failedEncryptionAlgorithm
|
||||
}
|
||||
|
||||
// Construct the envelope by combining the encrypted AES key, the encrypted date and the GCM tag.
|
||||
let ekFinal = Data(bytes: pubk, count: key.curve.keySize)
|
||||
let cipher = Data(bytes: encrypted, count: Int(encLength))
|
||||
let tagFinal = Data(bytes: tag, count: 16)
|
||||
return ekFinal + cipher + tagFinal
|
||||
#else
|
||||
var error: Unmanaged<CFError>? = nil
|
||||
guard let eData = SecKeyCreateEncryptedData(key.nativeKey,
|
||||
key.curve.encryptionAlgorithm,
|
||||
self as CFData,
|
||||
&error)
|
||||
else {
|
||||
guard let error = error?.takeRetainedValue() else {
|
||||
throw ECError.failedEncryptionAlgorithm
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
return eData as Data
|
||||
#endif
|
||||
}
|
||||
}
|
||||
72
Pods/BlueECC/Sources/CryptorECC/ECError.swift
generated
Normal file
72
Pods/BlueECC/Sources/CryptorECC/ECError.swift
generated
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright IBM Corporation 2019
|
||||
*
|
||||
* 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
|
||||
|
||||
/// A struct representing the different errors that can be thrown by BlueECC.
|
||||
public struct ECError: Error, Equatable {
|
||||
|
||||
/// A human readable description of the error.
|
||||
public let localizedDescription: String
|
||||
|
||||
private let internalError: InternalError
|
||||
|
||||
private enum InternalError {
|
||||
case invalidPEMString, unknownPEMHeader, failedBase64Encoding, failedASN1Decoding, unsupportedCurve, failedNativeKeyCreation, failedEvpInit, failedSigningAlgorithm, invalidRSLength, failedEncryptionAlgorithm, failedUTF8Decoding, failedDecryptionAlgorithm
|
||||
}
|
||||
|
||||
/// Error thrown when an invalid PEM String used to initialize a key.
|
||||
public static let invalidPEMString = ECError(localizedDescription: "Input was not a valid PEM String", internalError: .invalidPEMString)
|
||||
|
||||
/// Error thrown when the PEM header is not recognized.
|
||||
public static let unknownPEMHeader = ECError(localizedDescription: "Input PEM header was not recognized", internalError: .unknownPEMHeader)
|
||||
|
||||
/// Error thrown when a String fails to be Base64 encoded.
|
||||
public static let failedBase64Encoding = ECError(localizedDescription: "Failed to base64 encode the String", internalError: .failedBase64Encoding)
|
||||
|
||||
/// Error thrown when the ASN1 data could not be decoded to the expected structure.
|
||||
public static let failedASN1Decoding = ECError(localizedDescription: "ASN1 data could not be decoded to expected structure", internalError: .failedASN1Decoding)
|
||||
|
||||
/// Error thrown when the key's object identifier is for a curve that is not supported.
|
||||
public static let unsupportedCurve = ECError(localizedDescription: "The key object identifier is for a non-supported curve", internalError: .unsupportedCurve)
|
||||
|
||||
/// Error thrown when the key could not be converted to a native key (`SecKey` for Apple, `EC_KEY` for linux).
|
||||
public static let failedNativeKeyCreation = ECError(localizedDescription: "The key data could not be converted to a native key", internalError: .failedNativeKeyCreation)
|
||||
|
||||
/// Error thrown when the encryption envelope fails to initialize.
|
||||
public static let failedEvpInit = ECError(localizedDescription: "Failed to initialize the signing envelope", internalError: .failedEvpInit)
|
||||
|
||||
/// Error thrown when the signing algorithm could not create the signature.
|
||||
public static let failedSigningAlgorithm = ECError(localizedDescription: "Signing algorithm failed to create the signature", internalError: .failedSigningAlgorithm)
|
||||
|
||||
/// Error thrown when the provided R and S Data was not a valid length.
|
||||
/// They must be the same length and either 32, 48 or 66 bytes (depending on the curve used).
|
||||
public static let invalidRSLength = ECError(localizedDescription: "The provided R and S values were not a valid length", internalError: .invalidRSLength)
|
||||
|
||||
/// Error thrown when the encryption algorithm could not encrypt the plaintext.
|
||||
public static let failedEncryptionAlgorithm = ECError(localizedDescription: "Encryption algorithm failed to encrypt the data", internalError: .failedEncryptionAlgorithm)
|
||||
|
||||
/// Error thrown when the decryption algorithm could not decrypt the encrypted Data.
|
||||
public static let failedDecryptionAlgorithm = ECError(localizedDescription: "Decryption algorithm failed to decrypt the data", internalError: .failedDecryptionAlgorithm)
|
||||
|
||||
/// Error thrown when the Data could not be decoded into a UTF8 String.
|
||||
public static let failedUTF8Decoding = ECError(localizedDescription: "Data could not be decoded as a UTF8 String", internalError: .failedUTF8Decoding)
|
||||
|
||||
/// Checks if ECSigningErrors are equal, required for Equatable protocol.
|
||||
public static func == (lhs: ECError, rhs: ECError) -> Bool {
|
||||
return lhs.internalError == rhs.internalError
|
||||
}
|
||||
}
|
||||
474
Pods/BlueECC/Sources/CryptorECC/ECPrivateKey.swift
generated
Normal file
474
Pods/BlueECC/Sources/CryptorECC/ECPrivateKey.swift
generated
Normal file
@ -0,0 +1,474 @@
|
||||
// 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
|
||||
|
||||
|
||||
/**
|
||||
Represents an elliptic curve private key.
|
||||
Supported curves are:
|
||||
- prime256v1
|
||||
- secp384r1
|
||||
- NID_secp521r1
|
||||
You can generate an elliptic curve Key using OpenSSL:
|
||||
https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations#Generating_EC_Keys_and_Parameters
|
||||
|
||||
### Usage Example:
|
||||
```swift
|
||||
let pemKey = """
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIJX+87WJ7Gh19sohyZnhxZeXYNOcuGv4Q+8MLge4UkaZoAoGCCqGSM49
|
||||
AwEHoUQDQgAEikc5m6C2xtDWeeAeT18WElO37zvFOz8p4kAlhvgIHN23XIClNESg
|
||||
KVmLgSSq2asqiwdrU5YHbcHFkgdABM1SPA==
|
||||
-----END EC PRIVATE KEY-----
|
||||
"""
|
||||
let privateKey = try ECPrivateKey(key: pemKey)
|
||||
let signature = "Hello world".sign(with: privateKey)
|
||||
```
|
||||
*/
|
||||
@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *)
|
||||
public class ECPrivateKey {
|
||||
/// A String description of the curve this key was generated from.
|
||||
public let curveId: String
|
||||
|
||||
/// The `EllipticCurve` this key was generated from.
|
||||
public let curve: EllipticCurve
|
||||
|
||||
/// The private key represented as a PEM String.
|
||||
public let pemString: String
|
||||
|
||||
#if os(Linux)
|
||||
typealias NativeKey = OpaquePointer?
|
||||
deinit { EC_KEY_free(.make(optional: self.nativeKey)) }
|
||||
#else
|
||||
typealias NativeKey = SecKey
|
||||
#endif
|
||||
let nativeKey: NativeKey
|
||||
let pubKeyBytes: Data
|
||||
private var stripped: Bool = false
|
||||
|
||||
|
||||
/**
|
||||
Initialize an ECPrivateKey from a PEM String.
|
||||
This can either be from a `.p8` file with the header "-----BEGIN PRIVATE KEY-----",
|
||||
or from a `.pem` file with the header "-----BEGIN EC PRIVATE KEY-----".
|
||||
### Usage Example: ###
|
||||
```swift
|
||||
let privateKeyString = """
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIJX+87WJ7Gh19sohyZnhxZeXYNOcuGv4Q+8MLge4UkaZoAoGCCqGSM49
|
||||
AwEHoUQDQgAEikc5m6C2xtDWeeAeT18WElO37zvFOz8p4kAlhvgIHN23XIClNESg
|
||||
KVmLgSSq2asqiwdrU5YHbcHFkgdABM1SPA==
|
||||
-----END EC PRIVATE KEY-----
|
||||
"""
|
||||
let key = try ECPrivateKey(key: privateKeyString)
|
||||
```
|
||||
- Parameter key: The elliptic curve private key as a PEM string.
|
||||
- Returns: An ECPrivateKey.
|
||||
- Throws: An ECError if the PEM string can't be decoded or is not a valid key.
|
||||
*/
|
||||
public convenience init(key: String) throws {
|
||||
// Strip whitespace characters
|
||||
let strippedKey = String(key.filter { !" \n\t\r".contains($0) })
|
||||
var pemComponents = strippedKey.components(separatedBy: "-----")
|
||||
guard pemComponents.count >= 5 else {
|
||||
throw ECError.invalidPEMString
|
||||
}
|
||||
// Remove any EC parameters since Curve is determined by OID
|
||||
if pemComponents[1] == "BEGINECPARAMETERS" {
|
||||
pemComponents.removeFirst(5)
|
||||
guard pemComponents.count >= 5 else {
|
||||
throw ECError.invalidPEMString
|
||||
}
|
||||
}
|
||||
guard let der = Data(base64Encoded: pemComponents[2]) else {
|
||||
throw ECError.failedBase64Encoding
|
||||
}
|
||||
if pemComponents[1] == "BEGINECPRIVATEKEY" {
|
||||
try self.init(sec1DER: der)
|
||||
} else if pemComponents[1] == "BEGINPRIVATEKEY" {
|
||||
try self.init(pkcs8DER: der)
|
||||
} else {
|
||||
throw ECError.unknownPEMHeader
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize an ECPrivateKey from a PKCS8 `.der` file data.
|
||||
/// This is equivalent to a PEM String that has had the "-----BEGIN PRIVATE KEY-----"
|
||||
/// header and footer stripped and been base64 encoded to ASN1 Data.
|
||||
/// - Parameter pkcs8DER: The elliptic curve private key Data.
|
||||
/// - Returns: An ECPrivateKey.
|
||||
/// - Throws: An ECError if the Data can't be decoded or is not a valid key.
|
||||
public init(pkcs8DER: Data) throws {
|
||||
let (result, _) = ASN1.toASN1Element(data: pkcs8DER)
|
||||
guard case let ASN1.ASN1Element.seq(elements: es) = result,
|
||||
es.count > 2,
|
||||
case let ASN1.ASN1Element.seq(elements: ids) = es[1],
|
||||
ids.count > 1,
|
||||
case let ASN1.ASN1Element.bytes(data: privateKeyID) = ids[1]
|
||||
else {
|
||||
throw ECError.failedASN1Decoding
|
||||
}
|
||||
self.curve = try EllipticCurve.objectToCurve(ObjectIdentifier: privateKeyID)
|
||||
guard case let ASN1.ASN1Element.bytes(data: privateOctest) = es[2] else {
|
||||
throw ECError.failedASN1Decoding
|
||||
}
|
||||
let (octest, _) = ASN1.toASN1Element(data: privateOctest)
|
||||
guard case let ASN1.ASN1Element.seq(elements: seq) = octest,
|
||||
seq.count >= 3,
|
||||
case let ASN1.ASN1Element.bytes(data: privateKeyData) = seq[1]
|
||||
else {
|
||||
throw ECError.failedASN1Decoding
|
||||
}
|
||||
let publicKeyData: Data
|
||||
if case let ASN1.ASN1Element.constructed(tag: 1, elem: publicElement) = seq[2],
|
||||
case let ASN1.ASN1Element.bytes(data: pubKeyData) = publicElement
|
||||
{
|
||||
publicKeyData = pubKeyData
|
||||
} else if seq.count >= 4,
|
||||
case let ASN1.ASN1Element.constructed(tag: 1, elem: publicElement) = seq[3],
|
||||
case let ASN1.ASN1Element.bytes(data: pubKeyData) = publicElement
|
||||
{
|
||||
publicKeyData = pubKeyData
|
||||
} else {
|
||||
throw ECError.failedASN1Decoding
|
||||
}
|
||||
let trimmedPubBytes = publicKeyData.drop(while: { $0 == 0x00})
|
||||
if trimmedPubBytes.count != publicKeyData.count {
|
||||
stripped = true
|
||||
}
|
||||
self.nativeKey = try ECPrivateKey.bytesToNativeKey(privateKeyData: privateKeyData,
|
||||
publicKeyData: trimmedPubBytes,
|
||||
curve: curve)
|
||||
let derData = ECPrivateKey.generateASN1(privateKey: privateKeyData,
|
||||
publicKey: publicKeyData,
|
||||
curve: curve)
|
||||
self.pemString = ECPrivateKey.derToPrivatePEM(derData: derData)
|
||||
self.pubKeyBytes = trimmedPubBytes
|
||||
self.curveId = curve.description
|
||||
}
|
||||
|
||||
/// Initialize an ECPrivateKey from a SEC1 `.der` file data.
|
||||
/// This is equivalent to a PEM String that has had the "-----BEGIN EC PRIVATE KEY-----"
|
||||
/// header and footer stripped and been base64 encoded to ASN1 Data.
|
||||
/// - Parameter sec1DER: The elliptic curve private key Data.
|
||||
/// - Returns: An ECPrivateKey.
|
||||
/// - Throws: An ECError if the Data can't be decoded or is not a valid key.
|
||||
public init(sec1DER: Data) throws {
|
||||
self.pemString = ECPrivateKey.derToPrivatePEM(derData: sec1DER)
|
||||
let (result, _) = ASN1.toASN1Element(data: sec1DER)
|
||||
guard case let ASN1.ASN1Element.seq(elements: seq) = result,
|
||||
seq.count > 3,
|
||||
case let ASN1.ASN1Element.constructed(tag: _, elem: objectElement) = seq[2],
|
||||
case let ASN1.ASN1Element.bytes(data: objectId) = objectElement,
|
||||
case let ASN1.ASN1Element.bytes(data: privateKeyData) = seq[1]
|
||||
else {
|
||||
throw ECError.failedASN1Decoding
|
||||
}
|
||||
self.curve = try EllipticCurve.objectToCurve(ObjectIdentifier: objectId)
|
||||
guard case let ASN1.ASN1Element.constructed(tag: _, elem: publicElement) = seq[3],
|
||||
case let ASN1.ASN1Element.bytes(data: publicKeyData) = publicElement
|
||||
else {
|
||||
throw ECError.failedASN1Decoding
|
||||
}
|
||||
let trimmedPubBytes = publicKeyData.drop(while: { $0 == 0x00})
|
||||
if trimmedPubBytes.count != publicKeyData.count {
|
||||
stripped = true
|
||||
}
|
||||
self.nativeKey = try ECPrivateKey.bytesToNativeKey(privateKeyData: privateKeyData,
|
||||
publicKeyData: trimmedPubBytes,
|
||||
curve: curve)
|
||||
self.pubKeyBytes = trimmedPubBytes
|
||||
self.curveId = curve.description
|
||||
}
|
||||
|
||||
/// Initialize the `ECPublicKey`for this private key by extracting the public key bytes.
|
||||
/// - Returns: An ECPublicKey.
|
||||
/// - Throws: An ECError if the public key fails to be initialized from this private key.
|
||||
public func extractPublicKey() throws -> ECPublicKey {
|
||||
let keyHeader: Data
|
||||
// Add the ASN1 header for the public key. The bytes have the following structure:
|
||||
// SEQUENCE (2 elem)
|
||||
// SEQUENCE (2 elem)
|
||||
// OBJECT IDENTIFIER
|
||||
// OBJECT IDENTIFIER
|
||||
// BIT STRING (This is the `pubKeyBytes` added afterwards)
|
||||
if self.curve == .prime256v1 {
|
||||
keyHeader = Data([0x30, 0x59,
|
||||
0x30, 0x13,
|
||||
0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01,
|
||||
0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42])
|
||||
} else if self.curve == .secp384r1 {
|
||||
keyHeader = Data([0x30, 0x76,
|
||||
0x30, 0x10,
|
||||
0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01,
|
||||
0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62])
|
||||
} else if self.curve == .secp521r1 {
|
||||
keyHeader = Data([0x30, 0x81, 0x9B,
|
||||
0x30, 0x10,
|
||||
0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01,
|
||||
0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23, 0x03, 0x81, 0x86])
|
||||
} else {
|
||||
throw ECError.unsupportedCurve
|
||||
}
|
||||
// If we stripped the leading zero earlier, add it back here
|
||||
var pubBytes = self.pubKeyBytes
|
||||
if stripped {
|
||||
pubBytes = Data(count: 1) + self.pubKeyBytes
|
||||
}
|
||||
return try ECPublicKey(der: keyHeader + pubBytes)
|
||||
}
|
||||
|
||||
/**
|
||||
Make an new ECPrivate key from a supported `EllipticCurve`.
|
||||
- Parameter for curve: The elliptic curve that is used to generate the key.
|
||||
- Returns: An ECPrivateKey.
|
||||
- Throws: An ECError if the key fails to be created.
|
||||
*/
|
||||
public static func make(for curve: EllipticCurve) throws -> ECPrivateKey {
|
||||
return try ECPrivateKey(for: curve)
|
||||
}
|
||||
|
||||
/**
|
||||
Initialise an new ECPrivate key from a supported `Curve`
|
||||
- Parameter for curve: The elliptic curve that is used to generate the key.
|
||||
- Returns: An ECPrivateKey.
|
||||
- Throws: An ECError if the key fails to be created.
|
||||
*/
|
||||
private init(for curve: EllipticCurve) throws {
|
||||
self.curve = curve
|
||||
self.curveId = curve.description
|
||||
self.stripped = true
|
||||
#if os(Linux)
|
||||
let ec_key = EC_KEY_new_by_curve_name(curve.nativeCurve)
|
||||
EC_KEY_generate_key(ec_key)
|
||||
self.nativeKey = ec_key
|
||||
let pub_bn_ctx = BN_CTX_new()
|
||||
BN_CTX_start(pub_bn_ctx)
|
||||
let pub = EC_KEY_get0_public_key(ec_key)
|
||||
let ec_group = EC_KEY_get0_group(ec_key)
|
||||
let pub_bn = BN_new()
|
||||
EC_POINT_point2bn(ec_group, pub, POINT_CONVERSION_UNCOMPRESSED, pub_bn, pub_bn_ctx)
|
||||
let pubk = UnsafeMutablePointer<UInt8>.allocate(capacity: curve.keySize)
|
||||
BN_bn2bin(pub_bn, pubk)
|
||||
self.pubKeyBytes = Data(bytes: pubk, count: curve.keySize)
|
||||
defer {
|
||||
BN_CTX_end(pub_bn_ctx)
|
||||
BN_CTX_free(pub_bn_ctx)
|
||||
BN_clear_free(pub_bn)
|
||||
#if swift(>=4.1)
|
||||
pubk.deallocate()
|
||||
#else
|
||||
pubk.deallocate(capacity: curve.keySize)
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
let kAsymmetricCryptoManagerKeyType = kSecAttrKeyTypeECSECPrimeRandom
|
||||
let kAsymmetricCryptoManagerKeySize: Int
|
||||
if curve == .prime256v1 {
|
||||
kAsymmetricCryptoManagerKeySize = 256
|
||||
} else if curve == .secp384r1 {
|
||||
kAsymmetricCryptoManagerKeySize = 384
|
||||
} else {
|
||||
kAsymmetricCryptoManagerKeySize = 521
|
||||
}
|
||||
// parameters
|
||||
let parameters: [String: AnyObject] = [
|
||||
kSecAttrKeyType as String: kAsymmetricCryptoManagerKeyType,
|
||||
kSecAttrKeySizeInBits as String: kAsymmetricCryptoManagerKeySize as AnyObject,
|
||||
]
|
||||
var pubKey, privKey: SecKey?
|
||||
let status = SecKeyGeneratePair(parameters as CFDictionary, &pubKey, &privKey)
|
||||
guard status == 0, let newPubKey = pubKey, let newPrivKey = privKey else {
|
||||
throw ECError.failedNativeKeyCreation
|
||||
}
|
||||
var error: Unmanaged<CFError>? = nil
|
||||
guard let pubBytes = SecKeyCopyExternalRepresentation(newPubKey, &error) else {
|
||||
guard let error = error?.takeRetainedValue() else {
|
||||
throw ECError.failedNativeKeyCreation
|
||||
}
|
||||
throw error
|
||||
}
|
||||
self.pubKeyBytes = pubBytes as Data
|
||||
self.nativeKey = newPrivKey
|
||||
#endif
|
||||
self.pemString = try ECPrivateKey.decodeToPEM(nativeKey: self.nativeKey, curve: self.curve)
|
||||
}
|
||||
|
||||
/// Decode this ECPrivateKey to it's PEM format
|
||||
private static func decodeToPEM(nativeKey: NativeKey, curve: EllipticCurve) throws -> String {
|
||||
#if os(Linux)
|
||||
let asn1Bio = BIO_new(BIO_s_mem())
|
||||
defer { BIO_free_all(asn1Bio) }
|
||||
// The return value of i2d_ECPrivateKey_bio is supposed to be the DER size.
|
||||
// However it is just returning 1 for success.
|
||||
// Since the size is fixed we have just used the known values here.
|
||||
guard i2d_ECPrivateKey_bio(asn1Bio, nativeKey) >= 0 else {
|
||||
throw ECError.failedNativeKeyCreation
|
||||
}
|
||||
let asn1Size: Int32
|
||||
if curve == .prime256v1 {
|
||||
asn1Size = 364
|
||||
} else if curve == .secp384r1 {
|
||||
asn1Size = 510
|
||||
} else {
|
||||
asn1Size = 673
|
||||
}
|
||||
let asn1 = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(asn1Size))
|
||||
let readLength = BIO_read(asn1Bio, asn1, asn1Size)
|
||||
guard readLength > 0 else {
|
||||
throw ECError.failedASN1Decoding
|
||||
}
|
||||
let asn1Data = Data(bytes: asn1, count: Int(readLength))
|
||||
// OpenSSL 1.1 already returns the shortened ANS1 so can return it straight away
|
||||
if readLength < asn1Size - 1 {
|
||||
return ECPrivateKey.derToPrivatePEM(derData: asn1Data)
|
||||
}
|
||||
// Otherwise need to decode ASN1 to get public and private key
|
||||
#if swift(>=4.1)
|
||||
asn1.deallocate()
|
||||
#else
|
||||
asn1.deallocate(capacity: Int(asn1Size))
|
||||
#endif
|
||||
let (result, _) = ASN1.toASN1Element(data: asn1Data)
|
||||
guard case let ASN1.ASN1Element.seq(elements: seq) = result,
|
||||
seq.count > 3,
|
||||
case let ASN1.ASN1Element.bytes(data: privateKeyData) = seq[1]
|
||||
else {
|
||||
throw ECError.failedASN1Decoding
|
||||
}
|
||||
guard case let ASN1.ASN1Element.constructed(tag: _, elem: publicElement) = seq[3],
|
||||
case let ASN1.ASN1Element.bytes(data: publicKeyData) = publicElement
|
||||
else {
|
||||
throw ECError.failedASN1Decoding
|
||||
}
|
||||
#else
|
||||
var error: Unmanaged<CFError>? = nil
|
||||
/*
|
||||
From Apple docs:
|
||||
For an elliptic curve private key, `SecKeyCopyExternalRepresentation` output is formatted as the public key concatenated with the big endian encoding of the secret scalar, or 04 || X || Y || K.
|
||||
*/
|
||||
guard let keyBytes = SecKeyCopyExternalRepresentation(nativeKey, &error) else {
|
||||
guard let error = error?.takeRetainedValue() else {
|
||||
throw ECError.failedNativeKeyCreation
|
||||
}
|
||||
throw error
|
||||
}
|
||||
let keyData = keyBytes as Data
|
||||
let privateKeyData = keyData.dropFirst(curve.keySize)
|
||||
let publicKeyData = Data(count: 1) + keyData.dropLast(keyData.count - curve.keySize)
|
||||
#endif
|
||||
let derData = ECPrivateKey.generateASN1(privateKey: privateKeyData, publicKey: publicKeyData, curve: curve)
|
||||
return ECPrivateKey.derToPrivatePEM(derData: derData)
|
||||
}
|
||||
|
||||
private static func generateASN1(privateKey: Data, publicKey: Data, curve: EllipticCurve) -> Data {
|
||||
var keyHeader: Data
|
||||
// Add the ASN1 header for the private key. The bytes have the following structure:
|
||||
// SEQUENCE (4 elem)
|
||||
// INTEGER 1
|
||||
// OCTET STRING (32 byte) (This is the `privateKeyBytes`)
|
||||
// [0] (1 elem)
|
||||
// OBJECT IDENTIFIER
|
||||
// [1] (1 elem)
|
||||
// BIT STRING (This is the `pubKeyBytes`)
|
||||
if curve == .prime256v1 {
|
||||
keyHeader = Data([0x30, 0x77,
|
||||
0x02, 0x01, 0x01,
|
||||
0x04, 0x20])
|
||||
keyHeader += privateKey
|
||||
keyHeader += Data([0xA0,
|
||||
0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07,
|
||||
0xA1,
|
||||
0x44, 0x03, 0x42])
|
||||
keyHeader += publicKey
|
||||
} else if curve == .secp384r1 {
|
||||
keyHeader = Data([0x30, 0x81, 0xA4,
|
||||
0x02, 0x01, 0x01,
|
||||
0x04, 0x30])
|
||||
keyHeader += privateKey
|
||||
keyHeader += Data([0xA0,
|
||||
0x07, 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22,
|
||||
0xA1,
|
||||
0x64, 0x03, 0x62])
|
||||
keyHeader += publicKey
|
||||
} else {
|
||||
// 521 Private key can be 65 or 66 bytes long
|
||||
if privateKey.count == 65 {
|
||||
keyHeader = Data([0x30, 0x81, 0xDB,
|
||||
0x02, 0x01, 0x01,
|
||||
0x04, 0x41])
|
||||
} else {
|
||||
keyHeader = Data([0x30, 0x81, 0xDC,
|
||||
0x02, 0x01, 0x01,
|
||||
0x04, 0x42])
|
||||
}
|
||||
keyHeader += privateKey
|
||||
keyHeader += Data([0xA0,
|
||||
0x07, 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23,
|
||||
0xA1,
|
||||
0x81, 0x89, 0x03, 0x81, 0x86])
|
||||
keyHeader += publicKey
|
||||
}
|
||||
return keyHeader
|
||||
}
|
||||
|
||||
private static func bytesToNativeKey(privateKeyData: Data, publicKeyData: Data, curve: EllipticCurve) throws -> NativeKey {
|
||||
#if os(Linux)
|
||||
let bigNum = BN_new()
|
||||
defer {
|
||||
BN_free(bigNum)
|
||||
}
|
||||
privateKeyData.withUnsafeBytes({ (privateKeyBytes: UnsafeRawBufferPointer) -> Void in
|
||||
BN_bin2bn(privateKeyBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(privateKeyData.count), bigNum)
|
||||
})
|
||||
let ecKey = EC_KEY_new_by_curve_name(curve.nativeCurve)
|
||||
guard EC_KEY_set_private_key(ecKey, bigNum) == 1 else {
|
||||
EC_KEY_free(ecKey)
|
||||
throw ECError.failedNativeKeyCreation
|
||||
}
|
||||
return ecKey
|
||||
#else
|
||||
let keyData = publicKeyData + privateKeyData
|
||||
var error: Unmanaged<CFError>? = nil
|
||||
guard let secKey = SecKeyCreateWithData(keyData as CFData,
|
||||
[kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
|
||||
kSecAttrKeyClass: kSecAttrKeyClassPrivate] as CFDictionary,
|
||||
&error)
|
||||
else {
|
||||
if let secError = error?.takeRetainedValue() {
|
||||
throw secError
|
||||
} else {
|
||||
throw ECError.failedNativeKeyCreation
|
||||
}
|
||||
}
|
||||
return secKey
|
||||
#endif
|
||||
}
|
||||
|
||||
private static func derToPrivatePEM(derData: Data) -> String {
|
||||
// First convert the DER data to a base64 string...
|
||||
let base64String = derData.base64EncodedString()
|
||||
// Split the string into strings of length 64.
|
||||
let lines = base64String.split(to: 64)
|
||||
// Join those lines with a new line...
|
||||
let joinedLines = lines.joined(separator: "\n")
|
||||
return "-----BEGIN EC PRIVATE KEY-----\n" + joinedLines + "\n-----END EC PRIVATE KEY-----"
|
||||
}
|
||||
}
|
||||
167
Pods/BlueECC/Sources/CryptorECC/ECPublicKey.swift
generated
Normal file
167
Pods/BlueECC/Sources/CryptorECC/ECPublicKey.swift
generated
Normal file
@ -0,0 +1,167 @@
|
||||
// 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
|
||||
|
||||
/**
|
||||
Represents an elliptic curve public key.
|
||||
Supported curves are:
|
||||
- prime256v1
|
||||
- secp384r1
|
||||
- NID_secp521r1
|
||||
You can generate an elliptic curve Key using OpenSSL:
|
||||
https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations#Generating_EC_Keys_and_Parameters
|
||||
|
||||
### Usage Example:
|
||||
```swift
|
||||
let pemKey = """
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEikc5m6C2xtDWeeAeT18WElO37zvF
|
||||
Oz8p4kAlhvgIHN23XIClNESgKVmLgSSq2asqiwdrU5YHbcHFkgdABM1SPA==
|
||||
-----END PUBLIC KEY-----
|
||||
"""
|
||||
let publicKey = try ECPublicKey(key: pemKey)
|
||||
|
||||
let base64Sig = "MEYCIQCvgBLn+tQoBDBR3D2G3485GloYGNxuk6PqR4qjr5GDqAIhAKNvsqvesVBD/MLub/KAyzLLNGtUZyQDxYZj/4vmHwWF"
|
||||
let signature = try ECSignature(asn1: Data(base64Encoded: base64Sig))
|
||||
|
||||
let verified = signature.verify(plaintext: "Hello world", using: publicKey)
|
||||
```
|
||||
*/
|
||||
@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *)
|
||||
public class ECPublicKey {
|
||||
/// A String description of the curve this key was generated from.
|
||||
public let curveId: String
|
||||
|
||||
/// The `EllipticCurve` this key was generated from.
|
||||
public let curve: EllipticCurve
|
||||
#if os(Linux)
|
||||
typealias NativeKey = OpaquePointer?
|
||||
let pubKeyBytes: Data
|
||||
deinit { EC_KEY_free(.make(optional: self.nativeKey)) }
|
||||
#else
|
||||
typealias NativeKey = SecKey
|
||||
#endif
|
||||
let nativeKey: NativeKey
|
||||
|
||||
/// The public key represented as a PEM String.
|
||||
public let pemString: String
|
||||
|
||||
/**
|
||||
Initialize an ECPublicKey from a `.pem` file format.
|
||||
### Usage Example: ###
|
||||
```swift
|
||||
let publicKeyString = """
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEikc5m6C2xtDWeeAeT18WElO37zvF
|
||||
Oz8p4kAlhvgIHN23XIClNESgKVmLgSSq2asqiwdrU5YHbcHFkgdABM1SPA==
|
||||
-----END PUBLIC KEY-----
|
||||
"""
|
||||
let pemKey = try ECPublicKey(key: publicKeyString)
|
||||
```
|
||||
- Parameter key: The elliptic curve public key as a PEM string.
|
||||
- Returns: An ECPublicKey.
|
||||
- Throws: An ECError if the PEM string can't be decoded or is not a valid key.
|
||||
*/
|
||||
public convenience init(key: String) throws {
|
||||
let strippedKey = String(key.filter { !" \n\t\r".contains($0) })
|
||||
let pemComponents = strippedKey.components(separatedBy: "-----")
|
||||
guard pemComponents.count == 5 else {
|
||||
throw ECError.invalidPEMString
|
||||
}
|
||||
guard let der = Data(base64Encoded: pemComponents[2]) else {
|
||||
throw ECError.invalidPEMString
|
||||
}
|
||||
if pemComponents[1] == "BEGINPUBLICKEY" {
|
||||
try self.init(der: der)
|
||||
} else {
|
||||
throw ECError.unknownPEMHeader
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize an ECPublicKey from `.der` file data.
|
||||
/// This is equivalent to a PEM String that has had the "-----BEGIN PUBLIC KEY-----"
|
||||
/// header and footer stripped and been base64 encoded to ASN1 Data.
|
||||
/// - Parameter der: The elliptic curve public key Data.
|
||||
/// - Returns: An ECPublicKey.
|
||||
/// - Throws: An ECError if the Data can't be decoded or is not a valid key.
|
||||
public init(der: Data) throws {
|
||||
pemString = ECPublicKey.derToPublicPEM(derData: der)
|
||||
let (result, _) = ASN1.toASN1Element(data: der)
|
||||
guard case let ASN1.ASN1Element.seq(elements: seq) = result,
|
||||
seq.count > 1,
|
||||
case let ASN1.ASN1Element.seq(elements: ids) = seq[0],
|
||||
ids.count > 1,
|
||||
case let ASN1.ASN1Element.bytes(data: privateKeyID) = ids[1],
|
||||
case let ASN1.ASN1Element.bytes(data: publicKeyData) = seq[1]
|
||||
else {
|
||||
throw ECError.failedASN1Decoding
|
||||
}
|
||||
self.curve = try EllipticCurve.objectToCurve(ObjectIdentifier: privateKeyID)
|
||||
self.curveId = curve.description
|
||||
let keyData = publicKeyData.drop(while: { $0 == 0x00})
|
||||
#if os(Linux)
|
||||
self.pubKeyBytes = keyData
|
||||
let bigNum = BN_new()
|
||||
defer {
|
||||
BN_free(bigNum)
|
||||
}
|
||||
publicKeyData.withUnsafeBytes({ (pubKeyBytes: UnsafeRawBufferPointer) -> Void in
|
||||
BN_bin2bn(pubKeyBytes.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(publicKeyData.count), bigNum)
|
||||
})
|
||||
let ecKey = EC_KEY_new_by_curve_name(curve.nativeCurve)
|
||||
let ecGroup = EC_KEY_get0_group(ecKey)
|
||||
let ecPoint = EC_POINT_new(ecGroup)
|
||||
defer {
|
||||
EC_POINT_free(ecPoint)
|
||||
}
|
||||
EC_POINT_bn2point(ecGroup, bigNum, ecPoint, nil)
|
||||
guard EC_KEY_set_public_key(ecKey, ecPoint) == 1 else {
|
||||
EC_KEY_free(ecKey)
|
||||
throw ECError.failedNativeKeyCreation
|
||||
}
|
||||
self.nativeKey = ecKey
|
||||
#else
|
||||
var error: Unmanaged<CFError>? = nil
|
||||
guard let secKey = SecKeyCreateWithData(keyData as CFData,
|
||||
[kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
|
||||
kSecAttrKeyClass: kSecAttrKeyClassPublic] as CFDictionary,
|
||||
&error)
|
||||
else {
|
||||
if let secError = error?.takeRetainedValue() {
|
||||
throw secError
|
||||
} else {
|
||||
throw ECError.failedNativeKeyCreation
|
||||
}
|
||||
}
|
||||
self.nativeKey = secKey
|
||||
#endif
|
||||
}
|
||||
|
||||
private static func derToPublicPEM(derData: Data) -> String {
|
||||
// First convert the DER data to a base64 string...
|
||||
let base64String = derData.base64EncodedString()
|
||||
// Split the string into strings of length 64.
|
||||
let lines = base64String.split(to: 64)
|
||||
// Join those lines with a new line...
|
||||
let joinedLines = lines.joined(separator: "\n")
|
||||
return "-----BEGIN PUBLIC KEY-----\n" + joinedLines + "\n-----END PUBLIC KEY-----"
|
||||
}
|
||||
}
|
||||
105
Pods/BlueECC/Sources/CryptorECC/ECSignable.swift
generated
Normal file
105
Pods/BlueECC/Sources/CryptorECC/ECSignable.swift
generated
Normal file
@ -0,0 +1,105 @@
|
||||
// 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<UInt8>.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<CFError>? = 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
|
||||
}
|
||||
}
|
||||
202
Pods/BlueECC/Sources/CryptorECC/ECSignature.swift
generated
Normal file
202
Pods/BlueECC/Sources/CryptorECC/ECSignature.swift
generated
Normal file
@ -0,0 +1,202 @@
|
||||
// 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
|
||||
|
||||
/// The signature produced by applying an Elliptic Curve Digital Signature Algorithm to some Plaintext data.
|
||||
/// It consists of two binary unsigned integers, `r` and `s`.
|
||||
@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *)
|
||||
public struct ECSignature {
|
||||
|
||||
// MARK: Signature Values
|
||||
|
||||
/// The r value of the signature.
|
||||
/// The size of the signature data depends on the Secure Hash Algorithm used; it will be 32 bytes of data for SHA256, 48 bytes for SHA384, or 66 bytes for SHA512.
|
||||
public let r: Data
|
||||
|
||||
/// The s value of the signature.
|
||||
/// The size of the signature data depends on the Secure Hash Algorithm used; it will be 32 bytes of data for SHA256, 48 bytes for SHA384, or 66 bytes for SHA512.
|
||||
public let s: Data
|
||||
|
||||
/// The r and s values of the signature encoded into an ASN1 sequence.
|
||||
public let asn1: Data
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
/// Initialize an ECSignature by providing the r and s values.
|
||||
/// These must be the same length and either 32, 48 or 66 bytes (Depending on the curve used).
|
||||
/// - Parameter r: The r value of the signature as raw data.
|
||||
/// - Parameter s: The s value of the signature as raw data.
|
||||
/// - Returns: A new instance of `ECSignature`.
|
||||
/// - Throws: An ECError if the r or s values are not a valid length.
|
||||
public init(r: Data, s: Data) throws {
|
||||
let asn1 = try ECSignature.rsSigToASN1(r: r, s: s)
|
||||
self.r = r
|
||||
self.s = s
|
||||
self.asn1 = asn1
|
||||
}
|
||||
|
||||
/// Initialize an ECSignature by providing an ASN1 encoded sequence containing the r and s values.
|
||||
/// - Parameter asn1: The r and s values of the signature encoded as an ASN1 sequence.
|
||||
/// - Returns: A new instance of `ECSignature`.
|
||||
/// - Throws: An ECError if the ASN1 data can't be decoded.
|
||||
public init(asn1: Data) throws {
|
||||
self.asn1 = asn1
|
||||
let (r,s) = try ECSignature.asn1ToRSSig(asn1: asn1)
|
||||
self.r = r
|
||||
self.s = s
|
||||
}
|
||||
|
||||
// MARK: Verify Signature
|
||||
|
||||
/// Verify the signature for a given String using the provided public key.
|
||||
/// The Data is verified using ECDSA with either SHA256, SHA384 or SHA512, depending on the key's curve.
|
||||
/// - Parameter plaintext: The String that was originally signed to produce the signature.
|
||||
/// - Parameter using ecPublicKey: The ECPublicKey that will be used to verify the plaintext.
|
||||
/// - Returns: true if the plaintext is valid for the provided signature. Otherwise it returns false.
|
||||
public func verify(plaintext: String, using ecPublicKey: ECPublicKey) -> Bool {
|
||||
let plainTextData = Data(plaintext.utf8)
|
||||
return verify(plaintext: plainTextData, using: ecPublicKey)
|
||||
}
|
||||
|
||||
/// Verify the signature for the given Data using the provided public key.
|
||||
/// The Data is verified using ECDSA with either SHA256, SHA384 or SHA512, depending on the key's curve.
|
||||
/// - Parameter plaintext: The Data that was originally signed to produce the signature.
|
||||
/// - Parameter using ecPublicKey: The ECPublicKey that will be used to verify the plaintext.
|
||||
/// - Returns: true if the plaintext is valid for the provided signature. Otherwise it returns false.
|
||||
public func verify(plaintext: Data, using ecPublicKey: ECPublicKey) -> Bool {
|
||||
#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: ecPublicKey.nativeKey)) == 1 else {
|
||||
return false
|
||||
}
|
||||
|
||||
EVP_DigestVerifyInit(md_ctx, nil, .make(optional: ecPublicKey.curve.signingAlgorithm), nil, evp_key)
|
||||
guard plaintext.withUnsafeBytes({ (message: UnsafeRawBufferPointer) -> Int32 in
|
||||
return EVP_DigestUpdate(md_ctx, message.baseAddress?.assumingMemoryBound(to: UInt8.self), plaintext.count)
|
||||
}) == 1 else {
|
||||
return false
|
||||
}
|
||||
let rc = self.asn1.withUnsafeBytes({ (sig: UnsafeRawBufferPointer) -> Int32 in
|
||||
return SSL_EVP_digestVerifyFinal_wrapper(md_ctx, sig.baseAddress?.assumingMemoryBound(to: UInt8.self), self.asn1.count)
|
||||
})
|
||||
return rc == 1
|
||||
#else
|
||||
let hash = ecPublicKey.curve.digest(data: plaintext)
|
||||
|
||||
// Memory storage for error from SecKeyVerifySignature
|
||||
var error: Unmanaged<CFError>? = nil
|
||||
return SecKeyVerifySignature(ecPublicKey.nativeKey,
|
||||
ecPublicKey.curve.signingAlgorithm,
|
||||
hash as CFData,
|
||||
self.asn1 as CFData,
|
||||
&error)
|
||||
#endif
|
||||
}
|
||||
// ASN1 encode the r and s values.
|
||||
static func rsSigToASN1(r: Data, s: Data) throws -> Data {
|
||||
|
||||
guard r.count == s.count, r.count == 32 || r.count == 48 || r.count == 66 else {
|
||||
throw ECError.invalidRSLength
|
||||
}
|
||||
// Convert r,s signature to ASN1 for SecKeyVerifySignature
|
||||
var asnSignature = Data()
|
||||
// r value is first 32 bytes
|
||||
var rSig = r
|
||||
// If first bit is 1, add a 00 byte to mark it as positive for ASN1
|
||||
if rSig[0] == 0 {
|
||||
rSig = rSig.advanced(by: 1)
|
||||
}
|
||||
if rSig[0].leadingZeroBitCount == 0 {
|
||||
rSig = Data(count: 1) + rSig
|
||||
}
|
||||
// r value is last 32 bytes
|
||||
var sSig = s
|
||||
// If first bit is 1, add a 00 byte to mark it as positive for ASN1
|
||||
if sSig[0] == 0 {
|
||||
sSig = sSig.advanced(by: 1)
|
||||
}
|
||||
if sSig[0].leadingZeroBitCount == 0 {
|
||||
sSig = Data(count: 1) + sSig
|
||||
}
|
||||
// Count Byte lengths for ASN1 length bytes
|
||||
let rLengthByte = UInt8(rSig.count)
|
||||
let sLengthByte = UInt8(sSig.count)
|
||||
// total bytes is r + s + rLengthByte + sLengthByte byte + Integer marking bytes
|
||||
let tLengthByte = rLengthByte + sLengthByte + 4
|
||||
// 0x30 means sequence, 0x02 means Integer
|
||||
if tLengthByte > 127 {
|
||||
asnSignature.append(contentsOf: [0x30, 0x81, tLengthByte])
|
||||
} else {
|
||||
asnSignature.append(contentsOf: [0x30, tLengthByte])
|
||||
}
|
||||
asnSignature.append(contentsOf: [0x02, rLengthByte])
|
||||
asnSignature.append(rSig)
|
||||
asnSignature.append(contentsOf: [0x02, sLengthByte])
|
||||
asnSignature.append(sSig)
|
||||
return asnSignature
|
||||
}
|
||||
|
||||
static func asn1ToRSSig(asn1: Data) throws -> (Data, Data) {
|
||||
|
||||
let signatureLength: Int
|
||||
if asn1.count < 96 {
|
||||
signatureLength = 64
|
||||
} else if asn1.count < 132 {
|
||||
signatureLength = 96
|
||||
} else {
|
||||
signatureLength = 132
|
||||
}
|
||||
|
||||
// Parse ASN into just r,s data as defined in:
|
||||
// https://tools.ietf.org/html/rfc7518#section-3.4
|
||||
let (asnSig, _) = ASN1.toASN1Element(data: asn1)
|
||||
guard case let ASN1.ASN1Element.seq(elements: seq) = asnSig,
|
||||
seq.count >= 2,
|
||||
case let ASN1.ASN1Element.bytes(data: rData) = seq[0],
|
||||
case let ASN1.ASN1Element.bytes(data: sData) = seq[1]
|
||||
else {
|
||||
throw ECError.failedASN1Decoding
|
||||
}
|
||||
// ASN adds 00 bytes in front of negative Int to mark it as positive.
|
||||
// These must be removed to make r,a a valid EC signature
|
||||
let trimmedRData: Data
|
||||
let trimmedSData: Data
|
||||
let rExtra = rData.count - signatureLength/2
|
||||
if rExtra < 0 {
|
||||
trimmedRData = Data(count: 1) + rData
|
||||
} else {
|
||||
trimmedRData = rData.dropFirst(rExtra)
|
||||
}
|
||||
let sExtra = sData.count - signatureLength/2
|
||||
if sExtra < 0 {
|
||||
trimmedSData = Data(count: 1) + sData
|
||||
} else {
|
||||
trimmedSData = sData.dropFirst(sExtra)
|
||||
}
|
||||
return (trimmedRData, trimmedSData)
|
||||
}
|
||||
}
|
||||
172
Pods/BlueECC/Sources/CryptorECC/EllipticCurve.swift
generated
Normal file
172
Pods/BlueECC/Sources/CryptorECC/EllipticCurve.swift
generated
Normal file
@ -0,0 +1,172 @@
|
||||
// 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
|
||||
|
||||
/// An extensible list of elliptic curves supported by this repository.
|
||||
@available(macOS 10.13, iOS 11, watchOS 4.0, tvOS 11.0, *)
|
||||
public struct EllipticCurve: Equatable, CustomStringConvertible {
|
||||
|
||||
private let internalRepresentation: InternalRepresentation
|
||||
|
||||
// enum for faster comparisons
|
||||
private enum InternalRepresentation: String {
|
||||
case prime256v1, secp384r1, secp521r1
|
||||
}
|
||||
|
||||
/// A prime256v1 curve.
|
||||
public static let prime256v1 = EllipticCurve.p256
|
||||
|
||||
/// A secp384r1 curve.
|
||||
public static let secp384r1 = EllipticCurve.p384
|
||||
|
||||
/// A secp521r1 curve.
|
||||
public static let secp521r1 = EllipticCurve.p521
|
||||
|
||||
/// Checks if two Curves are equal, required for Equatable protocol.
|
||||
public static func == (lhs: EllipticCurve, rhs: EllipticCurve) -> Bool {
|
||||
return lhs.internalRepresentation == rhs.internalRepresentation
|
||||
}
|
||||
|
||||
/// A String description of the Curve. Required for CustomStringConvertible protocol.
|
||||
public var description: String {
|
||||
return internalRepresentation.rawValue
|
||||
}
|
||||
|
||||
#if os(Linux)
|
||||
typealias CC_LONG = size_t
|
||||
let signingAlgorithm: OpaquePointer?
|
||||
let nativeCurve: Int32
|
||||
let hashEngine = SHA256
|
||||
let hashLength = CC_LONG(SHA256_DIGEST_LENGTH)
|
||||
#else
|
||||
let signingAlgorithm: SecKeyAlgorithm
|
||||
let encryptionAlgorithm = SecKeyAlgorithm.eciesEncryptionStandardVariableIVX963SHA256AESGCM
|
||||
let hashEngine: (_ data: UnsafeRawPointer?, _ len: CC_LONG, _ md: UnsafeMutablePointer<UInt8>?) -> UnsafeMutablePointer<UInt8>?
|
||||
let hashLength: CC_LONG
|
||||
#endif
|
||||
let keySize: Int
|
||||
|
||||
#if os(Linux)
|
||||
/// Secure Hash Algorithm 2 256-bit
|
||||
static let p256 = EllipticCurve(internalRepresentation: .prime256v1,
|
||||
signingAlgorithm: .init(EVP_sha256()),
|
||||
nativeCurve: NID_X9_62_prime256v1,
|
||||
keySize: 65)
|
||||
|
||||
/// Secure Hash Algorithm 2 384-bit
|
||||
static let p384 = EllipticCurve(internalRepresentation: .secp384r1,
|
||||
signingAlgorithm: .init(EVP_sha384()),
|
||||
nativeCurve: NID_secp384r1,
|
||||
keySize: 97)
|
||||
|
||||
/// Secure Hash Algorithm 512-bit
|
||||
static let p521 = EllipticCurve(internalRepresentation: .secp521r1,
|
||||
signingAlgorithm: .init(EVP_sha512()),
|
||||
nativeCurve: NID_secp521r1,
|
||||
keySize: 133)
|
||||
#else
|
||||
/// Secure Hash Algorithm 2 256-bit
|
||||
static let p256 = EllipticCurve(internalRepresentation: .prime256v1,
|
||||
signingAlgorithm: .ecdsaSignatureDigestX962SHA256,
|
||||
hashEngine: CC_SHA256,
|
||||
hashLength: CC_LONG(CC_SHA256_DIGEST_LENGTH),
|
||||
keySize: 65)
|
||||
|
||||
/// Secure Hash Algorithm 2 384-bit
|
||||
static let p384 = EllipticCurve(internalRepresentation: .secp384r1,
|
||||
signingAlgorithm: .ecdsaSignatureDigestX962SHA384,
|
||||
hashEngine: CC_SHA384,
|
||||
hashLength: CC_LONG(CC_SHA384_DIGEST_LENGTH),
|
||||
keySize: 97)
|
||||
|
||||
/// Secure Hash Algorithm 512-bit
|
||||
static let p521 = EllipticCurve(internalRepresentation: .secp521r1,
|
||||
signingAlgorithm: .ecdsaSignatureDigestX962SHA512,
|
||||
hashEngine: CC_SHA512,
|
||||
hashLength: CC_LONG(CC_SHA512_DIGEST_LENGTH),
|
||||
keySize: 133)
|
||||
#endif
|
||||
|
||||
// Select the ECAlgorithm based on the object identifier (OID) extracted from the EC key.
|
||||
static func objectToCurve(ObjectIdentifier: Data) throws -> EllipticCurve {
|
||||
|
||||
if [UInt8](ObjectIdentifier) == [0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07] {
|
||||
// p-256 (e.g: prime256v1, secp256r1) private key
|
||||
return .prime256v1
|
||||
} else if [UInt8](ObjectIdentifier) == [0x2B, 0x81, 0x04, 0x00, 0x22] {
|
||||
// p-384 (e.g: secp384r1) private key
|
||||
return .secp384r1
|
||||
} else if [UInt8](ObjectIdentifier) == [0x2B, 0x81, 0x04, 0x00, 0x23] {
|
||||
// p-521 (e.g: secp521r1) private key
|
||||
return .secp521r1
|
||||
} else {
|
||||
throw ECError.unsupportedCurve
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a digest of the data based on the hashEngine.
|
||||
func digest(data: Data) -> Data {
|
||||
|
||||
var hash = [UInt8](repeating: 0, count: Int(self.hashLength))
|
||||
data.withUnsafeBytes { ptr in
|
||||
guard let baseAddress = ptr.baseAddress else { return }
|
||||
_ = self.hashEngine(baseAddress.assumingMemoryBound(to: UInt8.self), CC_LONG(data.count), &hash)
|
||||
}
|
||||
return Data(hash)
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
|
||||
///
|
||||
/// Split a string to a specified length.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - length: Length of each split string.
|
||||
///
|
||||
/// - Returns: `[String]` containing each string.
|
||||
///
|
||||
func split(to length: Int) -> [String] {
|
||||
|
||||
var result = [String]()
|
||||
var collectedCharacters = [Character]()
|
||||
collectedCharacters.reserveCapacity(length)
|
||||
var count = 0
|
||||
|
||||
for character in self {
|
||||
collectedCharacters.append(character)
|
||||
count += 1
|
||||
if count == length {
|
||||
// Reached the desired length
|
||||
count = 0
|
||||
result.append(String(collectedCharacters))
|
||||
collectedCharacters.removeAll(keepingCapacity: true)
|
||||
}
|
||||
}
|
||||
|
||||
// Append the remainder
|
||||
if !collectedCharacters.isEmpty {
|
||||
result.append(String(collectedCharacters))
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
100
Pods/BlueECC/Sources/CryptorECC/SSLPointerTricks.swift
generated
Normal file
100
Pods/BlueECC/Sources/CryptorECC/SSLPointerTricks.swift
generated
Normal file
@ -0,0 +1,100 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is taken from SwiftNIO open source project
|
||||
//
|
||||
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
|
||||
// Licensed under Apache License v2.0
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// MARK:- Awful code begins here
|
||||
// Hello dear reader. Let me explain what we're doing here.
|
||||
//
|
||||
// From OpenSSL 1.0 to OpenSSL 1.1 one of the major breaking changes was the so-called
|
||||
// "great opaquifiying". Essentially, OpenSSL took all of its public structures and made
|
||||
// them opaque, such that they cannot be introspected from client code. This is a great
|
||||
// forward step, and brings them more in line with modern C library practices.
|
||||
//
|
||||
// However, it's an *enormous* inconvenience from Swift code. This is because the Swift
|
||||
// translation of the C type `SSL_CTX *` changed from `UnsafeMutablePointer<SSL_CTX>` to
|
||||
// `OpaquePointer`.
|
||||
//
|
||||
// This change exists for reasonable enough reasons in Swift land (see
|
||||
// https://forums.swift.org/t/opaque-pointers-in-swift/6875 for a discussion), but
|
||||
// nonetheless causes enormous problems in our codebase.
|
||||
//
|
||||
// Our cheap way out is to make everything an OpaquePointer, and then provide initializers
|
||||
// between OpaquePointer and the typed pointers. This allows us to tolerate either pointer
|
||||
// type in our Swift code by bridging them over to OpaquePointer and back, and lets the
|
||||
// compiler worry about how exactly to make that work.
|
||||
//
|
||||
// Now, in fact, Swift already has initializers between the pointer types. What it does
|
||||
// not have is self-initializers: the ability to create an `OpaquePointer` from an `OpaquePointer`,
|
||||
// or an `UnsafePointer<T>` from an `UnsafePointer<T>`. We add those two initializers here.
|
||||
// We also add a special "make" function that exists to handle the special case of optional pointer
|
||||
// values, which we mostly encounter in the ALPN callbacks.
|
||||
//
|
||||
// The *downside* of this approach is that we totally break the pointer type system. It becomes
|
||||
// trivially possible to alias a pointer of type T to type U through two calls to init. This
|
||||
// is not a thing we want to widely promote. For this reason, these extensions are hidden in
|
||||
// this file, where we can laugh and jeer at them and generally make them feel bad about
|
||||
// themselves.
|
||||
//
|
||||
// Hopefully, in time, these extensions can be removed.
|
||||
|
||||
extension UnsafePointer {
|
||||
init(_ ptr: UnsafePointer<Pointee>) {
|
||||
self = ptr
|
||||
}
|
||||
|
||||
static func make(optional ptr: UnsafePointer<Pointee>?) -> UnsafePointer<Pointee>? {
|
||||
return ptr.map(UnsafePointer<Pointee>.init)
|
||||
}
|
||||
|
||||
static func make(optional ptr: OpaquePointer?) -> UnsafePointer<Pointee>? {
|
||||
return ptr.map(UnsafePointer<Pointee>.init)
|
||||
}
|
||||
}
|
||||
|
||||
extension UnsafeMutablePointer {
|
||||
init(_ ptr: UnsafeMutableRawPointer) {
|
||||
let x = UnsafeMutablePointer<Pointee>(bitPattern: UInt(bitPattern: ptr))!
|
||||
self = x
|
||||
}
|
||||
|
||||
static func make(optional ptr: UnsafeMutablePointer<Pointee>?) -> UnsafeMutablePointer<Pointee>? {
|
||||
return ptr.map(UnsafeMutablePointer<Pointee>.init)
|
||||
}
|
||||
|
||||
static func make(optional ptr: UnsafeMutableRawPointer?) -> UnsafeMutablePointer<Pointee>? {
|
||||
return ptr.map(UnsafeMutablePointer<Pointee>.init)
|
||||
}
|
||||
|
||||
static func make(optional ptr: OpaquePointer?) -> UnsafeMutablePointer<Pointee>? {
|
||||
return ptr.map(UnsafeMutablePointer<Pointee>.init)
|
||||
}
|
||||
}
|
||||
|
||||
extension UnsafeMutableRawPointer {
|
||||
static func make(optional ptr: OpaquePointer?) -> UnsafeMutableRawPointer? {
|
||||
return ptr.map(UnsafeMutableRawPointer.init)
|
||||
}
|
||||
}
|
||||
|
||||
extension OpaquePointer {
|
||||
init(_ ptr: OpaquePointer) {
|
||||
self = ptr
|
||||
}
|
||||
|
||||
static func make(optional ptr: OpaquePointer?) -> OpaquePointer? {
|
||||
return ptr.map(OpaquePointer.init)
|
||||
}
|
||||
|
||||
static func make(optional ptr: UnsafeMutableRawPointer?) -> OpaquePointer? {
|
||||
return ptr.map(OpaquePointer.init)
|
||||
}
|
||||
|
||||
static func make<Pointee>(optional ptr: UnsafeMutablePointer<Pointee>?) -> OpaquePointer? {
|
||||
return ptr.map(OpaquePointer.init)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user