From 09684f21c726edca87500818e1374bc0f6881773 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Thu, 8 Apr 2021 21:12:43 +0200 Subject: [PATCH 01/26] Initial Commit --- PatientScannerDemo.xcodeproj/project.pbxproj | 598 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcschemes/xcschememanagement.plist | 14 + PatientScannerDemo/AppDelegate.swift | 36 ++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 +++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 + PatientScannerDemo/Base.lproj/Main.storyboard | 24 + PatientScannerDemo/Info.plist | 66 ++ PatientScannerDemo/SceneDelegate.swift | 52 ++ PatientScannerDemo/ViewController.swift | 19 + PatientScannerDemoTests/Info.plist | 22 + .../PatientScannerDemoTests.swift | 33 + PatientScannerDemoUITests/Info.plist | 22 + .../PatientScannerDemoUITests.swift | 42 ++ 17 files changed, 1083 insertions(+) create mode 100644 PatientScannerDemo.xcodeproj/project.pbxproj create mode 100644 PatientScannerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 PatientScannerDemo.xcodeproj/xcuserdata/user.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 PatientScannerDemo/AppDelegate.swift create mode 100644 PatientScannerDemo/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 PatientScannerDemo/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 PatientScannerDemo/Assets.xcassets/Contents.json create mode 100644 PatientScannerDemo/Base.lproj/LaunchScreen.storyboard create mode 100644 PatientScannerDemo/Base.lproj/Main.storyboard create mode 100644 PatientScannerDemo/Info.plist create mode 100644 PatientScannerDemo/SceneDelegate.swift create mode 100644 PatientScannerDemo/ViewController.swift create mode 100644 PatientScannerDemoTests/Info.plist create mode 100644 PatientScannerDemoTests/PatientScannerDemoTests.swift create mode 100644 PatientScannerDemoUITests/Info.plist create mode 100644 PatientScannerDemoUITests/PatientScannerDemoUITests.swift diff --git a/PatientScannerDemo.xcodeproj/project.pbxproj b/PatientScannerDemo.xcodeproj/project.pbxproj new file mode 100644 index 0000000..81b6a2c --- /dev/null +++ b/PatientScannerDemo.xcodeproj/project.pbxproj @@ -0,0 +1,598 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6EB261F8D2700715333 /* AppDelegate.swift */; }; + CEA6D6EE261F8D2700715333 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6ED261F8D2700715333 /* SceneDelegate.swift */; }; + CEA6D6F0261F8D2700715333 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6EF261F8D2700715333 /* ViewController.swift */; }; + CEA6D6F3261F8D2700715333 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CEA6D6F1261F8D2700715333 /* Main.storyboard */; }; + CEA6D6F5261F8D2900715333 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CEA6D6F4261F8D2900715333 /* Assets.xcassets */; }; + CEA6D6F8261F8D2900715333 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CEA6D6F6261F8D2900715333 /* LaunchScreen.storyboard */; }; + CEA6D703261F8D2900715333 /* PatientScannerDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D702261F8D2900715333 /* PatientScannerDemoTests.swift */; }; + CEA6D70E261F8D2900715333 /* PatientScannerDemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D70D261F8D2900715333 /* PatientScannerDemoUITests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + CEA6D6FF261F8D2900715333 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = CEA6D6E0261F8D2700715333 /* Project object */; + proxyType = 1; + remoteGlobalIDString = CEA6D6E7261F8D2700715333; + remoteInfo = PatientScannerDemo; + }; + CEA6D70A261F8D2900715333 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = CEA6D6E0261F8D2700715333 /* Project object */; + proxyType = 1; + remoteGlobalIDString = CEA6D6E7261F8D2700715333; + remoteInfo = PatientScannerDemo; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + CEA6D6E8261F8D2700715333 /* PatientScannerDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PatientScannerDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + CEA6D6EB261F8D2700715333 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + CEA6D6ED261F8D2700715333 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + CEA6D6EF261F8D2700715333 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + CEA6D6F2261F8D2700715333 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + CEA6D6F4261F8D2900715333 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + CEA6D6F7261F8D2900715333 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + CEA6D6F9261F8D2900715333 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CEA6D6FE261F8D2900715333 /* PatientScannerDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PatientScannerDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + CEA6D702261F8D2900715333 /* PatientScannerDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatientScannerDemoTests.swift; sourceTree = ""; }; + CEA6D704261F8D2900715333 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CEA6D709261F8D2900715333 /* PatientScannerDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PatientScannerDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + CEA6D70D261F8D2900715333 /* PatientScannerDemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatientScannerDemoUITests.swift; sourceTree = ""; }; + CEA6D70F261F8D2900715333 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CEA6D6E5261F8D2700715333 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEA6D6FB261F8D2900715333 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEA6D706261F8D2900715333 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CEA6D6DF261F8D2700715333 = { + isa = PBXGroup; + children = ( + CEA6D6EA261F8D2700715333 /* PatientScannerDemo */, + CEA6D701261F8D2900715333 /* PatientScannerDemoTests */, + CEA6D70C261F8D2900715333 /* PatientScannerDemoUITests */, + CEA6D6E9261F8D2700715333 /* Products */, + ); + sourceTree = ""; + }; + CEA6D6E9261F8D2700715333 /* Products */ = { + isa = PBXGroup; + children = ( + CEA6D6E8261F8D2700715333 /* PatientScannerDemo.app */, + CEA6D6FE261F8D2900715333 /* PatientScannerDemoTests.xctest */, + CEA6D709261F8D2900715333 /* PatientScannerDemoUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + CEA6D6EA261F8D2700715333 /* PatientScannerDemo */ = { + isa = PBXGroup; + children = ( + CEA6D6EB261F8D2700715333 /* AppDelegate.swift */, + CEA6D6ED261F8D2700715333 /* SceneDelegate.swift */, + CEA6D6EF261F8D2700715333 /* ViewController.swift */, + CEA6D6F1261F8D2700715333 /* Main.storyboard */, + CEA6D6F4261F8D2900715333 /* Assets.xcassets */, + CEA6D6F6261F8D2900715333 /* LaunchScreen.storyboard */, + CEA6D6F9261F8D2900715333 /* Info.plist */, + ); + path = PatientScannerDemo; + sourceTree = ""; + }; + CEA6D701261F8D2900715333 /* PatientScannerDemoTests */ = { + isa = PBXGroup; + children = ( + CEA6D702261F8D2900715333 /* PatientScannerDemoTests.swift */, + CEA6D704261F8D2900715333 /* Info.plist */, + ); + path = PatientScannerDemoTests; + sourceTree = ""; + }; + CEA6D70C261F8D2900715333 /* PatientScannerDemoUITests */ = { + isa = PBXGroup; + children = ( + CEA6D70D261F8D2900715333 /* PatientScannerDemoUITests.swift */, + CEA6D70F261F8D2900715333 /* Info.plist */, + ); + path = PatientScannerDemoUITests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + CEA6D6E7261F8D2700715333 /* PatientScannerDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEA6D712261F8D2900715333 /* Build configuration list for PBXNativeTarget "PatientScannerDemo" */; + buildPhases = ( + CEA6D6E4261F8D2700715333 /* Sources */, + CEA6D6E5261F8D2700715333 /* Frameworks */, + CEA6D6E6261F8D2700715333 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PatientScannerDemo; + productName = PatientScannerDemo; + productReference = CEA6D6E8261F8D2700715333 /* PatientScannerDemo.app */; + productType = "com.apple.product-type.application"; + }; + CEA6D6FD261F8D2900715333 /* PatientScannerDemoTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEA6D715261F8D2900715333 /* Build configuration list for PBXNativeTarget "PatientScannerDemoTests" */; + buildPhases = ( + CEA6D6FA261F8D2900715333 /* Sources */, + CEA6D6FB261F8D2900715333 /* Frameworks */, + CEA6D6FC261F8D2900715333 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + CEA6D700261F8D2900715333 /* PBXTargetDependency */, + ); + name = PatientScannerDemoTests; + productName = PatientScannerDemoTests; + productReference = CEA6D6FE261F8D2900715333 /* PatientScannerDemoTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + CEA6D708261F8D2900715333 /* PatientScannerDemoUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEA6D718261F8D2900715333 /* Build configuration list for PBXNativeTarget "PatientScannerDemoUITests" */; + buildPhases = ( + CEA6D705261F8D2900715333 /* Sources */, + CEA6D706261F8D2900715333 /* Frameworks */, + CEA6D707261F8D2900715333 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + CEA6D70B261F8D2900715333 /* PBXTargetDependency */, + ); + name = PatientScannerDemoUITests; + productName = PatientScannerDemoUITests; + productReference = CEA6D709261F8D2900715333 /* PatientScannerDemoUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CEA6D6E0261F8D2700715333 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1240; + LastUpgradeCheck = 1240; + TargetAttributes = { + CEA6D6E7261F8D2700715333 = { + CreatedOnToolsVersion = 12.4; + }; + CEA6D6FD261F8D2900715333 = { + CreatedOnToolsVersion = 12.4; + TestTargetID = CEA6D6E7261F8D2700715333; + }; + CEA6D708261F8D2900715333 = { + CreatedOnToolsVersion = 12.4; + TestTargetID = CEA6D6E7261F8D2700715333; + }; + }; + }; + buildConfigurationList = CEA6D6E3261F8D2700715333 /* Build configuration list for PBXProject "PatientScannerDemo" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CEA6D6DF261F8D2700715333; + productRefGroup = CEA6D6E9261F8D2700715333 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CEA6D6E7261F8D2700715333 /* PatientScannerDemo */, + CEA6D6FD261F8D2900715333 /* PatientScannerDemoTests */, + CEA6D708261F8D2900715333 /* PatientScannerDemoUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CEA6D6E6261F8D2700715333 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEA6D6F8261F8D2900715333 /* LaunchScreen.storyboard in Resources */, + CEA6D6F5261F8D2900715333 /* Assets.xcassets in Resources */, + CEA6D6F3261F8D2700715333 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEA6D6FC261F8D2900715333 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEA6D707261F8D2900715333 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CEA6D6E4261F8D2700715333 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEA6D6F0261F8D2700715333 /* ViewController.swift in Sources */, + CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */, + CEA6D6EE261F8D2700715333 /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEA6D6FA261F8D2900715333 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEA6D703261F8D2900715333 /* PatientScannerDemoTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEA6D705261F8D2900715333 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEA6D70E261F8D2900715333 /* PatientScannerDemoUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + CEA6D700261F8D2900715333 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = CEA6D6E7261F8D2700715333 /* PatientScannerDemo */; + targetProxy = CEA6D6FF261F8D2900715333 /* PBXContainerItemProxy */; + }; + CEA6D70B261F8D2900715333 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = CEA6D6E7261F8D2700715333 /* PatientScannerDemo */; + targetProxy = CEA6D70A261F8D2900715333 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + CEA6D6F1261F8D2700715333 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + CEA6D6F2261F8D2700715333 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + CEA6D6F6261F8D2900715333 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + CEA6D6F7261F8D2900715333 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + CEA6D710261F8D2900715333 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + CEA6D711261F8D2900715333 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + CEA6D713261F8D2900715333 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = KH99XNF745; + INFOPLIST_FILE = PatientScannerDemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.t-systems.PatientScannerDemo"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CEA6D714261F8D2900715333 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = KH99XNF745; + INFOPLIST_FILE = PatientScannerDemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.t-systems.PatientScannerDemo"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + CEA6D716261F8D2900715333 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = KH99XNF745; + INFOPLIST_FILE = PatientScannerDemoTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.t-systems.PatientScannerDemoTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PatientScannerDemo.app/PatientScannerDemo"; + }; + name = Debug; + }; + CEA6D717261F8D2900715333 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = KH99XNF745; + INFOPLIST_FILE = PatientScannerDemoTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.t-systems.PatientScannerDemoTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PatientScannerDemo.app/PatientScannerDemo"; + }; + name = Release; + }; + CEA6D719261F8D2900715333 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = KH99XNF745; + INFOPLIST_FILE = PatientScannerDemoUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.t-systems.PatientScannerDemoUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = PatientScannerDemo; + }; + name = Debug; + }; + CEA6D71A261F8D2900715333 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = KH99XNF745; + INFOPLIST_FILE = PatientScannerDemoUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.t-systems.PatientScannerDemoUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = PatientScannerDemo; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CEA6D6E3261F8D2700715333 /* Build configuration list for PBXProject "PatientScannerDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEA6D710261F8D2900715333 /* Debug */, + CEA6D711261F8D2900715333 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CEA6D712261F8D2900715333 /* Build configuration list for PBXNativeTarget "PatientScannerDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEA6D713261F8D2900715333 /* Debug */, + CEA6D714261F8D2900715333 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CEA6D715261F8D2900715333 /* Build configuration list for PBXNativeTarget "PatientScannerDemoTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEA6D716261F8D2900715333 /* Debug */, + CEA6D717261F8D2900715333 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CEA6D718261F8D2900715333 /* Build configuration list for PBXNativeTarget "PatientScannerDemoUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEA6D719261F8D2900715333 /* Debug */, + CEA6D71A261F8D2900715333 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CEA6D6E0261F8D2700715333 /* Project object */; +} diff --git a/PatientScannerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/PatientScannerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/PatientScannerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/PatientScannerDemo.xcodeproj/xcuserdata/user.xcuserdatad/xcschemes/xcschememanagement.plist b/PatientScannerDemo.xcodeproj/xcuserdata/user.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..3949b55 --- /dev/null +++ b/PatientScannerDemo.xcodeproj/xcuserdata/user.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + PatientScannerDemo.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/PatientScannerDemo/AppDelegate.swift b/PatientScannerDemo/AppDelegate.swift new file mode 100644 index 0000000..8294195 --- /dev/null +++ b/PatientScannerDemo/AppDelegate.swift @@ -0,0 +1,36 @@ +// +// AppDelegate.swift +// PatientScannerDemo +// +// Created by Yannick Spreen on 4/8/21. +// + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/PatientScannerDemo/Assets.xcassets/AccentColor.colorset/Contents.json b/PatientScannerDemo/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/PatientScannerDemo/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PatientScannerDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/PatientScannerDemo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/PatientScannerDemo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PatientScannerDemo/Assets.xcassets/Contents.json b/PatientScannerDemo/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/PatientScannerDemo/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PatientScannerDemo/Base.lproj/LaunchScreen.storyboard b/PatientScannerDemo/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/PatientScannerDemo/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PatientScannerDemo/Base.lproj/Main.storyboard b/PatientScannerDemo/Base.lproj/Main.storyboard new file mode 100644 index 0000000..25a7638 --- /dev/null +++ b/PatientScannerDemo/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PatientScannerDemo/Info.plist b/PatientScannerDemo/Info.plist new file mode 100644 index 0000000..5b531f7 --- /dev/null +++ b/PatientScannerDemo/Info.plist @@ -0,0 +1,66 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/PatientScannerDemo/SceneDelegate.swift b/PatientScannerDemo/SceneDelegate.swift new file mode 100644 index 0000000..22880e4 --- /dev/null +++ b/PatientScannerDemo/SceneDelegate.swift @@ -0,0 +1,52 @@ +// +// SceneDelegate.swift +// PatientScannerDemo +// +// Created by Yannick Spreen on 4/8/21. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let _ = (scene as? UIWindowScene) else { return } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/PatientScannerDemo/ViewController.swift b/PatientScannerDemo/ViewController.swift new file mode 100644 index 0000000..5ff7552 --- /dev/null +++ b/PatientScannerDemo/ViewController.swift @@ -0,0 +1,19 @@ +// +// ViewController.swift +// PatientScannerDemo +// +// Created by Yannick Spreen on 4/8/21. +// + +import UIKit + +class ViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + + +} + diff --git a/PatientScannerDemoTests/Info.plist b/PatientScannerDemoTests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/PatientScannerDemoTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/PatientScannerDemoTests/PatientScannerDemoTests.swift b/PatientScannerDemoTests/PatientScannerDemoTests.swift new file mode 100644 index 0000000..11b58a1 --- /dev/null +++ b/PatientScannerDemoTests/PatientScannerDemoTests.swift @@ -0,0 +1,33 @@ +// +// PatientScannerDemoTests.swift +// PatientScannerDemoTests +// +// Created by Yannick Spreen on 4/8/21. +// + +import XCTest +@testable import PatientScannerDemo + +class PatientScannerDemoTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/PatientScannerDemoUITests/Info.plist b/PatientScannerDemoUITests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/PatientScannerDemoUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/PatientScannerDemoUITests/PatientScannerDemoUITests.swift b/PatientScannerDemoUITests/PatientScannerDemoUITests.swift new file mode 100644 index 0000000..2dadbd5 --- /dev/null +++ b/PatientScannerDemoUITests/PatientScannerDemoUITests.swift @@ -0,0 +1,42 @@ +// +// PatientScannerDemoUITests.swift +// PatientScannerDemoUITests +// +// Created by Yannick Spreen on 4/8/21. +// + +import XCTest + +class PatientScannerDemoUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} From 69fdcf7ae7143191f2724c79e20be8a0d0526af8 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Mon, 12 Apr 2021 22:56:44 +0200 Subject: [PATCH 02/26] Scan code via iOS APIs, decode b45 and CBOR. Not compiling without errors: Currently experimenting with the Kitura Cryptor library which has some issues on iOS. --- .gitignore | 220 ++++++++++++++++++ PatientScannerDemo.xcodeproj/project.pbxproj | 75 +++++- .../xcshareddata/swiftpm/Package.resolved | 43 ++++ PatientScannerDemo/AppDelegate.swift | 17 -- PatientScannerDemo/Base45.swift | 73 ++++++ PatientScannerDemo/Info.plist | 2 + PatientScannerDemo/SceneDelegate.swift | 37 --- PatientScannerDemo/ViewController.swift | 217 ++++++++++++++++- PatientScannerDemo/ZLib.swift | 28 +++ 9 files changed, 654 insertions(+), 58 deletions(-) create mode 100644 .gitignore create mode 100644 PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 PatientScannerDemo/Base45.swift create mode 100644 PatientScannerDemo/ZLib.swift diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..83296ca --- /dev/null +++ b/.gitignore @@ -0,0 +1,220 @@ +######################### +# .gitignore file for Xcode4 and Xcode5 Source projects +# +# Apple bugs, waiting for Apple to fix/respond: +# +# 15564624 - what does the xccheckout file in Xcode5 do? Where's the documentation? +# +# Version 2.6 +# For latest version, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects +# +# 2015 updates: +# - Fixed typo in "xccheckout" line - thanks to @lyck for pointing it out! +# - Fixed the .idea optional ignore. Thanks to @hashier for pointing this out +# - Finally added "xccheckout" to the ignore. Apple still refuses to answer support requests about this, but in practice it seems you should ignore it. +# - minor tweaks from Jona and Coeur (slightly more precise xc* filtering/names) +# 2014 updates: +# - appended non-standard items DISABLED by default (uncomment if you use those tools) +# - removed the edit that an SO.com moderator made without bothering to ask me +# - researched CocoaPods .lock more carefully, thanks to Gokhan Celiker +# 2013 updates: +# - fixed the broken "save personal Schemes" +# - added line-by-line explanations for EVERYTHING (some were missing) +# +# NB: if you are storing "built" products, this WILL NOT WORK, +# and you should use a different .gitignore (or none at all) +# This file is for SOURCE projects, where there are many extra +# files that we want to exclude +# +######################### + +##### +# OS X temporary files that should never be committed +# +# c.f. http://www.westwind.com/reference/os-x/invisibles.html + +.DS_Store + +# c.f. http://www.westwind.com/reference/os-x/invisibles.html + +.Trashes + +# c.f. http://www.westwind.com/reference/os-x/invisibles.html + +*.swp + +# +# *.lock - this is used and abused by many editors for many different things. +# For the main ones I use (e.g. Eclipse), it should be excluded +# from source-control, but YMMV. +# (lock files are usually local-only file-synchronization on the local FS that should NOT go in git) +# c.f. the "OPTIONAL" section at bottom though, for tool-specific variations! +# +# In particular, if you're using CocoaPods, you'll want to comment-out this line: +# *.lock + + +# +# profile - REMOVED temporarily (on double-checking, I can't find it in OS X docs?) +#profile + + +#### +# Xcode temporary files that should never be committed +# +# NB: NIB/XIB files still exist even on Storyboard projects, so we want this... + +*~.nib + + +#### +# Xcode build files - +# +# NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData" + +DerivedData/ + +# NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build" + +build/ + + +##### +# Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups) +# +# This is complicated: +# +# SOMETIMES you need to put this file in version control. +# Apple designed it poorly - if you use "custom executables", they are +# saved in this file. +# 99% of projects do NOT use those, so they do NOT want to version control this file. +# ..but if you're in the 1%, comment out the line "*.pbxuser" + +# .pbxuser: http://lists.apple.com/archives/xcode-users/2004/Jan/msg00193.html + +*.pbxuser + +# .mode1v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html + +*.mode1v3 + +# .mode2v3: http://lists.apple.com/archives/xcode-users/2007/Oct/msg00465.html + +*.mode2v3 + +# .perspectivev3: http://stackoverflow.com/questions/5223297/xcode-projects-what-is-a-perspectivev3-file + +*.perspectivev3 + +# NB: also, whitelist the default ones, some projects need to use these +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + + +#### +# Xcode 4 - semi-personal settings +# +# Apple Shared data that Apple put in the wrong folder +# c.f. http://stackoverflow.com/a/19260712/153422 +# FROM ANSWER: Apple says "don't ignore it" +# FROM COMMENTS: Apple is wrong; Apple code is too buggy to trust; there are no known negative side-effects to ignoring Apple's unofficial advice and instead doing the thing that actively fixes bugs in Xcode +# Up to you, but ... current advice: ignore it. +*.xccheckout + +# +# +# OPTION 1: --------------------------------- +# throw away ALL personal settings (including custom schemes! +# - unless they are "shared") +# As per build/ and DerivedData/, this ought to have a trailing slash +# +# NB: this is exclusive with OPTION 2 below +xcuserdata/ + +# OPTION 2: --------------------------------- +# get rid of ALL personal settings, but KEEP SOME OF THEM +# - NB: you must manually uncomment the bits you want to keep +# +# NB: this *requires* git v1.8.2 or above; you may need to upgrade to latest OS X, +# or manually install git over the top of the OS X version +# NB: this is exclusive with OPTION 1 above +# +#xcuserdata/**/* + +# (requires option 2 above): Personal Schemes +# +#!xcuserdata/**/xcschemes/* + +#### +# XCode 4 workspaces - more detailed +# +# Workspaces are important! They are a core feature of Xcode - don't exclude them :) +# +# Workspace layout is quite spammy. For reference: +# +# /(root)/ +# /(project-name).xcodeproj/ +# project.pbxproj +# /project.xcworkspace/ +# contents.xcworkspacedata +# /xcuserdata/ +# /(your name)/xcuserdatad/ +# UserInterfaceState.xcuserstate +# /xcshareddata/ +# /xcschemes/ +# (shared scheme name).xcscheme +# /xcuserdata/ +# /(your name)/xcuserdatad/ +# (private scheme).xcscheme +# xcschememanagement.plist +# +# + +#### +# Xcode 4 - Deprecated classes +# +# Allegedly, if you manually "deprecate" your classes, they get moved here. +# +# We're using source-control, so this is a "feature" that we do not want! + +*.moved-aside + +#### +# OPTIONAL: Some well-known tools that people use side-by-side with Xcode / iOS development +# +# NB: I'd rather not include these here, but gitignore's design is weak and doesn't allow +# modular gitignore: you have to put EVERYTHING in one file. +# +# COCOAPODS: +# +# c.f. http://guides.cocoapods.org/using/using-cocoapods.html#what-is-a-podfilelock +# c.f. http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control +# +#!Podfile.lock +# +# RUBY: +# +# c.f. http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/ +# +#!Gemfile.lock +# +# IDEA: +# +# c.f. https://www.jetbrains.com/objc/help/managing-projects-under-version-control.html?search=workspace.xml +# +#.idea/workspace.xml +# +# TEXTMATE: +# +# -- UNVERIFIED: c.f. http://stackoverflow.com/a/50283/153422 +# +#tm_build_errors + +#### +# UNKNOWN: recommended by others, but I can't discover what these files are +# + + +/Pods/ \ No newline at end of file diff --git a/PatientScannerDemo.xcodeproj/project.pbxproj b/PatientScannerDemo.xcodeproj/project.pbxproj index 81b6a2c..4d61507 100644 --- a/PatientScannerDemo.xcodeproj/project.pbxproj +++ b/PatientScannerDemo.xcodeproj/project.pbxproj @@ -3,10 +3,12 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ + CE5D7A392620D6CA001DFEEF /* SwiftJWKtoPEM in Frameworks */ = {isa = PBXBuildFile; productRef = CE5D7A382620D6CA001DFEEF /* SwiftJWKtoPEM */; }; + CE5D7A422620DA45001DFEEF /* CryptorECC in Frameworks */ = {isa = PBXBuildFile; productRef = CE5D7A412620DA45001DFEEF /* CryptorECC */; }; CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6EB261F8D2700715333 /* AppDelegate.swift */; }; CEA6D6EE261F8D2700715333 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6ED261F8D2700715333 /* SceneDelegate.swift */; }; CEA6D6F0261F8D2700715333 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6EF261F8D2700715333 /* ViewController.swift */; }; @@ -15,6 +17,9 @@ CEA6D6F8261F8D2900715333 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CEA6D6F6261F8D2900715333 /* LaunchScreen.storyboard */; }; CEA6D703261F8D2900715333 /* PatientScannerDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D702261F8D2900715333 /* PatientScannerDemoTests.swift */; }; CEA6D70E261F8D2900715333 /* PatientScannerDemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D70D261F8D2900715333 /* PatientScannerDemoUITests.swift */; }; + CEB78C4A261F8DA200FB8F68 /* Base45.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB78C48261F8DA200FB8F68 /* Base45.swift */; }; + CEB78C4B261F8DA200FB8F68 /* ZLib.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB78C49261F8DA200FB8F68 /* ZLib.swift */; }; + CEB78C57261F8DE100FB8F68 /* SwiftCBOR in Frameworks */ = {isa = PBXBuildFile; productRef = CEB78C56261F8DE100FB8F68 /* SwiftCBOR */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -49,6 +54,8 @@ CEA6D709261F8D2900715333 /* PatientScannerDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PatientScannerDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; CEA6D70D261F8D2900715333 /* PatientScannerDemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatientScannerDemoUITests.swift; sourceTree = ""; }; CEA6D70F261F8D2900715333 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CEB78C48261F8DA200FB8F68 /* Base45.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Base45.swift; sourceTree = ""; }; + CEB78C49261F8DA200FB8F68 /* ZLib.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZLib.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -56,6 +63,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + CE5D7A422620DA45001DFEEF /* CryptorECC in Frameworks */, + CEB78C57261F8DE100FB8F68 /* SwiftCBOR in Frameworks */, + CE5D7A392620D6CA001DFEEF /* SwiftJWKtoPEM in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -99,6 +109,8 @@ CEA6D6EA261F8D2700715333 /* PatientScannerDemo */ = { isa = PBXGroup; children = ( + CEB78C48261F8DA200FB8F68 /* Base45.swift */, + CEB78C49261F8DA200FB8F68 /* ZLib.swift */, CEA6D6EB261F8D2700715333 /* AppDelegate.swift */, CEA6D6ED261F8D2700715333 /* SceneDelegate.swift */, CEA6D6EF261F8D2700715333 /* ViewController.swift */, @@ -144,6 +156,11 @@ dependencies = ( ); name = PatientScannerDemo; + packageProductDependencies = ( + CEB78C56261F8DE100FB8F68 /* SwiftCBOR */, + CE5D7A382620D6CA001DFEEF /* SwiftJWKtoPEM */, + CE5D7A412620DA45001DFEEF /* CryptorECC */, + ); productName = PatientScannerDemo; productReference = CEA6D6E8261F8D2700715333 /* PatientScannerDemo.app */; productType = "com.apple.product-type.application"; @@ -215,6 +232,11 @@ Base, ); mainGroup = CEA6D6DF261F8D2700715333; + packageReferences = ( + CEB78C55261F8DE100FB8F68 /* XCRemoteSwiftPackageReference "SwiftCBOR" */, + CE5D7A372620D6CA001DFEEF /* XCRemoteSwiftPackageReference "Swift-JWK-to-PEM" */, + CE5D7A402620DA45001DFEEF /* XCRemoteSwiftPackageReference "BlueECC" */, + ); productRefGroup = CEA6D6E9261F8D2700715333 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -258,9 +280,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CEB78C4B261F8DA200FB8F68 /* ZLib.swift in Sources */, CEA6D6F0261F8D2700715333 /* ViewController.swift in Sources */, CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */, CEA6D6EE261F8D2700715333 /* SceneDelegate.swift in Sources */, + CEB78C4A261F8DA200FB8F68 /* Base45.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -366,7 +390,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.4; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -421,7 +445,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.4; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -593,6 +617,51 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + CE5D7A372620D6CA001DFEEF /* XCRemoteSwiftPackageReference "Swift-JWK-to-PEM" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ibm-cloud-security/Swift-JWK-to-PEM"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.4.0; + }; + }; + CE5D7A402620DA45001DFEEF /* XCRemoteSwiftPackageReference "BlueECC" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Kitura/BlueECC"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.2.5; + }; + }; + CEB78C55261F8DE100FB8F68 /* XCRemoteSwiftPackageReference "SwiftCBOR" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/unrelentingtech/SwiftCBOR"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.4.3; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + CE5D7A382620D6CA001DFEEF /* SwiftJWKtoPEM */ = { + isa = XCSwiftPackageProductDependency; + package = CE5D7A372620D6CA001DFEEF /* XCRemoteSwiftPackageReference "Swift-JWK-to-PEM" */; + productName = SwiftJWKtoPEM; + }; + CE5D7A412620DA45001DFEEF /* CryptorECC */ = { + isa = XCSwiftPackageProductDependency; + package = CE5D7A402620DA45001DFEEF /* XCRemoteSwiftPackageReference "BlueECC" */; + productName = CryptorECC; + }; + CEB78C56261F8DE100FB8F68 /* SwiftCBOR */ = { + isa = XCSwiftPackageProductDependency; + package = CEB78C55261F8DE100FB8F68 /* XCRemoteSwiftPackageReference "SwiftCBOR" */; + productName = SwiftCBOR; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = CEA6D6E0261F8D2700715333 /* Project object */; } diff --git a/PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..91582d9 --- /dev/null +++ b/PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,43 @@ +{ + "object": { + "pins": [ + { + "package": "CryptorECC", + "repositoryURL": "https://github.com/Kitura/BlueECC", + "state": { + "branch": null, + "revision": "baf6ed3fc1a622675f0041b4aff7c02dd1a93818", + "version": "1.2.200" + } + }, + { + "package": "OpenSSL", + "repositoryURL": "https://github.com/IBM-Swift/OpenSSL.git", + "state": { + "branch": null, + "revision": "80b04f33b086fc90e28d9ae159d43705fb348e16", + "version": "2.2.200" + } + }, + { + "package": "SwiftJWKtoPEM", + "repositoryURL": "https://github.com/ibm-cloud-security/Swift-JWK-to-PEM", + "state": { + "branch": null, + "revision": "b18e2af1392bb84f1cde83b1e00847051c9fdda6", + "version": "0.4.0" + } + }, + { + "package": "SwiftCBOR", + "repositoryURL": "https://github.com/unrelentingtech/SwiftCBOR", + "state": { + "branch": null, + "revision": "668c26fc3373d5f1bccbaad7665ca6048797e324", + "version": "0.4.3" + } + } + ] + }, + "version": 1 +} diff --git a/PatientScannerDemo/AppDelegate.swift b/PatientScannerDemo/AppDelegate.swift index 8294195..9e403d6 100644 --- a/PatientScannerDemo/AppDelegate.swift +++ b/PatientScannerDemo/AppDelegate.swift @@ -10,27 +10,10 @@ import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } - // MARK: UISceneSession Lifecycle - - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - // Called when a new scene session is being created. - // Use this method to select a configuration to create the new scene with. - return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) - } - - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { - // Called when the user discards a scene session. - // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. - // Use this method to release any resources that were specific to the discarded scenes, as they will not return. - } - - } diff --git a/PatientScannerDemo/Base45.swift b/PatientScannerDemo/Base45.swift new file mode 100644 index 0000000..a4453ef --- /dev/null +++ b/PatientScannerDemo/Base45.swift @@ -0,0 +1,73 @@ +// +// Base45.swift +// Base45-Swift +// +// Created by Dirk-Willem van Gulik on 01/04/2021. +// +import Foundation + +extension String { + enum Base45Error: Error { + case Base64InvalidCharacter + case Base64InvalidLength + } + + public func fromBase45() throws ->Data { + let BASE45_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:" + var d = Data() + var o = Data() + + for c in self.uppercased() { + if let at = BASE45_CHARSET.firstIndex(of: c) { + let idx = BASE45_CHARSET.distance(from: BASE45_CHARSET.startIndex, to: at) + d.append(UInt8(idx)) + } else { + throw Base45Error.Base64InvalidCharacter + } + } + for i in stride(from:0, to:d.count, by: 3) { + if (d.count - i < 2) { + throw Base45Error.Base64InvalidLength + } + var x : UInt32 = UInt32(d[i]) + UInt32(d[i+1])*45 + if (d.count - i >= 3) { + x += 45 * 45 * UInt32(d[i+2]) + o.append(UInt8(x / 256)) + } + o.append(UInt8(x % 256)) + } + return o + } +} + +extension String { + subscript(i: Int) -> String { + return String(self[index(startIndex, offsetBy: i)]) + } +} + +extension Data { + public func toBase45()->String { + let BASE45_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:" + var o = String() + for i in stride(from:0, to:self.count, by: 2) { + if (self.count - i > 1) { + let x : Int = (Int(self[i])<<8) + Int(self[i+1]) + let e : Int = x / (45*45) + let x2 : Int = x % (45*45) + let d : Int = x2 / 45 + let c : Int = x2 % 45 + o.append(BASE45_CHARSET[c]) + o.append(BASE45_CHARSET[d]) + o.append(BASE45_CHARSET[e]) + } else { + let x2 : Int = Int(self[i]) + let d : Int = x2 / 45 + let c : Int = x2 % 45 + o.append(BASE45_CHARSET[c]) + o.append(BASE45_CHARSET[d]) + } + } + return o + } +} diff --git a/PatientScannerDemo/Info.plist b/PatientScannerDemo/Info.plist index 5b531f7..8c2a309 100644 --- a/PatientScannerDemo/Info.plist +++ b/PatientScannerDemo/Info.plist @@ -2,6 +2,8 @@ + NSCameraUsageDescription + Scan barcodes with your camera. CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable diff --git a/PatientScannerDemo/SceneDelegate.swift b/PatientScannerDemo/SceneDelegate.swift index 22880e4..ef39680 100644 --- a/PatientScannerDemo/SceneDelegate.swift +++ b/PatientScannerDemo/SceneDelegate.swift @@ -11,42 +11,5 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. - // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. - // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). - guard let _ = (scene as? UIWindowScene) else { return } - } - - func sceneDidDisconnect(_ scene: UIScene) { - // Called as the scene is being released by the system. - // This occurs shortly after the scene enters the background, or when its session is discarded. - // Release any resources associated with this scene that can be re-created the next time the scene connects. - // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). - } - - func sceneDidBecomeActive(_ scene: UIScene) { - // Called when the scene has moved from an inactive state to an active state. - // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. - } - - func sceneWillResignActive(_ scene: UIScene) { - // Called when the scene will move from an active state to an inactive state. - // This may occur due to temporary interruptions (ex. an incoming phone call). - } - - func sceneWillEnterForeground(_ scene: UIScene) { - // Called as the scene transitions from the background to the foreground. - // Use this method to undo the changes made on entering the background. - } - - func sceneDidEnterBackground(_ scene: UIScene) { - // Called as the scene transitions from the foreground to the background. - // Use this method to save data, release shared resources, and store enough scene-specific state information - // to restore the scene back to its current state. - } - - } diff --git a/PatientScannerDemo/ViewController.swift b/PatientScannerDemo/ViewController.swift index 5ff7552..5d46fe1 100644 --- a/PatientScannerDemo/ViewController.swift +++ b/PatientScannerDemo/ViewController.swift @@ -4,16 +4,231 @@ // // Created by Yannick Spreen on 4/8/21. // +// https://www.raywenderlich.com/12663654-vision-framework-tutorial-for-ios-scanning-barcodes +// import UIKit +import Vision +import AVFoundation +import SwiftCBOR +import SwiftJWKtoPEM +//import CryptorECC + class ViewController: UIViewController { + var captureSession = AVCaptureSession() + + lazy var detectBarcodeRequest = VNDetectBarcodesRequest { request, error in + guard error == nil else { + self.showAlert(withTitle: "Barcode error", message: error?.localizedDescription ?? "error") + return + } + self.processClassification(request) + } override func viewDidLoad() { super.viewDidLoad() - // Do any additional setup after loading the view. + checkPermissions() + setupCameraLiveView() + observationHandler(payloadS: nil) + } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + captureSession.stopRunning() + } +// let curve: EllipticCurve = .prime256v1 + let name: String = "ECDSA" + + // Send the base64URLencoded signature and `header.claims` to BlueECC for verification. +// func verifySignature(key: Data, signature: Data, for data: Data) -> Bool { +// do { +// guard let keyString = String(data: key, encoding: .utf8) else { +// return false +// } +// let r = signature.subdata(in: 0 ..< signature.count/2) +// let s = signature.subdata(in: signature.count/2 ..< signature.count) +// let signature = try ECSignature(r: r, s: s) +// let publicKey = try ECPublicKey(key: keyString) +// guard publicKey.curve == curve else { +// return false +// } +// return signature.verify(plaintext: data, using: publicKey) +// } +// catch { +// print("Verification failed: \(error)") +// return false +// } +// +// } +} + + +extension ViewController { + private func checkPermissions() { + switch AVCaptureDevice.authorizationStatus(for: .video) { + case .notDetermined: + AVCaptureDevice.requestAccess(for: .video) { [self] granted in + if !granted { + self.showPermissionsAlert() + } + } + case .denied, .restricted: + showPermissionsAlert() + default: + return + } + } + + private func setupCameraLiveView() { + captureSession.sessionPreset = .hd1280x720 + + let videoDevice = AVCaptureDevice + .default(.builtInWideAngleCamera, for: .video, position: .back) + + guard + let device = videoDevice, + let videoDeviceInput = try? AVCaptureDeviceInput(device: device), + captureSession.canAddInput(videoDeviceInput) else { + showAlert( + withTitle: "Cannot Find Camera", + message: "There seems to be a problem with the camera on your device.") + return + } + + captureSession.addInput(videoDeviceInput) + + let captureOutput = AVCaptureVideoDataOutput() + captureOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32BGRA)] + captureOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global(qos: DispatchQoS.QoSClass.default)) + captureSession.addOutput(captureOutput) + + configurePreviewLayer() + + captureSession.startRunning() + } + + func processClassification(_ request: VNRequest) { + guard let barcodes = request.results else { return } + DispatchQueue.main.async { [self] in + if captureSession.isRunning { + view.layer.sublayers?.removeSubrange(1...) + + for barcode in barcodes { + let _ = barcode + guard + let potentialQRCode = barcode as? VNBarcodeObservation, + [.Aztec, .QR, .DataMatrix].contains(potentialQRCode.symbology), + potentialQRCode.confidence > 0.9 + else { return } + + print(potentialQRCode.symbology) + observationHandler(payloadS: potentialQRCode.payloadStringValue) + } + } + } } + func observationHandler(payloadS: String?) { + let payloadS: String? = "HC1NCFOXNEG2NBJ5*H:QO-.O /MD064 PJ26UV0 XHLQ9HXKZNEQCSJ591MVBATW$4 S8$96NF6OR5UVBJUB4PJU47326/Z7PCL394Z/MWP4 N66ED6JC:JEG.CZJC0:C6JK:JM$JLAINN6BLHM035L8CCECS.CYMCPOJ5OI9YI:8DRFC%PD*ZLJ9CWVBREJFZM4A7Z/M*+Q.28+VQXCRAHAF27I9QQ60E2KYIJPOJI7J/VJ0 JYSJEZIK7B*IJS7BCLIOCISEBTKBRHSWKJ4:2POJ.GILYJ7GPSVBY4CJZIOMI$MI1VC3TCYR6HCRF46Q96W/6-DP+%PPMC1KJG%HJ*81.7 84-W6I RO5PH6UDUH+/F9PJCQFRVV 8UDJ5QEV2Y8%635OHH0E2:E5VUJZ4 VT3+6ZU598O:E0/ 0VP2IBO6ANG%6UD5RSRO4B6$ES40H/CQ1" + guard + let payloadString = payloadS, + let compressed = try? String(payloadString.dropFirst(3)).fromBase45() + else { return } + + let data = decompress(compressed) + var s = "" + for char in data { + s += String(data: Data([char]), encoding: .utf8) ?? "?" + } + + print(data) + let decoder = SwiftCBOR.CBORDecoder(input: [UInt8](data)) + guard + let cose = try? decoder.decodeItem(), + case let CBOR.tagged(tag, cborElement) = cose, + tag.rawValue == 18, + 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) + else { + return + } + + print("START") + print(protected) + print(unprotected) + print(payload) + print(signature) + print("END") + print() + print() + // print(s) + + let keyString = """ +{ + "crv": "P-256", + "kid": "MklRdnVDMEdIZ29EeVY5VHo2WDR5MGlHTFNCZTgxdE5iN2wzYTZsUElCUQ", + "kty": "EC", + "x": "DHJudz6lqcTXizpz4cb_bbi8NZ9ofoD3lYnvByrMfLc", + "y": "bP7L_fysDzwFc13DDEPllO2qu1nDiDd1btoVQ-XlKok" +} +""" + + let key = try! RSAKey(jwk: keyString) + + let publicPem = try! key.getPublicKey() + +// let verifier = verifySignature(key: publicPem!.data(using: .utf8)!, signature: Data(signature), for: Data(payloadBytes)) +// print(verifier) + + } } + +extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate { + func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { + guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } + + let imageRequestHandler = VNImageRequestHandler( + cvPixelBuffer: pixelBuffer, + orientation: .right + ) + + do { + try imageRequestHandler.perform([detectBarcodeRequest]) + } catch { + print(error) + } + } +} + + +extension ViewController { + private func configurePreviewLayer() { + let cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession) + cameraPreviewLayer.videoGravity = .resizeAspectFill + cameraPreviewLayer.connection?.videoOrientation = .portrait + cameraPreviewLayer.frame = view.frame + view.layer.insertSublayer(cameraPreviewLayer, at: 0) + } + + private func showAlert(withTitle title: String, message: String) { + DispatchQueue.main.async { + let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: "OK", style: .default)) + self.present(alertController, animated: true) + } + } + + private func showPermissionsAlert() { + showAlert( + withTitle: "Camera Permissions", + message: "Please open Settings and grant permission for this app to use your camera." + ) + } +} diff --git a/PatientScannerDemo/ZLib.swift b/PatientScannerDemo/ZLib.swift new file mode 100644 index 0000000..8c40f53 --- /dev/null +++ b/PatientScannerDemo/ZLib.swift @@ -0,0 +1,28 @@ +// https://stackoverflow.com/a/55558641/2585092 + +import Foundation +import Compression + +func decompressString(_ data: Data) -> String { + let size = 4 * data.count + let buffer = UnsafeMutablePointer.allocate(capacity: size) + let result = data.subdata(in: 2 ..< data.count).withUnsafeBytes ({ + let read = compression_decode_buffer(buffer, size, $0.baseAddress!.bindMemory(to: UInt8.self, capacity: 1), + data.count - 2, nil, COMPRESSION_ZLIB) + return String(decoding: Data(bytes: buffer, count:read), as: UTF8.self) + }) as String + buffer.deallocate() + return result +} + +func decompress(_ data: Data) -> Data { + let size = 4 * data.count + let buffer = UnsafeMutablePointer.allocate(capacity: size) + let result = data.subdata(in: 2 ..< data.count).withUnsafeBytes ({ + let read = compression_decode_buffer(buffer, size, $0.baseAddress!.bindMemory(to: UInt8.self, capacity: 1), + data.count - 2, nil, COMPRESSION_ZLIB) + return Data(bytes: buffer, count:read) + }) as Data + buffer.deallocate() + return result +} From 5513f4564c7ad3911ce0a4b9a50f978d3bd4c045 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Tue, 13 Apr 2021 16:09:22 +0200 Subject: [PATCH 03/26] Add JWK to SecKey function; Remove some crypt packages. --- PatientScannerDemo.xcodeproj/project.pbxproj | 38 +------- .../xcdebugger/Breakpoints_v2.xcbkptlist | 88 +++++++++++++++++++ PatientScannerDemo/JWK.swift | 48 ++++++++++ PatientScannerDemo/ViewController.swift | 7 +- 4 files changed, 143 insertions(+), 38 deletions(-) create mode 100644 PatientScannerDemo.xcodeproj/xcuserdata/user.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 PatientScannerDemo/JWK.swift diff --git a/PatientScannerDemo.xcodeproj/project.pbxproj b/PatientScannerDemo.xcodeproj/project.pbxproj index 4d61507..3849fee 100644 --- a/PatientScannerDemo.xcodeproj/project.pbxproj +++ b/PatientScannerDemo.xcodeproj/project.pbxproj @@ -7,8 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - CE5D7A392620D6CA001DFEEF /* SwiftJWKtoPEM in Frameworks */ = {isa = PBXBuildFile; productRef = CE5D7A382620D6CA001DFEEF /* SwiftJWKtoPEM */; }; - CE5D7A422620DA45001DFEEF /* CryptorECC in Frameworks */ = {isa = PBXBuildFile; productRef = CE5D7A412620DA45001DFEEF /* CryptorECC */; }; CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6EB261F8D2700715333 /* AppDelegate.swift */; }; CEA6D6EE261F8D2700715333 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6ED261F8D2700715333 /* SceneDelegate.swift */; }; CEA6D6F0261F8D2700715333 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6EF261F8D2700715333 /* ViewController.swift */; }; @@ -20,6 +18,7 @@ CEB78C4A261F8DA200FB8F68 /* Base45.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB78C48261F8DA200FB8F68 /* Base45.swift */; }; CEB78C4B261F8DA200FB8F68 /* ZLib.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB78C49261F8DA200FB8F68 /* ZLib.swift */; }; CEB78C57261F8DE100FB8F68 /* SwiftCBOR in Frameworks */ = {isa = PBXBuildFile; productRef = CEB78C56261F8DE100FB8F68 /* SwiftCBOR */; }; + CED0DCFE2625DC0100CBB5CE /* JWK.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED0DCFD2625DC0100CBB5CE /* JWK.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -56,6 +55,7 @@ CEA6D70F261F8D2900715333 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CEB78C48261F8DA200FB8F68 /* Base45.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Base45.swift; sourceTree = ""; }; CEB78C49261F8DA200FB8F68 /* ZLib.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZLib.swift; sourceTree = ""; }; + CED0DCFD2625DC0100CBB5CE /* JWK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JWK.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -63,9 +63,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CE5D7A422620DA45001DFEEF /* CryptorECC in Frameworks */, CEB78C57261F8DE100FB8F68 /* SwiftCBOR in Frameworks */, - CE5D7A392620D6CA001DFEEF /* SwiftJWKtoPEM in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -118,6 +116,7 @@ CEA6D6F4261F8D2900715333 /* Assets.xcassets */, CEA6D6F6261F8D2900715333 /* LaunchScreen.storyboard */, CEA6D6F9261F8D2900715333 /* Info.plist */, + CED0DCFD2625DC0100CBB5CE /* JWK.swift */, ); path = PatientScannerDemo; sourceTree = ""; @@ -158,8 +157,6 @@ name = PatientScannerDemo; packageProductDependencies = ( CEB78C56261F8DE100FB8F68 /* SwiftCBOR */, - CE5D7A382620D6CA001DFEEF /* SwiftJWKtoPEM */, - CE5D7A412620DA45001DFEEF /* CryptorECC */, ); productName = PatientScannerDemo; productReference = CEA6D6E8261F8D2700715333 /* PatientScannerDemo.app */; @@ -234,8 +231,6 @@ mainGroup = CEA6D6DF261F8D2700715333; packageReferences = ( CEB78C55261F8DE100FB8F68 /* XCRemoteSwiftPackageReference "SwiftCBOR" */, - CE5D7A372620D6CA001DFEEF /* XCRemoteSwiftPackageReference "Swift-JWK-to-PEM" */, - CE5D7A402620DA45001DFEEF /* XCRemoteSwiftPackageReference "BlueECC" */, ); productRefGroup = CEA6D6E9261F8D2700715333 /* Products */; projectDirPath = ""; @@ -282,6 +277,7 @@ files = ( CEB78C4B261F8DA200FB8F68 /* ZLib.swift in Sources */, CEA6D6F0261F8D2700715333 /* ViewController.swift in Sources */, + CED0DCFE2625DC0100CBB5CE /* JWK.swift in Sources */, CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */, CEA6D6EE261F8D2700715333 /* SceneDelegate.swift in Sources */, CEB78C4A261F8DA200FB8F68 /* Base45.swift in Sources */, @@ -619,22 +615,6 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - CE5D7A372620D6CA001DFEEF /* XCRemoteSwiftPackageReference "Swift-JWK-to-PEM" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ibm-cloud-security/Swift-JWK-to-PEM"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 0.4.0; - }; - }; - CE5D7A402620DA45001DFEEF /* XCRemoteSwiftPackageReference "BlueECC" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/Kitura/BlueECC"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.2.5; - }; - }; CEB78C55261F8DE100FB8F68 /* XCRemoteSwiftPackageReference "SwiftCBOR" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/unrelentingtech/SwiftCBOR"; @@ -646,16 +626,6 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - CE5D7A382620D6CA001DFEEF /* SwiftJWKtoPEM */ = { - isa = XCSwiftPackageProductDependency; - package = CE5D7A372620D6CA001DFEEF /* XCRemoteSwiftPackageReference "Swift-JWK-to-PEM" */; - productName = SwiftJWKtoPEM; - }; - CE5D7A412620DA45001DFEEF /* CryptorECC */ = { - isa = XCSwiftPackageProductDependency; - package = CE5D7A402620DA45001DFEEF /* XCRemoteSwiftPackageReference "BlueECC" */; - productName = CryptorECC; - }; CEB78C56261F8DE100FB8F68 /* SwiftCBOR */ = { isa = XCSwiftPackageProductDependency; package = CEB78C55261F8DE100FB8F68 /* XCRemoteSwiftPackageReference "SwiftCBOR" */; diff --git a/PatientScannerDemo.xcodeproj/xcuserdata/user.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/PatientScannerDemo.xcodeproj/xcuserdata/user.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..098e9a2 --- /dev/null +++ b/PatientScannerDemo.xcodeproj/xcuserdata/user.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PatientScannerDemo/JWK.swift b/PatientScannerDemo/JWK.swift new file mode 100644 index 0000000..558f47f --- /dev/null +++ b/PatientScannerDemo/JWK.swift @@ -0,0 +1,48 @@ +// +// JWK.swift +// PatientScannerDemo +// +// Created by Yannick Spreen on 4/13/21. +// +// https://medium.com/@vaibhav.pmeshram/creating-and-dismantling-ec-key-in-swift-f5bde8cb633f +// + +import Foundation + +struct JWK { + public func from(x: String, y: String) -> SecKey? { + var xStr = x // Base64 Formatted data + var yStr = y + + xStr = xStr.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/") + while xStr.count % 4 != 0 { + xStr.append("=") + } + yStr = yStr.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/") + while yStr.count % 4 != 0 { + yStr.append("=") + } + guard + let xBytes = Data(base64Encoded: xStr), + let yBytes = Data(base64Encoded: yStr) + else { return nil } + + // Now this bytes we have to append such that [0x04 , /* xBytes */, /* yBytes */, /* dBytes */] + // Initial byte for uncompressed y as Key. + let keyData = NSMutableData.init(bytes: [0x04], length: [0x04].count) + keyData.append(xBytes) + keyData.append(yBytes) + let attributes: [String: Any] = [ + kSecAttrKeyType as String: kSecAttrKeyTypeEC, + kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, + kSecAttrKeySizeInBits as String: 256, + kSecAttrIsPermanent as String: false + ] + var error: Unmanaged? + guard + let keyReference = SecKeyCreateWithData(keyData as CFData, attributes as CFDictionary, &error) + else { return nil } + + return keyReference + } +} diff --git a/PatientScannerDemo/ViewController.swift b/PatientScannerDemo/ViewController.swift index 5d46fe1..ac10aef 100644 --- a/PatientScannerDemo/ViewController.swift +++ b/PatientScannerDemo/ViewController.swift @@ -11,7 +11,6 @@ import UIKit import Vision import AVFoundation import SwiftCBOR -import SwiftJWKtoPEM //import CryptorECC @@ -178,9 +177,9 @@ extension ViewController { } """ - let key = try! RSAKey(jwk: keyString) - - let publicPem = try! key.getPublicKey() +// let key = try! RSAKey(jwk: keyString) +// +// let publicPem = try! key.getPublicKey() // let verifier = verifySignature(key: publicPem!.data(using: .utf8)!, signature: Data(signature), for: Data(payloadBytes)) // print(verifier) From f1799478b2af7b1126c9060fb1474f3e5c5723d7 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Tue, 13 Apr 2021 17:14:28 +0200 Subject: [PATCH 04/26] Reset project temporarily. --- PatientScannerDemo.xcodeproj/project.pbxproj | 45 +--------- .../xcshareddata/swiftpm/Package.resolved | 43 --------- .../xcdebugger/Breakpoints_v2.xcbkptlist | 88 ------------------- 3 files changed, 3 insertions(+), 173 deletions(-) delete mode 100644 PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved delete mode 100644 PatientScannerDemo.xcodeproj/xcuserdata/user.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist diff --git a/PatientScannerDemo.xcodeproj/project.pbxproj b/PatientScannerDemo.xcodeproj/project.pbxproj index 3849fee..81b6a2c 100644 --- a/PatientScannerDemo.xcodeproj/project.pbxproj +++ b/PatientScannerDemo.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -15,10 +15,6 @@ CEA6D6F8261F8D2900715333 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CEA6D6F6261F8D2900715333 /* LaunchScreen.storyboard */; }; CEA6D703261F8D2900715333 /* PatientScannerDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D702261F8D2900715333 /* PatientScannerDemoTests.swift */; }; CEA6D70E261F8D2900715333 /* PatientScannerDemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D70D261F8D2900715333 /* PatientScannerDemoUITests.swift */; }; - CEB78C4A261F8DA200FB8F68 /* Base45.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB78C48261F8DA200FB8F68 /* Base45.swift */; }; - CEB78C4B261F8DA200FB8F68 /* ZLib.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB78C49261F8DA200FB8F68 /* ZLib.swift */; }; - CEB78C57261F8DE100FB8F68 /* SwiftCBOR in Frameworks */ = {isa = PBXBuildFile; productRef = CEB78C56261F8DE100FB8F68 /* SwiftCBOR */; }; - CED0DCFE2625DC0100CBB5CE /* JWK.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED0DCFD2625DC0100CBB5CE /* JWK.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -53,9 +49,6 @@ CEA6D709261F8D2900715333 /* PatientScannerDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PatientScannerDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; CEA6D70D261F8D2900715333 /* PatientScannerDemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatientScannerDemoUITests.swift; sourceTree = ""; }; CEA6D70F261F8D2900715333 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - CEB78C48261F8DA200FB8F68 /* Base45.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Base45.swift; sourceTree = ""; }; - CEB78C49261F8DA200FB8F68 /* ZLib.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZLib.swift; sourceTree = ""; }; - CED0DCFD2625DC0100CBB5CE /* JWK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JWK.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -63,7 +56,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CEB78C57261F8DE100FB8F68 /* SwiftCBOR in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -107,8 +99,6 @@ CEA6D6EA261F8D2700715333 /* PatientScannerDemo */ = { isa = PBXGroup; children = ( - CEB78C48261F8DA200FB8F68 /* Base45.swift */, - CEB78C49261F8DA200FB8F68 /* ZLib.swift */, CEA6D6EB261F8D2700715333 /* AppDelegate.swift */, CEA6D6ED261F8D2700715333 /* SceneDelegate.swift */, CEA6D6EF261F8D2700715333 /* ViewController.swift */, @@ -116,7 +106,6 @@ CEA6D6F4261F8D2900715333 /* Assets.xcassets */, CEA6D6F6261F8D2900715333 /* LaunchScreen.storyboard */, CEA6D6F9261F8D2900715333 /* Info.plist */, - CED0DCFD2625DC0100CBB5CE /* JWK.swift */, ); path = PatientScannerDemo; sourceTree = ""; @@ -155,9 +144,6 @@ dependencies = ( ); name = PatientScannerDemo; - packageProductDependencies = ( - CEB78C56261F8DE100FB8F68 /* SwiftCBOR */, - ); productName = PatientScannerDemo; productReference = CEA6D6E8261F8D2700715333 /* PatientScannerDemo.app */; productType = "com.apple.product-type.application"; @@ -229,9 +215,6 @@ Base, ); mainGroup = CEA6D6DF261F8D2700715333; - packageReferences = ( - CEB78C55261F8DE100FB8F68 /* XCRemoteSwiftPackageReference "SwiftCBOR" */, - ); productRefGroup = CEA6D6E9261F8D2700715333 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -275,12 +258,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - CEB78C4B261F8DA200FB8F68 /* ZLib.swift in Sources */, CEA6D6F0261F8D2700715333 /* ViewController.swift in Sources */, - CED0DCFE2625DC0100CBB5CE /* JWK.swift in Sources */, CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */, CEA6D6EE261F8D2700715333 /* SceneDelegate.swift in Sources */, - CEB78C4A261F8DA200FB8F68 /* Base45.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -386,7 +366,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.1; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -441,7 +421,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.1; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -613,25 +593,6 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - CEB78C55261F8DE100FB8F68 /* XCRemoteSwiftPackageReference "SwiftCBOR" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/unrelentingtech/SwiftCBOR"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 0.4.3; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - CEB78C56261F8DE100FB8F68 /* SwiftCBOR */ = { - isa = XCSwiftPackageProductDependency; - package = CEB78C55261F8DE100FB8F68 /* XCRemoteSwiftPackageReference "SwiftCBOR" */; - productName = SwiftCBOR; - }; -/* End XCSwiftPackageProductDependency section */ }; rootObject = CEA6D6E0261F8D2700715333 /* Project object */; } diff --git a/PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 91582d9..0000000 --- a/PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,43 +0,0 @@ -{ - "object": { - "pins": [ - { - "package": "CryptorECC", - "repositoryURL": "https://github.com/Kitura/BlueECC", - "state": { - "branch": null, - "revision": "baf6ed3fc1a622675f0041b4aff7c02dd1a93818", - "version": "1.2.200" - } - }, - { - "package": "OpenSSL", - "repositoryURL": "https://github.com/IBM-Swift/OpenSSL.git", - "state": { - "branch": null, - "revision": "80b04f33b086fc90e28d9ae159d43705fb348e16", - "version": "2.2.200" - } - }, - { - "package": "SwiftJWKtoPEM", - "repositoryURL": "https://github.com/ibm-cloud-security/Swift-JWK-to-PEM", - "state": { - "branch": null, - "revision": "b18e2af1392bb84f1cde83b1e00847051c9fdda6", - "version": "0.4.0" - } - }, - { - "package": "SwiftCBOR", - "repositoryURL": "https://github.com/unrelentingtech/SwiftCBOR", - "state": { - "branch": null, - "revision": "668c26fc3373d5f1bccbaad7665ca6048797e324", - "version": "0.4.3" - } - } - ] - }, - "version": 1 -} diff --git a/PatientScannerDemo.xcodeproj/xcuserdata/user.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/PatientScannerDemo.xcodeproj/xcuserdata/user.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist deleted file mode 100644 index 098e9a2..0000000 --- a/PatientScannerDemo.xcodeproj/xcuserdata/user.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - From b0eb1bab692251b6da806dda63fc331b5f12588a Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Tue, 13 Apr 2021 17:25:30 +0200 Subject: [PATCH 05/26] Re-link existing code. --- PatientScannerDemo.xcodeproj/project.pbxproj | 41 ++++++++++++++++++- .../xcshareddata/swiftpm/Package.resolved | 16 ++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/PatientScannerDemo.xcodeproj/project.pbxproj b/PatientScannerDemo.xcodeproj/project.pbxproj index 81b6a2c..89d3447 100644 --- a/PatientScannerDemo.xcodeproj/project.pbxproj +++ b/PatientScannerDemo.xcodeproj/project.pbxproj @@ -3,10 +3,11 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ + CE7DE7FA2625EF18007E6694 /* SwiftCBOR in Frameworks */ = {isa = PBXBuildFile; productRef = CE7DE7F92625EF18007E6694 /* SwiftCBOR */; }; CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6EB261F8D2700715333 /* AppDelegate.swift */; }; CEA6D6EE261F8D2700715333 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6ED261F8D2700715333 /* SceneDelegate.swift */; }; CEA6D6F0261F8D2700715333 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6EF261F8D2700715333 /* ViewController.swift */; }; @@ -15,6 +16,9 @@ CEA6D6F8261F8D2900715333 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CEA6D6F6261F8D2900715333 /* LaunchScreen.storyboard */; }; CEA6D703261F8D2900715333 /* PatientScannerDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D702261F8D2900715333 /* PatientScannerDemoTests.swift */; }; CEA6D70E261F8D2900715333 /* PatientScannerDemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D70D261F8D2900715333 /* PatientScannerDemoUITests.swift */; }; + CEC2C4C22625ED030056E406 /* ZLib.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2C4BF2625ED030056E406 /* ZLib.swift */; }; + CEC2C4C32625ED030056E406 /* JWK.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2C4C02625ED030056E406 /* JWK.swift */; }; + CEC2C4C42625ED030056E406 /* Base45.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2C4C12625ED030056E406 /* Base45.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -49,6 +53,9 @@ CEA6D709261F8D2900715333 /* PatientScannerDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PatientScannerDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; CEA6D70D261F8D2900715333 /* PatientScannerDemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatientScannerDemoUITests.swift; sourceTree = ""; }; CEA6D70F261F8D2900715333 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CEC2C4BF2625ED030056E406 /* ZLib.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZLib.swift; sourceTree = ""; }; + CEC2C4C02625ED030056E406 /* JWK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JWK.swift; sourceTree = ""; }; + CEC2C4C12625ED030056E406 /* Base45.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Base45.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -56,6 +63,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + CE7DE7FA2625EF18007E6694 /* SwiftCBOR in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -99,6 +107,9 @@ CEA6D6EA261F8D2700715333 /* PatientScannerDemo */ = { isa = PBXGroup; children = ( + CEC2C4C12625ED030056E406 /* Base45.swift */, + CEC2C4C02625ED030056E406 /* JWK.swift */, + CEC2C4BF2625ED030056E406 /* ZLib.swift */, CEA6D6EB261F8D2700715333 /* AppDelegate.swift */, CEA6D6ED261F8D2700715333 /* SceneDelegate.swift */, CEA6D6EF261F8D2700715333 /* ViewController.swift */, @@ -144,6 +155,9 @@ dependencies = ( ); name = PatientScannerDemo; + packageProductDependencies = ( + CE7DE7F92625EF18007E6694 /* SwiftCBOR */, + ); productName = PatientScannerDemo; productReference = CEA6D6E8261F8D2700715333 /* PatientScannerDemo.app */; productType = "com.apple.product-type.application"; @@ -215,6 +229,9 @@ Base, ); mainGroup = CEA6D6DF261F8D2700715333; + packageReferences = ( + CE7DE7F82625EF18007E6694 /* XCRemoteSwiftPackageReference "SwiftCBOR" */, + ); productRefGroup = CEA6D6E9261F8D2700715333 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -258,8 +275,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CEC2C4C32625ED030056E406 /* JWK.swift in Sources */, + CEC2C4C42625ED030056E406 /* Base45.swift in Sources */, CEA6D6F0261F8D2700715333 /* ViewController.swift in Sources */, CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */, + CEC2C4C22625ED030056E406 /* ZLib.swift in Sources */, CEA6D6EE261F8D2700715333 /* SceneDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -593,6 +613,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + CE7DE7F82625EF18007E6694 /* XCRemoteSwiftPackageReference "SwiftCBOR" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/unrelentingtech/SwiftCBOR"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.4.3; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + CE7DE7F92625EF18007E6694 /* SwiftCBOR */ = { + isa = XCSwiftPackageProductDependency; + package = CE7DE7F82625EF18007E6694 /* XCRemoteSwiftPackageReference "SwiftCBOR" */; + productName = SwiftCBOR; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = CEA6D6E0261F8D2700715333 /* Project object */; } diff --git a/PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..bc6ed56 --- /dev/null +++ b/PatientScannerDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "SwiftCBOR", + "repositoryURL": "https://github.com/unrelentingtech/SwiftCBOR", + "state": { + "branch": null, + "revision": "668c26fc3373d5f1bccbaad7665ca6048797e324", + "version": "0.4.3" + } + } + ] + }, + "version": 1 +} From db108dd17eb0c7ffb7ed4893dc68d510d5c62040 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Tue, 13 Apr 2021 17:44:39 +0200 Subject: [PATCH 06/26] First attempt at verifying signature. --- PatientScannerDemo.xcodeproj/project.pbxproj | 8 +++ PatientScannerDemo/EC256.swift | 67 ++++++++++++++++++++ PatientScannerDemo/JWK.swift | 2 +- PatientScannerDemo/String+JSON.swift | 22 +++++++ PatientScannerDemo/ViewController.swift | 10 +++ 5 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 PatientScannerDemo/EC256.swift create mode 100644 PatientScannerDemo/String+JSON.swift diff --git a/PatientScannerDemo.xcodeproj/project.pbxproj b/PatientScannerDemo.xcodeproj/project.pbxproj index 89d3447..eaffd4a 100644 --- a/PatientScannerDemo.xcodeproj/project.pbxproj +++ b/PatientScannerDemo.xcodeproj/project.pbxproj @@ -19,6 +19,8 @@ CEC2C4C22625ED030056E406 /* ZLib.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2C4BF2625ED030056E406 /* ZLib.swift */; }; CEC2C4C32625ED030056E406 /* JWK.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2C4C02625ED030056E406 /* JWK.swift */; }; CEC2C4C42625ED030056E406 /* Base45.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2C4C12625ED030056E406 /* Base45.swift */; }; + CEFAD86D2625F164009AFEF9 /* EC256.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFAD86C2625F164009AFEF9 /* EC256.swift */; }; + CEFAD8722625F29E009AFEF9 /* String+JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFAD8712625F29E009AFEF9 /* String+JSON.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -56,6 +58,8 @@ CEC2C4BF2625ED030056E406 /* ZLib.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZLib.swift; sourceTree = ""; }; CEC2C4C02625ED030056E406 /* JWK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JWK.swift; sourceTree = ""; }; CEC2C4C12625ED030056E406 /* Base45.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Base45.swift; sourceTree = ""; }; + CEFAD86C2625F164009AFEF9 /* EC256.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EC256.swift; sourceTree = ""; }; + CEFAD8712625F29E009AFEF9 /* String+JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+JSON.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -107,6 +111,7 @@ CEA6D6EA261F8D2700715333 /* PatientScannerDemo */ = { isa = PBXGroup; children = ( + CEFAD8712625F29E009AFEF9 /* String+JSON.swift */, CEC2C4C12625ED030056E406 /* Base45.swift */, CEC2C4C02625ED030056E406 /* JWK.swift */, CEC2C4BF2625ED030056E406 /* ZLib.swift */, @@ -117,6 +122,7 @@ CEA6D6F4261F8D2900715333 /* Assets.xcassets */, CEA6D6F6261F8D2900715333 /* LaunchScreen.storyboard */, CEA6D6F9261F8D2900715333 /* Info.plist */, + CEFAD86C2625F164009AFEF9 /* EC256.swift */, ); path = PatientScannerDemo; sourceTree = ""; @@ -278,7 +284,9 @@ CEC2C4C32625ED030056E406 /* JWK.swift in Sources */, CEC2C4C42625ED030056E406 /* Base45.swift in Sources */, CEA6D6F0261F8D2700715333 /* ViewController.swift in Sources */, + CEFAD86D2625F164009AFEF9 /* EC256.swift in Sources */, CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */, + CEFAD8722625F29E009AFEF9 /* String+JSON.swift in Sources */, CEC2C4C22625ED030056E406 /* ZLib.swift in Sources */, CEA6D6EE261F8D2700715333 /* SceneDelegate.swift in Sources */, ); diff --git a/PatientScannerDemo/EC256.swift b/PatientScannerDemo/EC256.swift new file mode 100644 index 0000000..d620818 --- /dev/null +++ b/PatientScannerDemo/EC256.swift @@ -0,0 +1,67 @@ +// +// EC256.swift +// PatientScannerDemo +// +// Created by Yannick Spreen on 4/13/21. +// +// https://developer.apple.com/forums/thread/83136 +// + +import Foundation + +struct EC256 { + public static func verify(signature: Data, for data: Data, with publicKey: SecKey) -> Bool { + // create a certificate object +// let certURL = URL(fileURLWithPath: #file).appendingPathComponent("../../../cert.der").standardized +// let certData = NSData(contentsOf: certURL) + var error: Unmanaged? + +// +// // create a SecCertificate object +// +// var secCertificate: SecCertificate? +// +// if let certData = certData { +// secCertificate = SecCertificateCreateWithData(nil, certData) +// } +// +// // create a SecTrust object +// var trustCert: SecTrust? +// let secTrustError = SecTrustCreateWithCertificates(secCertificate!, nil, &trustCert) +// guard secTrustError == errSecSuccess else { +// return false +// } + + // read in OpenSSL generated signature + +// let openSSLSigURL = URL(fileURLWithPath: #file).appendingPathComponent("../../../signature.bin").standardized + let openSSLSig = signature + + + guard +// let trustCert = trustCert, + let targetsSignedData = data as NSData? + else { return false } + + // obtain public key from SecTrust object +// let publicKey = SecTrustCopyPublicKey(trustCert) + + // ensure key is of the correct algorithm + + guard SecKeyIsAlgorithmSupported(publicKey, .verify, SecKeyAlgorithm.ecdsaSignatureMessageX962SHA256) else { + return false + } + + // verify signature + if SecKeyVerifySignature(publicKey, SecKeyAlgorithm.ecdsaSignatureMessageX962SHA256, targetsSignedData, openSSLSig as NSData, &error) { + print("Verify Success!") + return true + } + else { + print("Verify Failed!") + print(error.debugDescription) + return false + } + } +} + diff --git a/PatientScannerDemo/JWK.swift b/PatientScannerDemo/JWK.swift index 558f47f..27b3671 100644 --- a/PatientScannerDemo/JWK.swift +++ b/PatientScannerDemo/JWK.swift @@ -10,7 +10,7 @@ import Foundation struct JWK { - public func from(x: String, y: String) -> SecKey? { + public static func from(x: String, y: String) -> SecKey? { var xStr = x // Base64 Formatted data var yStr = y diff --git a/PatientScannerDemo/String+JSON.swift b/PatientScannerDemo/String+JSON.swift new file mode 100644 index 0000000..dc6414b --- /dev/null +++ b/PatientScannerDemo/String+JSON.swift @@ -0,0 +1,22 @@ +// +// File.swift +// PatientScannerDemo +// +// Created by Yannick Spreen on 4/13/21. +// + +import Foundation + +extension String { + var asJSONDict: [String: AnyObject] { + if let data = data(using: .utf8) { + do { + let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:AnyObject] + return json ?? [:] + } catch { + return [:] + } + } + return [:] + } +} diff --git a/PatientScannerDemo/ViewController.swift b/PatientScannerDemo/ViewController.swift index ac10aef..addebc0 100644 --- a/PatientScannerDemo/ViewController.swift +++ b/PatientScannerDemo/ViewController.swift @@ -176,6 +176,16 @@ extension ViewController { "y": "bP7L_fysDzwFc13DDEPllO2qu1nDiDd1btoVQ-XlKok" } """ + let keyJWK = keyString.asJSONDict + + guard + let x = keyJWK["x"] as? String, + let y = keyJWK["y"] as? String, + let pubKey = JWK.from(x: x, y: y) + else { return } + + print(EC256.verify(signature: Data(signature), for: Data(payloadBytes), with: pubKey)) + // let key = try! RSAKey(jwk: keyString) // From 465461f586b5d06544e5808cbe9bbb48452974ef Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Tue, 13 Apr 2021 17:49:44 +0200 Subject: [PATCH 07/26] Print errors; Change key to public. --- PatientScannerDemo/EC256.swift | 2 +- PatientScannerDemo/JWK.swift | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/PatientScannerDemo/EC256.swift b/PatientScannerDemo/EC256.swift index d620818..4a9897c 100644 --- a/PatientScannerDemo/EC256.swift +++ b/PatientScannerDemo/EC256.swift @@ -59,7 +59,7 @@ struct EC256 { } else { print("Verify Failed!") - print(error.debugDescription) + print(error?.takeUnretainedValue().localizedDescription ?? "Something went wrong") return false } } diff --git a/PatientScannerDemo/JWK.swift b/PatientScannerDemo/JWK.swift index 27b3671..8e4ee92 100644 --- a/PatientScannerDemo/JWK.swift +++ b/PatientScannerDemo/JWK.swift @@ -34,14 +34,17 @@ struct JWK { keyData.append(yBytes) let attributes: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeEC, - kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, + kSecAttrKeyClass as String: kSecAttrKeyClassPublic, kSecAttrKeySizeInBits as String: 256, kSecAttrIsPermanent as String: false ] var error: Unmanaged? guard let keyReference = SecKeyCreateWithData(keyData as CFData, attributes as CFDictionary, &error) - else { return nil } + else { + print(error?.takeUnretainedValue().localizedDescription ?? "Something went wrong") + return nil + } return keyReference } From 1edab61086fb730aad45dcb84f39de8ae181c4ea Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Tue, 13 Apr 2021 17:57:50 +0200 Subject: [PATCH 08/26] Clean up EC256. --- PatientScannerDemo/EC256.swift | 49 +++++++--------------------------- 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/PatientScannerDemo/EC256.swift b/PatientScannerDemo/EC256.swift index 4a9897c..bb3d66b 100644 --- a/PatientScannerDemo/EC256.swift +++ b/PatientScannerDemo/EC256.swift @@ -11,54 +11,25 @@ import Foundation struct EC256 { public static func verify(signature: Data, for data: Data, with publicKey: SecKey) -> Bool { - // create a certificate object -// let certURL = URL(fileURLWithPath: #file).appendingPathComponent("../../../cert.der").standardized -// let certData = NSData(contentsOf: certURL) var error: Unmanaged? + let targetsSignedData = data as NSData -// -// // create a SecCertificate object -// -// var secCertificate: SecCertificate? -// -// if let certData = certData { -// secCertificate = SecCertificateCreateWithData(nil, certData) -// } -// -// // create a SecTrust object -// var trustCert: SecTrust? -// let secTrustError = SecTrustCreateWithCertificates(secCertificate!, nil, &trustCert) -// guard secTrustError == errSecSuccess else { -// return false -// } - - // read in OpenSSL generated signature - -// let openSSLSigURL = URL(fileURLWithPath: #file).appendingPathComponent("../../../signature.bin").standardized - let openSSLSig = signature - - - guard -// let trustCert = trustCert, - let targetsSignedData = data as NSData? - else { return false } - - // obtain public key from SecTrust object -// let publicKey = SecTrustCopyPublicKey(trustCert) - - // ensure key is of the correct algorithm - - guard SecKeyIsAlgorithmSupported(publicKey, .verify, SecKeyAlgorithm.ecdsaSignatureMessageX962SHA256) else { + guard SecKeyIsAlgorithmSupported(publicKey, .verify, .ecdsaSignatureMessageX962SHA256) else { + print("Pubkey not supported.") return false } // verify signature - if SecKeyVerifySignature(publicKey, SecKeyAlgorithm.ecdsaSignatureMessageX962SHA256, targetsSignedData, openSSLSig as NSData, &error) { - print("Verify Success!") + if SecKeyVerifySignature( + publicKey, + SecKeyAlgorithm.ecdsaSignatureMessageX962SHA256, + targetsSignedData, + signature as NSData, + &error + ) { return true } else { - print("Verify Failed!") print(error?.takeUnretainedValue().localizedDescription ?? "Something went wrong") return false } From 4c9447285f4572adbc91c604ba35ce0e37454aea Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Wed, 14 Apr 2021 14:57:53 +0200 Subject: [PATCH 09/26] Add hexString methods; Clean up VC. --- PatientScannerDemo.xcodeproj/project.pbxproj | 12 ++++++++ PatientScannerDemo/Data+hexString.swift | 29 ++++++++++++++++++++ PatientScannerDemo/ViewController.swift | 10 ++++--- 3 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 PatientScannerDemo/Data+hexString.swift diff --git a/PatientScannerDemo.xcodeproj/project.pbxproj b/PatientScannerDemo.xcodeproj/project.pbxproj index eaffd4a..c0ccf8b 100644 --- a/PatientScannerDemo.xcodeproj/project.pbxproj +++ b/PatientScannerDemo.xcodeproj/project.pbxproj @@ -21,6 +21,9 @@ CEC2C4C42625ED030056E406 /* Base45.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2C4C12625ED030056E406 /* Base45.swift */; }; CEFAD86D2625F164009AFEF9 /* EC256.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFAD86C2625F164009AFEF9 /* EC256.swift */; }; CEFAD8722625F29E009AFEF9 /* String+JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFAD8712625F29E009AFEF9 /* String+JSON.swift */; }; + CEFAD87A26271414009AFEF9 /* COSE.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFAD87926271414009AFEF9 /* COSE.swift */; }; + CEFAD87F262714C4009AFEF9 /* EHNTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFAD87E262714C4009AFEF9 /* EHNTests.swift */; }; + CEFAD88726271B9A009AFEF9 /* Data+hexString.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFAD88626271B9A009AFEF9 /* Data+hexString.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -60,6 +63,9 @@ CEC2C4C12625ED030056E406 /* Base45.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Base45.swift; sourceTree = ""; }; CEFAD86C2625F164009AFEF9 /* EC256.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EC256.swift; sourceTree = ""; }; CEFAD8712625F29E009AFEF9 /* String+JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+JSON.swift"; sourceTree = ""; }; + CEFAD87926271414009AFEF9 /* COSE.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = COSE.swift; sourceTree = ""; }; + CEFAD87E262714C4009AFEF9 /* EHNTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EHNTests.swift; sourceTree = ""; }; + CEFAD88626271B9A009AFEF9 /* Data+hexString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+hexString.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -123,6 +129,8 @@ CEA6D6F6261F8D2900715333 /* LaunchScreen.storyboard */, CEA6D6F9261F8D2900715333 /* Info.plist */, CEFAD86C2625F164009AFEF9 /* EC256.swift */, + CEFAD87926271414009AFEF9 /* COSE.swift */, + CEFAD88626271B9A009AFEF9 /* Data+hexString.swift */, ); path = PatientScannerDemo; sourceTree = ""; @@ -132,6 +140,7 @@ children = ( CEA6D702261F8D2900715333 /* PatientScannerDemoTests.swift */, CEA6D704261F8D2900715333 /* Info.plist */, + CEFAD87E262714C4009AFEF9 /* EHNTests.swift */, ); path = PatientScannerDemoTests; sourceTree = ""; @@ -281,9 +290,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CEFAD88726271B9A009AFEF9 /* Data+hexString.swift in Sources */, CEC2C4C32625ED030056E406 /* JWK.swift in Sources */, CEC2C4C42625ED030056E406 /* Base45.swift in Sources */, CEA6D6F0261F8D2700715333 /* ViewController.swift in Sources */, + CEFAD87A26271414009AFEF9 /* COSE.swift in Sources */, CEFAD86D2625F164009AFEF9 /* EC256.swift in Sources */, CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */, CEFAD8722625F29E009AFEF9 /* String+JSON.swift in Sources */, @@ -297,6 +308,7 @@ buildActionMask = 2147483647; files = ( CEA6D703261F8D2900715333 /* PatientScannerDemoTests.swift in Sources */, + CEFAD87F262714C4009AFEF9 /* EHNTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/PatientScannerDemo/Data+hexString.swift b/PatientScannerDemo/Data+hexString.swift new file mode 100644 index 0000000..00cdd59 --- /dev/null +++ b/PatientScannerDemo/Data+hexString.swift @@ -0,0 +1,29 @@ +// +// Data+hexString.swift +// PatientScannerDemo +// +// Created by Yannick Spreen on 4/14/21. +// + +import Foundation + +extension Data { + init?(hexString: String) { + let len = hexString.count / 2 + var data = Data(capacity: len) + var i = hexString.startIndex + for _ in 0.. Date: Wed, 14 Apr 2021 14:58:10 +0200 Subject: [PATCH 10/26] Add EHNTest by DWvG. --- PatientScannerDemoTests/EHNTests.swift | 142 +++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 PatientScannerDemoTests/EHNTests.swift diff --git a/PatientScannerDemoTests/EHNTests.swift b/PatientScannerDemoTests/EHNTests.swift new file mode 100644 index 0000000..20fee87 --- /dev/null +++ b/PatientScannerDemoTests/EHNTests.swift @@ -0,0 +1,142 @@ +// +// EHNTests.swift +// EHNTests +// +// Created by Dirk-Willem van Gulik on 01/04/2021. +// + +@testable import PatientScannerDemo +import XCTest +import CryptoKit +import SwiftCBOR + +var barcode = "HC1NCFY70R30FFWTWGSLKC 4O992$V M63TMF2V*D9LPC.3EHPCGEC27B72VF/347O4-M6Y9M6FOYG4ILDEI8GR3ZI$15MABL:E9CVBGEEWRMLE C39S0/ANZ52T82Z-73D63P1U 1$PKC 72H2XX09WDH889V5" + +let trust_json = """ +[ + { + \"kid\" : \"DEFBBA3378B322F5\", + \"coord\" : [ + \"230ca0433313f4ef14ec0ab0477b241781d135ee09369507fcf44ca988ed09d6\", + \"bf1bfe3d2bda606c841242b59c568d00e5c8dd114d223b2f5036d8c5bc68bf5d\" + ] + }, + { + \"kid\" : \"FFFBBA3378B322F5\", + \"coord\" : [ + \"9999a0433313f4ef14ec0ab0477b241781d135ee09369507fcf44ca988ed09d6\", + \"9999fe3d2bda606c841242b59c568d00e5c8dd114d223b2f5036d8c5bc68bf5d\" + ] + } +] +""" + + +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 + if (barcode.hasPrefix("HC1")) { + barcode = String(barcode.suffix(barcode.count-3)) + } + + guard + let compressed = try? barcode.fromBase45() + else { return } + + let data = decompress(compressed) + let decoder = SwiftCBOR.CBORDecoder(input: data.uint) + + guard + let cose = try? decoder.decodeItem(), + case let CBOR.tagged(tag, cborElement) = cose, + 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 { + 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 + } + + 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) + + var publicKey: P256.Signing.PublicKey + 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() + } + var x: [UInt8] = [] + var y: [UInt8] = [] + + let _ = unprotected // unused + + do { + 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) + + publicKey = try! P256.Signing.PublicKey(x963Representation: rawk) + + if (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 --") + } + print("Nope - all failed - sadness all around") + assert(false) + } + } + } catch let error as NSError { + print("JSON parse trust list failed: ",error.localizedDescription) + } + } +} From 2329b18f1f32a746efa2655367666be7c704d99e Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Wed, 14 Apr 2021 16:02:10 +0200 Subject: [PATCH 11/26] Clean up test code. --- PatientScannerDemoTests/EHNTests.swift | 70 +++++++++++++++----------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/PatientScannerDemoTests/EHNTests.swift b/PatientScannerDemoTests/EHNTests.swift index 20fee87..fcc69a7 100644 --- a/PatientScannerDemoTests/EHNTests.swift +++ b/PatientScannerDemoTests/EHNTests.swift @@ -46,7 +46,10 @@ class EHNTests: XCTestCase { guard let compressed = try? barcode.fromBase45() - else { return } + else { + assert(false) + return + } let data = decompress(compressed) let decoder = SwiftCBOR.CBORDecoder(input: data.uint) @@ -64,6 +67,7 @@ class EHNTests: XCTestCase { let payload = try? CBOR.decode(payloadBytes), case let CBOR.map(protectedMap) = protected else { + assert(false) return } var kid: [UInt8] = [] @@ -72,6 +76,8 @@ class EHNTests: XCTestCase { print("SIG: ", sig) if case let CBOR.byteString(k) = protectedMap[COSE_PHDR_KID] ?? .null { kid = k + } else { + assert(false) } print("Signature: ", signature) @@ -92,7 +98,6 @@ class EHNTests: XCTestCase { let digest = SHA256.hash(data: signed_payload) print("Digest: ", digest) - var publicKey: P256.Signing.PublicKey let signatureForData = try! P256.Signing.ECDSASignature(rawRepresentation: signature) // use KID to find the right X,Y coordinates from the JSON @@ -107,36 +112,43 @@ class EHNTests: XCTestCase { let _ = unprotected // unused - do { - 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) - - publicKey = try! P256.Signing.PublicKey(x963Representation: rawk) - - if (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 --") + 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("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") + assert(false) } - } catch let error as NSError { - print("JSON parse trust list failed: ",error.localizedDescription) } } } + +/** + + Produces: + + All is WELL ! + Payload (decoded) + byteString([161, 99, 102, 111, 111, 99, 98, 97, 114]) + + */ From 5a632a344e1a8f147f3d2711c42609d589e47650 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Wed, 14 Apr 2021 16:37:28 +0200 Subject: [PATCH 12/26] Add COSE Service. --- PatientScannerDemo/COSE.swift | 69 ++++++++++++++++ PatientScannerDemoTests/EHNTests.swift | 107 +++++++------------------ 2 files changed, 99 insertions(+), 77 deletions(-) create mode 100644 PatientScannerDemo/COSE.swift 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")]) + */ From 74e80f6b824b600fd83f35cdc488a33fe500b068 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Thu, 15 Apr 2021 00:31:30 +0200 Subject: [PATCH 13/26] Remove unneeded struct. --- PatientScannerDemo/COSE.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/PatientScannerDemo/COSE.swift b/PatientScannerDemo/COSE.swift index 73d15ba..66ddd81 100644 --- a/PatientScannerDemo/COSE.swift +++ b/PatientScannerDemo/COSE.swift @@ -46,12 +46,6 @@ struct COSE { 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 From 1ee4bc51eeed3e0dd918782ed171fe5444f20f9c Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Thu, 15 Apr 2021 18:59:30 +0200 Subject: [PATCH 14/26] Add Asn1Encoder. --- PatientScannerDemo/Asn1Encoder.swift | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 PatientScannerDemo/Asn1Encoder.swift diff --git a/PatientScannerDemo/Asn1Encoder.swift b/PatientScannerDemo/Asn1Encoder.swift new file mode 100644 index 0000000..4085186 --- /dev/null +++ b/PatientScannerDemo/Asn1Encoder.swift @@ -0,0 +1,34 @@ +// +// Asn1Encoder.swift +// OegvatClient +// +// Created by Christian Kollmann on 23.04.20. +// + +import Foundation + +public class Asn1Encoder { + + public init() {} + + // 32 for ES256 + public func convertRawSignatureIntoAsn1(_ data: Data, _ digestLengthInBytes: Int = 32) -> Data { + let sigR = encodeIntegerToAsn1(data.prefix(data.count - digestLengthInBytes)) + let sigS = encodeIntegerToAsn1(data.suffix(digestLengthInBytes)) + let tagSequence: UInt8 = 0x30 + return Data([tagSequence] + [UInt8(sigR.count + sigS.count)] + sigR + sigS) + } + + private func encodeIntegerToAsn1(_ data: Data) -> Data { + let firstBitIsSet: UInt8 = 0x80 // would be decoded as a negative number + let tagInteger: UInt8 = 0x02 + if (data.first! >= firstBitIsSet) { + return Data([tagInteger] + [UInt8(data.count + 1)] + [0x00] + data) + } else if (data.first! == 0x00) { + return encodeIntegerToAsn1(data.dropFirst()) + } else { + return Data([tagInteger] + [UInt8(data.count)] + data) + } + } + +} From 96c7896ea90cbf5dfc2d65fae1ce170571910902 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Thu, 15 Apr 2021 19:02:12 +0200 Subject: [PATCH 15/26] Abandon CryptoKit. --- PatientScannerDemo.xcodeproj/project.pbxproj | 4 ++ .../{Asn1Encoder.swift => ASN1.swift} | 9 +-- PatientScannerDemo/COSE.swift | 36 +++--------- PatientScannerDemo/EC256.swift | 31 +++++----- PatientScannerDemo/JWK.swift | 58 +++++++++++-------- PatientScannerDemo/ViewController.swift | 2 +- PatientScannerDemoTests/EHNTests.swift | 43 +++++++------- 7 files changed, 84 insertions(+), 99 deletions(-) rename PatientScannerDemo/{Asn1Encoder.swift => ASN1.swift} (79%) diff --git a/PatientScannerDemo.xcodeproj/project.pbxproj b/PatientScannerDemo.xcodeproj/project.pbxproj index c0ccf8b..619ec16 100644 --- a/PatientScannerDemo.xcodeproj/project.pbxproj +++ b/PatientScannerDemo.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + CE3CC93C2628A7820079FB78 /* ASN1.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3CC93B2628A7820079FB78 /* ASN1.swift */; }; CE7DE7FA2625EF18007E6694 /* SwiftCBOR in Frameworks */ = {isa = PBXBuildFile; productRef = CE7DE7F92625EF18007E6694 /* SwiftCBOR */; }; CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6EB261F8D2700715333 /* AppDelegate.swift */; }; CEA6D6EE261F8D2700715333 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6ED261F8D2700715333 /* SceneDelegate.swift */; }; @@ -44,6 +45,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + CE3CC93B2628A7820079FB78 /* ASN1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1.swift; sourceTree = ""; }; CEA6D6E8261F8D2700715333 /* PatientScannerDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PatientScannerDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; CEA6D6EB261F8D2700715333 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; CEA6D6ED261F8D2700715333 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -129,6 +131,7 @@ CEA6D6F6261F8D2900715333 /* LaunchScreen.storyboard */, CEA6D6F9261F8D2900715333 /* Info.plist */, CEFAD86C2625F164009AFEF9 /* EC256.swift */, + CE3CC93B2628A7820079FB78 /* ASN1.swift */, CEFAD87926271414009AFEF9 /* COSE.swift */, CEFAD88626271B9A009AFEF9 /* Data+hexString.swift */, ); @@ -294,6 +297,7 @@ CEC2C4C32625ED030056E406 /* JWK.swift in Sources */, CEC2C4C42625ED030056E406 /* Base45.swift in Sources */, CEA6D6F0261F8D2700715333 /* ViewController.swift in Sources */, + CE3CC93C2628A7820079FB78 /* ASN1.swift in Sources */, CEFAD87A26271414009AFEF9 /* COSE.swift in Sources */, CEFAD86D2625F164009AFEF9 /* EC256.swift in Sources */, CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */, diff --git a/PatientScannerDemo/Asn1Encoder.swift b/PatientScannerDemo/ASN1.swift similarity index 79% rename from PatientScannerDemo/Asn1Encoder.swift rename to PatientScannerDemo/ASN1.swift index 4085186..badc02d 100644 --- a/PatientScannerDemo/Asn1Encoder.swift +++ b/PatientScannerDemo/ASN1.swift @@ -7,19 +7,16 @@ import Foundation -public class Asn1Encoder { - - public init() {} - +public class ASN1 { // 32 for ES256 - public func convertRawSignatureIntoAsn1(_ data: Data, _ digestLengthInBytes: Int = 32) -> Data { + public static func signature(from data: Data, _ digestLengthInBytes: Int = 32) -> Data { let sigR = encodeIntegerToAsn1(data.prefix(data.count - digestLengthInBytes)) let sigS = encodeIntegerToAsn1(data.suffix(digestLengthInBytes)) let tagSequence: UInt8 = 0x30 return Data([tagSequence] + [UInt8(sigR.count + sigS.count)] + sigR + sigS) } - private func encodeIntegerToAsn1(_ data: Data) -> Data { + private static func encodeIntegerToAsn1(_ data: Data) -> Data { let firstBitIsSet: UInt8 = 0x80 // would be decoded as a negative number let tagInteger: UInt8 = 0x02 if (data.first! >= firstBitIsSet) { diff --git a/PatientScannerDemo/COSE.swift b/PatientScannerDemo/COSE.swift index 66ddd81..3afe71b 100644 --- a/PatientScannerDemo/COSE.swift +++ b/PatientScannerDemo/COSE.swift @@ -7,25 +7,17 @@ import Foundation import SwiftCBOR -import CryptoKit +//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] + case let CBOR.byteString(signature) = array[3] else { return false } @@ -38,26 +30,12 @@ struct COSE { 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 { + let d = Data(signedPayload)//Data(bytes: signedPayload, count: signedPayload.count) +// let digest = SHA256.hash(data: signedPayload) + let s = Data(signature)//Data(bytes: signature, count: signature.count) + guard let key = JWK.ecFrom(x: xHex, y: yHex) else { return false } - - 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 + return EC256.verify(signature: s, for: d, with: key) } } diff --git a/PatientScannerDemo/EC256.swift b/PatientScannerDemo/EC256.swift index bb3d66b..55e6ece 100644 --- a/PatientScannerDemo/EC256.swift +++ b/PatientScannerDemo/EC256.swift @@ -11,28 +11,27 @@ import Foundation struct EC256 { public static func verify(signature: Data, for data: Data, with publicKey: SecKey) -> Bool { - var error: Unmanaged? - let targetsSignedData = data as NSData - guard SecKeyIsAlgorithmSupported(publicKey, .verify, .ecdsaSignatureMessageX962SHA256) else { print("Pubkey not supported.") return false } + let sig = ASN1.signature(from: signature) + // verify signature - if SecKeyVerifySignature( - publicKey, - SecKeyAlgorithm.ecdsaSignatureMessageX962SHA256, - targetsSignedData, - signature as NSData, - &error - ) { - return true - } - else { - print(error?.takeUnretainedValue().localizedDescription ?? "Something went wrong") - return false + var error: Unmanaged? + let result = SecKeyVerifySignature( + publicKey, + .ecdsaSignatureMessageX962SHA256, + data as NSData, + sig as NSData, + &error + ) + if let err = error?.takeUnretainedValue().localizedDescription { + print(err) } + error?.release() + + return result } } - diff --git a/PatientScannerDemo/JWK.swift b/PatientScannerDemo/JWK.swift index 8e4ee92..fb7adba 100644 --- a/PatientScannerDemo/JWK.swift +++ b/PatientScannerDemo/JWK.swift @@ -10,42 +10,50 @@ import Foundation struct JWK { - public static func from(x: String, y: String) -> SecKey? { - var xStr = x // Base64 Formatted data - var yStr = y + public static func ecFrom(x: String, y: String) -> SecKey? { + var xBytes: Data? + var yBytes: Data? + if (x + y).count == 128 { + xBytes = Data(hexString: x) + yBytes = Data(hexString: y) + } else { + var xStr = x // Base64 Formatted data + var yStr = y - xStr = xStr.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/") - while xStr.count % 4 != 0 { - xStr.append("=") + xStr = xStr.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/") + while xStr.count % 4 != 0 { + xStr.append("=") + } + yStr = yStr.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/") + while yStr.count % 4 != 0 { + yStr.append("=") + } + xBytes = Data(base64Encoded: xStr) + yBytes = Data(base64Encoded: yStr) } - yStr = yStr.replacingOccurrences(of: "-", with: "+").replacingOccurrences(of: "_", with: "/") - while yStr.count % 4 != 0 { - yStr.append("=") - } - guard - let xBytes = Data(base64Encoded: xStr), - let yBytes = Data(base64Encoded: yStr) - else { return nil } - // Now this bytes we have to append such that [0x04 , /* xBytes */, /* yBytes */, /* dBytes */] + // Now this bytes we have to append such that [0x04 , /* xBytes */, /* yBytes */] // Initial byte for uncompressed y as Key. - let keyData = NSMutableData.init(bytes: [0x04], length: [0x04].count) - keyData.append(xBytes) - keyData.append(yBytes) + let keyData = NSMutableData.init(bytes: [0x04], length: 1) + keyData.append(xBytes ?? Data()) + keyData.append(yBytes ?? Data()) let attributes: [String: Any] = [ - kSecAttrKeyType as String: kSecAttrKeyTypeEC, - kSecAttrKeyClass as String: kSecAttrKeyClassPublic, - kSecAttrKeySizeInBits as String: 256, - kSecAttrIsPermanent as String: false + String(kSecAttrKeyType): kSecAttrKeyTypeEC, + String(kSecAttrKeyClass): kSecAttrKeyClassPublic, + String(kSecAttrKeySizeInBits): 256, + String(kSecAttrIsPermanent): false ] var error: Unmanaged? + let keyReference = SecKeyCreateWithData(keyData as CFData, attributes as CFDictionary, &error) + let errorString = error?.takeUnretainedValue().localizedDescription ?? "Something went wrong" + error?.release() guard - let keyReference = SecKeyCreateWithData(keyData as CFData, attributes as CFDictionary, &error) + let key = keyReference else { - print(error?.takeUnretainedValue().localizedDescription ?? "Something went wrong") + print(errorString) return nil } - return keyReference + return key } } diff --git a/PatientScannerDemo/ViewController.swift b/PatientScannerDemo/ViewController.swift index e6422c1..8306eab 100644 --- a/PatientScannerDemo/ViewController.swift +++ b/PatientScannerDemo/ViewController.swift @@ -183,7 +183,7 @@ extension ViewController { guard let x = keyJWK["x"] as? String, let y = keyJWK["y"] as? String, - let pubKey = JWK.from(x: x, y: y) + let pubKey = JWK.ecFrom(x: x, y: y) else { return } print(EC256.verify(signature: Data(signature), for: Data(payloadBytes), with: pubKey)) diff --git a/PatientScannerDemoTests/EHNTests.swift b/PatientScannerDemoTests/EHNTests.swift index 92b07f7..e275320 100644 --- a/PatientScannerDemoTests/EHNTests.swift +++ b/PatientScannerDemoTests/EHNTests.swift @@ -10,30 +10,29 @@ import XCTest import CryptoKit import SwiftCBOR -var barcode = "HC1NCFY70R30FFWTWGSLKC 4O992$V M63TMF2V*D9LPC.3EHPCGEC27B72VF/347O4-M6Y9M6FOYG4ILDEI8GR3ZI$15MABL:E9CVBGEEWRMLE C39S0/ANZ52T82Z-73D63P1U 1$PKC 72H2XX09WDH889V5" - -let trustJson = """ -[ - { - \"kid\" : \"DEFBBA3378B322F5\", - \"coord\" : [ - \"230ca0433313f4ef14ec0ab0477b241781d135ee09369507fcf44ca988ed09d6\", - \"bf1bfe3d2bda606c841242b59c568d00e5c8dd114d223b2f5036d8c5bc68bf5d\" - ] - }, - { - \"kid\" : \"FFFBBA3378B322F5\", - \"coord\" : [ - \"9999a0433313f4ef14ec0ab0477b241781d135ee09369507fcf44ca988ed09d6\", - \"9999fe3d2bda606c841242b59c568d00e5c8dd114d223b2f5036d8c5bc68bf5d\" - ] - } -] -""" - - class EHNTests: XCTestCase { func test_cose() throws { + + var barcode = "HC1NCFY70R30FFWTWGSLKC 4O992$V M63TMF2V*D9LPC.3EHPCGEC27B72VF/347O4-M6Y9M6FOYG4ILDEI8GR3ZI$15MABL:E9CVBGEEWRMLE C39S0/ANZ52T82Z-73D63P1U 1$PKC 72H2XX09WDH889V5" + + let trustJson = """ + [ + { + \"kid\" : \"DEFBBA3378B322F5\", + \"coord\" : [ + \"230ca0433313f4ef14ec0ab0477b241781d135ee09369507fcf44ca988ed09d6\", + \"bf1bfe3d2bda606c841242b59c568d00e5c8dd114d223b2f5036d8c5bc68bf5d\" + ] + }, + { + \"kid\" : \"FFFBBA3378B322F5\", + \"coord\" : [ + \"9999a0433313f4ef14ec0ab0477b241781d135ee09369507fcf44ca988ed09d6\", + \"9999fe3d2bda606c841242b59c568d00e5c8dd114d223b2f5036d8c5bc68bf5d\" + ] + } + ] + """ let COSE_TAG = UInt64(18) let COSE_PHDR_KID = CBOR.unsignedInt(4) From b430e5606b8791283ceff44682d6e8cf74efc29e Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Thu, 15 Apr 2021 20:55:32 +0200 Subject: [PATCH 16/26] Clean up code. --- PatientScannerDemo.xcodeproj/project.pbxproj | 4 ++ PatientScannerDemo/Base45.swift | 2 +- PatientScannerDemo/CBOR.swift | 48 ++++++++++++++++ PatientScannerDemo/COSE.swift | 26 +++++---- PatientScannerDemo/ViewController.swift | 60 +------------------- PatientScannerDemoTests/EHNTests.swift | 29 ++-------- 6 files changed, 74 insertions(+), 95 deletions(-) create mode 100644 PatientScannerDemo/CBOR.swift diff --git a/PatientScannerDemo.xcodeproj/project.pbxproj b/PatientScannerDemo.xcodeproj/project.pbxproj index 619ec16..72e86a0 100644 --- a/PatientScannerDemo.xcodeproj/project.pbxproj +++ b/PatientScannerDemo.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ CE3CC93C2628A7820079FB78 /* ASN1.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3CC93B2628A7820079FB78 /* ASN1.swift */; }; + CE3CC9442628C2130079FB78 /* CBOR.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3CC9432628C2130079FB78 /* CBOR.swift */; }; CE7DE7FA2625EF18007E6694 /* SwiftCBOR in Frameworks */ = {isa = PBXBuildFile; productRef = CE7DE7F92625EF18007E6694 /* SwiftCBOR */; }; CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6EB261F8D2700715333 /* AppDelegate.swift */; }; CEA6D6EE261F8D2700715333 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6D6ED261F8D2700715333 /* SceneDelegate.swift */; }; @@ -46,6 +47,7 @@ /* Begin PBXFileReference section */ CE3CC93B2628A7820079FB78 /* ASN1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1.swift; sourceTree = ""; }; + CE3CC9432628C2130079FB78 /* CBOR.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBOR.swift; sourceTree = ""; }; CEA6D6E8261F8D2700715333 /* PatientScannerDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PatientScannerDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; CEA6D6EB261F8D2700715333 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; CEA6D6ED261F8D2700715333 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -134,6 +136,7 @@ CE3CC93B2628A7820079FB78 /* ASN1.swift */, CEFAD87926271414009AFEF9 /* COSE.swift */, CEFAD88626271B9A009AFEF9 /* Data+hexString.swift */, + CE3CC9432628C2130079FB78 /* CBOR.swift */, ); path = PatientScannerDemo; sourceTree = ""; @@ -296,6 +299,7 @@ CEFAD88726271B9A009AFEF9 /* Data+hexString.swift in Sources */, CEC2C4C32625ED030056E406 /* JWK.swift in Sources */, CEC2C4C42625ED030056E406 /* Base45.swift in Sources */, + CE3CC9442628C2130079FB78 /* CBOR.swift in Sources */, CEA6D6F0261F8D2700715333 /* ViewController.swift in Sources */, CE3CC93C2628A7820079FB78 /* ASN1.swift in Sources */, CEFAD87A26271414009AFEF9 /* COSE.swift in Sources */, diff --git a/PatientScannerDemo/Base45.swift b/PatientScannerDemo/Base45.swift index a4453ef..a9ea3e1 100644 --- a/PatientScannerDemo/Base45.swift +++ b/PatientScannerDemo/Base45.swift @@ -1,9 +1,9 @@ // // Base45.swift -// Base45-Swift // // Created by Dirk-Willem van Gulik on 01/04/2021. // + import Foundation extension String { diff --git a/PatientScannerDemo/CBOR.swift b/PatientScannerDemo/CBOR.swift new file mode 100644 index 0000000..2bb19ba --- /dev/null +++ b/PatientScannerDemo/CBOR.swift @@ -0,0 +1,48 @@ +// +// CBOR.swift +// PatientScannerDemo +// +// Created by Yannick Spreen on 4/15/21. +// + +import Foundation +import SwiftCBOR + +struct CBOR { + public static func payload(from data: Data) -> SwiftCBOR.CBOR? { + let COSE_TAG = UInt64(18) + let decoder = SwiftCBOR.CBORDecoder(input: data.uint) + + guard + let cbor = try? decoder.decodeItem(), + case let SwiftCBOR.CBOR.tagged(tag, cborElement) = cbor, + tag.rawValue == COSE_TAG, // SIGN1 + case let SwiftCBOR.CBOR.array(array) = cborElement, + case let SwiftCBOR.CBOR.byteString(payloadBytes) = array[2], + let payload = try? SwiftCBOR.CBOR.decode(payloadBytes) + else { + return nil + } + return payload + } + + public static func kid(from data: Data) -> [UInt8]? { + let COSE_TAG = UInt64(18) + let COSE_PHDR_KID = SwiftCBOR.CBOR.unsignedInt(4) + let decoder = SwiftCBOR.CBORDecoder(input: data.uint) + + guard + let cbor = try? decoder.decodeItem(), + case let SwiftCBOR.CBOR.tagged(tag, cborElement) = cbor, + tag.rawValue == COSE_TAG, // SIGN1 + case let SwiftCBOR.CBOR.array(array) = cborElement, + case let SwiftCBOR.CBOR.byteString(protectedBytes) = array[0], + let protected = try? SwiftCBOR.CBOR.decode(protectedBytes), + case let SwiftCBOR.CBOR.map(protectedMap) = protected, + case let SwiftCBOR.CBOR.byteString(kid) = protectedMap[COSE_PHDR_KID] ?? .null + else { + return nil + } + return kid + } +} diff --git a/PatientScannerDemo/COSE.swift b/PatientScannerDemo/COSE.swift index 3afe71b..6e470d9 100644 --- a/PatientScannerDemo/COSE.swift +++ b/PatientScannerDemo/COSE.swift @@ -7,32 +7,38 @@ import Foundation import SwiftCBOR -//import CryptoKit struct COSE { - public static func verify(_ cbor: CBOR, with xHex: String, and yHex: String) -> Bool { + public static func verify(_ cborData: Data, with xHex: String, and yHex: String) -> Bool { + let decoder = SwiftCBOR.CBORDecoder(input: cborData.uint) + + guard let cbor = try? decoder.decodeItem() else { + return false + } + return verify(cbor, with: xHex, and: yHex) + } + public static func verify(_ cbor: SwiftCBOR.CBOR, with xHex: String, and yHex: String) -> Bool { let COSE_TAG = UInt64(18) guard - case let CBOR.tagged(tag, cborElement) = cbor, + case let SwiftCBOR.CBOR.tagged(tag, cborElement) = cbor, tag.rawValue == COSE_TAG, // SIGN1 - case let CBOR.array(array) = cborElement, - case let CBOR.byteString(signature) = array[3] + case let SwiftCBOR.CBOR.array(array) = cborElement, + case let SwiftCBOR.CBOR.byteString(signature) = array[3] else { return false } - let signedPayload: [UInt8] = CBOR.encode( + let signedPayload: [UInt8] = SwiftCBOR.CBOR.encode( [ "Signature1", array[0], - CBOR.byteString([]), + SwiftCBOR.CBOR.byteString([]), array[2] ] ) - let d = Data(signedPayload)//Data(bytes: signedPayload, count: signedPayload.count) -// let digest = SHA256.hash(data: signedPayload) - let s = Data(signature)//Data(bytes: signature, count: signature.count) + let d = Data(signedPayload) + let s = Data(signature) guard let key = JWK.ecFrom(x: xHex, y: yHex) else { return false } diff --git a/PatientScannerDemo/ViewController.swift b/PatientScannerDemo/ViewController.swift index 8306eab..8d99795 100644 --- a/PatientScannerDemo/ViewController.swift +++ b/PatientScannerDemo/ViewController.swift @@ -135,66 +135,8 @@ extension ViewController { else { return } let data = decompress(compressed) - var s = "" - for char in data { - s += String(data: Data([char]), encoding: .utf8) ?? "?" - } - - print(data) - let decoder = SwiftCBOR.CBORDecoder(input: data.uint) - guard - let cose = try? decoder.decodeItem(), - case let CBOR.tagged(tag, cborElement) = cose, - tag.rawValue == 18, // 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 { - return - } - - print("START") - print(protected) - print(protectedMap) - print(unprotected) - print(payload) - print(signature) - print("END") - print() - print() - // print(s) - - let keyString = """ -{ - "crv": "P-256", - "kid": "MklRdnVDMEdIZ29EeVY5VHo2WDR5MGlHTFNCZTgxdE5iN2wzYTZsUElCUQ", - "kty": "EC", - "x": "DHJudz6lqcTXizpz4cb_bbi8NZ9ofoD3lYnvByrMfLc", - "y": "bP7L_fysDzwFc13DDEPllO2qu1nDiDd1btoVQ-XlKok" -} -""" - let keyJWK = keyString.asJSONDict - - guard - let x = keyJWK["x"] as? String, - let y = keyJWK["y"] as? String, - let pubKey = JWK.ecFrom(x: x, y: y) - else { return } - - print(EC256.verify(signature: Data(signature), for: Data(payloadBytes), with: pubKey)) - - -// let key = try! RSAKey(jwk: keyString) -// -// let publicPem = try! key.getPublicKey() -// let verifier = verifySignature(key: publicPem!.data(using: .utf8)!, signature: Data(signature), for: Data(payloadBytes)) -// print(verifier) + /// TODO } diff --git a/PatientScannerDemoTests/EHNTests.swift b/PatientScannerDemoTests/EHNTests.swift index e275320..55218be 100644 --- a/PatientScannerDemoTests/EHNTests.swift +++ b/PatientScannerDemoTests/EHNTests.swift @@ -7,8 +7,7 @@ @testable import PatientScannerDemo import XCTest -import CryptoKit -import SwiftCBOR + class EHNTests: XCTestCase { func test_cose() throws { @@ -33,8 +32,6 @@ class EHNTests: XCTestCase { } ] """ - let COSE_TAG = UInt64(18) - let COSE_PHDR_KID = CBOR.unsignedInt(4) // Remove HC1 header if any if (barcode.hasPrefix("HC1")) { @@ -49,28 +46,10 @@ class EHNTests: XCTestCase { } let data = decompress(compressed) - let decoder = SwiftCBOR.CBORDecoder(input: data.uint) - - guard - 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.byteString(payloadBytes) = array[2], - let protected = try? CBOR.decode(protectedBytes), - let payload = try? CBOR.decode(payloadBytes), - case let CBOR.map(protectedMap) = protected - else { - XCTAssert(false) - return - } - guard case let CBOR.byteString(kid) = protectedMap[COSE_PHDR_KID] ?? .null else { - XCTAssert(false) - return - } guard + let payload = CBOR.payload(from: data), + let kid = CBOR.kid(from: data), let trustData = trustJson.data(using: .utf8), let trustSerialization = try? JSONSerialization.jsonObject(with: trustData, options: []), let trust = trustSerialization as? [[String: Any]] @@ -85,7 +64,7 @@ class EHNTests: XCTestCase { 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) { + if COSE.verify(data, with: x, and: y) { print("All is well! Payload: ", payload) return } From 6b69ae3940f010b33aa3e2cc65442c290ec4e4e6 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Fri, 16 Apr 2021 17:00:00 +0200 Subject: [PATCH 17/26] Simplify zlib. --- PatientScannerDemo/ZLib.swift | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/PatientScannerDemo/ZLib.swift b/PatientScannerDemo/ZLib.swift index 8c40f53..46e9abe 100644 --- a/PatientScannerDemo/ZLib.swift +++ b/PatientScannerDemo/ZLib.swift @@ -4,15 +4,7 @@ import Foundation import Compression func decompressString(_ data: Data) -> String { - let size = 4 * data.count - let buffer = UnsafeMutablePointer.allocate(capacity: size) - let result = data.subdata(in: 2 ..< data.count).withUnsafeBytes ({ - let read = compression_decode_buffer(buffer, size, $0.baseAddress!.bindMemory(to: UInt8.self, capacity: 1), - data.count - 2, nil, COMPRESSION_ZLIB) - return String(decoding: Data(bytes: buffer, count:read), as: UTF8.self) - }) as String - buffer.deallocate() - return result + return String(decoding: decompress(data), as: UTF8.self) } func decompress(_ data: Data) -> Data { From 4ac7ccba11d57359c2efc677af8a608ee9a18107 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Fri, 16 Apr 2021 17:00:13 +0200 Subject: [PATCH 18/26] Support utf8 KID. --- PatientScannerDemo/CBOR.swift | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/PatientScannerDemo/CBOR.swift b/PatientScannerDemo/CBOR.swift index 2bb19ba..ca7395f 100644 --- a/PatientScannerDemo/CBOR.swift +++ b/PatientScannerDemo/CBOR.swift @@ -38,11 +38,18 @@ struct CBOR { case let SwiftCBOR.CBOR.array(array) = cborElement, case let SwiftCBOR.CBOR.byteString(protectedBytes) = array[0], let protected = try? SwiftCBOR.CBOR.decode(protectedBytes), - case let SwiftCBOR.CBOR.map(protectedMap) = protected, - case let SwiftCBOR.CBOR.byteString(kid) = protectedMap[COSE_PHDR_KID] ?? .null + case let SwiftCBOR.CBOR.map(protectedMap) = protected else { return nil } - return kid + let kid = protectedMap[COSE_PHDR_KID] ?? .null + switch kid { + case let .utf8String(str): + return str.encode() + case let .byteString(str): + return str + default: + return nil + } } } From e5e29df5d6408b57903945fe4597dd91cb872652 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Fri, 16 Apr 2021 19:50:53 +0200 Subject: [PATCH 19/26] Enlarge zip buffer. --- PatientScannerDemo/ZLib.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PatientScannerDemo/ZLib.swift b/PatientScannerDemo/ZLib.swift index 46e9abe..6eed4cc 100644 --- a/PatientScannerDemo/ZLib.swift +++ b/PatientScannerDemo/ZLib.swift @@ -8,7 +8,7 @@ func decompressString(_ data: Data) -> String { } func decompress(_ data: Data) -> Data { - let size = 4 * data.count + let size = 4 * data.count + 8 * 1024 let buffer = UnsafeMutablePointer.allocate(capacity: size) let result = data.subdata(in: 2 ..< data.count).withUnsafeBytes ({ let read = compression_decode_buffer(buffer, size, $0.baseAddress!.bindMemory(to: UInt8.self, capacity: 1), From b2d3d41e06be36bbf5a563dd74229e81f0534a10 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Fri, 16 Apr 2021 19:51:10 +0200 Subject: [PATCH 20/26] Only support byte arrays as KID in production. --- PatientScannerDemo/CBOR.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/PatientScannerDemo/CBOR.swift b/PatientScannerDemo/CBOR.swift index ca7395f..91173f7 100644 --- a/PatientScannerDemo/CBOR.swift +++ b/PatientScannerDemo/CBOR.swift @@ -45,9 +45,14 @@ struct CBOR { let kid = protectedMap[COSE_PHDR_KID] ?? .null switch kid { case let .utf8String(str): - return str.encode() - case let .byteString(str): - return str + #if DEBUG + print("Warning, CBOR not fully compliant!!") + #else + return nil + #endif + return Data(hexString: str)?.uint + case let .byteString(uint): + return uint default: return nil } From 2d3337b7c1fffb8e2f136792e362c16d10a50ad0 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Fri, 16 Apr 2021 21:57:05 +0200 Subject: [PATCH 21/26] Add kid fallback. --- PatientScannerDemo/CBOR.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PatientScannerDemo/CBOR.swift b/PatientScannerDemo/CBOR.swift index 91173f7..ceb750b 100644 --- a/PatientScannerDemo/CBOR.swift +++ b/PatientScannerDemo/CBOR.swift @@ -46,11 +46,11 @@ struct CBOR { switch kid { case let .utf8String(str): #if DEBUG - print("Warning, CBOR not fully compliant!!") + print("Warning, CBOR not fully compliant!! Trying to understand it as Hex String. Fallback utf8 (which is against the spec).") #else return nil #endif - return Data(hexString: str)?.uint + return Data(hexString: str)?.uint ?? str.encode() case let .byteString(uint): return uint default: From 72258606335dd23726e256e0ab0be6cf7159e1dd Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Sat, 17 Apr 2021 01:12:03 +0200 Subject: [PATCH 22/26] Add RSA support. --- PatientScannerDemo.xcodeproj/project.pbxproj | 12 ++- PatientScannerDemo/CBOR.swift | 2 +- PatientScannerDemo/COSE.swift | 37 +++++++- PatientScannerDemo/EC256.swift | 37 -------- PatientScannerDemo/Signature.swift | 60 +++++++++++++ PatientScannerDemo/X509.swift | 21 +++++ PatientScannerDemoTests/EHNTests.swift | 93 +++++++++++++++++++- 7 files changed, 217 insertions(+), 45 deletions(-) delete mode 100644 PatientScannerDemo/EC256.swift create mode 100644 PatientScannerDemo/Signature.swift create mode 100644 PatientScannerDemo/X509.swift diff --git a/PatientScannerDemo.xcodeproj/project.pbxproj b/PatientScannerDemo.xcodeproj/project.pbxproj index 72e86a0..7f197b0 100644 --- a/PatientScannerDemo.xcodeproj/project.pbxproj +++ b/PatientScannerDemo.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + CE1BDF99262A4CD600766F97 /* X509.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1BDF98262A4CD600766F97 /* X509.swift */; }; CE3CC93C2628A7820079FB78 /* ASN1.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3CC93B2628A7820079FB78 /* ASN1.swift */; }; CE3CC9442628C2130079FB78 /* CBOR.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3CC9432628C2130079FB78 /* CBOR.swift */; }; CE7DE7FA2625EF18007E6694 /* SwiftCBOR in Frameworks */ = {isa = PBXBuildFile; productRef = CE7DE7F92625EF18007E6694 /* SwiftCBOR */; }; @@ -21,7 +22,7 @@ CEC2C4C22625ED030056E406 /* ZLib.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2C4BF2625ED030056E406 /* ZLib.swift */; }; CEC2C4C32625ED030056E406 /* JWK.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2C4C02625ED030056E406 /* JWK.swift */; }; CEC2C4C42625ED030056E406 /* Base45.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2C4C12625ED030056E406 /* Base45.swift */; }; - CEFAD86D2625F164009AFEF9 /* EC256.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFAD86C2625F164009AFEF9 /* EC256.swift */; }; + CEFAD86D2625F164009AFEF9 /* Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFAD86C2625F164009AFEF9 /* Signature.swift */; }; CEFAD8722625F29E009AFEF9 /* String+JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFAD8712625F29E009AFEF9 /* String+JSON.swift */; }; CEFAD87A26271414009AFEF9 /* COSE.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFAD87926271414009AFEF9 /* COSE.swift */; }; CEFAD87F262714C4009AFEF9 /* EHNTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFAD87E262714C4009AFEF9 /* EHNTests.swift */; }; @@ -46,6 +47,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + CE1BDF98262A4CD600766F97 /* X509.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = X509.swift; sourceTree = ""; }; CE3CC93B2628A7820079FB78 /* ASN1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1.swift; sourceTree = ""; }; CE3CC9432628C2130079FB78 /* CBOR.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBOR.swift; sourceTree = ""; }; CEA6D6E8261F8D2700715333 /* PatientScannerDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PatientScannerDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -65,7 +67,7 @@ CEC2C4BF2625ED030056E406 /* ZLib.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZLib.swift; sourceTree = ""; }; CEC2C4C02625ED030056E406 /* JWK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JWK.swift; sourceTree = ""; }; CEC2C4C12625ED030056E406 /* Base45.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Base45.swift; sourceTree = ""; }; - CEFAD86C2625F164009AFEF9 /* EC256.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EC256.swift; sourceTree = ""; }; + CEFAD86C2625F164009AFEF9 /* Signature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Signature.swift; sourceTree = ""; }; CEFAD8712625F29E009AFEF9 /* String+JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+JSON.swift"; sourceTree = ""; }; CEFAD87926271414009AFEF9 /* COSE.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = COSE.swift; sourceTree = ""; }; CEFAD87E262714C4009AFEF9 /* EHNTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EHNTests.swift; sourceTree = ""; }; @@ -132,11 +134,12 @@ CEA6D6F4261F8D2900715333 /* Assets.xcassets */, CEA6D6F6261F8D2900715333 /* LaunchScreen.storyboard */, CEA6D6F9261F8D2900715333 /* Info.plist */, - CEFAD86C2625F164009AFEF9 /* EC256.swift */, + CEFAD86C2625F164009AFEF9 /* Signature.swift */, CE3CC93B2628A7820079FB78 /* ASN1.swift */, CEFAD87926271414009AFEF9 /* COSE.swift */, CEFAD88626271B9A009AFEF9 /* Data+hexString.swift */, CE3CC9432628C2130079FB78 /* CBOR.swift */, + CE1BDF98262A4CD600766F97 /* X509.swift */, ); path = PatientScannerDemo; sourceTree = ""; @@ -300,10 +303,11 @@ CEC2C4C32625ED030056E406 /* JWK.swift in Sources */, CEC2C4C42625ED030056E406 /* Base45.swift in Sources */, CE3CC9442628C2130079FB78 /* CBOR.swift in Sources */, + CE1BDF99262A4CD600766F97 /* X509.swift in Sources */, CEA6D6F0261F8D2700715333 /* ViewController.swift in Sources */, CE3CC93C2628A7820079FB78 /* ASN1.swift in Sources */, CEFAD87A26271414009AFEF9 /* COSE.swift in Sources */, - CEFAD86D2625F164009AFEF9 /* EC256.swift in Sources */, + CEFAD86D2625F164009AFEF9 /* Signature.swift in Sources */, CEA6D6EC261F8D2700715333 /* AppDelegate.swift in Sources */, CEFAD8722625F29E009AFEF9 /* String+JSON.swift in Sources */, CEC2C4C22625ED030056E406 /* ZLib.swift in Sources */, diff --git a/PatientScannerDemo/CBOR.swift b/PatientScannerDemo/CBOR.swift index ceb750b..02ae8b2 100644 --- a/PatientScannerDemo/CBOR.swift +++ b/PatientScannerDemo/CBOR.swift @@ -50,7 +50,7 @@ struct CBOR { #else return nil #endif - return Data(hexString: str)?.uint ?? str.encode() + return Data(hexString: str)?.uint ?? str.data(using: .utf8)?.uint case let .byteString(uint): return uint default: diff --git a/PatientScannerDemo/COSE.swift b/PatientScannerDemo/COSE.swift index 6e470d9..5977616 100644 --- a/PatientScannerDemo/COSE.swift +++ b/PatientScannerDemo/COSE.swift @@ -17,6 +17,14 @@ struct COSE { } return verify(cbor, with: xHex, and: yHex) } + public static func verify(_ cborData: Data, with rsa: String) -> Bool { + let decoder = SwiftCBOR.CBORDecoder(input: cborData.uint) + + guard let cbor = try? decoder.decodeItem() else { + return false + } + return verify(cbor, with: rsa) + } public static func verify(_ cbor: SwiftCBOR.CBOR, with xHex: String, and yHex: String) -> Bool { let COSE_TAG = UInt64(18) @@ -42,6 +50,33 @@ struct COSE { guard let key = JWK.ecFrom(x: xHex, y: yHex) else { return false } - return EC256.verify(signature: s, for: d, with: key) + return Signature.verify(s, for: d, with: key) + } + public static func verify(_ cbor: SwiftCBOR.CBOR, with rsa: String) -> Bool { + let COSE_TAG = UInt64(18) + + guard + case let SwiftCBOR.CBOR.tagged(tag, cborElement) = cbor, + tag.rawValue == COSE_TAG, // SIGN1 + case let SwiftCBOR.CBOR.array(array) = cborElement, + case let SwiftCBOR.CBOR.byteString(signature) = array[3] + else { + return false + } + + let signedPayload: [UInt8] = SwiftCBOR.CBOR.encode( + [ + "Signature1", + array[0], + SwiftCBOR.CBOR.byteString([]), + array[2] + ] + ) + let d = Data(signedPayload) + let s = Data(signature) + guard let key = X509.rsa(from: rsa) else { + return false + } + return Signature.verify(s, for: d, with: key) } } diff --git a/PatientScannerDemo/EC256.swift b/PatientScannerDemo/EC256.swift deleted file mode 100644 index 55e6ece..0000000 --- a/PatientScannerDemo/EC256.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// EC256.swift -// PatientScannerDemo -// -// Created by Yannick Spreen on 4/13/21. -// -// https://developer.apple.com/forums/thread/83136 -// - -import Foundation - -struct EC256 { - public static func verify(signature: Data, for data: Data, with publicKey: SecKey) -> Bool { - guard SecKeyIsAlgorithmSupported(publicKey, .verify, .ecdsaSignatureMessageX962SHA256) else { - print("Pubkey not supported.") - return false - } - - let sig = ASN1.signature(from: signature) - - // verify signature - var error: Unmanaged? - let result = SecKeyVerifySignature( - publicKey, - .ecdsaSignatureMessageX962SHA256, - data as NSData, - sig as NSData, - &error - ) - if let err = error?.takeUnretainedValue().localizedDescription { - print(err) - } - error?.release() - - return result - } -} diff --git a/PatientScannerDemo/Signature.swift b/PatientScannerDemo/Signature.swift new file mode 100644 index 0000000..467decb --- /dev/null +++ b/PatientScannerDemo/Signature.swift @@ -0,0 +1,60 @@ +// +// EC256.swift +// PatientScannerDemo +// +// Created by Yannick Spreen on 4/13/21. +// +// https://developer.apple.com/forums/thread/83136 +// + +import Foundation + +struct Signature { + public static func verify(_ signature: Data, for data: Data, with publicKey: SecKey) -> Bool { + if SecKeyIsAlgorithmSupported(publicKey, .verify, .ecdsaSignatureMessageX962SHA256) { + return verifyEC(signature, for: data, with: publicKey) + } + if SecKeyIsAlgorithmSupported(publicKey, .verify, .rsaSignatureMessagePSSSHA256) { + return verifyRSA(signature, for: data, with: publicKey) + } + return false + } + + static func verifyEC(_ signature: Data, for data: Data, with publicKey: SecKey) -> Bool { + let sig = ASN1.signature(from: signature) + + var error: Unmanaged? + let result = SecKeyVerifySignature( + publicKey, + .ecdsaSignatureMessageX962SHA256, + data as NSData, + sig as NSData, + &error + ) + if let err = error?.takeUnretainedValue().localizedDescription { + print(err) + } + error?.release() + + return result + } + + static func verifyRSA(_ signature: Data, for data: Data, with publicKey: SecKey) -> Bool { + let sig = signature + + var error: Unmanaged? + let result = SecKeyVerifySignature( + publicKey, + .rsaSignatureMessagePSSSHA256, + data as NSData, + sig as NSData, + &error + ) + if let err = error?.takeUnretainedValue().localizedDescription { + print(err) + } + error?.release() + + return result + } +} diff --git a/PatientScannerDemo/X509.swift b/PatientScannerDemo/X509.swift new file mode 100644 index 0000000..a8c37e0 --- /dev/null +++ b/PatientScannerDemo/X509.swift @@ -0,0 +1,21 @@ +// +// X509.swift +// PatientScannerDemo +// +// Created by Yannick Spreen on 4/17/21. +// + +import Foundation + +struct X509 { + public static func rsa(from encodedCert: String) -> SecKey? { + guard + let encodedCertData = Data(base64Encoded: encodedCert), + let cert = SecCertificateCreateWithData(nil, encodedCertData as CFData), + let publicKey = SecCertificateCopyKey(cert) + else { + return nil + } + return publicKey + } +} diff --git a/PatientScannerDemoTests/EHNTests.swift b/PatientScannerDemoTests/EHNTests.swift index 55218be..1295814 100644 --- a/PatientScannerDemoTests/EHNTests.swift +++ b/PatientScannerDemoTests/EHNTests.swift @@ -10,8 +10,7 @@ import XCTest class EHNTests: XCTestCase { - func test_cose() throws { - + func testCoseEcdsa() throws { var barcode = "HC1NCFY70R30FFWTWGSLKC 4O992$V M63TMF2V*D9LPC.3EHPCGEC27B72VF/347O4-M6Y9M6FOYG4ILDEI8GR3ZI$15MABL:E9CVBGEEWRMLE C39S0/ANZ52T82Z-73D63P1U 1$PKC 72H2XX09WDH889V5" let trustJson = """ @@ -74,6 +73,96 @@ class EHNTests: XCTestCase { print("Nope - all failed - sadness all around") XCTAssert(false) } + func testCoseATCose() throws { + var barcode = "HC1NCFI.L3B6AP2YQ2%MNBCHC51/CAOZDL+OL7S99U60SVOGSHD%24R4PCTH7Y9KL6EVJBIMBL364D X1KKLZ*I9K4RLDR7B011EROZS6022WR5VX4QG9W72HWCC12B:TWZG.%N8GWBCOV0O%5F9U7H 8P4L.%2/:6TXB/-J6ZJ.*QYIBYSD6/T.6RDMMK0PX6HTPO1REO*CWMFG5CJ38+FBS+8DB9*2FFP9Z8HEM137CRBQ.893$I.R7 DRFOVMUQRVDP4IU613/G-0LZ:43+MM:QO.CQPJU21-7PJ*L/*DVLO:Q5G-7%WF4ZO*JJR:KH+O1707M6.73VXG0+0050+IMIFH:3V+DL3JK/21/LEI$T5TUT8P:40P10AXGBRS%-GZS8TGF-IT3-HU2SVJUAVG%AGO98R00Y.QT23DVP0TB*-J6YJP 8TU9U/OJSQGGMK+K3MH IR U7H%9/UU07AGEWMVTOBAYIV4SPGC2Y5FJJ67I4/J4FXM3GP:SAOZQJPL%WGP4BT59Q8K5MB*M44%K9JTWB9K+LS-2I9ST+3J%0X%1YGN0E0KL3/FHTZHEF9WIC4Q1HVK74L186BWT9+TJR7C2M%9JXN8S%0PVU8GFBET%0PG RR1KD2F11G-CW8A4124N9VN-T-:292AX*B%ER*$12-O:6K:/N$ 7IYFFXQ/5F NC49M/W5501B2OKU7E:NHMM8SETWUK*I$JR" + + // Remove HC1 header if any + if (barcode.hasPrefix("HC1")) { + barcode = String(barcode.suffix(barcode.count-3)) + } + + guard + let compressed = try? barcode.fromBase45() + else { + XCTAssert(false) + return + } + + let data = decompress(compressed) + + guard + let kidBytes = CBOR.kid(from: data), + let kid = String(data: Data(kidBytes), encoding: .utf8), + let url = URL(string: "https://dev.a-sit.at/certservice/cert/\(kid)") + else { + XCTAssert(false) + return + } + let expectation = XCTestExpectation(description: "Download PubKey") + URLSession.shared.dataTask(with: URLRequest(url: url)) { body, response, error in + guard + error == nil, + let status = (response as? HTTPURLResponse)?.statusCode, + 200 == status, + let body = body + else { + XCTAssert(false) + return + } + let encodedCert = body.base64EncodedString() + if COSE.verify(data, with: encodedCert) { + expectation.fulfill() + } else { + XCTAssert(false) + } + }.resume() + wait(for: [expectation], timeout: 15) + } + func testCoseATRsa() throws { + var barcode = "HC1NCFI.LUZB.P2YQ2E R002$DLTLIYXM/+0JNEAFUP.CV5GKCL /A0SQT/O8ZA85D0IJ$5D4.J+MR8-EGK6DM1R58V1ERUCWM4.E4ZM4$WG O E6KX9$KHCQMFE0B$KPKA*A2+ELQ*5ZQE/*1ZMSM5S.:KGQ33LQOWN67QW68LXM867D6837QCWEU*E*OKZCJPSCPIM1:Q83FBODLRD/W8K$E4 SG4P2JM6GCMU9FFSZAKNJH6ZNZVSWU78MHB4TO5J0N51U2F:17AB8O0*N0CQSA-9VIN904*STT$6R1LLXDT8J1ZN4I4368L02N72ZMGV496.0NO3OQCPFVF+C$BKQH1$+8L:AAEGRIB548B68D/3-ZTZ.JNM84E0F59$N6DY2*6FACA448900U42ZEMMO7-LO.45*IMXYIODF:82RN4L8Q%20HDJ-YCHIA1YT87A2/C1NO1F6DPCLCK9+BDXEEK23JH0/I8XJQEQ1T8BQ5GNDRFPQKKV6WT$A+98TIOUSAFA0J8OMY87$P-LESPS1KO+:O%HOY*C8XH4ZE7D16QR WNN$EP:U7E2HTLS023X0GJBW887LCKVHDGJ+Q88I0A73$H6R30XYH-LDC$4+24*JSE*H98A471BJ3:R0LU3MZA+ 4/GMZ9J D4E7L.TI$L8L:TN4G2S552748V$%D0CPH9RJ-7Z4AXFN*SS/ZC44O3P5%%AE936OUSR1H7WP%CP8KQUCY9I%A5FDBV+0A%HPVP5SKP9TKAFKT9FYROAABDBEHVQ9317O0D7MS5Z.Q2+K2.1RGGS3B5OTO2ROD60Z6LE98E94TV09JXM395VA0N.98EP3*4E5SHA7I96Q70UF74SPSIJI.BUHQJ$:K$9M51H9Z6RPHR.K-XT855UJJZC8C*MSITGMRE%O+:JQXV$LS7SJ1:DE LCX7RP7T JH4RR-NK.PASIWKA8J5Z1NA*BE7KFTJXO4BM5QVF.S0-5CG8KQWB8RCA1VP*1Q.ELZ1Q21G.JEGLOFH1:FR09$SCY/OGBU1QIE/2 9SK3" + + // Remove HC1 header if any + if (barcode.hasPrefix("HC1")) { + barcode = String(barcode.suffix(barcode.count-3)) + } + + guard + let compressed = try? barcode.fromBase45() + else { + XCTAssert(false) + return + } + + let data = decompress(compressed) + + guard + let kidBytes = CBOR.kid(from: data), + let kid = String(data: Data(kidBytes), encoding: .utf8), + let url = URL(string: "https://dev.a-sit.at/certservice/cert/\(kid)") + else { + XCTAssert(false) + return + } + let expectation = XCTestExpectation(description: "Download PubKey") + URLSession.shared.dataTask(with: URLRequest(url: url)) { body, response, error in + guard + error == nil, + let status = (response as? HTTPURLResponse)?.statusCode, + 200 == status, + let body = body + else { + XCTAssert(false) + return + } + let encodedCert = body.base64EncodedString() + if COSE.verify(data, with: encodedCert) { + expectation.fulfill() + } else { + XCTAssert(false) + } + }.resume() + wait(for: [expectation], timeout: 15) + } } /** From ad3838a391fc936b802a9937a4fbf0917f436627 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Sat, 17 Apr 2021 01:18:02 +0200 Subject: [PATCH 23/26] Fix names. --- PatientScannerDemoTests/EHNTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PatientScannerDemoTests/EHNTests.swift b/PatientScannerDemoTests/EHNTests.swift index 1295814..1de7cd4 100644 --- a/PatientScannerDemoTests/EHNTests.swift +++ b/PatientScannerDemoTests/EHNTests.swift @@ -73,7 +73,7 @@ class EHNTests: XCTestCase { print("Nope - all failed - sadness all around") XCTAssert(false) } - func testCoseATCose() throws { + func testCoseEcAT() throws { var barcode = "HC1NCFI.L3B6AP2YQ2%MNBCHC51/CAOZDL+OL7S99U60SVOGSHD%24R4PCTH7Y9KL6EVJBIMBL364D X1KKLZ*I9K4RLDR7B011EROZS6022WR5VX4QG9W72HWCC12B:TWZG.%N8GWBCOV0O%5F9U7H 8P4L.%2/:6TXB/-J6ZJ.*QYIBYSD6/T.6RDMMK0PX6HTPO1REO*CWMFG5CJ38+FBS+8DB9*2FFP9Z8HEM137CRBQ.893$I.R7 DRFOVMUQRVDP4IU613/G-0LZ:43+MM:QO.CQPJU21-7PJ*L/*DVLO:Q5G-7%WF4ZO*JJR:KH+O1707M6.73VXG0+0050+IMIFH:3V+DL3JK/21/LEI$T5TUT8P:40P10AXGBRS%-GZS8TGF-IT3-HU2SVJUAVG%AGO98R00Y.QT23DVP0TB*-J6YJP 8TU9U/OJSQGGMK+K3MH IR U7H%9/UU07AGEWMVTOBAYIV4SPGC2Y5FJJ67I4/J4FXM3GP:SAOZQJPL%WGP4BT59Q8K5MB*M44%K9JTWB9K+LS-2I9ST+3J%0X%1YGN0E0KL3/FHTZHEF9WIC4Q1HVK74L186BWT9+TJR7C2M%9JXN8S%0PVU8GFBET%0PG RR1KD2F11G-CW8A4124N9VN-T-:292AX*B%ER*$12-O:6K:/N$ 7IYFFXQ/5F NC49M/W5501B2OKU7E:NHMM8SETWUK*I$JR" // Remove HC1 header if any @@ -118,7 +118,7 @@ class EHNTests: XCTestCase { }.resume() wait(for: [expectation], timeout: 15) } - func testCoseATRsa() throws { + func testCoseRsaAT() throws { var barcode = "HC1NCFI.LUZB.P2YQ2E R002$DLTLIYXM/+0JNEAFUP.CV5GKCL /A0SQT/O8ZA85D0IJ$5D4.J+MR8-EGK6DM1R58V1ERUCWM4.E4ZM4$WG O E6KX9$KHCQMFE0B$KPKA*A2+ELQ*5ZQE/*1ZMSM5S.:KGQ33LQOWN67QW68LXM867D6837QCWEU*E*OKZCJPSCPIM1:Q83FBODLRD/W8K$E4 SG4P2JM6GCMU9FFSZAKNJH6ZNZVSWU78MHB4TO5J0N51U2F:17AB8O0*N0CQSA-9VIN904*STT$6R1LLXDT8J1ZN4I4368L02N72ZMGV496.0NO3OQCPFVF+C$BKQH1$+8L:AAEGRIB548B68D/3-ZTZ.JNM84E0F59$N6DY2*6FACA448900U42ZEMMO7-LO.45*IMXYIODF:82RN4L8Q%20HDJ-YCHIA1YT87A2/C1NO1F6DPCLCK9+BDXEEK23JH0/I8XJQEQ1T8BQ5GNDRFPQKKV6WT$A+98TIOUSAFA0J8OMY87$P-LESPS1KO+:O%HOY*C8XH4ZE7D16QR WNN$EP:U7E2HTLS023X0GJBW887LCKVHDGJ+Q88I0A73$H6R30XYH-LDC$4+24*JSE*H98A471BJ3:R0LU3MZA+ 4/GMZ9J D4E7L.TI$L8L:TN4G2S552748V$%D0CPH9RJ-7Z4AXFN*SS/ZC44O3P5%%AE936OUSR1H7WP%CP8KQUCY9I%A5FDBV+0A%HPVP5SKP9TKAFKT9FYROAABDBEHVQ9317O0D7MS5Z.Q2+K2.1RGGS3B5OTO2ROD60Z6LE98E94TV09JXM395VA0N.98EP3*4E5SHA7I96Q70UF74SPSIJI.BUHQJ$:K$9M51H9Z6RPHR.K-XT855UJJZC8C*MSITGMRE%O+:JQXV$LS7SJ1:DE LCX7RP7T JH4RR-NK.PASIWKA8J5Z1NA*BE7KFTJXO4BM5QVF.S0-5CG8KQWB8RCA1VP*1Q.ELZ1Q21G.JEGLOFH1:FR09$SCY/OGBU1QIE/2 9SK3" // Remove HC1 header if any From dfdb380223e685ff2d26683a5ca556086843bde7 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Sat, 17 Apr 2021 01:20:19 +0200 Subject: [PATCH 24/26] Rename method. --- PatientScannerDemo/COSE.swift | 2 +- PatientScannerDemo/X509.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PatientScannerDemo/COSE.swift b/PatientScannerDemo/COSE.swift index 5977616..daa65cd 100644 --- a/PatientScannerDemo/COSE.swift +++ b/PatientScannerDemo/COSE.swift @@ -74,7 +74,7 @@ struct COSE { ) let d = Data(signedPayload) let s = Data(signature) - guard let key = X509.rsa(from: rsa) else { + guard let key = X509.pubKey(from: rsa) else { return false } return Signature.verify(s, for: d, with: key) diff --git a/PatientScannerDemo/X509.swift b/PatientScannerDemo/X509.swift index a8c37e0..57af78a 100644 --- a/PatientScannerDemo/X509.swift +++ b/PatientScannerDemo/X509.swift @@ -8,9 +8,9 @@ import Foundation struct X509 { - public static func rsa(from encodedCert: String) -> SecKey? { + public static func pubKey(from b64EncodedCert: String) -> SecKey? { guard - let encodedCertData = Data(base64Encoded: encodedCert), + let encodedCertData = Data(base64Encoded: b64EncodedCert), let cert = SecCertificateCreateWithData(nil, encodedCertData as CFData), let publicKey = SecCertificateCopyKey(cert) else { From 343dc7380078dc264d10f6dea70c39496948c6e7 Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Sat, 17 Apr 2021 01:23:25 +0200 Subject: [PATCH 25/26] Streamline signature service. --- PatientScannerDemo/Signature.swift | 41 ++++++++---------------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/PatientScannerDemo/Signature.swift b/PatientScannerDemo/Signature.swift index 467decb..d325818 100644 --- a/PatientScannerDemo/Signature.swift +++ b/PatientScannerDemo/Signature.swift @@ -11,43 +11,24 @@ import Foundation struct Signature { public static func verify(_ signature: Data, for data: Data, with publicKey: SecKey) -> Bool { - if SecKeyIsAlgorithmSupported(publicKey, .verify, .ecdsaSignatureMessageX962SHA256) { - return verifyEC(signature, for: data, with: publicKey) - } - if SecKeyIsAlgorithmSupported(publicKey, .verify, .rsaSignatureMessagePSSSHA256) { - return verifyRSA(signature, for: data, with: publicKey) - } - return false - } - - static func verifyEC(_ signature: Data, for data: Data, with publicKey: SecKey) -> Bool { - let sig = ASN1.signature(from: signature) + var signature = signature + var alg: SecKeyAlgorithm - var error: Unmanaged? - let result = SecKeyVerifySignature( - publicKey, - .ecdsaSignatureMessageX962SHA256, - data as NSData, - sig as NSData, - &error - ) - if let err = error?.takeUnretainedValue().localizedDescription { - print(err) + if SecKeyIsAlgorithmSupported(publicKey, .verify, .ecdsaSignatureMessageX962SHA256) { + alg = .ecdsaSignatureMessageX962SHA256 + signature = ASN1.signature(from: signature) + } else if SecKeyIsAlgorithmSupported(publicKey, .verify, .rsaSignatureMessagePSSSHA256) { + alg = .rsaSignatureMessagePSSSHA256 + } else { + return false } - error?.release() - - return result - } - - static func verifyRSA(_ signature: Data, for data: Data, with publicKey: SecKey) -> Bool { - let sig = signature var error: Unmanaged? let result = SecKeyVerifySignature( publicKey, - .rsaSignatureMessagePSSSHA256, + alg, data as NSData, - sig as NSData, + signature as NSData, &error ) if let err = error?.takeUnretainedValue().localizedDescription { From be593802f4af1d3df11f90153571c00b924ee6dd Mon Sep 17 00:00:00 2001 From: Yannick Spreen Date: Sat, 17 Apr 2021 01:27:07 +0200 Subject: [PATCH 26/26] Streamline tests. --- PatientScannerDemoTests/EHNTests.swift | 52 ++++---------------------- 1 file changed, 7 insertions(+), 45 deletions(-) diff --git a/PatientScannerDemoTests/EHNTests.swift b/PatientScannerDemoTests/EHNTests.swift index 1de7cd4..374534d 100644 --- a/PatientScannerDemoTests/EHNTests.swift +++ b/PatientScannerDemoTests/EHNTests.swift @@ -74,53 +74,15 @@ class EHNTests: XCTestCase { XCTAssert(false) } func testCoseEcAT() throws { - var barcode = "HC1NCFI.L3B6AP2YQ2%MNBCHC51/CAOZDL+OL7S99U60SVOGSHD%24R4PCTH7Y9KL6EVJBIMBL364D X1KKLZ*I9K4RLDR7B011EROZS6022WR5VX4QG9W72HWCC12B:TWZG.%N8GWBCOV0O%5F9U7H 8P4L.%2/:6TXB/-J6ZJ.*QYIBYSD6/T.6RDMMK0PX6HTPO1REO*CWMFG5CJ38+FBS+8DB9*2FFP9Z8HEM137CRBQ.893$I.R7 DRFOVMUQRVDP4IU613/G-0LZ:43+MM:QO.CQPJU21-7PJ*L/*DVLO:Q5G-7%WF4ZO*JJR:KH+O1707M6.73VXG0+0050+IMIFH:3V+DL3JK/21/LEI$T5TUT8P:40P10AXGBRS%-GZS8TGF-IT3-HU2SVJUAVG%AGO98R00Y.QT23DVP0TB*-J6YJP 8TU9U/OJSQGGMK+K3MH IR U7H%9/UU07AGEWMVTOBAYIV4SPGC2Y5FJJ67I4/J4FXM3GP:SAOZQJPL%WGP4BT59Q8K5MB*M44%K9JTWB9K+LS-2I9ST+3J%0X%1YGN0E0KL3/FHTZHEF9WIC4Q1HVK74L186BWT9+TJR7C2M%9JXN8S%0PVU8GFBET%0PG RR1KD2F11G-CW8A4124N9VN-T-:292AX*B%ER*$12-O:6K:/N$ 7IYFFXQ/5F NC49M/W5501B2OKU7E:NHMM8SETWUK*I$JR" - - // Remove HC1 header if any - if (barcode.hasPrefix("HC1")) { - barcode = String(barcode.suffix(barcode.count-3)) - } - - guard - let compressed = try? barcode.fromBase45() - else { - XCTAssert(false) - return - } - - let data = decompress(compressed) - - guard - let kidBytes = CBOR.kid(from: data), - let kid = String(data: Data(kidBytes), encoding: .utf8), - let url = URL(string: "https://dev.a-sit.at/certservice/cert/\(kid)") - else { - XCTAssert(false) - return - } - let expectation = XCTestExpectation(description: "Download PubKey") - URLSession.shared.dataTask(with: URLRequest(url: url)) { body, response, error in - guard - error == nil, - let status = (response as? HTTPURLResponse)?.statusCode, - 200 == status, - let body = body - else { - XCTAssert(false) - return - } - let encodedCert = body.base64EncodedString() - if COSE.verify(data, with: encodedCert) { - expectation.fulfill() - } else { - XCTAssert(false) - } - }.resume() - wait(for: [expectation], timeout: 15) + let barcode = "HC1NCFI.L3B6AP2YQ2%MNBCHC51/CAOZDL+OL7S99U60SVOGSHD%24R4PCTH7Y9KL6EVJBIMBL364D X1KKLZ*I9K4RLDR7B011EROZS6022WR5VX4QG9W72HWCC12B:TWZG.%N8GWBCOV0O%5F9U7H 8P4L.%2/:6TXB/-J6ZJ.*QYIBYSD6/T.6RDMMK0PX6HTPO1REO*CWMFG5CJ38+FBS+8DB9*2FFP9Z8HEM137CRBQ.893$I.R7 DRFOVMUQRVDP4IU613/G-0LZ:43+MM:QO.CQPJU21-7PJ*L/*DVLO:Q5G-7%WF4ZO*JJR:KH+O1707M6.73VXG0+0050+IMIFH:3V+DL3JK/21/LEI$T5TUT8P:40P10AXGBRS%-GZS8TGF-IT3-HU2SVJUAVG%AGO98R00Y.QT23DVP0TB*-J6YJP 8TU9U/OJSQGGMK+K3MH IR U7H%9/UU07AGEWMVTOBAYIV4SPGC2Y5FJJ67I4/J4FXM3GP:SAOZQJPL%WGP4BT59Q8K5MB*M44%K9JTWB9K+LS-2I9ST+3J%0X%1YGN0E0KL3/FHTZHEF9WIC4Q1HVK74L186BWT9+TJR7C2M%9JXN8S%0PVU8GFBET%0PG RR1KD2F11G-CW8A4124N9VN-T-:292AX*B%ER*$12-O:6K:/N$ 7IYFFXQ/5F NC49M/W5501B2OKU7E:NHMM8SETWUK*I$JR" + return baseTestAT(barcode: barcode) } func testCoseRsaAT() throws { - var barcode = "HC1NCFI.LUZB.P2YQ2E R002$DLTLIYXM/+0JNEAFUP.CV5GKCL /A0SQT/O8ZA85D0IJ$5D4.J+MR8-EGK6DM1R58V1ERUCWM4.E4ZM4$WG O E6KX9$KHCQMFE0B$KPKA*A2+ELQ*5ZQE/*1ZMSM5S.:KGQ33LQOWN67QW68LXM867D6837QCWEU*E*OKZCJPSCPIM1:Q83FBODLRD/W8K$E4 SG4P2JM6GCMU9FFSZAKNJH6ZNZVSWU78MHB4TO5J0N51U2F:17AB8O0*N0CQSA-9VIN904*STT$6R1LLXDT8J1ZN4I4368L02N72ZMGV496.0NO3OQCPFVF+C$BKQH1$+8L:AAEGRIB548B68D/3-ZTZ.JNM84E0F59$N6DY2*6FACA448900U42ZEMMO7-LO.45*IMXYIODF:82RN4L8Q%20HDJ-YCHIA1YT87A2/C1NO1F6DPCLCK9+BDXEEK23JH0/I8XJQEQ1T8BQ5GNDRFPQKKV6WT$A+98TIOUSAFA0J8OMY87$P-LESPS1KO+:O%HOY*C8XH4ZE7D16QR WNN$EP:U7E2HTLS023X0GJBW887LCKVHDGJ+Q88I0A73$H6R30XYH-LDC$4+24*JSE*H98A471BJ3:R0LU3MZA+ 4/GMZ9J D4E7L.TI$L8L:TN4G2S552748V$%D0CPH9RJ-7Z4AXFN*SS/ZC44O3P5%%AE936OUSR1H7WP%CP8KQUCY9I%A5FDBV+0A%HPVP5SKP9TKAFKT9FYROAABDBEHVQ9317O0D7MS5Z.Q2+K2.1RGGS3B5OTO2ROD60Z6LE98E94TV09JXM395VA0N.98EP3*4E5SHA7I96Q70UF74SPSIJI.BUHQJ$:K$9M51H9Z6RPHR.K-XT855UJJZC8C*MSITGMRE%O+:JQXV$LS7SJ1:DE LCX7RP7T JH4RR-NK.PASIWKA8J5Z1NA*BE7KFTJXO4BM5QVF.S0-5CG8KQWB8RCA1VP*1Q.ELZ1Q21G.JEGLOFH1:FR09$SCY/OGBU1QIE/2 9SK3" - + let barcode = "HC1NCFI.LUZB.P2YQ2E R002$DLTLIYXM/+0JNEAFUP.CV5GKCL /A0SQT/O8ZA85D0IJ$5D4.J+MR8-EGK6DM1R58V1ERUCWM4.E4ZM4$WG O E6KX9$KHCQMFE0B$KPKA*A2+ELQ*5ZQE/*1ZMSM5S.:KGQ33LQOWN67QW68LXM867D6837QCWEU*E*OKZCJPSCPIM1:Q83FBODLRD/W8K$E4 SG4P2JM6GCMU9FFSZAKNJH6ZNZVSWU78MHB4TO5J0N51U2F:17AB8O0*N0CQSA-9VIN904*STT$6R1LLXDT8J1ZN4I4368L02N72ZMGV496.0NO3OQCPFVF+C$BKQH1$+8L:AAEGRIB548B68D/3-ZTZ.JNM84E0F59$N6DY2*6FACA448900U42ZEMMO7-LO.45*IMXYIODF:82RN4L8Q%20HDJ-YCHIA1YT87A2/C1NO1F6DPCLCK9+BDXEEK23JH0/I8XJQEQ1T8BQ5GNDRFPQKKV6WT$A+98TIOUSAFA0J8OMY87$P-LESPS1KO+:O%HOY*C8XH4ZE7D16QR WNN$EP:U7E2HTLS023X0GJBW887LCKVHDGJ+Q88I0A73$H6R30XYH-LDC$4+24*JSE*H98A471BJ3:R0LU3MZA+ 4/GMZ9J D4E7L.TI$L8L:TN4G2S552748V$%D0CPH9RJ-7Z4AXFN*SS/ZC44O3P5%%AE936OUSR1H7WP%CP8KQUCY9I%A5FDBV+0A%HPVP5SKP9TKAFKT9FYROAABDBEHVQ9317O0D7MS5Z.Q2+K2.1RGGS3B5OTO2ROD60Z6LE98E94TV09JXM395VA0N.98EP3*4E5SHA7I96Q70UF74SPSIJI.BUHQJ$:K$9M51H9Z6RPHR.K-XT855UJJZC8C*MSITGMRE%O+:JQXV$LS7SJ1:DE LCX7RP7T JH4RR-NK.PASIWKA8J5Z1NA*BE7KFTJXO4BM5QVF.S0-5CG8KQWB8RCA1VP*1Q.ELZ1Q21G.JEGLOFH1:FR09$SCY/OGBU1QIE/2 9SK3" + return baseTestAT(barcode: barcode) + } + func baseTestAT(barcode: String) { + var barcode = barcode // Remove HC1 header if any if (barcode.hasPrefix("HC1")) { barcode = String(barcode.suffix(barcode.count-3))