1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftCrypto open source project
//
// Copyright (c) 2019 Apple Inc. and the SwiftCrypto project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.md for the list of SwiftCrypto project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import Foundation
import XCTest
#if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API
// Skip tests that require @testable imports of CryptoKit.
#else
#if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API
@testable import CryptoKit
#else
@_implementationOnly import CCryptoBoringSSL
@testable import Crypto
#endif
extension NISTECDHTests {
func testGroupOpenSSL<PrivKey: NISTECPrivateKey & DiffieHellmanKeyAgreement, Curve: OpenSSLSupportedNISTCurve>(group: ECDHTestGroup, privateKeys: PrivKey.Type, onCurve curve: Curve.Type, file: StaticString = #file, line: UInt = #line) {
for testVector in group.tests {
do {
let pkBytes = try Array(hexString: testVector.publicKey)
let publicKey = try PrivKey.PK(derBytes: pkBytes, curve: Curve.self)
var privateBytes = [UInt8]()
privateBytes = try padKeyIfNecessary(curve: curve, vector: testVector.privateKey)
let privateKey = try PrivKey(rawRepresentation: privateBytes)
let agreement = try unwrap(publicKey as? PrivKey.PublicKey, file: file, line: line)
let result = try privateKey.sharedSecretFromKeyAgreement(with: agreement)
let expectedResult = try Array(hexString: testVector.shared)
XCTAssertEqual(Array(result.ss), Array(expectedResult), file: file, line: line)
} catch ECDHTestErrors.PublicKeyFailure {
XCTAssert(testVector.flags.contains("CompressedPoint") || testVector.result == "invalid" || testVector.flags.contains("InvalidPublic") || testVector.flags.contains("InvalidAsn"), file: file, line: line)
} catch ECDHTestErrors.ParseSPKIFailure {
XCTAssert(testVector.flags.contains("InvalidAsn") || testVector.flags.contains("UnnamedCurve"), file: file, line: line)
} catch {
if testVector.result == "valid" {
XCTAssert(testVector.tcId == 31 || testVector.tcId == 20 || testVector.tcId == 25, file: file, line: line)
}
}
}
}
func testGroupPointOpenSSL<PrivKey: NISTECPrivateKey & DiffieHellmanKeyAgreement, Curve: OpenSSLSupportedNISTCurve>(group: ECDHTestGroup, privateKeys: PrivKey.Type, onCurve curve: Curve.Type, file: StaticString = #file, line: UInt = #line) {
for testVector in group.tests {
do {
let pkBytes = try Array(hexString: testVector.publicKey)
let publicKey: PrivKey.PK
if testVector.flags.contains("CompressedPoint") {
publicKey = try PrivKey.PK(compressedRepresentation: pkBytes)
} else {
publicKey = try PrivKey.PK(x963Representation: pkBytes)
}
var privateBytes = [UInt8]()
privateBytes = try padKeyIfNecessary(curve: curve, vector: testVector.privateKey)
let privateKey = try PrivKey(rawRepresentation: privateBytes)
let agreement = try unwrap(publicKey as? PrivKey.PublicKey, file: file, line: line)
let result = try privateKey.sharedSecretFromKeyAgreement(with: agreement)
let expectedResult = try Array(hexString: testVector.shared)
XCTAssertEqual(Array(result.ss), Array(expectedResult), file: file, line: line)
// If we didn't throw here, assert that the test is not invalid
XCTAssertTrue(testVector.result == "valid" || testVector.result == "acceptable")
} catch {
XCTAssertEqual(testVector.result, "invalid")
}
}
}
private func padKeyIfNecessary<Curve: OpenSSLSupportedNISTCurve>(curve: Curve.Type, vector: String, file: StaticString = #file, line: UInt = #line) throws -> [UInt8] {
// There are a few edge cases here.
//
// First, our raw bytes function requires the
// input buffer to be exactly as long as the curve size.
//
// Second, Wycheproof inputs may be too short or too long with
// leading zeros.
let curveSize = curve.coordinateByteCount
var privateBytes = [UInt8](repeating: 0, count: curveSize)
let hexStringFromVector = (vector.count % 2 == 0) ? vector : "0\(vector)"
let privateKeyVector = try! Array(hexString: hexStringFromVector)
// Input is too long (i.e. we have leading zeros)
if privateKeyVector.count > curveSize {
privateBytes = privateKeyVector.suffix(curveSize)
} else if privateKeyVector.count == curveSize {
privateBytes = privateKeyVector
} else {
// Input is too short
privateBytes.replaceSubrange((privateBytes.count - privateKeyVector.count) ..< privateBytes.count, with: privateKeyVector)
}
return privateBytes
}
}
extension NISTECPublicKey {
/// Creates the given EC public key using the DER encoding of the key.
init<Curve: OpenSSLSupportedNISTCurve>(derBytes: [UInt8], curve: Curve.Type = Curve.self) throws {
// Bad news everybody. Using the EC DER parsing from OpenSSL limits our ability to tell the difference
// between an invalid SPKI layout (which we don't care about, as the production library doesn't support DER-encoded
// EC keys) and a SPKI layout that is syntactically valid but doesn't represent a valid point on the curve. We _do_
// care about passing this into the production library.
//
// This means we've only one option: we have to implement "just enough" ASN.1.
var derBytes = derBytes[...]
let spki = try ASN1SubjectPublicKeyInfo(fromASN1: &derBytes)
guard derBytes.count == 0, spki.algorithm.algorithm == ASN1ObjectIdentifier.AlgorithmIdentifier.idEcPublicKey else {
throw ECDHTestErrors.ParseSPKIFailure
}
// Ok, the bitstring we are holding is the X963 representation of the public key. Try to create it.
do {
try self.init(x963Representation: spki.subjectPublicKey)
} catch {
throw ECDHTestErrors.PublicKeyFailure
}
}
}
#endif // CRYPTO_IN_SWIFTPM
|