Merge pull request #29 from maxgoedjen/differentalgorithims

Support different EC variants
This commit is contained in:
Max Goedjen 2020-03-09 22:14:57 -07:00 committed by GitHub
commit 8853b2ec6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 84 additions and 25 deletions

View File

@ -65,7 +65,7 @@ extension Agent {
for secret in secrets {
let keyBlob = writer.data(secret: secret)
keyData.append(writer.lengthAndData(of: keyBlob))
let curveData = OpenSSHKeyWriter.Constants.curveType.data(using: .utf8)!
let curveData = writer.curveType(for: secret.algorithm, length: secret.keySize).data(using: .utf8)!
keyData.append(writer.lengthAndData(of: curveData))
}
os_log(.debug, "Agent enumerated %@ identities", secrets.count as NSNumber)
@ -92,13 +92,24 @@ extension Agent {
let derSignature = try store.sign(data: dataToSign, with: secret)
// TODO: Move this
notifier.notify(accessTo: secret)
let curveData = OpenSSHKeyWriter.Constants.curveType.data(using: .utf8)!
let curveData = writer.curveType(for: secret.algorithm, length: secret.keySize).data(using: .utf8)!
// Convert from DER formatted rep to raw (r||s)
let signature = try CryptoKit.P256.Signing.ECDSASignature(derRepresentation: derSignature)
let rawLength = signature.rawRepresentation.count/2
let r = signature.rawRepresentation[0..<rawLength]
let s = signature.rawRepresentation[rawLength...]
let rawRepresentation: Data
switch (secret.algorithm, secret.keySize) {
case (.ellipticCurve, 256):
rawRepresentation = try CryptoKit.P256.Signing.ECDSASignature(derRepresentation: derSignature).rawRepresentation
case (.ellipticCurve, 384):
rawRepresentation = try CryptoKit.P384.Signing.ECDSASignature(derRepresentation: derSignature).rawRepresentation
default:
fatalError()
}
let rawLength = rawRepresentation.count/2
let r = rawRepresentation[0..<rawLength]
let s = rawRepresentation[rawLength...]
var signatureChunk = Data()
signatureChunk.append(writer.lengthAndData(of: r))

View File

@ -6,6 +6,8 @@ public struct AnySecret: Secret {
fileprivate let hashable: AnyHashable
fileprivate let _id: () -> AnyHashable
fileprivate let _name: () -> String
fileprivate let _algorithm: () -> Algorithm
fileprivate let _keySize: () -> Int
fileprivate let _publicKey: () -> Data
public init<T>(_ secret: T) where T: Secret {
@ -14,26 +16,38 @@ public struct AnySecret: Secret {
hashable = secret.hashable
_id = secret._id
_name = secret._name
_algorithm = secret._algorithm
_keySize = secret._keySize
_publicKey = secret._publicKey
} else {
base = secret as Any
self.hashable = secret
_id = { secret.id as AnyHashable }
_name = { secret.name }
_algorithm = { secret.algorithm }
_keySize = { secret.keySize }
_publicKey = { secret.publicKey }
}
}
public var id: AnyHashable {
return _id()
_id()
}
public var name: String {
return _name()
_name()
}
public var algorithm: Algorithm {
_algorithm()
}
public var keySize: Int {
_keySize()
}
public var publicKey: Data {
return _publicKey()
_publicKey()
}
public static func == (lhs: AnySecret, rhs: AnySecret) -> Bool {

View File

@ -1,20 +1,20 @@
import Foundation
import CryptoKit
// For the moment, only supports ecdsa-sha2-nistp256 keys
// For the moment, only supports ecdsa-sha2-nistp256 and ecdsa-sha2-nistp386 keys
public struct OpenSSHKeyWriter {
public init() {
}
public func data<SecretType: Secret>(secret: SecretType) -> Data {
lengthAndData(of: Constants.curveType.data(using: .utf8)!) +
lengthAndData(of: Constants.curveIdentifier.data(using: .utf8)!) +
lengthAndData(of: curveType(for: secret.algorithm, length: secret.keySize).data(using: .utf8)!) +
lengthAndData(of: curveIdentifier(for: secret.algorithm, length: secret.keySize).data(using: .utf8)!) +
lengthAndData(of: secret.publicKey)
}
public func openSSHString<SecretType: Secret>(secret: SecretType) -> String {
"\(Constants.curveType) \(data(secret: secret).base64EncodedString())"
"\(curveType(for: secret.algorithm, length: secret.keySize)) \(data(secret: secret).base64EncodedString())"
}
public func openSSHFingerprint<SecretType: Secret>(secret: SecretType) -> String {
@ -33,14 +33,17 @@ extension OpenSSHKeyWriter {
return Data(bytes: &endian, count: UInt32.bitWidth/8) + data
}
public func readData() {}
}
extension OpenSSHKeyWriter {
public enum Constants {
public static let curveIdentifier = "nistp256"
public static let curveType = "ecdsa-sha2-nistp256"
public func curveIdentifier(for algorithm: Algorithm, length: Int) -> String {
switch algorithm {
case .ellipticCurve:
return "nistp" + String(describing: length)
}
}
public func curveType(for algorithm: Algorithm, length: Int) -> String {
switch algorithm {
case .ellipticCurve:
return "ecdsa-sha2-nistp" + String(describing: length)
}
}
}

View File

@ -1,6 +1,21 @@
public protocol Secret: Identifiable, Hashable {
var name: String { get }
var algorithm: Algorithm { get }
var keySize: Int { get }
var publicKey: Data { get }
}
public enum Algorithm {
case ellipticCurve
public init(secAttr: NSNumber) {
let secAttrString = secAttr.stringValue as CFString
switch secAttrString {
case kSecAttrKeyTypeEC:
self = .ellipticCurve
default:
fatalError()
}
}
}

View File

@ -7,6 +7,8 @@ extension SecureEnclave {
public let id: Data
public let name: String
public let algorithm = Algorithm.ellipticCurve
public let keySize = 256
public let publicKey: Data
}

View File

@ -7,6 +7,8 @@ extension SmartCard {
public let id: Data
public let name: String
public let algorithm: Algorithm
public let keySize: Int
public let publicKey: Data
}

View File

@ -61,7 +61,16 @@ extension SmartCard {
}
let key = untypedSafe as! SecKey
var signError: SecurityError?
guard let signature = SecKeyCreateSignature(key, .ecdsaSignatureMessageX962SHA256, data as CFData, &signError) else {
let signatureAlgorithm: SecKeyAlgorithm
switch (secret.algorithm, secret.keySize) {
case (.ellipticCurve, 256):
signatureAlgorithm = .ecdsaSignatureMessageX962SHA256
case (.ellipticCurve, 384):
signatureAlgorithm = .ecdsaSignatureMessageX962SHA384
default:
fatalError()
}
guard let signature = SecKeyCreateSignature(key, signatureAlgorithm, data as CFData, &signError) else {
throw SigningError(error: signError)
}
return signature as Data
@ -90,9 +99,8 @@ extension SmartCard.Store {
guard let tokenID = tokenID else { return }
let attributes = [
kSecClass: kSecClassKey,
kSecAttrKeyType: kSecAttrKeyTypeEC,
kSecAttrKeySizeInBits: 256,
kSecAttrTokenID: tokenID,
kSecAttrKeyType: kSecAttrKeyTypeEC, // Restrict to EC
kSecReturnRef: true,
kSecMatchLimit: kSecMatchLimitAll,
kSecReturnAttributes: true
@ -103,11 +111,13 @@ extension SmartCard.Store {
let wrapped: [SmartCard.Secret] = typed.map {
let name = $0[kSecAttrLabel] as? String ?? "Unnamed"
let tokenID = $0[kSecAttrApplicationLabel] as! Data
let algorithm = Algorithm(secAttr: $0[kSecAttrKeyType] as! NSNumber)
let keySize = $0[kSecAttrKeySizeInBits] as! Int
let publicKeyRef = $0[kSecValueRef] as! SecKey
let publicKeySecRef = SecKeyCopyPublicKey(publicKeyRef)!
let publicKeyAttributes = SecKeyCopyAttributes(publicKeySecRef) as! [CFString: Any]
let publicKey = publicKeyAttributes[kSecValueData] as! Data
return SmartCard.Secret(id: tokenID, name: name, publicKey: publicKey)
return SmartCard.Secret(id: tokenID, name: name, algorithm: algorithm, keySize: keySize, publicKey: publicKey)
}
secrets.append(contentsOf: wrapped)
}

View File

@ -9,6 +9,8 @@ extension Preview {
let id = UUID().uuidString
let name: String
let algorithm = Algorithm.ellipticCurve
let keySize = 256
let publicKey = UUID().uuidString.data(using: .utf8)!
}