First commit.

This commit is contained in:
Wolf McNally 2019-04-24 00:43:27 -07:00
commit 481e1cffa7
33 changed files with 2213 additions and 0 deletions

26
.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
# OS X
.DS_Store
# Xcode
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
*.xccheckout
profile
*.moved-aside
DerivedData
*.hmap
*.ipa
# Bundler
.bundle
Carthage/Build
Pods/

14
.travis.yml Normal file
View File

@ -0,0 +1,14 @@
# references:
# * https://www.objc.io/issues/6-build-tools/travis-ci/
# * https://github.com/supermarin/xcpretty#usage
osx_image: xcode7.3
language: objective-c
# cache: cocoapods
# podfile: Example/Podfile
# before_install:
# - gem install cocoapods # Since Travis is not always on latest version
# - pod install --project-directory=Example
script:
- set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/AirgappedSigning.xcworkspace -scheme AirgappedSigning-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty
- pod lib lint

28
AirgappedSigning.podspec Normal file
View File

@ -0,0 +1,28 @@
Pod::Spec.new do |s|
s.name = 'AirgappedSigning'
s.version = '0.1.0'
s.summary = 'A protocol for transmission of messages between hot and cold cryptographic wallets.'
# s.description = <<-DESC
# TODO: Add long description of the pod here.
# DESC
s.homepage = 'https://github.com/wolfmcnally/AirgappedSigning'
s.license = { :type => 'Apache', :file => 'LICENSE' }
s.author = { 'Wolf McNally' => 'wolf@wolfmcnally.com' }
s.source = { :git => 'https://github.com/blockchainCommons/AirgappedSigning.git', :tag => s.version.to_s }
s.source_files = 'Sources/AirgappedSigning/**/*'
s.swift_version = '5.0'
s.ios.deployment_target = '11.0'
# s.macos.deployment_target = '10.13'
# s.tvos.deployment_target = '11.0'
s.module_name = 'AirgappedSigning'
s.dependency 'Bitcoin'
s.dependency 'WolfCore'
s.dependency 'NonEmpty'
end

View File

@ -0,0 +1,148 @@
# Airgapped Signing Protocol Examples
### Examples
Note that while the examples in this section should validate against the JSON schema, these examples are not test vectors and should not be expected to validate. Please see the JSON schema below for further explanatory comments.
#### MultiPart
Used when a document is too large to fit into a packet (like a QR code) and must be broken into multiple parts. The concatenated `data` fields must decode to another document in this specification.
```json
{
"header": {
"format": "AirgappedSigning",
"version": 1
},
"multiPart": {
"uid": "449C40FE-E207-4AC9-B552-51B007B68D50",
"part": 0,
"count": 1,
"data": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4K"
}
}
```
#### RecoveryWords
Used to back up the seed from which all other keys are derived. The passphrase (if any) is never transmitted in this structure-- it is always entered directly by the user.
```json
{
"header": {
"format": "AirgappedSigning",
"version": 1
},
"recoveryWords": {
"name": "Rainy Day",
"format": "BIP39",
"words": ["panda", "diary", "marriage", "suffer", "basic", "glare", "surge", "auto", "scissors", "describe", "sell", "unique"]
}
}
```
#### KnownReceiver
Used to send the address of a known receiver to the cold wallet to be used when confirming the contents of transactions.
```json
{
"header": {
"format": "AirgappedSigning",
"version": 1
},
"knownReceiver": {
"name": "Example",
"asset": "BTC",
"address": "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2"
}
}
```
#### Account
Used to send a public key, asset, and associated account path to the hot wallet to be used in watching the block chain and/or creating new transactions.
```json
{
"header": {
"format": "AirgappedSigning",
"version": 1
},
"account": {
"name": "Example Account",
"asset": "BTC",
"hdPublicKey": "xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz",
"index": 1
}
}
```
#### Transaction Signing Request
"Transaction" as used here is rougly equivalent to a Partially Signed Bitcoin Transaction (PSBT). It is designed so multiple parties can provide inputs, outputs, and signatures to be combined later.
```json
{
"header": {
"format": "AirgappedSigning",
"version": 1
},
"transaction": {
"uid": "819AACCF-984A-43EF-AC1D-D4CD37C9A7DC",
"asset": "BTC",
"inputs": [
{
"uid": "A6B0DF6D-E670-4233-8EAC-626C0A9AAC9A",
"txHash": "ba34fa2ed2c42e7ba66887a96d22e14dec1244a3c47b271cd69a679afbaab868",
"inputIndex": 1,
"sender": "18f8tN4PLdCXF2JHy5PjRerqXg994yGme3",
"derivation": {
"accountIndex": 0,
"addressIndex": 12
},
"amount": 59943910
}
],
"outputs": [
{
"uid": "79296757-6C80-49BF-996F-87FCB46A566C",
"receiver": "1N5YvoDTEbkKsE1SiPEKbih6BM1HAD3dV9",
"amount": 9240
},
{
"uid": "69F19CB5-A9B1-4C80-B5CA-A304E37C04C7",
"receiver": "19MUKz42a951wmXtJBa5fdvRvGvAGh6NDG",
"derivation": {
"accountIndex": 0,
"addressIndex": 4
},
"amount": 59855564
}
]
}
}
```
#### Transaction Signing Response
This example is a reply from the cold wallet that signs the input provided in the previous example.
```json
{
"header": {
"format": "AirgappedSigning",
"version": 1
},
"transaction": {
"uid": "819AACCF-984A-43EF-AC1D-D4CD37C9A7DC",
"inputSignatures": [
{
"uid": "A6B0DF6D-E670-4233-8EAC-626C0A9AAC9A",
"ecPublicKey": "AzfAJTIa8S38RRcVnZvZgWTdL/n715cbKdCqJx1yxATK",
"ecSignature": "MEQCIBBhpg0KAmDqJu/0v/L8a9kCWGMNh3QVLYpq/tfzEQdmAiAMLrZKT5H2maQXPvEm6iYTNIcpKk6B+8Lg4z+xaLVCbAE="
}
]
}
}
```

View File

@ -0,0 +1,326 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://blockchaincommons.org/schemas/AirgappedSigning.json",
"$comment": "This scheme follows the convention of naming keys with `Capitalized` words when they represent types, and `lowercased` words when they represent instances. In general, `camelCase` is used for identifiers.",
"definitions": {
"Name": {
"$comment": "A human-assigned name for an object. In required field lists herein, `name` is intentionally left optional.",
"type": "string",
"minLength": 1
},
"UID": {
"$comment": "A unique identifier used to associate messages or parts of documents.",
"type": "string",
"format": "uuid"
},
"Asset": {
"$comment": "Identifies a particular kind of asset, for example Bitcoin. Could also represent the type of any other document capable of being signed.\n`BTC`: Bitcoin, `BTCT`: Bitcoin Testnet.",
"type": "string",
"enum": ["BTC", "BTCT"]
},
"Fragments": {
"$comment": "A whole number count of the smallest unit of an asset; in the case of Bitcoin, the Satoshi.",
"type": "number",
"multipleOf": 1
},
"MultiPart": {
"$comment": "Used when a document is too large to fit into a packet (like a QR code) and must be broken into multiple parts. The `data` field contains a base64-encoded chunck of another document that conforms to this spec.",
"type": "object",
"properties": {
"uid": {
"$comment": "`uid` must be the same for every part of the same multipart document.",
"$ref": "#/definitions/UID"
},
"part": {
"type": "number",
"multipleOf": 1,
"minimum": 0,
"$comment": "Zero-based. Must be < `count`."
},
"count": {
"type": "number",
"multipleOf": 1,
"minimum": 1
},
"data": {
"type": "string",
"contentEncoding": "base64"
}
},
"required": [
"uid",
"part",
"count",
"data"
],
"additionalProperties": false
},
"RecoveryWords": {
"$comment": "A list of recovery words used for BIP39 or similar.",
"type": "object",
"properties": {
"name": { "$ref": "#/definitions/Name" },
"format": { "enum": ["BIP39"] },
"words": {
"type": "array",
"minItems": 1,
"maxItems": 100,
"items": {
"type": "string",
"minLength": 3,
"maxLength": 30
}
}
},
"required": [
"format",
"words"
],
"additionalProperties": false
},
"PaymentAddress": {
"$comment": "A payment address.",
"type": "string",
"minLength": 1
},
"KnownReceiver": {
"$comment": "A payment address associated with a name and asset type.",
"type": "object",
"properties": {
"name": { "$ref": "#/definitions/Name" },
"asset": { "$ref": "#/definitions/Asset" },
"address": { "$ref": "#/definitions/PaymentAddress" }
},
"required": [
"asset",
"address"
],
"additionalProperties": false
},
"AccountIndex": {
"$comment": "The `account` part of a BIP44 derivation path.",
"type": "number",
"multipleOf": 1,
"minimum": 0
},
"AddressIndex": {
"$comment": "The `address_index` part of a BIP44 derivation path.",
"type": "number",
"multipleOf": 1,
"minimum": 0
},
"HDPublicKey": {
"$comment": "An HD public key that can be watched for activity on the network, and from which payment addresses can be derived.",
"type": "string",
"minLength": 1
},
"ECPublicKey": {
"$comment": "A public key that can be used as part of a transaction signing.",
"type": "string",
"contentEncoding": "base64"
},
"ECSignature": {
"$comment": "An EC signature.",
"type": "string",
"contentEncoding": "base64"
},
"Account": {
"$comment": "A particular account derived from a particular public key.",
"type": "object",
"properties": {
"name": { "$ref": "#/definitions/Name" },
"asset": { "$ref": "#/definitions/Asset" },
"hdPublicKey": { "$ref": "#/definitions/HDPublicKey" },
"index": { "$ref": "#/definitions/AccountIndex" }
},
"required": [
"asset",
"hdPublicKey",
"index"
],
"additionalProperties": false
},
"Derivation": {
"type": "object",
"properties": {
"accountIndex": { "$ref": "#/definitions/AccountIndex" },
"addressIndex": { "$ref": "#/definitions/AddressIndex" },
},
"required": [
"accountIndex",
"addressIndex"
],
"additionalProperties": false
},
"Input": {
"type": "object",
"properties": {
"uid": {
"$comment": "Associates this input with its signature(s).",
"$ref": "#/definitions/UID"
},
"txHash": { "type": "string" },
"inputIndex": { "type": "number" },
"sender": {
"$comment": "Which private key to use for signing can be determined by searching for the private key from which `sender` can be derived by applying `accountIndex` and `addressIndex` in the derivation path.",
"$ref": "#/definitions/PaymentAddress"
},
"derivation": { "$ref": "#/definitions/Derivation" },
"amount": { "$ref": "#/definitions/Fragments" }
},
"required": [
"uid",
"txHash",
"inputIndex",
"sender",
"derivation",
"amount"
],
"additionalProperties": false
},
"Output": {
"type": "object",
"properties": {
"uid": { "$ref": "#/definitions/UID" },
"receiver": { "$ref": "#/definitions/PaymentAddress" },
"amount": { "$ref": "#/definitions/Fragments" },
"derivation": { "$ref": "#/definitions/Derivation" }
},
"required": [
"uid",
"receiver",
"amount"
],
"$comment": "`derivation` intentionally left optional because only change outputs need a derivation path so they can be confirmed as spendable.",
"additionalProperties": false
},
"InputSignature": {
"type": "object",
"properties": {
"uid": {
"$comment": "Associates this signature with an input in the transaction.",
"$ref": "#/definitions/UID"
},
"ecPublicKey": {
"$comment": "The public key which corresponds to this signature.",
"$ref": "#/definitions/ECPublicKey"
},
"ecSignature": {
"$comment": "The signature as would be pushed to the stack from a scriptSig or witness.",
"$ref": "#/definitions/ECSignature"
}
},
"required": [
"uid",
"ecPublicKey",
"ecSignature"
]
},
"Transaction": {
"$comment": "Roughly equivalent to a Partially Signed Bitcoin Transaction (PSBT). Designed so multiple parties can provide inputs, outputs, and signatures to be combined later.",
"type": "object",
"properties": {
"uid": {
"$comment": "`uid` must be the same for all messages of the same signing transaction.",
"$ref": "#/definitions/UID"
},
"asset": { "$ref": "#/definitions/Asset" },
"inputs": {
"$comment": "If present, may not be empty.",
"type": "array",
"items": { "$ref": "#/definitions/Input" }
},
"outputs": {
"$comment": "If present, may not be empty.",
"type": "array",
"items": { "$ref": "#/definitions/Output" }
},
"inputSignatures": {
"$comment": "If present, may not be empty.",
"type": "array",
"items": { "$ref": "#/definitions/InputSignature" }
}
},
"required": [
"uid"
],
"$comment": "`asset`, `inputs`, `outputs`, and `inputSignatures` intentionally left optional for representing partial transactions.",
"additionalProperties": false
},
"Header": {
"type": "object",
"properties": {
"format": { "const": "AirgappedSigning" },
"version": { "const": 1 }
},
"required": [
"type",
"version"
],
"additionalProperties": false
}
},
"oneOf": [
{
"type": "object",
"properties": {
"header": { "$ref": "#/definitions/Header" },
"multiPart": { "$ref": "#/definitions/MultiPart" }
},
"required": [
"header",
"multiPart"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"header": { "$ref": "#/definitions/Header" },
"recoveryWords": { "$ref": "#/definitions/RecoveryWords" }
},
"required": [
"header",
"recoveryWords"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"header": { "$ref": "#/definitions/Header" },
"knownReceiver": { "$ref": "#/definitions/KnownReceiver" }
},
"required": [
"header",
"knownReceiver"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"header": { "$ref": "#/definitions/Header" },
"account": { "$ref": "#/definitions/Account" }
},
"required": [
"header",
"account"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"header": { "$ref": "#/definitions/Header" },
"transaction": { "$ref": "#/definitions/Transaction" }
},
"required": [
"header",
"transaction"
],
"additionalProperties": false
}
]
}

View File

@ -0,0 +1,610 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; };
607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; };
607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; };
607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; };
607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; };
607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; };
BC2B1DE04520DF63ECB1180C /* Pods_AirgappedSigning_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0482D64EDEE9715D175CF016 /* Pods_AirgappedSigning_Tests.framework */; };
EF6DB9BDC7D6962179D78605 /* Pods_AirgappedSigning_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94FEB700BCF9984F9E44F826 /* Pods_AirgappedSigning_Example.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 607FACC81AFB9204008FA782 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 607FACCF1AFB9204008FA782;
remoteInfo = AirgappedSigning;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
0355E88BCB373C4803A6A18C /* AirgappedSigning.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = AirgappedSigning.podspec; path = ../AirgappedSigning.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
0482D64EDEE9715D175CF016 /* Pods_AirgappedSigning_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AirgappedSigning_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
05D0FE05BA7A72641201C4E1 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
4EB2C27D3619D1CA0346AA96 /* Pods-AirgappedSigning_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AirgappedSigning_Tests.debug.xcconfig"; path = "Target Support Files/Pods-AirgappedSigning_Tests/Pods-AirgappedSigning_Tests.debug.xcconfig"; sourceTree = "<group>"; };
51400B6C08379C0E6EC2285F /* Pods-AirgappedSigning_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AirgappedSigning_Tests.release.xcconfig"; path = "Target Support Files/Pods-AirgappedSigning_Tests/Pods-AirgappedSigning_Tests.release.xcconfig"; sourceTree = "<group>"; };
5E267DBE350210DDA6D5F071 /* Pods-AirgappedSigning_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AirgappedSigning_Example.debug.xcconfig"; path = "Target Support Files/Pods-AirgappedSigning_Example/Pods-AirgappedSigning_Example.debug.xcconfig"; sourceTree = "<group>"; };
607FACD01AFB9204008FA782 /* AirgappedSigning_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AirgappedSigning_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
607FACE51AFB9204008FA782 /* AirgappedSigning_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AirgappedSigning_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = "<group>"; };
653EE703F7545ACADEA15A65 /* Pods-AirgappedSigning_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AirgappedSigning_Example.release.xcconfig"; path = "Target Support Files/Pods-AirgappedSigning_Example/Pods-AirgappedSigning_Example.release.xcconfig"; sourceTree = "<group>"; };
844B88A490A39412A1A97776 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
94FEB700BCF9984F9E44F826 /* Pods_AirgappedSigning_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AirgappedSigning_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
607FACCD1AFB9204008FA782 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
EF6DB9BDC7D6962179D78605 /* Pods_AirgappedSigning_Example.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
607FACE21AFB9204008FA782 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
BC2B1DE04520DF63ECB1180C /* Pods_AirgappedSigning_Tests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2E2E27C47B446D8DF8E09FC9 /* Pods */ = {
isa = PBXGroup;
children = (
5E267DBE350210DDA6D5F071 /* Pods-AirgappedSigning_Example.debug.xcconfig */,
653EE703F7545ACADEA15A65 /* Pods-AirgappedSigning_Example.release.xcconfig */,
4EB2C27D3619D1CA0346AA96 /* Pods-AirgappedSigning_Tests.debug.xcconfig */,
51400B6C08379C0E6EC2285F /* Pods-AirgappedSigning_Tests.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
5A95E4B96D10D8515149C1BF /* Frameworks */ = {
isa = PBXGroup;
children = (
94FEB700BCF9984F9E44F826 /* Pods_AirgappedSigning_Example.framework */,
0482D64EDEE9715D175CF016 /* Pods_AirgappedSigning_Tests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
607FACC71AFB9204008FA782 = {
isa = PBXGroup;
children = (
607FACF51AFB993E008FA782 /* Podspec Metadata */,
607FACD21AFB9204008FA782 /* Example for AirgappedSigning */,
607FACE81AFB9204008FA782 /* Tests */,
607FACD11AFB9204008FA782 /* Products */,
2E2E27C47B446D8DF8E09FC9 /* Pods */,
5A95E4B96D10D8515149C1BF /* Frameworks */,
);
sourceTree = "<group>";
};
607FACD11AFB9204008FA782 /* Products */ = {
isa = PBXGroup;
children = (
607FACD01AFB9204008FA782 /* AirgappedSigning_Example.app */,
607FACE51AFB9204008FA782 /* AirgappedSigning_Tests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
607FACD21AFB9204008FA782 /* Example for AirgappedSigning */ = {
isa = PBXGroup;
children = (
607FACD51AFB9204008FA782 /* AppDelegate.swift */,
607FACD71AFB9204008FA782 /* ViewController.swift */,
607FACD91AFB9204008FA782 /* Main.storyboard */,
607FACDC1AFB9204008FA782 /* Images.xcassets */,
607FACDE1AFB9204008FA782 /* LaunchScreen.xib */,
607FACD31AFB9204008FA782 /* Supporting Files */,
);
name = "Example for AirgappedSigning";
path = AirgappedSigning;
sourceTree = "<group>";
};
607FACD31AFB9204008FA782 /* Supporting Files */ = {
isa = PBXGroup;
children = (
607FACD41AFB9204008FA782 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
607FACE81AFB9204008FA782 /* Tests */ = {
isa = PBXGroup;
children = (
607FACEB1AFB9204008FA782 /* Tests.swift */,
607FACE91AFB9204008FA782 /* Supporting Files */,
);
path = Tests;
sourceTree = "<group>";
};
607FACE91AFB9204008FA782 /* Supporting Files */ = {
isa = PBXGroup;
children = (
607FACEA1AFB9204008FA782 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
607FACF51AFB993E008FA782 /* Podspec Metadata */ = {
isa = PBXGroup;
children = (
0355E88BCB373C4803A6A18C /* AirgappedSigning.podspec */,
05D0FE05BA7A72641201C4E1 /* README.md */,
844B88A490A39412A1A97776 /* LICENSE */,
);
name = "Podspec Metadata";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
607FACCF1AFB9204008FA782 /* AirgappedSigning_Example */ = {
isa = PBXNativeTarget;
buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "AirgappedSigning_Example" */;
buildPhases = (
D1057688AA0D43C557F0FCBA /* [CP] Check Pods Manifest.lock */,
607FACCC1AFB9204008FA782 /* Sources */,
607FACCD1AFB9204008FA782 /* Frameworks */,
607FACCE1AFB9204008FA782 /* Resources */,
288882641AE88ECD39CD0290 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = AirgappedSigning_Example;
productName = AirgappedSigning;
productReference = 607FACD01AFB9204008FA782 /* AirgappedSigning_Example.app */;
productType = "com.apple.product-type.application";
};
607FACE41AFB9204008FA782 /* AirgappedSigning_Tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "AirgappedSigning_Tests" */;
buildPhases = (
31D8847F2CE4CC7175566907 /* [CP] Check Pods Manifest.lock */,
607FACE11AFB9204008FA782 /* Sources */,
607FACE21AFB9204008FA782 /* Frameworks */,
607FACE31AFB9204008FA782 /* Resources */,
);
buildRules = (
);
dependencies = (
607FACE71AFB9204008FA782 /* PBXTargetDependency */,
);
name = AirgappedSigning_Tests;
productName = Tests;
productReference = 607FACE51AFB9204008FA782 /* AirgappedSigning_Tests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
607FACC81AFB9204008FA782 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0830;
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = WolfMcNally.com;
TargetAttributes = {
607FACCF1AFB9204008FA782 = {
CreatedOnToolsVersion = 6.3.1;
LastSwiftMigration = 0900;
};
607FACE41AFB9204008FA782 = {
CreatedOnToolsVersion = 6.3.1;
LastSwiftMigration = 0900;
TestTargetID = 607FACCF1AFB9204008FA782;
};
};
};
buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "AirgappedSigning" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 607FACC71AFB9204008FA782;
productRefGroup = 607FACD11AFB9204008FA782 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
607FACCF1AFB9204008FA782 /* AirgappedSigning_Example */,
607FACE41AFB9204008FA782 /* AirgappedSigning_Tests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
607FACCE1AFB9204008FA782 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */,
607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */,
607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
607FACE31AFB9204008FA782 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
288882641AE88ECD39CD0290 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-AirgappedSigning_Example/Pods-AirgappedSigning_Example-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/AirgappedSigning/AirgappedSigning.framework",
"${BUILT_PRODUCTS_DIR}/Bitcoin/Bitcoin.framework",
"${BUILT_PRODUCTS_DIR}/CBitcoin/CBitcoin.framework",
"${BUILT_PRODUCTS_DIR}/ExtensibleEnumeratedName/ExtensibleEnumeratedName.framework",
"${BUILT_PRODUCTS_DIR}/NonEmpty/NonEmpty.framework",
"${BUILT_PRODUCTS_DIR}/WolfConcurrency/WolfConcurrency.framework",
"${BUILT_PRODUCTS_DIR}/WolfCore/WolfCore.framework",
"${BUILT_PRODUCTS_DIR}/WolfFoundation/WolfFoundation.framework",
"${BUILT_PRODUCTS_DIR}/WolfNesting/WolfNesting.framework",
"${BUILT_PRODUCTS_DIR}/WolfNumerics/WolfNumerics.framework",
"${BUILT_PRODUCTS_DIR}/WolfOSBridge/WolfOSBridge.framework",
"${BUILT_PRODUCTS_DIR}/WolfPipe/WolfPipe.framework",
"${BUILT_PRODUCTS_DIR}/WolfStrings/WolfStrings.framework",
"${BUILT_PRODUCTS_DIR}/WolfWith/WolfWith.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AirgappedSigning.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Bitcoin.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CBitcoin.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ExtensibleEnumeratedName.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NonEmpty.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WolfConcurrency.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WolfCore.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WolfFoundation.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WolfNesting.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WolfNumerics.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WolfOSBridge.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WolfPipe.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WolfStrings.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WolfWith.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AirgappedSigning_Example/Pods-AirgappedSigning_Example-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
31D8847F2CE4CC7175566907 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-AirgappedSigning_Tests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
D1057688AA0D43C557F0FCBA /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-AirgappedSigning_Example-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
607FACCC1AFB9204008FA782 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
607FACD81AFB9204008FA782 /* ViewController.swift in Sources */,
607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
607FACE11AFB9204008FA782 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
607FACEC1AFB9204008FA782 /* Tests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
607FACE71AFB9204008FA782 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 607FACCF1AFB9204008FA782 /* AirgappedSigning_Example */;
targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
607FACD91AFB9204008FA782 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
607FACDA1AFB9204008FA782 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = {
isa = PBXVariantGroup;
children = (
607FACDF1AFB9204008FA782 /* Base */,
);
name = LaunchScreen.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
607FACED1AFB9204008FA782 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = 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_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_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
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 = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
607FACEE1AFB9204008FA782 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = 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_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_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
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 = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
607FACF01AFB9204008FA782 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 5E267DBE350210DDA6D5F071 /* Pods-AirgappedSigning_Example.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = AirgappedSigning/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
607FACF11AFB9204008FA782 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 653EE703F7545ACADEA15A65 /* Pods-AirgappedSigning_Example.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = AirgappedSigning/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 5.0;
};
name = Release;
};
607FACF31AFB9204008FA782 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 4EB2C27D3619D1CA0346AA96 /* Pods-AirgappedSigning_Tests.debug.xcconfig */;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AirgappedSigning_Example.app/AirgappedSigning_Example";
};
name = Debug;
};
607FACF41AFB9204008FA782 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 51400B6C08379C0E6EC2285F /* Pods-AirgappedSigning_Tests.release.xcconfig */;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
INFOPLIST_FILE = Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AirgappedSigning_Example.app/AirgappedSigning_Example";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "AirgappedSigning" */ = {
isa = XCConfigurationList;
buildConfigurations = (
607FACED1AFB9204008FA782 /* Debug */,
607FACEE1AFB9204008FA782 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "AirgappedSigning_Example" */ = {
isa = XCConfigurationList;
buildConfigurations = (
607FACF01AFB9204008FA782 /* Debug */,
607FACF11AFB9204008FA782 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "AirgappedSigning_Tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
607FACF31AFB9204008FA782 /* Debug */,
607FACF41AFB9204008FA782 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 607FACC81AFB9204008FA782 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:AirgappedSigning.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
BuildableName = "AirgappedSigning_Example.app"
BlueprintName = "AirgappedSigning_Example"
ReferencedContainer = "container:AirgappedSigning.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "607FACE41AFB9204008FA782"
BuildableName = "AirgappedSigning_Tests.xctest"
BlueprintName = "AirgappedSigning_Tests"
ReferencedContainer = "container:AirgappedSigning.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "607FACE41AFB9204008FA782"
BuildableName = "AirgappedSigning_Tests.xctest"
BlueprintName = "AirgappedSigning_Tests"
ReferencedContainer = "container:AirgappedSigning.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
BuildableName = "AirgappedSigning_Example.app"
BlueprintName = "AirgappedSigning_Example"
ReferencedContainer = "container:AirgappedSigning.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
BuildableName = "AirgappedSigning_Example.app"
BlueprintName = "AirgappedSigning_Example"
ReferencedContainer = "container:AirgappedSigning.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
BuildableName = "AirgappedSigning_Example.app"
BlueprintName = "AirgappedSigning_Example"
ReferencedContainer = "container:AirgappedSigning.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:AirgappedSigning.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@ -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>

View File

@ -0,0 +1,34 @@
//
// AppDelegate.swift
// AirgappedSigning
//
// Created by Wolf McNally on 09/15/2018.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
}

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="AirgappedSigning" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
<rect key="frame" x="20" y="139.5" width="440" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
</view>
</objects>
</document>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="vXZ-lx-hvc">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="ufC-wZ-h7g">
<objects>
<viewController id="vXZ-lx-hvc" customClass="ViewController" customModule="AirgappedSigning_Example" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="jyV-Pf-zRb"/>
<viewControllerLayoutGuide type="bottom" id="2fi-mo-0CV"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,53 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,39 @@
<?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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,28 @@
//
// ViewController.swift
// AirgappedSigning
//
// Created by Wolf McNally on 09/15/2018.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
class ViewController: UIViewController {
}

13
Example/Podfile Normal file
View File

@ -0,0 +1,13 @@
use_frameworks!
platform :ios, '12.0'
# pod 'Bitcoin', :path => '../../Bitcoin'
target 'AirgappedSigning_Example' do
pod 'AirgappedSigning', :path => '../'
target 'AirgappedSigning_Tests' do
inherit! :search_paths
end
end

81
Example/Podfile.lock Normal file
View File

@ -0,0 +1,81 @@
PODS:
- AirgappedSigning (0.1.0):
- Bitcoin
- NonEmpty
- WolfCore
- Bitcoin (0.5.1):
- CBitcoin
- WolfCore
- CBitcoin (0.5.1)
- ExtensibleEnumeratedName (2.0)
- NonEmpty (0.2.0)
- WolfConcurrency (3.0.2):
- WolfFoundation
- WolfNumerics
- WolfCore (4.0.8):
- ExtensibleEnumeratedName
- WolfConcurrency
- WolfFoundation
- WolfNesting
- WolfNumerics
- WolfOSBridge
- WolfPipe
- WolfStrings
- WolfWith
- WolfFoundation (3.0.3):
- WolfNumerics
- WolfPipe
- WolfNesting (2.0.1)
- WolfNumerics (4.0)
- WolfOSBridge (2.0.0)
- WolfPipe (2.0.0)
- WolfStrings (2.0.0):
- ExtensibleEnumeratedName
- WolfNumerics
- WolfOSBridge
- WolfPipe
- WolfWith
- WolfWith (2.0.1)
DEPENDENCIES:
- AirgappedSigning (from `../`)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
- Bitcoin
- CBitcoin
- ExtensibleEnumeratedName
- NonEmpty
- WolfConcurrency
- WolfCore
- WolfFoundation
- WolfNesting
- WolfNumerics
- WolfOSBridge
- WolfPipe
- WolfStrings
- WolfWith
EXTERNAL SOURCES:
AirgappedSigning:
:path: "../"
SPEC CHECKSUMS:
AirgappedSigning: 70ccc68e9502cac566b3b42935dc7f1c200d2378
Bitcoin: 94763986cdecff4b05b89c84f0afd5439d53ba6b
CBitcoin: 3f0f2e4e211aa10573940d8d9c9b0b0123ad24b5
ExtensibleEnumeratedName: 50f2d256f6d56baecf681f6fbb6362343dcf885b
NonEmpty: ffdc4086fbb2d7b977529a1b56bd6e7d4514ad76
WolfConcurrency: 50458b6d801209f89e41d4bb9d57c80c65984770
WolfCore: aaa510f76683e9f038e878d11356836eb135fd52
WolfFoundation: c38312563289428f5f0396df9af43bc81918bc53
WolfNesting: 891247e766da604f0e8124d85a158fe123ceb8d8
WolfNumerics: 6a57756b668073414da8a6911b86781096aded37
WolfOSBridge: e9260681790a7c89f725e134c0c1d4172d2d2dc9
WolfPipe: 18ffb77a188263392893a62630defc2a5b92b4f3
WolfStrings: edc63c08fbd1149ccbf5eca5020b13465084513b
WolfWith: 2320a88a03a943df9ff1284c9cca10f86cb73c60
PODFILE CHECKSUM: cdec0639a121f10bcbb4c9ad550349d37a0d14c9
COCOAPODS: 1.7.0.beta.2

24
Example/Tests/Info.plist Normal file
View File

@ -0,0 +1,24 @@
<?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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

97
Example/Tests/Tests.swift Normal file
View File

@ -0,0 +1,97 @@
import XCTest
import AirgappedSigning
class Tests: XCTestCase {
func decodeDocument(from json: String) throws -> Document {
let data = json.data(using: .utf8)!
let document = try JSONDecoder().decode(Document.self, from: data)
try document.check()
return document
}
func testThrowsOnNoContent() {
let json = """
{
"header": {
"format": "AirgappedSigning",
"version": 1
}
}
"""
XCTAssertThrowsError( try decodeDocument(from: json) )
}
func testThrowsOnNoHeader() {
let json = """
{
"recoveryWords": {
"name": "Rainy Day",
"format": "BIP39",
"words": ["panda", "diary", "marriage", "suffer", "basic", "glare", "surge", "auto", "scissors", "describe", "sell", "unique"]
}
}
"""
XCTAssertThrowsError( try decodeDocument(from: json) )
}
func testThrowsOnMoreThanOneContent() {
let json = """
{
"header": {
"format": "AirgappedSigning",
"version": 1
},
"recoveryWords": {
"name": "Rainy Day",
"format": "BIP39",
"words": ["panda", "diary", "marriage", "suffer", "basic", "glare", "surge", "auto", "scissors", "describe", "sell", "unique"]
},
"knownReceiver": {
"name": "Example",
"asset": "BTC",
"address": "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2"
}
}
"""
XCTAssertThrowsError( try decodeDocument(from: json) )
}
func testThrowsOnEmptyWords() {
let json = """
{
"header": {
"format": "AirgappedSigning",
"version": 1
},
"recoveryWords": {
"name": "Rainy Day",
"format": "BIP39",
"words": []
}
}
"""
XCTAssertThrowsError( try decodeDocument(from: json) )
}
func test1() throws {
let json = """
{
"header": {
"format": "AirgappedSigning",
"version": 1
},
"recoveryWords": {
"name": "Rainy Day",
"format": "BIP39",
"words": ["panda", "diary", "marriage", "suffer", "basic", "glare", "surge", "auto", "scissors", "describe", "sell", "unique"]
}
}
"""
let data = json.data(using: .utf8)!
let decoded = try JSONDecoder().decode(AirgappedSigning.Document.self, from: data)
dump(decoded)
}
}

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright © 2018 Wolf McNally <wolf@wolfmcnally.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

16
Package.swift Normal file
View File

@ -0,0 +1,16 @@
// swift-tools-version:5.0
import PackageDescription
let package = Package(
name: "AirgappedSigning",
products: [
.library(
name: "AirgappedSigning",
targets: ["AirgappedSigning"]),
],
targets: [
.target(
name: "AirgappedSigning",
dependencies: [])
]
)

31
README.md Normal file
View File

@ -0,0 +1,31 @@
# AirgappedSigning
[![CI Status](https://img.shields.io/travis/wolfmcnally/AirgappedSigning.svg?style=flat)](https://travis-ci.org/wolfmcnally/AirgappedSigning)
[![Version](https://img.shields.io/cocoapods/v/AirgappedSigning.svg?style=flat)](https://cocoapods.org/pods/AirgappedSigning)
[![License](https://img.shields.io/cocoapods/l/AirgappedSigning.svg?style=flat)](https://cocoapods.org/pods/AirgappedSigning)
[![Platform](https://img.shields.io/cocoapods/p/AirgappedSigning.svg?style=flat)](https://cocoapods.org/pods/AirgappedSigning)
## Example
To run the example project, clone the repo, and run `pod install` from the Example directory first.
## Requirements
Swift 4.2
## Installation
AirgappedSigning is available through [CocoaPods](https://cocoapods.org). To install
it, simply add the following line to your Podfile:
```ruby
pod 'AirgappedSigning'
```
## Author
Wolf McNally, wolf@wolfmcnally.com
## License
AirgappedSigning is available under the MIT license. See the LICENSE file for more info.

View File

View File

@ -0,0 +1,35 @@
//
// Account.swift
// AirgappedSigning_Example
//
// Created by Wolf McNally on 4/23/19.
//
// Copyright © 2019 Blockchain Commons.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public struct Account: Codable, Checked {
public var name: String?
public var asset: String
public var hdPublicKey: String
public var index: Int
public func check() throws {
try checkName(name, context: "Account.name")
try checkAsset(asset, context: "Account")
try checkNotEmpty(hdPublicKey, context: "Account.hdPublicKey")
try checkNotNegative(index, context: "Account.index")
}
}

View File

@ -0,0 +1,37 @@
//
// AirgappedSigningError.swift
// AirgappedSigning
//
// Created by Wolf McNally on 9/15/18.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Foundation
public struct AirgappedSigningError: Error, CustomStringConvertible {
public let message: String
public init(_ message: String) {
self.message = message
}
public var description: String {
return message
}
}

View File

@ -0,0 +1,68 @@
//
// Checked.swift
// AirgappedSigning
//
// Created by Wolf McNally on 4/23/19.
//
// Copyright © 2019 Blockchain Commons.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public protocol Checked {
/// Performs second-level validation and throws if it fails.
func check() throws
}
func checkName(_ name: String?, context: String) throws {
if let name = name {
try checkNotEmpty(name, context: context)
}
}
func checkAsset(_ asset: String, context: String) throws {
guard ["BTC", "BTCT"].contains(asset) else {
throw AirgappedSigningError("\(context): Unsupported asset: \(asset).")
}
}
func checkNotEmpty(_ string: String, context: String) throws {
guard !string.isEmpty else {
throw AirgappedSigningError("\(context): Must be non-empty.")
}
}
func checkNotEmpty(_ data: Data, context: String) throws {
guard !data.isEmpty else {
throw AirgappedSigningError("\(context): Must be non-empty.")
}
}
func checkNotEmpty<C>(_ collection: C, context: String) throws where C: Collection {
guard !collection.isEmpty else {
throw AirgappedSigningError("\(context): Must be non-empty.")
}
}
func checkNotNegative<N: BinaryInteger>(_ n: N, context: String) throws {
guard n >= 0 else {
throw AirgappedSigningError("\(context): Must be non-negative.")
}
}
func checkPositive<N: BinaryInteger>(_ n: N, context: String) throws {
guard n > 0 else {
throw AirgappedSigningError("\(context): Must be positive.")
}
}

View File

@ -0,0 +1,42 @@
//
// Document.swift
// AirgappedSigning_Example
//
// Created by Wolf McNally on 4/23/19.
//
// Copyright © 2019 Blockchain Commons.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public struct Document: Codable, Checked {
public var header: Header
public var multiPart: MultiPart?
public var recoveryWords: RecoveryWords?
public var knownReceiver: KnownReceiver?
public var account: Account?
public var transaction: Transaction?
public func check() throws {
try header.check()
let possibleContent: [Checked?] = [multiPart, recoveryWords, knownReceiver, account, transaction]
let content = possibleContent.compactMap { $0 }
guard content.count == 1 else {
throw AirgappedSigningError("Document: Exactly one content type must be included. Got: \(content.count).")
}
try content.first!.check()
}
}

View File

@ -0,0 +1,37 @@
//
// Header.swift
// AirgappedSigning_Example
//
// Created by Wolf McNally on 4/23/19.
//
// Copyright © 2019 Blockchain Commons.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public struct Header: Codable, Checked {
public var format: Format
public var version: Version
public func check() throws {
}
public enum Format: String, Codable {
case AirgappedSigning
}
public enum Version: Int, Codable {
case v1 = 1
}
}

View File

@ -0,0 +1,33 @@
//
// KnownReceiver.swift
// AirgappedSigning_Example
//
// Created by Wolf McNally on 4/23/19.
//
// Copyright © 2019 Blockchain Commons.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public struct KnownReceiver: Codable, Checked {
public var name: String?
public var asset: String
public var address: String
public func check() throws {
try checkName(name, context: "KnownReceiver.name")
try checkAsset(asset, context: "KnownReceiver")
try checkNotEmpty(address, context: "KnownReceiver.address")
}
}

View File

@ -0,0 +1,34 @@
//
// MultiPart.swift
// AirgappedSigning_Example
//
// Created by Wolf McNally on 4/23/19.
//
// Copyright © 2019 Blockchain Commons.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public struct MultiPart: Codable, Checked {
public var uid: UUID
public var part: Int
public var count: Int
public var data: Data
public func check() throws {
try checkNotNegative(part, context: "Multipart.part")
try checkPositive(count, context: "Multipart.count")
try checkNotEmpty(data, context: "Multipart.data")
}
}

View File

@ -0,0 +1,37 @@
//
// RecoveryWords.swift
// AirgappedSigning_Example
//
// Created by Wolf McNally on 4/23/19.
//
// Copyright © 2019 Blockchain Commons.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
import NonEmpty
public struct RecoveryWords: Codable, Checked {
public var name: String?
public var format: Format
public var words: [String]
public func check() throws {
try checkName(name, context: "RecoveryWords.name")
try checkNotEmpty(words, context: "RecoveryWords.words")
}
public enum Format: String, Codable {
case BIP39
}
}

View File

@ -0,0 +1,98 @@
//
// Transaction.swift
// AirgappedSigning_Example
//
// Created by Wolf McNally on 4/23/19.
//
// Copyright © 2019 Blockchain Commons.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
public struct Transaction: Codable, Checked {
public var uid: UUID
public var asset: String?
public var inputs: [Input]?
public var outputs: [Output]?
public var inputSignatures: [InputSignature]?
public func check() throws {
if let asset = asset {
try checkAsset(asset, context: "Transaction")
}
if let inputs = inputs {
try checkNotEmpty(inputs, context: "Transaction.inputs")
try inputs.forEach { try $0.check() }
}
if let outputs = outputs {
try checkNotEmpty(outputs, context: "Transaction.outputs")
try outputs.forEach { try $0.check() }
}
if let inputSignatures = inputSignatures {
try checkNotEmpty(inputSignatures, context: "Transaction.inputSignatures")
try inputSignatures.forEach { try $0.check() }
}
}
public struct Derivation: Codable, Checked {
public var accountIndex: Int
public var addressIndex: Int
public func check() throws {
try checkNotNegative(accountIndex, context: "Derivation.accountIndex")
try checkNotNegative(addressIndex, context: "Derivation.addressIndex")
}
}
public struct Input: Codable, Checked {
public var uid: UUID
public var txHash: String
public var inputIndex: Int
public var sender: String
public var derivation: Derivation
public var amount: UInt64
public func check() throws {
try checkNotEmpty(txHash, context: "Input.txHash")
try checkNotNegative(inputIndex, context: "Input.inputIndex")
try checkNotEmpty(sender, context: "Input.sender")
try derivation.check()
try checkPositive(amount, context: "Input.amount")
}
}
public struct Output: Codable, Checked {
public var uid: UUID
public var receiver: String
public var amount: UInt64
public var derivation: Derivation?
public func check() throws {
try checkNotEmpty(receiver, context: "Output.receiver")
try checkPositive(amount, context: "Output.amount")
try derivation?.check()
}
}
public struct InputSignature: Codable, Checked {
public var uid: UUID
public var ecPublicKey: Data
public var ecSignature: Data
public func check() throws {
try checkNotEmpty(ecPublicKey, context: "InputSignature.ecPublicKey")
try checkNotEmpty(ecSignature, context: "InputSignature.ecSignature")
}
}
}