diff --git a/PatientScannerDemo/COSE.swift b/PatientScannerDemo/COSE.swift new file mode 100644 index 0000000..73d15ba --- /dev/null +++ b/PatientScannerDemo/COSE.swift @@ -0,0 +1,69 @@ +// +// COSE.swift +// PatientScannerDemo +// +// Created by Yannick Spreen on 4/14/21. +// + +import Foundation +import SwiftCBOR +import CryptoKit + +struct COSE { + public static func verify(_ cbor: CBOR, with xHex: String, and yHex: String) -> Bool { + let COSE_TAG = UInt64(18) + let COSE_PHDR_SIG = CBOR.unsignedInt(1) + + guard + case let CBOR.tagged(tag, cborElement) = cbor, + tag.rawValue == COSE_TAG, // SIGN1 + case let CBOR.array(array) = cborElement, + case let CBOR.byteString(protectedBytes) = array[0], + case let CBOR.map(unprotected) = array[1], + case let CBOR.byteString(payloadBytes) = array[2], + case let CBOR.byteString(signature) = array[3], + let protected = try? CBOR.decode(protectedBytes), + let payload = try? CBOR.decode(payloadBytes), + case let CBOR.map(protectedMap) = protected, + let sig = protectedMap[COSE_PHDR_SIG] + else { + return false + } + + let signedPayload: [UInt8] = CBOR.encode( + [ + "Signature1", + array[0], + CBOR.byteString([]), + array[2] + ] + ) + let d = Data(bytes: signedPayload, count: signedPayload.count) + let digest = SHA256.hash(data: signedPayload) + guard + let signatureForData = try? P256.Signing.ECDSASignature(rawRepresentation: signature) + else { + return false + } + + struct TE : CustomStringConvertible { + var description: String + let kid : String + //let coord : Array() + } + + let x = Data(hexString: xHex)?.uint ?? [] + let y = Data(hexString: yHex)?.uint ?? [] + let rawk: [UInt8] = [04] + x + y + let _ = (unprotected, sig, d, payload) // unused + + if + rawk.count == 32+32+1, + let publicKey = try? P256.Signing.PublicKey(x963Representation: rawk), + publicKey.isValidSignature(signatureForData, for: digest) + { + return true + } + return false + } +} diff --git a/PatientScannerDemoTests/EHNTests.swift b/PatientScannerDemoTests/EHNTests.swift index fcc69a7..92b07f7 100644 --- a/PatientScannerDemoTests/EHNTests.swift +++ b/PatientScannerDemoTests/EHNTests.swift @@ -12,7 +12,7 @@ import SwiftCBOR var barcode = "HC1NCFY70R30FFWTWGSLKC 4O992$V M63TMF2V*D9LPC.3EHPCGEC27B72VF/347O4-M6Y9M6FOYG4ILDEI8GR3ZI$15MABL:E9CVBGEEWRMLE C39S0/ANZ52T82Z-73D63P1U 1$PKC 72H2XX09WDH889V5" -let trust_json = """ +let trustJson = """ [ { \"kid\" : \"DEFBBA3378B322F5\", @@ -33,10 +33,8 @@ let trust_json = """ class EHNTests: XCTestCase { - func test_cose() throws { let COSE_TAG = UInt64(18) - let COSE_PHDR_SIG = CBOR.unsignedInt(1) let COSE_PHDR_KID = CBOR.unsignedInt(4) // Remove HC1 header if any @@ -47,7 +45,7 @@ class EHNTests: XCTestCase { guard let compressed = try? barcode.fromBase45() else { - assert(false) + XCTAssert(false) return } @@ -55,91 +53,48 @@ class EHNTests: XCTestCase { let decoder = SwiftCBOR.CBORDecoder(input: data.uint) guard - let cose = try? decoder.decodeItem(), - case let CBOR.tagged(tag, cborElement) = cose, + let cbor = try? decoder.decodeItem(), + case let CBOR.tagged(tag, cborElement) = cbor, tag.rawValue == COSE_TAG, // SIGN1 case let CBOR.array(array) = cborElement, case let CBOR.byteString(protectedBytes) = array[0], - case let CBOR.map(unprotected) = array[1], case let CBOR.byteString(payloadBytes) = array[2], - case let CBOR.byteString(signature) = array[3], let protected = try? CBOR.decode(protectedBytes), let payload = try? CBOR.decode(payloadBytes), case let CBOR.map(protectedMap) = protected else { - assert(false) + XCTAssert(false) return } - var kid: [UInt8] = [] - let sig = protectedMap[COSE_PHDR_SIG]! - - print("SIG: ", sig) - if case let CBOR.byteString(k) = protectedMap[COSE_PHDR_KID] ?? .null { - kid = k - } else { - assert(false) + guard case let CBOR.byteString(kid) = protectedMap[COSE_PHDR_KID] ?? .null else { + XCTAssert(false) + return } - print("Signature: ", signature) - print("Payload: ", payload) - print("KID: ", kid) - - let externalData = CBOR.byteString([]) - let signed_payload: [UInt8] = CBOR.encode( - [ - "Signature1", - array[0], - externalData, - array[2] - ] - ) - let d = Data(bytes: signed_payload, count: signed_payload.count) - print("Signing: ", d.base64EncodedString()) - let digest = SHA256.hash(data: signed_payload) - print("Digest: ", digest) - - let signatureForData = try! P256.Signing.ECDSASignature(rawRepresentation: signature) - - // use KID to find the right X,Y coordinates from the JSON - // - struct TE : CustomStringConvertible { - var description: String - let kid : String - //let coord : Array() + guard + let trustData = trustJson.data(using: .utf8), + let trustSerialization = try? JSONSerialization.jsonObject(with: trustData, options: []), + let trust = trustSerialization as? [[String: Any]] + else { + XCTAssert(false) + return } - var x: [UInt8] = [] - var y: [UInt8] = [] - - let _ = unprotected // unused - - if let trust = try? JSONSerialization.jsonObject(with: trust_json.data(using: .utf8)!, options: []) as? [[String: Any]] { - for case let elem: Dictionary in trust { - if kid == Data(hexString: elem["kid"] as! String)?.uint { - print("We know this KID - check if this sig works...") - x = Data(hexString: ((elem["coord"] as! Array)[0] as? String) ?? "")?.uint ?? [] - y = Data(hexString: ((elem["coord"] as! Array)[1] as? String) ?? "")?.uint ?? [] - - var rawk: [UInt8] = [04] - rawk.append(contentsOf: x) - rawk.append(contentsOf: y) - XCTAssert(rawk.count == 32+32+1) - - if - let publicKey = try? P256.Signing.PublicKey(x963Representation: rawk), - publicKey.isValidSignature(signatureForData, for: digest) - { - print("All is WELL !") - - print("Payload (decoded)") - print(array[2]) - return - } - print("- sig failed - which is OK - we may have more matching KIDS --") + for case let elem: Dictionary in trust { + if + kid == Data(hexString: elem["kid"] as! String)?.uint, + let x = (elem["coord"] as? Array)?[0] as? String, + let y = (elem["coord"] as? Array)?[1] as? String + { + print("We know this KID - check if this sig works...") + if COSE.verify(cbor, with: x, and: y) { + print("All is well! Payload: ", payload) + return } - print("Nope - all failed - sadness all around") - assert(false) + print("- sig failed - which is OK - we may have more matching KIDS --") } } + print("Nope - all failed - sadness all around") + XCTAssert(false) } } @@ -147,8 +102,6 @@ class EHNTests: XCTestCase { Produces: - All is WELL ! - Payload (decoded) - byteString([161, 99, 102, 111, 111, 99, 98, 97, 114]) - + All is well! Payload: map([SwiftCBOR.CBOR.utf8String("foo"): SwiftCBOR.CBOR.utf8String("bar")]) + */