Initial version
This commit is contained in:
parent
bf6061a6d0
commit
b8a04fdf57
372
Sesame.xcodeproj/project.pbxproj
Normal file
372
Sesame.xcodeproj/project.pbxproj
Normal file
@ -0,0 +1,372 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 55;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
884A45B7279F48C100D6E650 /* SesameApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45B6279F48C100D6E650 /* SesameApp.swift */; };
|
||||
884A45B9279F48C100D6E650 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45B8279F48C100D6E650 /* ContentView.swift */; };
|
||||
884A45BB279F48C300D6E650 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 884A45BA279F48C300D6E650 /* Assets.xcassets */; };
|
||||
884A45BE279F48C300D6E650 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 884A45BD279F48C300D6E650 /* Preview Assets.xcassets */; };
|
||||
884A45C5279F4BBE00D6E650 /* KeyManagement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45C4279F4BBE00D6E650 /* KeyManagement.swift */; };
|
||||
884A45C727A429EF00D6E650 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45C627A429EF00D6E650 /* ShareSheet.swift */; };
|
||||
884A45C927A43D7900D6E650 /* ClientState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45C827A43D7900D6E650 /* ClientState.swift */; };
|
||||
884A45CB27A464C000D6E650 /* SymmetricKey+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45CA27A464C000D6E650 /* SymmetricKey+Extensions.swift */; };
|
||||
884A45CD27A465F500D6E650 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45CC27A465F500D6E650 /* Client.swift */; };
|
||||
884A45CF27A5402D00D6E650 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 884A45CE27A5402D00D6E650 /* Response.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
884A45B3279F48C100D6E650 /* Sesame.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sesame.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
884A45B6279F48C100D6E650 /* SesameApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SesameApp.swift; sourceTree = "<group>"; };
|
||||
884A45B8279F48C100D6E650 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
884A45BA279F48C300D6E650 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
884A45BD279F48C300D6E650 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
884A45C4279F4BBE00D6E650 /* KeyManagement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyManagement.swift; sourceTree = "<group>"; };
|
||||
884A45C627A429EF00D6E650 /* ShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheet.swift; sourceTree = "<group>"; };
|
||||
884A45C827A43D7900D6E650 /* ClientState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientState.swift; sourceTree = "<group>"; };
|
||||
884A45CA27A464C000D6E650 /* SymmetricKey+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SymmetricKey+Extensions.swift"; sourceTree = "<group>"; };
|
||||
884A45CC27A465F500D6E650 /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = "<group>"; };
|
||||
884A45CE27A5402D00D6E650 /* Response.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
884A45B0279F48C100D6E650 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
884A45AA279F48C100D6E650 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
884A45B5279F48C100D6E650 /* Sesame */,
|
||||
884A45B4279F48C100D6E650 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
884A45B4279F48C100D6E650 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
884A45B3279F48C100D6E650 /* Sesame.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
884A45B5279F48C100D6E650 /* Sesame */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
884A45B6279F48C100D6E650 /* SesameApp.swift */,
|
||||
884A45B8279F48C100D6E650 /* ContentView.swift */,
|
||||
884A45CC27A465F500D6E650 /* Client.swift */,
|
||||
884A45CE27A5402D00D6E650 /* Response.swift */,
|
||||
884A45C827A43D7900D6E650 /* ClientState.swift */,
|
||||
884A45C627A429EF00D6E650 /* ShareSheet.swift */,
|
||||
884A45C4279F4BBE00D6E650 /* KeyManagement.swift */,
|
||||
884A45CA27A464C000D6E650 /* SymmetricKey+Extensions.swift */,
|
||||
884A45BA279F48C300D6E650 /* Assets.xcassets */,
|
||||
884A45BC279F48C300D6E650 /* Preview Content */,
|
||||
);
|
||||
path = Sesame;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
884A45BC279F48C300D6E650 /* Preview Content */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
884A45BD279F48C300D6E650 /* Preview Assets.xcassets */,
|
||||
);
|
||||
path = "Preview Content";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
884A45B2279F48C100D6E650 /* Sesame */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 884A45C1279F48C300D6E650 /* Build configuration list for PBXNativeTarget "Sesame" */;
|
||||
buildPhases = (
|
||||
884A45AF279F48C100D6E650 /* Sources */,
|
||||
884A45B0279F48C100D6E650 /* Frameworks */,
|
||||
884A45B1279F48C100D6E650 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Sesame;
|
||||
productName = Sesame;
|
||||
productReference = 884A45B3279F48C100D6E650 /* Sesame.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
884A45AB279F48C100D6E650 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 1320;
|
||||
LastUpgradeCheck = 1320;
|
||||
TargetAttributes = {
|
||||
884A45B2279F48C100D6E650 = {
|
||||
CreatedOnToolsVersion = 13.2.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 884A45AE279F48C100D6E650 /* Build configuration list for PBXProject "Sesame" */;
|
||||
compatibilityVersion = "Xcode 13.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 884A45AA279F48C100D6E650;
|
||||
productRefGroup = 884A45B4279F48C100D6E650 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
884A45B2279F48C100D6E650 /* Sesame */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
884A45B1279F48C100D6E650 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
884A45BE279F48C300D6E650 /* Preview Assets.xcassets in Resources */,
|
||||
884A45BB279F48C300D6E650 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
884A45AF279F48C100D6E650 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
884A45CF27A5402D00D6E650 /* Response.swift in Sources */,
|
||||
884A45B9279F48C100D6E650 /* ContentView.swift in Sources */,
|
||||
884A45CD27A465F500D6E650 /* Client.swift in Sources */,
|
||||
884A45CB27A464C000D6E650 /* SymmetricKey+Extensions.swift in Sources */,
|
||||
884A45C927A43D7900D6E650 /* ClientState.swift in Sources */,
|
||||
884A45B7279F48C100D6E650 /* SesameApp.swift in Sources */,
|
||||
884A45C5279F4BBE00D6E650 /* KeyManagement.swift in Sources */,
|
||||
884A45C727A429EF00D6E650 /* ShareSheet.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
884A45BF279F48C300D6E650 /* 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++17";
|
||||
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 = 15.2;
|
||||
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;
|
||||
};
|
||||
884A45C0279F48C300D6E650 /* 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++17";
|
||||
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 = 15.2;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
884A45C2279F48C300D6E650 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Sesame/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = H8WR4M6QQ4;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = de.christophhagen.Sesame;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
884A45C3279F48C300D6E650 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Sesame/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = H8WR4M6QQ4;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = de.christophhagen.Sesame;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
884A45AE279F48C100D6E650 /* Build configuration list for PBXProject "Sesame" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
884A45BF279F48C300D6E650 /* Debug */,
|
||||
884A45C0279F48C300D6E650 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
884A45C1279F48C300D6E650 /* Build configuration list for PBXNativeTarget "Sesame" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
884A45C2279F48C300D6E650 /* Debug */,
|
||||
884A45C3279F48C300D6E650 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 884A45AB279F48C100D6E650 /* Project object */;
|
||||
}
|
7
Sesame.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
Sesame.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
BIN
Sesame.xcodeproj/project.xcworkspace/xcuserdata/imac.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
BIN
Sesame.xcodeproj/project.xcworkspace/xcuserdata/imac.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
Binary file not shown.
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>Sesame.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
11
Sesame/Assets.xcassets/AccentColor.colorset/Contents.json
Normal file
11
Sesame/Assets.xcassets/AccentColor.colorset/Contents.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
98
Sesame/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
98
Sesame/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
@ -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
|
||||
}
|
||||
}
|
6
Sesame/Assets.xcassets/Contents.json
Normal file
6
Sesame/Assets.xcassets/Contents.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
105
Sesame/Client.swift
Normal file
105
Sesame/Client.swift
Normal file
@ -0,0 +1,105 @@
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
|
||||
struct Client {
|
||||
|
||||
let server: URL
|
||||
|
||||
private let delegate = NeverCacheDelegate()
|
||||
|
||||
init(server: URL) {
|
||||
self.server = server
|
||||
}
|
||||
|
||||
private enum RequestReponse: Error {
|
||||
case requestFailed
|
||||
case unknownResponse
|
||||
case success(UInt8)
|
||||
}
|
||||
|
||||
func deviceStatus() async throws -> ClientState {
|
||||
let url = server.appendingPathComponent("status")
|
||||
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData)
|
||||
let response = await integerReponse(to: request)
|
||||
switch response {
|
||||
case .requestFailed:
|
||||
return .statusRequestFailed
|
||||
case .unknownResponse:
|
||||
return .unknownDeviceStatus
|
||||
case .success(let int):
|
||||
switch int {
|
||||
case 0:
|
||||
return .deviceDisconnected
|
||||
case 1:
|
||||
return .deviceConnected
|
||||
default:
|
||||
print("Unexpected device status '\(int)'")
|
||||
return .unknownDeviceStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func keyResponse(key: SymmetricKey, id: Int) async throws -> ClientState {
|
||||
let url = server.appendingPathComponent("key/\(id)")
|
||||
var request = URLRequest(url: url)
|
||||
request.httpBody = key.data
|
||||
request.httpMethod = "POST"
|
||||
let response = await integerReponse(to: request)
|
||||
switch response {
|
||||
case .requestFailed:
|
||||
return .statusRequestFailed
|
||||
case .unknownResponse:
|
||||
return .unknownDeviceStatus
|
||||
case .success(let int):
|
||||
guard let status = KeyResult(rawValue: int) else {
|
||||
print("Invalid key response: \(int)")
|
||||
return .unknownDeviceStatus
|
||||
}
|
||||
return ClientState(keyResult: status)
|
||||
}
|
||||
}
|
||||
|
||||
private func fulfill(_ request: URLRequest) async -> Result<Data, RequestReponse> {
|
||||
do {
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
guard let code = (response as? HTTPURLResponse)?.statusCode else {
|
||||
print("No response from server")
|
||||
return .failure(.requestFailed)
|
||||
}
|
||||
guard code == 200 else {
|
||||
print("Invalid server response \(code)")
|
||||
return .failure(.requestFailed)
|
||||
}
|
||||
return .success(data)
|
||||
} catch {
|
||||
print("Request failed: \(error)")
|
||||
return .failure(.requestFailed)
|
||||
}
|
||||
}
|
||||
|
||||
private func integerReponse(to request: URLRequest) async -> RequestReponse {
|
||||
let response = await fulfill(request)
|
||||
switch response {
|
||||
case .failure(let cause):
|
||||
return cause
|
||||
case .success(let data):
|
||||
guard let string = String(data: data, encoding: .utf8) else {
|
||||
print("Unexpected device status data: \([UInt8](data))")
|
||||
return .unknownResponse
|
||||
}
|
||||
guard let int = UInt8(string) else {
|
||||
print("Unexpected device status '\(string)'")
|
||||
return .unknownResponse
|
||||
}
|
||||
return .success(int)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class NeverCacheDelegate: NSObject, NSURLConnectionDataDelegate {
|
||||
|
||||
func connection(_ connection: NSURLConnection, willCacheResponse cachedResponse: CachedURLResponse) -> CachedURLResponse? {
|
||||
return nil
|
||||
}
|
||||
}
|
137
Sesame/ClientState.swift
Normal file
137
Sesame/ClientState.swift
Normal file
@ -0,0 +1,137 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
enum ClientState {
|
||||
|
||||
/// The initial state after app launch
|
||||
case initial
|
||||
|
||||
/// There are no keys stored locally on the client. New keys must be generated before use.
|
||||
case noKeysAvailable
|
||||
|
||||
/// New keys have been generated and can now be transmitted to the device.
|
||||
case newKeysGenerated
|
||||
|
||||
/// The device status could not be determined
|
||||
case statusRequestFailed
|
||||
|
||||
/// The status received from the server could not be decoded
|
||||
case unknownDeviceStatus
|
||||
|
||||
/// The remote device is not connected (no socket opened)
|
||||
case deviceDisconnected
|
||||
|
||||
/// The device is connected and ready to receive a key
|
||||
case deviceConnected
|
||||
|
||||
/// The key is being transmitted and a response is awaited
|
||||
case waitingForResponse
|
||||
|
||||
/// The transmitted key was rejected (multiple possible reasons)
|
||||
case keyRejected
|
||||
|
||||
/// Internal errors with the implementation
|
||||
case internalError
|
||||
|
||||
/// The configuration of the devices doesn't match
|
||||
case configurationError
|
||||
|
||||
/// The device responded that the opening action was started
|
||||
case openSesame
|
||||
|
||||
/// All keys have been used
|
||||
case allKeysUsed
|
||||
|
||||
var canSendKey: Bool {
|
||||
switch self {
|
||||
case .deviceConnected, .openSesame, .keyRejected:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
init(keyResult: KeyResult) {
|
||||
switch keyResult {
|
||||
case .textReceived, .unexpectedSocketEvent, .unknownDeviceError:
|
||||
self = .unknownDeviceStatus
|
||||
case .invalidPayloadSize, .invalidKeyIndex, .invalidKey:
|
||||
self = .configurationError
|
||||
case .keyAlreadyUsed, .keyWasSkipped:
|
||||
self = .keyRejected
|
||||
case .keyAccepted:
|
||||
self = .openSesame
|
||||
case .noBodyData, .corruptkeyData:
|
||||
self = .internalError
|
||||
case .deviceNotConnected, .deviceTimedOut:
|
||||
self = .deviceDisconnected
|
||||
}
|
||||
}
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .initial:
|
||||
return "Checking state..."
|
||||
case .noKeysAvailable:
|
||||
return "No keys found"
|
||||
case .newKeysGenerated:
|
||||
return "New keys generated"
|
||||
case .deviceDisconnected:
|
||||
return "Device not connected"
|
||||
case .statusRequestFailed:
|
||||
return "Unable to get device status"
|
||||
case .unknownDeviceStatus:
|
||||
return "Unknown device status"
|
||||
case .deviceConnected:
|
||||
return "Device connected"
|
||||
case .waitingForResponse:
|
||||
return "Waiting for response"
|
||||
case .internalError:
|
||||
return "An internal error occured"
|
||||
case .configurationError:
|
||||
return "Configuration error"
|
||||
|
||||
case .allKeysUsed:
|
||||
return "No fresh keys available"
|
||||
case .keyRejected:
|
||||
return "The key was rejected"
|
||||
case .openSesame:
|
||||
return "Unlocked"
|
||||
}
|
||||
}
|
||||
|
||||
var openButtonText: String {
|
||||
switch self {
|
||||
case .initial, .statusRequestFailed, .unknownDeviceStatus, .deviceDisconnected, .newKeysGenerated, .configurationError, .internalError:
|
||||
return "Connect"
|
||||
case .allKeysUsed, .noKeysAvailable:
|
||||
return "Disabled"
|
||||
case .deviceConnected, .keyRejected, .openSesame:
|
||||
return "Unlock"
|
||||
case .waitingForResponse:
|
||||
return "Unlocking..."
|
||||
}
|
||||
}
|
||||
|
||||
var openButtonColor: Color {
|
||||
switch self {
|
||||
case .initial, .newKeysGenerated, .statusRequestFailed, .waitingForResponse:
|
||||
return .yellow
|
||||
case .noKeysAvailable, .allKeysUsed, .deviceDisconnected, .unknownDeviceStatus, .keyRejected, .configurationError, .internalError:
|
||||
return .red
|
||||
case .deviceConnected, .openSesame:
|
||||
return .green
|
||||
}
|
||||
}
|
||||
|
||||
var openActionIsEnabled: Bool {
|
||||
switch self {
|
||||
case .allKeysUsed, .noKeysAvailable, .waitingForResponse:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
164
Sesame/ContentView.swift
Normal file
164
Sesame/ContentView.swift
Normal file
@ -0,0 +1,164 @@
|
||||
import SwiftUI
|
||||
import CryptoKit
|
||||
|
||||
let keyManager = try! KeyManagement()
|
||||
let server = Client(server: URL(string: "https://christophhagen.de/sesame/")!)
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@State var state: ClientState = .initial
|
||||
|
||||
var canShareKey = false
|
||||
|
||||
@State var showNewKeyWarning = false
|
||||
|
||||
@State var showKeyGenerationFailedWarning = false
|
||||
|
||||
@State var showShareSheetForNewKeys = false
|
||||
|
||||
@State var activeRequestCount = 0
|
||||
|
||||
var isPerformingRequests: Bool {
|
||||
activeRequestCount > 0
|
||||
}
|
||||
|
||||
var keyText: String {
|
||||
let totalKeys = keyManager.numberOfKeys
|
||||
guard totalKeys > 0 else {
|
||||
return "No keys available"
|
||||
}
|
||||
let unusedKeys = keyManager.unusedKeyCount
|
||||
guard unusedKeys > 0 else {
|
||||
return "All keys used"
|
||||
}
|
||||
return "\(totalKeys - unusedKeys) / \(totalKeys) keys used"
|
||||
}
|
||||
|
||||
private let buttonWidth: CGFloat = 200
|
||||
|
||||
private let topButtonHeight: CGFloat = 60
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 20) {
|
||||
Text(keyText)
|
||||
Button("Generate new keys", action: {
|
||||
showNewKeyWarning = true
|
||||
print("Key regeneration requested")
|
||||
})
|
||||
.padding()
|
||||
.frame(width: buttonWidth, height: topButtonHeight)
|
||||
.background(.blue)
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(topButtonHeight / 2)
|
||||
Button("Share one-time key", action: shareKey)
|
||||
.padding()
|
||||
.frame(width: buttonWidth, height: topButtonHeight)
|
||||
.background(.mint)
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(topButtonHeight / 2)
|
||||
.disabled(!canShareKey)
|
||||
|
||||
Spacer()
|
||||
HStack {
|
||||
if isPerformingRequests {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle())
|
||||
}
|
||||
Text(state.description)
|
||||
.padding()
|
||||
}
|
||||
Button(state.openButtonText, action: mainButtonPressed)
|
||||
.frame(width: buttonWidth, height: 80, alignment: .center)
|
||||
.background(state.openButtonColor)
|
||||
.cornerRadius(100)
|
||||
.foregroundColor(.white)
|
||||
.font(.title2)
|
||||
.disabled(!state.openActionIsEnabled)
|
||||
}
|
||||
.padding(20)
|
||||
.onAppear {
|
||||
checkInitialDeviceStatus()
|
||||
}.alert(isPresented: $showKeyGenerationFailedWarning) {
|
||||
Alert(title: Text("The keys could not be generated"),
|
||||
message: Text("All previous keys will be deleted and the lock will be blocked. Are you sure?"),
|
||||
dismissButton: .default(Text("Okay")))
|
||||
}.shareSheet(isPresented: $showShareSheetForNewKeys, items: [keyManager.exportFile])
|
||||
.alert(isPresented: $showNewKeyWarning) {
|
||||
Alert(title: Text("Generate new keys"),
|
||||
message: Text("All previous keys will be deleted and the lock will be blocked. Are you sure?"),
|
||||
primaryButton: .destructive(Text("Generate"), action: regenerateKeys),
|
||||
secondaryButton: .cancel())
|
||||
}
|
||||
}
|
||||
|
||||
func mainButtonPressed() {
|
||||
print("Main button pressed")
|
||||
if state.canSendKey {
|
||||
sendKey()
|
||||
} else {
|
||||
checkInitialDeviceStatus()
|
||||
}
|
||||
}
|
||||
|
||||
func sendKey() {
|
||||
guard let key = keyManager.useNextKey() else {
|
||||
state = .allKeysUsed
|
||||
return
|
||||
}
|
||||
state = .waitingForResponse
|
||||
activeRequestCount += 1
|
||||
print("Sending key \(key.id)")
|
||||
Task {
|
||||
let newState = try await server.keyResponse(key: key.key, id: key.id)
|
||||
activeRequestCount -= 1
|
||||
state = newState
|
||||
}
|
||||
}
|
||||
|
||||
func checkInitialDeviceStatus() {
|
||||
print("Checking device status")
|
||||
Task {
|
||||
do {
|
||||
activeRequestCount += 1
|
||||
let newState = try await server.deviceStatus()
|
||||
activeRequestCount -= 1
|
||||
print("Device status: \(newState)")
|
||||
switch newState {
|
||||
case .noKeysAvailable, .allKeysUsed:
|
||||
return
|
||||
default:
|
||||
state = newState
|
||||
}
|
||||
} catch {
|
||||
print("Failed to get device status: \(error)")
|
||||
state = .statusRequestFailed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func regenerateKeys() {
|
||||
print("Regenerate keys")
|
||||
do {
|
||||
try keyManager.regenerateKeys()
|
||||
state = .newKeysGenerated
|
||||
showKeyGenerationFailedWarning = false
|
||||
showShareSheetForNewKeys = true
|
||||
checkInitialDeviceStatus()
|
||||
} catch {
|
||||
state = .noKeysAvailable
|
||||
showKeyGenerationFailedWarning = true
|
||||
showShareSheetForNewKeys = false
|
||||
}
|
||||
}
|
||||
|
||||
func shareKey() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ContentView()
|
||||
.previewDevice("iPhone 8")
|
||||
}
|
||||
}
|
118
Sesame/KeyManagement.swift
Normal file
118
Sesame/KeyManagement.swift
Normal file
@ -0,0 +1,118 @@
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
import SwiftUI
|
||||
|
||||
final class KeyManagement {
|
||||
|
||||
static let securityKeySize: SymmetricKeySize = .bits128
|
||||
|
||||
enum KeyError: Error {
|
||||
/// Keys which are already in use can't be exported
|
||||
case exportAttemptOfUsedKeys
|
||||
}
|
||||
|
||||
static var documentsDirectory: URL {
|
||||
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
|
||||
return paths[0]
|
||||
}
|
||||
|
||||
private let keyFile = KeyManagement.documentsDirectory.appendingPathComponent("keys")
|
||||
|
||||
let exportFile = KeyManagement.documentsDirectory.appendingPathComponent("export.cpp")
|
||||
|
||||
private var keys: [(key: SymmetricKey, used: Bool)] {
|
||||
didSet {
|
||||
do {
|
||||
try saveKeys()
|
||||
} catch {
|
||||
print("Failed to save changed keys: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var numberOfKeys: Int {
|
||||
keys.count
|
||||
}
|
||||
|
||||
var hasUsedKeys: Bool {
|
||||
keys.contains { $0.used }
|
||||
}
|
||||
|
||||
var hasUnusedKeys: Bool {
|
||||
unusedKeyCount > 0
|
||||
}
|
||||
|
||||
var unusedKeyCount: Int {
|
||||
guard let id = nextKeyId else {
|
||||
return 0
|
||||
}
|
||||
return keys.count - id + 1
|
||||
}
|
||||
|
||||
var usedKeyCount: Int {
|
||||
nextKeyId ?? keys.count
|
||||
}
|
||||
|
||||
var lastKeyId: Int? {
|
||||
keys.lastIndex { $0.used }
|
||||
}
|
||||
|
||||
var nextKeyId: Int? {
|
||||
let index = lastKeyId ?? -1 + 1
|
||||
guard index < keys.count else {
|
||||
return nil
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
init() throws {
|
||||
guard FileManager.default.fileExists(atPath: keyFile.path) else {
|
||||
self.keys = []
|
||||
return
|
||||
}
|
||||
let content = try String(contentsOf: keyFile)
|
||||
self.keys = content.components(separatedBy: "\n")
|
||||
.enumerated().compactMap { (index, line) -> (SymmetricKey, Bool)? in
|
||||
let parts = line.components(separatedBy: ":")
|
||||
guard parts.count == 2 else {
|
||||
return nil
|
||||
}
|
||||
let keyData = Data(base64Encoded: parts[0])!
|
||||
return (SymmetricKey(data: keyData), parts[1] != "0")
|
||||
}
|
||||
print("\(unusedKeyCount) / \(keys.count) keys remaining")
|
||||
}
|
||||
|
||||
func useNextKey() -> (key: SymmetricKey, id: Int)? {
|
||||
guard let index = nextKeyId else {
|
||||
return nil
|
||||
}
|
||||
let key = keys[index].key
|
||||
keys[index].used = true
|
||||
return (key, index)
|
||||
}
|
||||
|
||||
func regenerateKeys(count: Int = 100) throws {
|
||||
self.keys = Self.generateKeys(count: count)
|
||||
.map { ($0, false) }
|
||||
let keyString = keys.map { $0.key.codeString }.joined(separator: "\n")
|
||||
try keyString.write(to: exportFile, atomically: false, encoding: .utf8)
|
||||
}
|
||||
|
||||
private func saveKeys() throws {
|
||||
let content = keys.map { key, used -> String in
|
||||
let keyString = key.withUnsafeBytes {
|
||||
return Data(Array($0)).base64EncodedString()
|
||||
}
|
||||
return keyString + ":" + (used ? "1" : "0")
|
||||
}.joined(separator: "\n")
|
||||
try content.write(to: keyFile, atomically: true, encoding: .utf8)
|
||||
print("Keys saved")
|
||||
}
|
||||
|
||||
static func generateKeys(count: Int = 100) -> [SymmetricKey] {
|
||||
(0..<count).map { _ in
|
||||
SymmetricKey(size: securityKeySize)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
80
Sesame/Response.swift
Normal file
80
Sesame/Response.swift
Normal file
@ -0,0 +1,80 @@
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
A result from sending a key to the device.
|
||||
*/
|
||||
enum KeyResult: UInt8 {
|
||||
|
||||
/// Text content was received, although binary data was expected
|
||||
case textReceived = 1
|
||||
|
||||
/// A socket event on the device was unexpected (not binary data)
|
||||
case unexpectedSocketEvent = 2
|
||||
|
||||
/// The size of the payload (key id + key data, or just key) was invalid
|
||||
case invalidPayloadSize = 3
|
||||
|
||||
/// The index of the key was out of bounds
|
||||
case invalidKeyIndex = 4
|
||||
|
||||
/// The transmitted key data did not match the expected key
|
||||
case invalidKey = 5
|
||||
|
||||
/// The key has been previously used and is no longer valid
|
||||
case keyAlreadyUsed = 6
|
||||
|
||||
/// A later key has been used, invalidating this key (to prevent replay attacks after blocked communication)
|
||||
case keyWasSkipped = 7
|
||||
|
||||
/// The key was accepted by the device, and the door will be opened
|
||||
case keyAccepted = 8
|
||||
|
||||
/// The device produced an unknown error
|
||||
case unknownDeviceError = 9
|
||||
|
||||
/// The request did not contain body data with the key
|
||||
case noBodyData = 10
|
||||
|
||||
/// The body data could not be read
|
||||
case corruptkeyData = 11
|
||||
|
||||
/// The device is not connected
|
||||
case deviceNotConnected = 12
|
||||
|
||||
/// The device did not respond within the timeout
|
||||
case deviceTimedOut = 13
|
||||
}
|
||||
|
||||
extension KeyResult: CustomStringConvertible {
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .invalidKeyIndex:
|
||||
return "Invalid key id (too large)"
|
||||
case .noBodyData:
|
||||
return "No body data included in the request"
|
||||
case .invalidPayloadSize:
|
||||
return "Invalid key size"
|
||||
case .corruptkeyData:
|
||||
return "Key data corrupted"
|
||||
case .deviceNotConnected:
|
||||
return "Device not connected"
|
||||
case .textReceived:
|
||||
return "The device received unexpected text"
|
||||
case .unexpectedSocketEvent:
|
||||
return "Unexpected socket event for the device"
|
||||
case .invalidKey:
|
||||
return "The transmitted key was not correct"
|
||||
case .keyAlreadyUsed:
|
||||
return "The transmitted key was already used"
|
||||
case .keyWasSkipped:
|
||||
return "A newer key was already used"
|
||||
case .keyAccepted:
|
||||
return "Key successfully sent"
|
||||
case .unknownDeviceError:
|
||||
return "The device experienced an unknown error"
|
||||
case .deviceTimedOut:
|
||||
return "The device did not respond"
|
||||
}
|
||||
}
|
||||
}
|
17
Sesame/SesameApp.swift
Normal file
17
Sesame/SesameApp.swift
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// SesameApp.swift
|
||||
// Sesame
|
||||
//
|
||||
// Created by iMac on 24.01.22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct SesameApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
}
|
21
Sesame/ShareSheet.swift
Normal file
21
Sesame/ShareSheet.swift
Normal file
@ -0,0 +1,21 @@
|
||||
import SwiftUI
|
||||
|
||||
extension UIApplication {
|
||||
|
||||
static let keyWindow = keyWindowScene?.windows.filter(\.isKeyWindow).first
|
||||
static let keyWindowScene = shared.connectedScenes.first { $0.activationState == .foregroundActive } as? UIWindowScene
|
||||
|
||||
}
|
||||
|
||||
extension View {
|
||||
|
||||
func shareSheet(isPresented: Binding<Bool>, items: [Any]) -> some View {
|
||||
guard isPresented.wrappedValue else { return self }
|
||||
let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
|
||||
let presentedViewController = UIApplication.keyWindow?.rootViewController?.presentedViewController ?? UIApplication.keyWindow?.rootViewController
|
||||
activityViewController.completionWithItemsHandler = { _, _, _, _ in isPresented.wrappedValue = false }
|
||||
presentedViewController?.present(activityViewController, animated: true)
|
||||
return self
|
||||
}
|
||||
|
||||
}
|
21
Sesame/SymmetricKey+Extensions.swift
Normal file
21
Sesame/SymmetricKey+Extensions.swift
Normal file
@ -0,0 +1,21 @@
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
|
||||
extension SymmetricKey {
|
||||
|
||||
var data: Data {
|
||||
withUnsafeBytes { Data(Array($0)) }
|
||||
}
|
||||
|
||||
var base64: String {
|
||||
data.base64EncodedString()
|
||||
}
|
||||
|
||||
var codeString: String {
|
||||
" {" +
|
||||
withUnsafeBytes {
|
||||
return Data(Array($0))
|
||||
}.map(String.init).joined(separator: ", ") +
|
||||
"},"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user