similar refactor for responses

This commit is contained in:
blinkasdf 2017-02-07 17:25:44 -07:00
Родитель cd25a91f00
Коммит 4e183bfb94
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E9C423BE17EFEE70
16 изменённых файлов: 439 добавлений и 395 удалений

Просмотреть файл

@ -50,6 +50,14 @@ public struct AuthenticationRequest: RawConvertible {
self.header = CommandHeader(ins: .Authenticate, p1: control.rawValue, dataLength: body.count)
self.trailer = CommandTrailer(noBody: false)
}
}
extension AuthenticationRequest: Command {
init(header: CommandHeader, body: Data, trailer: CommandTrailer) {
self.header = header
self.body = body
self.trailer = trailer
}
func validateBody() throws {
// Make sure it's at least long enough to have key-handle length.
@ -66,11 +74,3 @@ public struct AuthenticationRequest: RawConvertible {
}
}
}
extension AuthenticationRequest: CommandProtocol {
init(header: CommandHeader, body: Data, trailer: CommandTrailer) {
self.header = header
self.body = body
self.trailer = trailer
}
}

Просмотреть файл

@ -8,52 +8,55 @@
import Foundation
public struct AuthenticationResponse: MessageProtocol {
public let userPresence: UInt8
public let counter: UInt32
public let signature: Data
public let status: ResponseStatus
public struct AuthenticationResponse: RawConvertible {
let body: Data
let trailer: ResponseStatus
public var raw: Data {
public var userPresence: UInt8 {
return body[0]
}
public var counter: UInt32 {
let lowerBound = MemoryLayout<UInt8>.size
let upperBound = lowerBound + MemoryLayout<UInt32>.size
let data = body.subdata(in: lowerBound..<upperBound)
return data.withUnsafeBytes { (ptr: UnsafePointer<UInt32>) -> UInt32 in
return ptr.pointee.bigEndian
}
}
public var signature: Data {
let lowerBound = MemoryLayout<UInt8>.size + MemoryLayout<UInt32>.size
let upperBound = body.count
return body.subdata(in: lowerBound..<upperBound)
}
public init(userPresence: UInt8, counter: UInt32, signature: Data) {
let writer = DataWriter()
writer.write(userPresence)
writer.write(counter)
writer.writeData(signature)
writer.write(status)
return writer.buffer
}
public init(raw: Data) throws {
let reader = DataReader(data: raw)
do {
userPresence = try reader.read()
counter = try reader.read()
signature = try reader.readData(reader.remaining - 2)
status = try reader.read()
} catch DataReaderError.End {
throw ResponseStatus.WrongLength
}
if reader.remaining > 0 {
throw ResponseStatus.WrongLength
}
}
public init(userPresence u: UInt8, counter c: UInt32, signature s: Data) {
userPresence = u
counter = c
signature = s
status = .NoError
}
public func debug() {
print("AuthenticationResponse:")
print(String(format: " User presence: 0x%02x", userPresence))
print(String(format: " Counter: 0x%08x", counter))
print( " Signature: \(signature.base64EncodedString())")
print( " Status: \(status)")
body = writer.buffer
trailer = .NoError
}
}
extension AuthenticationResponse: Response {
init(body: Data, trailer: ResponseStatus) {
self.body = body
self.trailer = trailer
}
func validateBody() throws {
// TODO: minimum signature size?
if body.count < MemoryLayout<UInt8>.size + MemoryLayout<UInt32>.size + 1 {
throw ResponseError.BadSize
}
if trailer != .NoError {
throw ResponseError.BadStatus
}
}
}

Просмотреть файл

@ -1,5 +1,5 @@
//
// CommandProtocol.swift
// Command.swift
// SoftU2FTool
//
// Created by Benjamin P Toews on 2/7/17.
@ -14,7 +14,7 @@ public func commandType(raw: Data) throws -> CommandCode {
return header.ins
}
protocol CommandProtocol {
protocol Command {
var header: CommandHeader { get }
var body: Data { get }
var trailer: CommandTrailer { get }
@ -25,7 +25,7 @@ protocol CommandProtocol {
}
// Implement RawConvertible
extension CommandProtocol {
extension Command {
public var raw: Data {
let writer = DataWriter()
writer.writeData(header.raw)

Просмотреть файл

@ -16,20 +16,6 @@ let U2F_EC_POINT_SIZE = ((U2F_EC_KEY_SIZE * 2) + 1) // Size of EC point
let MaxResponseSize = Int(UInt16.max) + 1
// ISO7816-4
public enum ResponseStatus: UInt16, EndianEnumProtocol, Error {
public typealias RawValue = UInt16
case NoError = 0x9000
case WrongData = 0x6A80
case ConditionsNotSatisfied = 0x6985
case CommandNotAllowed = 0x6986
case InsNotSupported = 0x6D00
case WrongLength = 0x6700
case ClassNotSupported = 0x6E00
case OtherError = 0x6F00 // "No precise diagnosis"
}
public enum CommandClass: UInt8 {
case Reserved = 0x00
}
@ -49,3 +35,17 @@ public enum Control: UInt8 {
// Used internally.
case Invalid = 0xFF
}
// ISO7816-4
public enum ResponseStatus: UInt16, EndianEnumProtocol, Error {
public typealias RawValue = UInt16
case NoError = 0x9000
case WrongData = 0x6A80
case ConditionsNotSatisfied = 0x6985
case CommandNotAllowed = 0x6986
case InsNotSupported = 0x6D00
case WrongLength = 0x6700
case ClassNotSupported = 0x6E00
case OtherError = 0x6F00 // "No precise diagnosis"
}

Просмотреть файл

@ -8,32 +8,25 @@
import Foundation
public struct ErrorResponse: MessageProtocol {
public let status: ResponseStatus
public var raw: Data {
let writer = DataWriter()
writer.write(status)
return writer.buffer
}
public struct ErrorResponse: RawConvertible {
let body: Data
let trailer: ResponseStatus
public init(status s: ResponseStatus) {
status = s
}
public init(raw: Data) throws {
let reader = DataReader(data: raw)
status = try reader.read()
if reader.remaining > 0 {
throw ResponseStatus.WrongLength
}
}
public func debug() {
print("Error Response:")
print(" Status: \(status)")
body = Data()
trailer = s
}
}
extension ErrorResponse: Response {
init(body: Data, trailer: ResponseStatus) {
self.body = body
self.trailer = trailer
}
func validateBody() throws {
if body.count != 0 {
throw ResponseError.BadSize
}
}
}

Просмотреть файл

@ -1,16 +0,0 @@
//
// APDUMessage.swift
// SecurityKeyBLE
//
// Created by Benjamin P Toews on 9/12/16.
// Copyright © 2017 GitHub. All rights reserved.
//
import Foundation
public protocol MessageProtocol {
var raw: Data { get }
init(raw: Data) throws
func debug()
}

Просмотреть файл

@ -34,6 +34,14 @@ public struct RegisterRequest: RawConvertible {
self.header = CommandHeader(ins: .Register, dataLength: body.count)
self.trailer = CommandTrailer(noBody: false)
}
}
extension RegisterRequest: Command {
init(header: CommandHeader, body: Data, trailer: CommandTrailer) {
self.header = header
self.body = body
self.trailer = trailer
}
func validateBody() throws {
if body.count != U2F_CHAL_SIZE + U2F_APPID_SIZE {
@ -41,11 +49,3 @@ public struct RegisterRequest: RawConvertible {
}
}
}
extension RegisterRequest: CommandProtocol {
init(header: CommandHeader, body: Data, trailer: CommandTrailer) {
self.header = header
self.body = body
self.trailer = trailer
}
}

Просмотреть файл

@ -9,81 +9,120 @@
import Foundation
import SelfSignedCertificate
public struct RegisterResponse: MessageProtocol {
// Parse a DER formatted X509 certificate from the beginning of a datum and return its length.
static func certLength(fromData d: Data) throws -> Int {
public struct RegisterResponse: RawConvertible {
let body: Data
let trailer: ResponseStatus
public var publicKey: Data {
return body.subdata(in: publicKeyRange)
}
var keyHandleLength: Int {
return Int(body.subdata(in: keyHandleLengthRange)[0])
}
public var keyHandle: Data {
return body.subdata(in: keyHandleRange)
}
public var certificate: Data {
return body.subdata(in: certificateRange)
}
public var signature: Data {
return body.subdata(in: signatureRange)
}
var publicKeyRange: Range<Int> {
let lowerBound = 0
let upperBound = lowerBound + U2F_EC_POINT_SIZE
return lowerBound..<upperBound
}
var keyHandleLengthRange: Range<Int> {
let lowerBound = publicKeyRange.upperBound
let upperBound = lowerBound + MemoryLayout<UInt8>.size
return lowerBound..<upperBound
}
var keyHandleRange: Range<Int> {
let lowerBound = keyHandleLengthRange.upperBound
let upperBound = lowerBound + keyHandleLength
return lowerBound..<upperBound
}
var certificateSize: Int {
let remainingRange: Range<Int> = keyHandleRange.upperBound..<body.count
let remaining = body.subdata(in: remainingRange)
var size: Int = 0
if SelfSignedCertificate.parseX509(d, consumed: &size) {
if SelfSignedCertificate.parseX509(remaining, consumed: &size) {
return size
} else {
throw ResponseStatus.OtherError
return 0
}
}
public let publicKey: Data
public let keyHandle: Data
public let certificate: Data
public let signature: Data
public let status: ResponseStatus
var certificateRange: Range<Int> {
let lowerBound = keyHandleRange.upperBound
let upperBound = lowerBound + certificateSize
return lowerBound..<upperBound
}
public var raw: Data {
var signatureRange: Range<Int> {
let lowerBound = certificateRange.upperBound
let upperBound = body.count
return lowerBound..<upperBound
}
public init(publicKey: Data, keyHandle: Data, certificate: Data, signature: Data) {
let writer = DataWriter()
writer.write(UInt8(0x05))
writer.writeData(publicKey)
writer.write(UInt8(keyHandle.count))
writer.writeData(keyHandle)
writer.writeData(certificate)
writer.writeData(signature)
writer.write(status)
return writer.buffer
}
public init(raw: Data) throws {
let reader = DataReader(data: raw)
do {
// reserved byte
let _: UInt8 = try reader.read()
publicKey = try reader.readData(U2F_EC_POINT_SIZE)
let khLen: UInt8 = try reader.read()
keyHandle = try reader.readData(Int(khLen))
// peek at cert to figure out its length
let certLen = try RegisterResponse.certLength(fromData: reader.rest)
certificate = try reader.readData(certLen)
signature = try reader.readData(reader.remaining - 2)
status = try reader.read()
} catch DataReaderError.End {
throw ResponseStatus.WrongLength
}
if reader.remaining > 0 {
throw ResponseStatus.WrongLength
}
}
public init(publicKey pk: Data, keyHandle kh: Data, certificate cert: Data, signature sig: Data) {
publicKey = pk
keyHandle = kh
certificate = cert
signature = sig
status = .NoError
}
public func debug() {
print("Registration Response:")
print( " Reserved: 0x05")
print( " Public key: \(publicKey.base64EncodedString())")
print(String(format: " KH Len: 0x%02x", keyHandle.count))
print( " Key Handle: \(keyHandle.base64EncodedString())")
print( " Certificate: \(certificate.base64EncodedString())")
print( " Signature: \(signature.base64EncodedString())")
print( " Status: \(status)")
body = writer.buffer
trailer = .NoError
}
}
extension RegisterResponse: Response {
init(body: Data, trailer: ResponseStatus) {
self.body = body
self.trailer = trailer
}
func validateBody() throws {
// Check that we at least have key-handle length.
var min = U2F_EC_POINT_SIZE + MemoryLayout<UInt8>.size
if body.count < min {
throw ResponseError.BadSize
}
// Check that we at least have one byte of cert.
// TODO: minimum cert size?
min += keyHandleLength + 1
if body.count < min {
throw ResponseError.BadSize
}
// Check that cert is parsable.
if certificateSize == 0 {
throw ResponseError.BadCertificate
}
// Check that we at least have one byte of signature.
// TODO: minimum signature size?
min += certificateSize + 1
if body.count < min {
throw ResponseError.BadSize
}
if trailer != .NoError {
throw ResponseError.BadStatus
}
}
}

52
APDU/Response.swift Normal file
Просмотреть файл

@ -0,0 +1,52 @@
//
// Response.swift
// SoftU2FTool
//
// Created by Benjamin P Toews on 2/7/17.
// Copyright © 2017 GitHub. All rights reserved.
//
import Foundation
enum ResponseError: Error {
case BadSize
case BadStatus
case BadCertificate
}
protocol Response {
var body: Data { get }
var trailer: ResponseStatus { get }
init(body: Data, trailer: ResponseStatus)
func validateBody() throws
}
// Implement RawConvertible
extension Response {
public var raw: Data {
let writer = DataWriter()
writer.writeData(body)
writer.write(trailer)
return writer.buffer
}
public init(raw: Data) throws {
let reader = DataReader(data: raw)
let body = try reader.readData(reader.remaining - 2)
let trailer: ResponseStatus = try reader.read()
self.init(body: body, trailer: trailer)
}
// For testing with libu2f-host
public init(raw: Data, bodyOnly: Bool) throws {
if bodyOnly {
self.init(body: raw, trailer: .NoError)
} else {
try self.init(raw: raw)
}
}
}

Просмотреть файл

@ -13,12 +13,6 @@ public struct VersionRequest: RawConvertible {
let body: Data
let trailer: CommandTrailer
func validateBody() throws {
if body.count > 0 {
throw ResponseStatus.WrongLength
}
}
init() {
self.header = CommandHeader(ins: .Version, dataLength: 0)
self.body = Data()
@ -26,10 +20,16 @@ public struct VersionRequest: RawConvertible {
}
}
extension VersionRequest: CommandProtocol {
extension VersionRequest: Command {
init(header: CommandHeader, body: Data, trailer: CommandTrailer) {
self.header = header
self.body = body
self.trailer = trailer
}
func validateBody() throws {
if body.count > 0 {
throw ResponseStatus.WrongLength
}
}
}

Просмотреть файл

@ -8,44 +8,33 @@
import Foundation
public struct VersionResponse: MessageProtocol {
public let version: String
public let status: ResponseStatus
public struct VersionResponse: RawConvertible {
let body: Data
let trailer: ResponseStatus
public var raw: Data {
let writer = DataWriter()
writer.writeData(version.data(using: .utf8)!)
writer.write(status)
return writer.buffer
public var version: String {
return String(data: body, encoding: .utf8) ?? ""
}
public init(version v: String) {
version = v
status = .NoError
}
public init(raw: Data) throws {
let reader = DataReader(data: raw)
let vData = try reader.readData(reader.remaining - 2)
if let v = String(data: vData, encoding: .utf8) {
version = v
} else {
throw ResponseStatus.WrongLength
}
status = try reader.read()
if reader.remaining > 0 {
throw ResponseStatus.WrongLength
}
}
public func debug() {
print("Version Response:")
print(" Version: \(version)")
print(" Status: \(status)")
public init(version: String) {
body = version.data(using: .utf8)!
trailer = .NoError
}
}
extension VersionResponse: Response {
init(body: Data, trailer: ResponseStatus) {
self.body = body
self.trailer = trailer
}
func validateBody() throws {
if version.lengthOfBytes(using: .utf8) < 1 {
throw ResponseError.BadSize
}
if trailer != .NoError {
throw ResponseError.BadStatus
}
}
}

Просмотреть файл

@ -23,7 +23,7 @@ class ResponseTests: XCTestCase {
XCTAssertEqual(r2.keyHandle, r.keyHandle)
XCTAssertEqual(r2.certificate, r.certificate)
XCTAssertEqual(r2.signature, r.signature)
XCTAssertEqual(r2.status, r.status)
XCTAssertEqual(r2.trailer, r.trailer)
XCTAssertEqual(r.raw, r2.raw)
}
@ -39,7 +39,7 @@ class ResponseTests: XCTestCase {
let r2 = try VersionResponse(raw: r.raw)
XCTAssertEqual(r.version, r2.version)
XCTAssertEqual(r.status, r2.status)
XCTAssertEqual(r.trailer, r2.trailer)
XCTAssertEqual(r.raw, r2.raw)
}
@ -47,7 +47,7 @@ class ResponseTests: XCTestCase {
let r = ErrorResponse(status: .ConditionsNotSatisfied)
let r2 = try ErrorResponse(raw: r.raw)
XCTAssertEqual(r.status, r2.status)
XCTAssertEqual(r.trailer, r2.trailer)
XCTAssertEqual(r.raw, r2.raw)
}
}

Просмотреть файл

@ -41,7 +41,6 @@
F738F5171E4A2CF6005680A2 /* VersionResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F50A1E4A2CF6005680A2 /* VersionResponse.swift */; };
F738F5201E4A2DDD005680A2 /* CommandHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F51B1E4A2DDD005680A2 /* CommandHeader.swift */; };
F738F5211E4A2DDD005680A2 /* CommandTrailer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F51C1E4A2DDD005680A2 /* CommandTrailer.swift */; };
F738F5221E4A2DDD005680A2 /* MessageProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F51D1E4A2DDD005680A2 /* MessageProtocol.swift */; };
F738F5281E4A2E57005680A2 /* DataReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5251E4A2E57005680A2 /* DataReader.swift */; };
F738F5291E4A2E57005680A2 /* DataWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5261E4A2E57005680A2 /* DataWriter.swift */; };
F738F52A1E4A2E57005680A2 /* Endian.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5271E4A2E57005680A2 /* Endian.swift */; };
@ -51,7 +50,6 @@
F738F5411E4A2ED6005680A2 /* ResponseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F53D1E4A2ED6005680A2 /* ResponseTests.swift */; };
F738F5431E4A2F91005680A2 /* CommandHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F51B1E4A2DDD005680A2 /* CommandHeader.swift */; };
F738F5441E4A2F91005680A2 /* CommandTrailer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F51C1E4A2DDD005680A2 /* CommandTrailer.swift */; };
F738F5451E4A2F91005680A2 /* MessageProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F51D1E4A2DDD005680A2 /* MessageProtocol.swift */; };
F738F5481E4A2F91005680A2 /* AuthenticationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5041E4A2CF6005680A2 /* AuthenticationRequest.swift */; };
F738F5491E4A2F91005680A2 /* AuthenticationResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5051E4A2CF6005680A2 /* AuthenticationResponse.swift */; };
F738F54A1E4A2F91005680A2 /* ErrorResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5061E4A2CF6005680A2 /* ErrorResponse.swift */; };
@ -77,15 +75,17 @@
F738F5871E4A3C09005680A2 /* DataReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5851E4A3C09005680A2 /* DataReaderTests.swift */; };
F738F5881E4A3C09005680A2 /* DataWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5861E4A3C09005680A2 /* DataWriterTests.swift */; };
F738F58A1E4A3C21005680A2 /* TestUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = F738F5891E4A3C21005680A2 /* TestUtil.swift */; };
F7B5DBAD1E4A5CED00E5ABD4 /* CommandProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBAC1E4A5CED00E5ABD4 /* CommandProtocol.swift */; };
F7B5DBAD1E4A5CED00E5ABD4 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBAC1E4A5CED00E5ABD4 /* Command.swift */; };
F7B5DBAF1E4A815700E5ABD4 /* RawConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBAE1E4A815700E5ABD4 /* RawConvertible.swift */; };
F7B5DBB01E4A827000E5ABD4 /* RawConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBAE1E4A815700E5ABD4 /* RawConvertible.swift */; };
F7B5DBB11E4A827400E5ABD4 /* CommandProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBAC1E4A5CED00E5ABD4 /* CommandProtocol.swift */; };
F7B5DBB11E4A827400E5ABD4 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBAC1E4A5CED00E5ABD4 /* Command.swift */; };
F7B5DBB31E4A82EB00E5ABD4 /* MessagePart.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBB21E4A82EB00E5ABD4 /* MessagePart.swift */; };
F7B5DBB41E4A83D900E5ABD4 /* MessagePart.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBB21E4A82EB00E5ABD4 /* MessagePart.swift */; };
F7B5DBC11E4A85EB00E5ABD4 /* AuthenticationRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBBD1E4A85EB00E5ABD4 /* AuthenticationRequestTests.swift */; };
F7B5DBC21E4A85EB00E5ABD4 /* RegisterRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBBE1E4A85EB00E5ABD4 /* RegisterRequestTests.swift */; };
F7B5DBC31E4A85EB00E5ABD4 /* VersionRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBBF1E4A85EB00E5ABD4 /* VersionRequestTests.swift */; };
F7B5DBC91E4A8CF000E5ABD4 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBC81E4A8CF000E5ABD4 /* Response.swift */; };
F7B5DBCA1E4A8CF000E5ABD4 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B5DBC81E4A8CF000E5ABD4 /* Response.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -226,7 +226,6 @@
F738F50A1E4A2CF6005680A2 /* VersionResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionResponse.swift; sourceTree = "<group>"; };
F738F51B1E4A2DDD005680A2 /* CommandHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandHeader.swift; sourceTree = "<group>"; };
F738F51C1E4A2DDD005680A2 /* CommandTrailer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandTrailer.swift; sourceTree = "<group>"; };
F738F51D1E4A2DDD005680A2 /* MessageProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageProtocol.swift; sourceTree = "<group>"; };
F738F5251E4A2E57005680A2 /* DataReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DataReader.swift; path = Data/DataReader.swift; sourceTree = "<group>"; };
F738F5261E4A2E57005680A2 /* DataWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DataWriter.swift; path = Data/DataWriter.swift; sourceTree = "<group>"; };
F738F5271E4A2E57005680A2 /* Endian.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Endian.swift; path = Data/Endian.swift; sourceTree = "<group>"; };
@ -248,12 +247,13 @@
F738F5851E4A3C09005680A2 /* DataReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DataReaderTests.swift; path = DataTests/DataReaderTests.swift; sourceTree = "<group>"; };
F738F5861E4A3C09005680A2 /* DataWriterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DataWriterTests.swift; path = DataTests/DataWriterTests.swift; sourceTree = "<group>"; };
F738F5891E4A3C21005680A2 /* TestUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestUtil.swift; sourceTree = "<group>"; };
F7B5DBAC1E4A5CED00E5ABD4 /* CommandProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandProtocol.swift; sourceTree = "<group>"; };
F7B5DBAC1E4A5CED00E5ABD4 /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
F7B5DBAE1E4A815700E5ABD4 /* RawConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawConvertible.swift; sourceTree = "<group>"; };
F7B5DBB21E4A82EB00E5ABD4 /* MessagePart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagePart.swift; sourceTree = "<group>"; };
F7B5DBBD1E4A85EB00E5ABD4 /* AuthenticationRequestTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationRequestTests.swift; sourceTree = "<group>"; };
F7B5DBBE1E4A85EB00E5ABD4 /* RegisterRequestTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegisterRequestTests.swift; sourceTree = "<group>"; };
F7B5DBBF1E4A85EB00E5ABD4 /* VersionRequestTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionRequestTests.swift; sourceTree = "<group>"; };
F7B5DBC81E4A8CF000E5ABD4 /* Response.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -404,17 +404,17 @@
F738F5521E4A2FF0005680A2 /* Constants.swift */,
F7B5DBAE1E4A815700E5ABD4 /* RawConvertible.swift */,
F7B5DBB21E4A82EB00E5ABD4 /* MessagePart.swift */,
F7B5DBAC1E4A5CED00E5ABD4 /* CommandProtocol.swift */,
F7B5DBAC1E4A5CED00E5ABD4 /* Command.swift */,
F738F51B1E4A2DDD005680A2 /* CommandHeader.swift */,
F738F51C1E4A2DDD005680A2 /* CommandTrailer.swift */,
F738F51D1E4A2DDD005680A2 /* MessageProtocol.swift */,
F7B5DBC81E4A8CF000E5ABD4 /* Response.swift */,
F738F5041E4A2CF6005680A2 /* AuthenticationRequest.swift */,
F738F5051E4A2CF6005680A2 /* AuthenticationResponse.swift */,
F738F5061E4A2CF6005680A2 /* ErrorResponse.swift */,
F738F5071E4A2CF6005680A2 /* RegisterRequest.swift */,
F738F5081E4A2CF6005680A2 /* RegisterResponse.swift */,
F738F5091E4A2CF6005680A2 /* VersionRequest.swift */,
F738F50A1E4A2CF6005680A2 /* VersionResponse.swift */,
F738F5061E4A2CF6005680A2 /* ErrorResponse.swift */,
F738F4F31E4A2CCC005680A2 /* APDU.h */,
F738F4F41E4A2CCC005680A2 /* Info.plist */,
);
@ -808,8 +808,9 @@
files = (
F738F5281E4A2E57005680A2 /* DataReader.swift in Sources */,
F738F5531E4A2FF0005680A2 /* Constants.swift in Sources */,
F7B5DBAD1E4A5CED00E5ABD4 /* CommandProtocol.swift in Sources */,
F7B5DBAD1E4A5CED00E5ABD4 /* Command.swift in Sources */,
F7B5DBB31E4A82EB00E5ABD4 /* MessagePart.swift in Sources */,
F7B5DBC91E4A8CF000E5ABD4 /* Response.swift in Sources */,
F738F5151E4A2CF6005680A2 /* RegisterResponse.swift in Sources */,
F738F5201E4A2DDD005680A2 /* CommandHeader.swift in Sources */,
F738F5141E4A2CF6005680A2 /* RegisterRequest.swift in Sources */,
@ -819,7 +820,6 @@
F738F5111E4A2CF6005680A2 /* AuthenticationRequest.swift in Sources */,
F738F5291E4A2E57005680A2 /* DataWriter.swift in Sources */,
F738F5171E4A2CF6005680A2 /* VersionResponse.swift in Sources */,
F738F5221E4A2DDD005680A2 /* MessageProtocol.swift in Sources */,
F738F5131E4A2CF6005680A2 /* ErrorResponse.swift in Sources */,
F738F5121E4A2CF6005680A2 /* AuthenticationResponse.swift in Sources */,
F738F5161E4A2CF6005680A2 /* VersionRequest.swift in Sources */,
@ -839,14 +839,14 @@
F7B5DBB01E4A827000E5ABD4 /* RawConvertible.swift in Sources */,
F7B5DBB41E4A83D900E5ABD4 /* MessagePart.swift in Sources */,
F738F5881E4A3C09005680A2 /* DataWriterTests.swift in Sources */,
F7B5DBB11E4A827400E5ABD4 /* CommandProtocol.swift in Sources */,
F7B5DBB11E4A827400E5ABD4 /* Command.swift in Sources */,
F738F5411E4A2ED6005680A2 /* ResponseTests.swift in Sources */,
F738F5431E4A2F91005680A2 /* CommandHeader.swift in Sources */,
F738F5441E4A2F91005680A2 /* CommandTrailer.swift in Sources */,
F7B5DBCA1E4A8CF000E5ABD4 /* Response.swift in Sources */,
F738F5541E4A2FF0005680A2 /* Constants.swift in Sources */,
F738F5401E4A2ED6005680A2 /* CommandTrailerTests.swift in Sources */,
F7B5DBC31E4A85EB00E5ABD4 /* VersionRequestTests.swift in Sources */,
F738F5451E4A2F91005680A2 /* MessageProtocol.swift in Sources */,
F738F5501E4A2F9E005680A2 /* DataWriter.swift in Sources */,
F738F5491E4A2F91005680A2 /* AuthenticationResponse.swift in Sources */,
F738F5481E4A2F91005680A2 /* AuthenticationRequest.swift in Sources */,

Просмотреть файл

@ -46,45 +46,33 @@ class U2FAuthenticator {
func installMsgHandler() {
u2fhid.handle(.Msg) { (_ msg: softu2f_hid_message) -> Bool in
let data = msg.data.takeUnretainedValue() as Data
let cmd: APDU.Command
do {
cmd = try APDU.Command(raw: data)
let ins = try APDU.commandType(raw: data)
switch ins {
case .Register:
try self.handleRegisterRequest(data, cid: msg.cid)
case .Authenticate:
try self.handleAuthenticationRequest(data, cid: msg.cid)
case .Version:
try self.handleVersionRequest(data, cid: msg.cid)
default:
self.sendError(status: .InsNotSupported, cid: msg.cid)
}
} catch let err as APDU.ResponseStatus {
self.sendError(status: err, cid: msg.cid)
return true
} catch {
self.sendError(status: .OtherError, cid: msg.cid)
return true
}
print("↓↓↓↓↓ Received message ↓↓↓↓↓")
cmd.debug()
print("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\n")
if let req = cmd.registerRequest {
self.handleRegisterRequest(req, cid: msg.cid)
return true
}
if let req = cmd.authenticationRequest {
if let control = APDU.Control(rawValue: cmd.header.p1) {
self.handleAuthenticationRequest(req, control: control, cid: msg.cid)
return true
}
}
if let req = cmd.versionRequest {
self.handleVersionRequest(req, cid: msg.cid)
return true
}
self.sendError(status: .OtherError, cid: msg.cid)
return true
}
}
func handleRegisterRequest(_ req: APDU.RegisterRequest, cid: UInt32) {
func handleRegisterRequest(_ raw: Data, cid: UInt32) throws {
let req = try APDU.RegisterRequest(raw: raw)
let facet = KnownFacets[req.applicationParameter]
let notification = UserPresence.Notification.Register(facet: facet)
@ -127,13 +115,15 @@ class U2FAuthenticator {
}
}
func handleAuthenticationRequest(_ req: APDU.AuthenticationRequest, control: APDU.Control, cid: UInt32) {
func handleAuthenticationRequest(_ raw: Data, cid: UInt32) throws {
let req = try APDU.AuthenticationRequest(raw: raw)
guard let reg = U2FRegistration(keyHandle: req.keyHandle, applicationParameter: req.applicationParameter) else {
sendError(status: .WrongData, cid: cid)
return
}
if control == .CheckOnly {
if req.control == .CheckOnly {
// success -> error response. It's weird...
sendError(status: .ConditionsNotSatisfied, cid: cid)
return
@ -170,7 +160,8 @@ class U2FAuthenticator {
}
}
func handleVersionRequest(_ req: APDU.VersionRequest, cid: UInt32) {
func handleVersionRequest(_ raw: Data, cid: UInt32) throws {
let _ = try APDU.VersionRequest(raw: raw)
let resp = APDU.VersionResponse(version: "U2F_V2")
sendMsg(msg: resp, cid: cid)
}
@ -180,14 +171,7 @@ class U2FAuthenticator {
sendMsg(msg: resp, cid: cid)
}
func sendMsg(msg: APDU.MessageProtocol, cid: UInt32) {
if u2fhid.sendMsg(cid: cid, data: msg.raw) {
print("↓↓↓↓↓ Sent message ↓↓↓↓↓")
} else {
print("↓↓↓↓↓ Error sending message ↓↓↓↓↓")
}
msg.debug()
print("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\n")
func sendMsg(msg: APDU.RawConvertible, cid: UInt32) {
let _ = u2fhid.sendMsg(cid: cid, data: msg.raw)
}
}

Просмотреть файл

@ -7,83 +7,84 @@
//
import XCTest
import APDU
@testable import SoftU2FTool
//class IntegrationTests: SoftU2FTestCase {
// override func tearDown() {
// let _ = U2FRegistration.deleteAll()
// }
//
// func testRegister() throws {
// var rc = u2fh_global_init(u2fh_initflags(rawValue: 0))
//// var rc = u2fh_global_init(U2FH_DEBUG)
// XCTAssertEqual(rc.name, U2FH_OK.name)
// defer { u2fh_global_done() }
//
//
// var devs: OpaquePointer? = nil
// rc = u2fh_devs_init(&devs)
// XCTAssertEqual(rc.name, U2FH_OK.name)
// XCTAssertNotNil(devs)
// defer { u2fh_devs_done(devs) }
//
// var maxIdx = UInt32(0xFFFFFFFF)
// while u2fh_devs_discover(devs, &maxIdx) == U2FH_NO_U2F_DEVICE {
// u2fh_devs_done(devs)
// sleep(1)
//
// rc = u2fh_devs_init(&devs)
// XCTAssertEqual(rc.name, U2FH_OK.name)
// XCTAssertNotNil(devs)
// }
//
// XCTAssertEqual(rc.name, U2FH_OK.name)
// XCTAssertEqual(maxIdx, 0)
//
// let appId = "https://github.com/u2f/trusted_facets"
// let challenge = "VA-qf-tVVQVuPmNI4U2_ShZNYgvaaHnMPp_EnL2dNWY"
// let challengeParamBytes = try JSONSerialization.data(withJSONObject: ["challenge": challenge, "version": "U2F_V2", "appId": appId])
// let challengeParam = String(bytes: challengeParamBytes, encoding: .utf8)!
// var respPtr: UnsafeMutablePointer<Int8>? = nil
//
// rc = u2fh_register(devs, challengeParam, appId, &respPtr, U2FH_REQUEST_USER_PRESENCE)
// XCTAssertEqual(rc.name, U2FH_OK.name)
//
// if respPtr == nil {
// XCTFail("Expected registration response")
// return
// }
//
// let respStr = String(cString: respPtr!)
//
// guard let respData = respStr.data(using: .utf8) else {
// XCTFail("Expected response to utf8 encoded")
// return
// }
//
// let respJSON = try JSONSerialization.jsonObject(with: respData, options: JSONSerialization.ReadingOptions.init(rawValue: 0))
//
// guard let respDict = respJSON as? [String: String] else {
// XCTFail("Expected response to be dictionary")
// return
// }
//
// guard let regDataStr = respDict["registrationData"] else {
// XCTFail("Expected response dictionary to include registrationData member")
// return
// }
//
// guard let regData = WebSafeBase64.decode(regDataStr) else {
// XCTFail("Expected response registrationData member to be b64 encoded")
// return
// }
//
// // TODO: this fails because we include the APDU trailer in the RegisterResponse....
// let regResp = try RegisterResponse(raw: regData)
//
// guard let _ = U2FRegistration(keyHandle: regResp.keyHandle, applicationParameter: randData()) else {
// XCTFail("Expected key handle from response to match registration")
// return
// }
// }
//}
class IntegrationTests: SoftU2FTestCase {
override func tearDown() {
let _ = U2FRegistration.deleteAll()
}
func testRegister() throws {
var rc = u2fh_global_init(u2fh_initflags(rawValue: 0))
// var rc = u2fh_global_init(U2FH_DEBUG)
XCTAssertEqual(rc.name, U2FH_OK.name)
defer { u2fh_global_done() }
var devs: OpaquePointer? = nil
rc = u2fh_devs_init(&devs)
XCTAssertEqual(rc.name, U2FH_OK.name)
XCTAssertNotNil(devs)
defer { u2fh_devs_done(devs) }
var maxIdx = UInt32(0xFFFFFFFF)
while u2fh_devs_discover(devs, &maxIdx) == U2FH_NO_U2F_DEVICE {
u2fh_devs_done(devs)
sleep(1)
rc = u2fh_devs_init(&devs)
XCTAssertEqual(rc.name, U2FH_OK.name)
XCTAssertNotNil(devs)
}
XCTAssertEqual(rc.name, U2FH_OK.name)
XCTAssertEqual(maxIdx, 0)
let appId = "https://github.com/u2f/trusted_facets"
let challenge = "VA-qf-tVVQVuPmNI4U2_ShZNYgvaaHnMPp_EnL2dNWY"
let challengeParamBytes = try JSONSerialization.data(withJSONObject: ["challenge": challenge, "version": "U2F_V2", "appId": appId])
let challengeParam = String(bytes: challengeParamBytes, encoding: .utf8)!
var respPtr: UnsafeMutablePointer<Int8>? = nil
rc = u2fh_register(devs, challengeParam, appId, &respPtr, U2FH_REQUEST_USER_PRESENCE)
XCTAssertEqual(rc.name, U2FH_OK.name)
if respPtr == nil {
XCTFail("Expected registration response")
return
}
let respStr = String(cString: respPtr!)
guard let respData = respStr.data(using: .utf8) else {
XCTFail("Expected response to utf8 encoded")
return
}
let respJSON = try JSONSerialization.jsonObject(with: respData, options: JSONSerialization.ReadingOptions.init(rawValue: 0))
guard let respDict = respJSON as? [String: String] else {
XCTFail("Expected response to be dictionary")
return
}
guard let regDataStr = respDict["registrationData"] else {
XCTFail("Expected response dictionary to include registrationData member")
return
}
guard let regData = WebSafeBase64.decode(regDataStr) else {
XCTFail("Expected response registrationData member to be b64 encoded")
return
}
// TODO: this fails because we include the APDU trailer in the RegisterResponse....
let regResp = try APDU.RegisterResponse(raw: regData, bodyOnly: true)
guard let _ = U2FRegistration(keyHandle: regResp.keyHandle, applicationParameter: randData()) else {
XCTFail("Expected key handle from response to match registration")
return
}
}
}

Просмотреть файл

@ -10,39 +10,38 @@ import XCTest
@testable import SoftU2FTool
func tupleDigestEqual(_ a: SHA256.TupleDigest, _ b: SHA256.TupleDigest) -> Bool {
return
a.0 == b.0 &&
a.1 == b.1 &&
a.2 == b.2 &&
a.3 == b.3 &&
a.4 == b.4 &&
a.5 == b.5 &&
a.6 == b.6 &&
a.7 == b.7 &&
a.8 == b.8 &&
a.9 == b.9 &&
a.10 == b.10 &&
a.11 == b.11 &&
a.12 == b.12 &&
a.13 == b.13 &&
a.14 == b.14 &&
a.15 == b.15 &&
a.16 == b.16 &&
a.17 == b.17 &&
a.18 == b.18 &&
a.19 == b.19 &&
a.20 == b.20 &&
a.21 == b.21 &&
a.22 == b.22 &&
a.23 == b.23 &&
a.24 == b.24 &&
a.25 == b.25 &&
a.26 == b.26 &&
a.27 == b.27 &&
a.28 == b.28 &&
a.29 == b.29 &&
a.30 == b.30 &&
a.31 == b.31
return a.0 == b.0 &&
a.1 == b.1 &&
a.2 == b.2 &&
a.3 == b.3 &&
a.4 == b.4 &&
a.5 == b.5 &&
a.6 == b.6 &&
a.7 == b.7 &&
a.8 == b.8 &&
a.9 == b.9 &&
a.10 == b.10 &&
a.11 == b.11 &&
a.12 == b.12 &&
a.13 == b.13 &&
a.14 == b.14 &&
a.15 == b.15 &&
a.16 == b.16 &&
a.17 == b.17 &&
a.18 == b.18 &&
a.19 == b.19 &&
a.20 == b.20 &&
a.21 == b.21 &&
a.22 == b.22 &&
a.23 == b.23 &&
a.24 == b.24 &&
a.25 == b.25 &&
a.26 == b.26 &&
a.27 == b.27 &&
a.28 == b.28 &&
a.29 == b.29 &&
a.30 == b.30 &&
a.31 == b.31
}
func randData(maxLen: Int = 4096) -> Data {