├── .gitignore
├── .swiftlint.yml
├── .travis.yml
├── CONTRIBUTING.md
├── EosioSwiftVault.podspec
├── LICENSE
├── Package.swift
├── README.md
├── Sources
├── EosioSwiftVault
│ ├── BioFactor.swift
│ ├── EosioVault.swift
│ ├── EosioVaultKey.swift
│ ├── Keychain.swift
│ └── KeychainECKey.swift
└── EosioSwiftVaultSignatureProvider
│ └── EosioVaultSignatureProvider.swift
├── Tests
├── EosioSwiftVaultSignatureProviderTests
│ └── EosioSwiftVaultSignatureProviderTests.swift
├── EosioSwiftVaultTests
│ └── EosioSwiftVaultTests.swift
└── LinuxMain.swift
├── docs
├── EosioSwiftVault
│ ├── Classes.html
│ ├── Classes
│ │ ├── EosioVault.html
│ │ ├── EosioVault
│ │ │ └── VaultKey.html
│ │ ├── Keychain.html
│ │ └── Keychain
│ │ │ ├── AccessibleProtection.html
│ │ │ └── ECKey.html
│ ├── Enums.html
│ ├── Enums
│ │ └── BioFactor.html
│ ├── Extensions.html
│ ├── Extensions
│ │ ├── Data.html
│ │ └── SecKey.html
│ ├── css
│ │ ├── highlight.css
│ │ └── jazzy.css
│ ├── docsets
│ │ ├── EosioSwiftVault.docset
│ │ │ └── Contents
│ │ │ │ ├── Info.plist
│ │ │ │ └── Resources
│ │ │ │ ├── Documents
│ │ │ │ ├── Classes.html
│ │ │ │ ├── Classes
│ │ │ │ │ ├── EosioVault.html
│ │ │ │ │ ├── EosioVault
│ │ │ │ │ │ └── VaultKey.html
│ │ │ │ │ ├── Keychain.html
│ │ │ │ │ └── Keychain
│ │ │ │ │ │ ├── AccessibleProtection.html
│ │ │ │ │ │ └── ECKey.html
│ │ │ │ ├── Enums.html
│ │ │ │ ├── Enums
│ │ │ │ │ └── BioFactor.html
│ │ │ │ ├── Extensions.html
│ │ │ │ ├── Extensions
│ │ │ │ │ ├── Data.html
│ │ │ │ │ └── SecKey.html
│ │ │ │ ├── css
│ │ │ │ │ ├── highlight.css
│ │ │ │ │ └── jazzy.css
│ │ │ │ ├── img
│ │ │ │ │ ├── carat.png
│ │ │ │ │ ├── dash.png
│ │ │ │ │ ├── gh.png
│ │ │ │ │ └── spinner.gif
│ │ │ │ ├── index.html
│ │ │ │ ├── js
│ │ │ │ │ ├── jazzy.js
│ │ │ │ │ ├── jazzy.search.js
│ │ │ │ │ ├── jquery.min.js
│ │ │ │ │ ├── lunr.min.js
│ │ │ │ │ └── typeahead.jquery.js
│ │ │ │ └── search.json
│ │ │ │ └── docSet.dsidx
│ │ └── EosioSwiftVault.tgz
│ ├── img
│ │ ├── carat.png
│ │ ├── dash.png
│ │ ├── gh.png
│ │ └── spinner.gif
│ ├── index.html
│ ├── js
│ │ ├── jazzy.js
│ │ ├── jazzy.search.js
│ │ ├── jquery.min.js
│ │ ├── lunr.min.js
│ │ └── typeahead.jquery.js
│ ├── search.json
│ └── undocumented.json
├── EosioSwiftVaultSignatureProvider
│ ├── Classes.html
│ ├── Classes
│ │ └── EosioVaultSignatureProvider.html
│ ├── css
│ │ ├── highlight.css
│ │ └── jazzy.css
│ ├── docsets
│ │ ├── EosioSwiftVaultSignatureProvider.docset
│ │ │ └── Contents
│ │ │ │ ├── Info.plist
│ │ │ │ └── Resources
│ │ │ │ ├── Documents
│ │ │ │ ├── Classes.html
│ │ │ │ ├── Classes
│ │ │ │ │ └── EosioVaultSignatureProvider.html
│ │ │ │ ├── css
│ │ │ │ │ ├── highlight.css
│ │ │ │ │ └── jazzy.css
│ │ │ │ ├── img
│ │ │ │ │ ├── carat.png
│ │ │ │ │ ├── dash.png
│ │ │ │ │ ├── gh.png
│ │ │ │ │ └── spinner.gif
│ │ │ │ ├── index.html
│ │ │ │ ├── js
│ │ │ │ │ ├── jazzy.js
│ │ │ │ │ ├── jazzy.search.js
│ │ │ │ │ ├── jquery.min.js
│ │ │ │ │ ├── lunr.min.js
│ │ │ │ │ └── typeahead.jquery.js
│ │ │ │ └── search.json
│ │ │ │ └── docSet.dsidx
│ │ └── EosioSwiftVaultSignatureProvider.tgz
│ ├── img
│ │ ├── carat.png
│ │ ├── dash.png
│ │ ├── gh.png
│ │ └── spinner.gif
│ ├── index.html
│ ├── js
│ │ ├── jazzy.js
│ │ ├── jazzy.search.js
│ │ ├── jquery.min.js
│ │ ├── lunr.min.js
│ │ └── typeahead.jquery.js
│ ├── search.json
│ └── undocumented.json
└── index.md
├── img
└── swift-logo.png
└── update_documentation.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | ## Jazzy
36 | docs/undocumented.json
37 |
38 | # Swift Package Manager
39 | #
40 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
41 | .swiftpm/
42 | Package.resolved
43 | .build/
44 |
45 | # CocoaPods
46 | #
47 | # We recommend against adding the Pods directory to your .gitignore. However
48 | # you should judge for yourself, the pros and cons are mentioned at:
49 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
50 | #
51 | Pods/
52 | xcworkspace.projects/
53 | EosioSwiftVault.xcworkspace/contents.xcworkspacedata
54 | EosioSwiftVault.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
55 |
56 | # Carthage
57 | #
58 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
59 | # Carthage/Checkouts
60 |
61 | Carthage/Build
62 |
63 | # fastlane
64 | #
65 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
66 | # screenshots whenever they are needed.
67 | # For more information about the recommended setup visit:
68 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
69 |
70 | fastlane/report.xml
71 | fastlane/Preview.html
72 | fastlane/screenshots/**/*.png
73 | fastlane/test_output
74 |
75 | .DS_Store
76 | docs/docsets/EosioSwiftVault.docset/Contents/Resources/Documents/undocumented.json
77 |
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | disabled_rules:
2 | - type_body_length
3 | - file_length
4 | included:
5 | - Sources
6 | - Tests
7 | line_length: 200
8 | identifier_name:
9 | min-length: 4
10 | max_length: 60
11 | excluded:
12 | - i
13 | - id
14 | - tx
15 | - trx
16 | - x
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: swift
2 | xcode_workspace: EosioSwiftVault.xcworkspace
3 | xcode_scheme: EosioSwiftVaultTests
4 | osx_image: xcode10.2
5 | xcode_destination: platform=iOS Simulator,OS=11.3,name=iPhone X
6 | before_install:
7 | - gem install cocoapods -v 1.7.3
8 | - pod repo update
9 | - pod install
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 |
2 | # Contributing to EOSIO SDK for Swift: Vault
3 |
4 | Interested in contributing? That's awesome! Here are some guidelines to get started quickly and easily:
5 |
6 | - [Reporting An Issue](#reporting-an-issue)
7 | - [Bug Reports](#bug-reports)
8 | - [Feature Requests](#feature-requests)
9 | - [Change Requests](#change-requests)
10 | - [Working on Vault](#working-on-vault)
11 | - [Feature Branches](#feature-branches)
12 | - [Developing With Local Pods](#developing-with-local-pods)
13 | - [Submitting Pull Requests](#submitting-pull-requests)
14 | - [Testing and Quality Assurance](#testing-and-quality-assurance)
15 | - [Code Style and Linting](#code-style-and-linting)
16 | - [Conduct](#conduct)
17 | - [Contributor License & Acknowledgments](#contributor-license--acknowledgments)
18 | - [References](#references)
19 |
20 | ## Reporting An Issue
21 |
22 | If you're about to raise an issue because you think you've found a problem with Vault, or you'd like to make a request for a new feature in the codebase, or any other reason… please read this first.
23 |
24 | The GitHub issue tracker is the preferred channel for [bug reports](#bug-reports), [feature requests](#feature-requests), and [submitting pull requests](#submitting-pull-requests), but please respect the following restrictions:
25 |
26 | * Please **search for existing issues**. Help us keep duplicate issues to a minimum by checking to see if someone has already reported your problem or requested your idea.
27 | * Please **be civil**. Keep the discussion on topic and respect the opinions of others. See also our [Contributor Code of Conduct](#conduct).
28 |
29 | ### Bug Reports
30 |
31 | A bug is a _demonstrable problem_ that is caused by the code in the repository. Good bug reports are extremely helpful - thank you!
32 |
33 | Guidelines for bug reports:
34 |
35 | 1. **Use the GitHub issue search** — check if the issue has already been reported.
36 | 1. **Check if the issue has been fixed** — look for [closed issues in the current milestone](/../../issues?q=is%3Aissue+is%3Aclosed) or try to reproduce it using the latest `develop` branch.
37 |
38 | A good bug report shouldn't leave others needing to chase you down for more information. Be sure to include the details of your environment and relevant tests that demonstrate the failure.
39 |
40 | [Report a bug](/../../issues/new?title=Bug%3A)
41 |
42 | ### Feature Requests
43 |
44 | Feature requests are welcome. Before you submit one, be sure to:
45 |
46 | 1. **Use the GitHub search** and check the feature hasn't already been requested.
47 | 1. Take a moment to think about whether your idea fits with the scope and aims of the project.
48 | 1. Remember, it's up to *you* to make a strong case to convince the project's leaders of the merits of this feature. Please provide as much detail and context as possible, this means explaining the use case and why it is likely to be common.
49 |
50 | ### Change Requests
51 |
52 | Change requests cover both architectural and functional changes to how Vault works. If you have an idea for a new or different dependency, a refactor, or an improvement to a feature, etc - please be sure to:
53 |
54 | 1. **Use the GitHub search** and check someone else didn't get there first.
55 | 1. Take a moment to think about the best way to make a case for, and explain what you're thinking. Are you sure this shouldn't really be a [bug report](#bug-reports) or a [feature request](#feature-requests)? Is it really one idea or is it many? What's the context? What problem are you solving? Why is what you are suggesting better than what's already there?
56 |
57 | ## Working on Vault
58 |
59 | Code contributions are welcome and encouraged! If you are looking for a good place to start, check out the [good first issue](/../../labels/good%20first%20issue) label in GitHub issues.
60 |
61 | Also, please follow these guidelines when submitting code:
62 |
63 | ### Feature Branches
64 |
65 | To get it out of the way:
66 |
67 | - **[develop](/../../tree/develop)** is the development branch. All work on the next release happens here so you should generally branch off `develop`. Do **NOT** use this branch for a production application.
68 | - **[master](/../../tree/master)** contains the latest release. This branch may be used in production. Do **NOT** use this branch to work on this library's source.
69 |
70 | ### Developing With Local Pods
71 |
72 | By default, pods are installed from remote CocoaPods. If, however, you wish to develop locally and you'd like to integrate with locally-cloned versions of EOSIO SDK for Swift and/or other Providers, follow these instructions:
73 |
74 | 1. Clone this and other repos into the same directory, as siblings of one another.
75 | 1. Export the environment variable `USE_LOCAL_PODS` and set it to `'true'` and then run `pod install` in this and other projects as necessary or for only one repository you can combine the two with `USE_LOCAL_PODS='true' pod install`. If `USE_LOCAL_PODS` is not set or is any other value than `'true'` then the pods will all be installed from remote CocoaPods.
76 | 1. Develop!
77 |
78 | ### Submitting Pull Requests
79 |
80 | Pull requests are awesome. If you're looking to raise a PR for something which doesn't have an open issue, please think carefully about [raising an issue](#reporting-an-issue) which your PR can close, especially if you're fixing a bug. This makes it more likely that there will be enough information available for your PR to be properly tested and merged.
81 |
82 | ### Testing and Quality Assurance
83 |
84 | Never underestimate just how useful quality assurance is. If you're looking to get involved with the code base and don't know where to start, checking out and testing a pull request is one of the most useful things you could do.
85 |
86 | Essentially, [check out the latest develop branch](#working-on-vault), take it for a spin, and if you find anything odd, please follow the [bug report guidelines](#bug-reports) and let us know!
87 |
88 | ### Code Style and Linting
89 |
90 | Vault leverages [SwiftLint](https://github.com/realm/SwiftLint) for linting and code format autocorrection. SwiftLint will be installed when you run `pod install`. Then linting warnings and errors will be flagged inline in Xcode when you Build.
91 |
92 | Please be sure to resolve any linting issues introduced by your contributions prior to requesting a review on your PR. You may also autocorrect formatting using the `swiftlint autocorrect --format --path Example.swift` command. (Requires a `brew install swiftlint` first.)
93 |
94 | ## Conduct
95 |
96 | While contributing, please be respectful and constructive, so that participation in our project is a positive experience for everyone.
97 |
98 | Examples of behavior that contributes to creating a positive environment include:
99 | - Using welcoming and inclusive language
100 | - Being respectful of differing viewpoints and experiences
101 | - Gracefully accepting constructive criticism
102 | - Focusing on what is best for the community
103 | - Showing empathy towards other community members
104 |
105 | Examples of unacceptable behavior include:
106 | - The use of sexualized language or imagery and unwelcome sexual attention or advances
107 | - Trolling, insulting/derogatory comments, and personal or political attacks
108 | - Public or private harassment
109 | - Publishing others’ private information, such as a physical or electronic address, without explicit permission
110 | - Other conduct which could reasonably be considered inappropriate in a professional setting
111 |
112 | ## Contributor License & Acknowledgments
113 |
114 | Whenever you make a contribution to this project, you license your contribution under the same terms as set out in LICENSE, and you represent and warrant that you have the right to license your contribution under those terms. Whenever you make a contribution to this project, you also certify in the terms of the Developer’s Certificate of Origin set out below:
115 |
116 | ```
117 | Developer Certificate of Origin
118 | Version 1.1
119 |
120 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
121 | 1 Letterman Drive
122 | Suite D4700
123 | San Francisco, CA, 94129
124 |
125 | Everyone is permitted to copy and distribute verbatim copies of this
126 | license document, but changing it is not allowed.
127 |
128 |
129 | Developer's Certificate of Origin 1.1
130 |
131 | By making a contribution to this project, I certify that:
132 |
133 | (a) The contribution was created in whole or in part by me and I
134 | have the right to submit it under the open source license
135 | indicated in the file; or
136 |
137 | (b) The contribution is based upon previous work that, to the best
138 | of my knowledge, is covered under an appropriate open source
139 | license and I have the right under that license to submit that
140 | work with modifications, whether created in whole or in part
141 | by me, under the same open source license (unless I am
142 | permitted to submit under a different license), as indicated
143 | in the file; or
144 |
145 | (c) The contribution was provided directly to me by some other
146 | person who certified (a), (b) or (c) and I have not modified
147 | it.
148 |
149 | (d) I understand and agree that this project and the contribution
150 | are public and that a record of the contribution (including all
151 | personal information I submit with it, including my sign-off) is
152 | maintained indefinitely and may be redistributed consistent with
153 | this project or the open source license(s) involved.
154 | ```
155 |
156 | ## References
157 |
158 | * Overall CONTRIB adapted from https://github.com/mathjax/MathJax/blob/master/CONTRIBUTING.md
159 | * Conduct section adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
160 |
--------------------------------------------------------------------------------
/EosioSwiftVault.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint EosioSwiftVault.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = 'EosioSwiftVault'
11 | s.version = '1.0.0'
12 | s.summary = 'Keychain and Secure Enclave functions for EOSIO.'
13 | s.homepage = 'https://github.com/EOSIO/eosio-swift-vault'
14 | s.license = { :type => 'MIT', :text => <<-LICENSE
15 | Copyright (c) 2017-2020 block.one and its contributors. All rights reserved.
16 | LICENSE
17 | }
18 | s.author = { 'Todd Bowden' => 'todd.bowden@block.one',
19 | 'Serguei Vinnitskii' => 'serguei.vinnitskii@block.one',
20 | 'Farid Rahmani' => 'farid.rahmani@block.one',
21 | 'Brandon Fancher' => 'brandon.fancher@block.one',
22 | 'Mark Johnson' => 'mark.johnson@block.one',
23 | 'Paul Kim' => 'paul.kim@block.one',
24 | 'Steve McCoole' => 'steve.mccoole@objectpartners.com',
25 | 'Ben Martell' => 'ben.martell@objectpartners.com' }
26 | s.source = { :git => 'https://github.com/EOSIO/eosio-swift-vault.git', :tag => s.version.to_s }
27 |
28 | s.swift_version = '5.0'
29 | s.ios.deployment_target = '12.0'
30 |
31 | s.subspec 'Vault' do |ss|
32 | ss.source_files = 'Sources/EosioSwiftVault/**/*.swift'
33 |
34 | ss.pod_target_xcconfig = {
35 | 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
36 | 'CLANG_ENABLE_MODULES' => 'YES',
37 | 'SWIFT_COMPILATION_MODE' => 'wholemodule',
38 | 'ENABLE_BITCODE' => 'YES' }
39 |
40 | ss.ios.dependency 'EosioSwift/Core', '~> 1.0.0'
41 | ss.ios.dependency 'EosioSwift/Ecc', '~> 1.0.0'
42 | end
43 |
44 | s.subspec 'VaultSignatureProvider' do |ss|
45 | ss.source_files = 'Sources/EosioSwiftVaultSignatureProvider/**/*.swift'
46 |
47 | ss.pod_target_xcconfig = {
48 | 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
49 | 'CLANG_ENABLE_MODULES' => 'YES',
50 | 'SWIFT_COMPILATION_MODE' => 'wholemodule',
51 | 'ENABLE_BITCODE' => 'YES' }
52 |
53 | ss.ios.dependency 'EosioSwift/Core', '~> 1.0.0'
54 | ss.ios.dependency 'EosioSwift/Ecc', '~> 1.0.0'
55 | ss.ios.dependency 'EosioSwiftVault/Vault'
56 | end
57 |
58 | end
59 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017-2019 block.one all rights reserved.
2 |
3 | The MIT License
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.2
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "EosioSwiftVault",
8 | platforms: [
9 | .iOS(.v12),
10 | .macOS(.v10_14)
11 | ],
12 | products: [
13 | .library(
14 | name: "EosioSwiftVaultSignatureProvider",
15 | targets: ["EosioSwiftVaultSignatureProvider"]),
16 | .library(
17 | name: "EosioSwiftVault",
18 | targets: ["EosioSwiftVault"]),
19 | ],
20 | dependencies: [
21 | .package(name: "EosioSwift", url: "https://github.com/EOSIO/eosio-swift", from: "1.0.0"),
22 | .package(url: "https://github.com/realm/SwiftLint", from: "0.39.1")
23 | ],
24 | targets: [
25 | .target(
26 | name: "EosioSwiftVault",
27 | dependencies: [
28 | .product(name: "EosioSwift", package: "EosioSwift"),
29 | .product(name: "EosioSwiftEcc", package: "EosioSwift")
30 | ],
31 | path: "Sources/EosioSwiftVault"),
32 | .target(
33 | name: "EosioSwiftVaultSignatureProvider",
34 | dependencies: [
35 | .product(name: "EosioSwift", package: "EosioSwift"),
36 | .product(name: "EosioSwiftEcc", package: "EosioSwift"),
37 | "EosioSwiftVault"
38 | ],
39 | path: "Sources/EosioSwiftVaultSignatureProvider"),
40 | .testTarget(
41 | name: "EosioSwiftVaultTests",
42 | dependencies: ["EosioSwiftVault"],
43 | path: "Tests/EosioSwiftVaultTests"),
44 | .testTarget(
45 | name: "EosioSwiftVaultSignatureProviderTests",
46 | dependencies: ["EosioSwiftVaultSignatureProvider"],
47 | path: "Tests/EosioSwiftVaultSignatureProviderTests"),
48 | ]
49 | )
50 |
--------------------------------------------------------------------------------
/Sources/EosioSwiftVault/BioFactor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BioFactor.swift
3 | // EosioSwiftVault
4 |
5 | // Created by Steve McCoole on 4/7/20
6 | // Copyright (c) 2017-2020 block.one and its contributors. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Setting on the key dictating biometric authentication requirements and whether the key persists after device's biometric settings are modified.
12 | public enum BioFactor: String {
13 | /// Biometric authentication is not required for the key.
14 | case none = ""
15 | /// Keys persist even after the device's biometric settings are modified.
16 | case flex = "bio flex"
17 | /// Keys are bricked in the event the device's biometric settings are modified.
18 | case fixed = "bio fixed"
19 |
20 | var accessFlag: SecAccessControlCreateFlags? {
21 | switch self {
22 | case .flex:
23 | return .biometryAny
24 | case .fixed:
25 | return .biometryCurrentSet
26 | case .none:
27 | return nil
28 | }
29 | }
30 |
31 | var tag: String? {
32 | switch self {
33 | case .flex:
34 | return self.rawValue
35 | case .fixed:
36 | return self.rawValue
37 | case .none:
38 | return nil
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/EosioSwiftVault/EosioVaultKey.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EosioVaultKey.swift
3 | // EosioVault
4 | //
5 | // Created by Todd Bowden on 8/26/18.
6 | // Copyright (c) 2017-2019 block.one and its contributors. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | #if SWIFT_PACKAGE
11 | import EosioSwiftEcc
12 | #else
13 | import EosioSwift
14 | #endif
15 |
16 | public extension EosioVault {
17 |
18 | /// VaultKey collects properties into a single object for an EOSIO elliptic curve key.
19 | struct VaultKey {
20 | /// The EOSIO public key.
21 | private (set) public var eosioPublicKey: String
22 | /// The EOSIO private key. (nil for Secure Enclave keys).
23 | public var eosioPrivateKey: String? {
24 | guard let privateKey = privateKey else { return nil }
25 | guard privateKey.count >= 32 else { return nil }
26 | let pk32 = privateKey.suffix(32)
27 | switch curve {
28 | case .k1: return pk32.toEosioK1PrivateKey
29 | case .r1: return pk32.toEosioR1PrivateKey
30 | }
31 | }
32 | /// The label for this key in the Keychain.
33 | private (set) public var label: String?
34 | /// The tag for this key in the Keychain.
35 | private (set) public var tag: String?
36 | /// The curve for this key (r1 or k1).
37 | private (set) public var curve: EllipticCurveType
38 | /// The access group for this key in the Keychain.
39 | private (set) public var accessGroup: String
40 | /// Is the private key stored in the Secure Enclave?
41 | private (set) public var isSecureEnclave: Bool
42 | /// The biometric factor enforced on this key by the Keychain.
43 | private (set) public var bioFactor: BioFactor
44 | /// The private SecKey.
45 | private (set) public var privateSecKey: SecKey?
46 | /// The private key in ANSI X9.63 format. (nil for Secure Enclave keys).
47 | public var privateKey: Data? {
48 | return privateSecKey?.externalRepresentation
49 | }
50 | /// The public SecKey.
51 | private (set) public var publicSecKey: SecKey?
52 | /// The uncompressed public key in ANSI X9.63 format (65 bytes, starts with 04).
53 | private (set) public var uncompressedPublicKey: Data?
54 | /// The compressed public key in ANSI X9.63 format (33 bytes, starts with 02 or 03).
55 | private (set) public var compressedPublicKey: Data?
56 | /// Is the key retired? Retired keys have metadata without a key in the Keychain.
57 | private (set) public var isRetired: Bool
58 | /// Metadata for this key.
59 | /// - Important: Metadata must follow the rules for JSONSerialization.
60 | /// - SeeAlso: https://developer.apple.com/documentation/foundation/jsonserialization
61 | public var metadata: [String: Any]
62 |
63 | /// Init a VaultKey.
64 | ///
65 | /// - Parameters:
66 | /// - eosioPublicKey: An EOSIO public key.
67 | /// - ecKey: An ECKey.
68 | /// - metadata: Metadata dictionary.
69 | /// - Important: Metadata must follow the rules for JSONSerialization.
70 | /// - SeeAlso: https://developer.apple.com/documentation/foundation/jsonserialization
71 | init?(eosioPublicKey: String? = nil, ecKey: Keychain.ECKey?, metadata: [String: Any]?) {
72 |
73 | // Case of publicKey + metadata with no ecKey = retired key
74 | guard let ecKey = ecKey else {
75 | guard let eosioPublicKey = eosioPublicKey else { return nil }
76 | self.eosioPublicKey = eosioPublicKey
77 | let version = (try? self.eosioPublicKey.eosioComponents().version) ?? ""
78 | self.curve = (try? EllipticCurveType(version)) ?? .r1
79 | self.bioFactor = .none
80 | self.accessGroup = ""
81 | self.isSecureEnclave = false
82 | self.isRetired = true
83 | self.metadata = metadata ?? [String: Any]()
84 | return
85 | }
86 |
87 | // Case with defined ecKey
88 | self.isSecureEnclave = ecKey.isSecureEnclave
89 | if isSecureEnclave {
90 | curve = .r1
91 | } else if let tag = ecKey.tag, tag.contains(words: EllipticCurveType.k1.rawValue) {
92 | curve = .k1
93 | } else {
94 | curve = .r1
95 | }
96 |
97 | guard let pubKey = try? ecKey.compressedPublicKey.toEosioPublicKey(curve: curve.rawValue) else { return nil }
98 | self.eosioPublicKey = pubKey
99 |
100 | // if eosioPublicKey defined, verify it matches the ecKey public key
101 | if let eosioPublicKey = eosioPublicKey {
102 | guard eosioPublicKey == self.eosioPublicKey else { return nil }
103 | }
104 |
105 | label = ecKey.label
106 | tag = ecKey.tag
107 | accessGroup = ecKey.accessGroup
108 |
109 | if let tag = self.tag {
110 | if tag.contains(words: BioFactor.fixed.rawValue) {
111 | bioFactor = .fixed
112 | } else if tag.contains(words: BioFactor.flex.rawValue) {
113 | bioFactor = .flex
114 | } else {
115 | bioFactor = .none
116 | }
117 | } else {
118 | bioFactor = .none
119 | }
120 |
121 | isRetired = false
122 | privateSecKey = ecKey.privateSecKey
123 | publicSecKey = ecKey.publicSecKey
124 | uncompressedPublicKey = ecKey.uncompressedPublicKey
125 | compressedPublicKey = ecKey.compressedPublicKey
126 | self.metadata = metadata ?? [String: Any]()
127 |
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/Sources/EosioSwiftVault/KeychainECKey.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Keychain.swift
3 | // EosioSwiftVault
4 |
5 | // Created by Todd Bowden on 8/13/18.
6 | // Copyright (c) 2017-2019 block.one and its contributors. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import EosioSwift
11 |
12 | public extension Keychain {
13 |
14 | /// ECKey collects properties into a single object for an elliptic curve key.
15 | class ECKey {
16 | /// The label for this key in the Keychain.
17 | private (set) public var label: String?
18 | /// The tag for this key in the Keychain.
19 | private (set) public var tag: String?
20 | /// The access group for this key in the Keychain.
21 | private (set) public var accessGroup: String
22 | /// Is the private key stored in the Secure Enclave?
23 | private (set) public var isSecureEnclave: Bool
24 | /// The private SecKey.
25 | private (set) public var privateSecKey: SecKey
26 | /// The public SecKey.
27 | private (set) public var publicSecKey: SecKey
28 | /// The uncompressed public key in ANSI X9.63 format (65 bytes, starts with 04).
29 | private (set) public var uncompressedPublicKey: Data
30 | /// The compressed public key in ANSI X9.63 format (33 bytes, starts with 02 or 03).
31 | private (set) public var compressedPublicKey: Data
32 |
33 | static func new(attributes: [String: Any]) throws -> ECKey {
34 | if let key = ECKey(attributes: attributes) {
35 | return key
36 | }
37 | guard let privkey = attributes[kSecValueRef as String] else {
38 | throw EosioError(.keyManagementError, reason: "Cannot get private key reference.")
39 | }
40 | let privateSecKey = privkey as! SecKey // swiftlint:disable:this force_cast
41 | guard let pubKey = SecKeyCopyPublicKey(privateSecKey) else {
42 | throw EosioError(.keyManagementError, reason: "Cannot get public key from private key.")
43 | }
44 | let publicSecKey = pubKey
45 | guard let ucpk = publicSecKey.externalRepresentation else {
46 | throw EosioError(.keyManagementError, reason: "Cannot get public key external representation.")
47 | }
48 | let uncompressedPublicKey = ucpk
49 | guard uncompressedPublicKey.compressedPublicKey != nil else {
50 | throw EosioError(.keyManagementError, reason: "Cannot get compressed public key.")
51 | }
52 | throw EosioError(.keyManagementError, reason: "Cannot create key")
53 | }
54 |
55 | /// Init an ECKey.
56 | ///
57 | /// - Parameter attributes: A dictionary of attributes from a Keychain query.
58 | public init?(attributes: [String: Any]) {
59 | label = attributes[kSecAttrLabel as String] as? String
60 | tag = attributes[kSecAttrApplicationTag as String] as? String
61 | accessGroup = attributes[kSecAttrAccessGroup as String] as? String ?? ""
62 | let tokenID = attributes[kSecAttrTokenID as String] as? String ?? ""
63 | isSecureEnclave = tokenID == kSecAttrTokenIDSecureEnclave as String
64 | guard let privkey = attributes[kSecValueRef as String] else {
65 | return nil
66 | }
67 | privateSecKey = privkey as! SecKey // swiftlint:disable:this force_cast
68 | guard let pubKey = SecKeyCopyPublicKey(privateSecKey) else {
69 | return nil
70 | }
71 | publicSecKey = pubKey
72 | guard let ucpk = publicSecKey.externalRepresentation else {
73 | return nil
74 | }
75 | uncompressedPublicKey = ucpk
76 | guard let cpk = uncompressedPublicKey.compressedPublicKey else {
77 | return nil
78 | }
79 | compressedPublicKey = cpk
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Sources/EosioSwiftVaultSignatureProvider/EosioVaultSignatureProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EosioVaultSignatureProvider.swift
3 | // EosioSwiftVaultSignatureProvider
4 | //
5 | // Created by Todd Bowden on 4/9/19.
6 | // Copyright (c) 2017-2019 block.one and its contributors. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import EosioSwift
11 | import EosioSwiftVault
12 |
13 | /// Signature provider implementation for EOSIO SDK for Swift using Keychain and/or Secure Enclave.
14 | public final class EosioVaultSignatureProvider: EosioSignatureProviderProtocol {
15 |
16 | private let vault: EosioVault
17 |
18 | /// Require biometric identification for all signatures even if the key does not require it. Defaults to `false`.
19 | public var requireBio = false
20 |
21 | /// Init an instance of EosioVaultSignatureProvider.
22 | ///
23 | /// - Parameters:
24 | /// - accessGroup: The access group to create an instance of EosioVault.
25 | /// - requireBio: Require biometric identification for all signatures even if the key does not require it. Defaults to `false`.
26 | public init(accessGroup: String, requireBio: Bool = false) {
27 | vault = EosioVault(accessGroup: accessGroup)
28 | self.requireBio = requireBio
29 | }
30 |
31 | /// Sign a transaction using an instance of EosioVault with the specified accessGroup.
32 | ///
33 | /// - Parameters:
34 | /// - request: The transaction signature request.
35 | /// - completion: The transaction signature response.
36 | public func signTransaction(request: EosioTransactionSignatureRequest,
37 | completion: @escaping (EosioTransactionSignatureResponse) -> Void) {
38 | self.signTransaction(request: request, prompt: "Sign Transaction", completion: completion)
39 | }
40 |
41 | /// Sign a transaction using an instance of EosioVault with the specified accessGroup.
42 | ///
43 | /// - Parameters:
44 | /// - request: The transaction signature request.
45 | /// - prompt: Prompt for biometric authentication if required.
46 | /// - completion: The transaction signature response.
47 | public func signTransaction(request: EosioTransactionSignatureRequest,
48 | prompt: String,
49 | completion: @escaping (EosioTransactionSignatureResponse) -> Void) {
50 | var response = EosioTransactionSignatureResponse()
51 |
52 | guard let chainIdData = try? Data(hex: request.chainId) else {
53 | response.error = EosioError(.signatureProviderError, reason: "\(request.chainId) is not a valid chain id")
54 | return completion(response)
55 | }
56 | var contextFreeDataHash = Data(repeating: 0, count: 32)
57 | if request.serializedContextFreeData.count > 0 {
58 | contextFreeDataHash = request.serializedContextFreeData.sha256
59 | }
60 | let message = chainIdData + request.serializedTransaction + contextFreeDataHash
61 | sign(message: message, publicKeys: request.publicKeys, prompt: prompt) { (signatures, error) in
62 | guard let signatures = signatures else {
63 | response.error = error
64 | return completion(response)
65 | }
66 | guard signatures.count > 0 else {
67 | response.error = EosioError(.signatureProviderError, reason: "No signatures")
68 | return completion(response)
69 | }
70 | var signedTransaction = EosioTransactionSignatureResponse.SignedTransaction()
71 | signedTransaction.signatures = signatures
72 | signedTransaction.serializedTransaction = request.serializedTransaction
73 | signedTransaction.serializedContextFreeData = request.serializedContextFreeData
74 | response.signedTransaction = signedTransaction
75 | completion(response)
76 | }
77 |
78 | }
79 |
80 | /// Recursive function to sign a message with public keys. If there are multiple keys, the function will sign with the first and call itself with the remaining keys.
81 | private func sign(message: Data, publicKeys: [String], prompt: String, completion: @escaping ([String]?, EosioError?) -> Void) {
82 | guard let firstPublicKey = publicKeys.first else {
83 | return completion([String](), nil)
84 | }
85 | vault.sign(message: message, eosioPublicKey: firstPublicKey, requireBio: requireBio, prompt: prompt) { [weak self] (signature, error) in
86 | guard let signature = signature else {
87 | return completion(nil, error)
88 | }
89 | var remainingPublicKeys = publicKeys
90 | remainingPublicKeys.removeFirst()
91 |
92 | if remainingPublicKeys.count == 0 {
93 | return completion([signature], nil)
94 | }
95 | guard let strongSelf = self else {
96 | return completion(nil, EosioError(.unexpectedError, reason: "self does not exist"))
97 | }
98 | strongSelf.sign(message: message, publicKeys: remainingPublicKeys, prompt: prompt, completion: { (signatures, error) in
99 | guard let signatures = signatures else {
100 | return completion(nil, error)
101 | }
102 | completion([signature] + signatures, nil)
103 | })
104 | }
105 | }
106 |
107 | /// Get all available EOSIO keys for the instance of EosioVault with the specified accessGroup.
108 | ///
109 | /// - Parameters:
110 | /// - completion: The available keys response.
111 | public func getAvailableKeys(completion: @escaping (EosioAvailableKeysResponse) -> Void) {
112 | var response = EosioAvailableKeysResponse()
113 | do {
114 | let vaultKeys = try vault.getAllVaultKeys()
115 | response.keys = vaultKeys.compactMap({ (vaultKey) -> String? in
116 | return vaultKey.eosioPublicKey
117 | })
118 | completion(response)
119 | } catch {
120 | response.error = error.eosioError
121 | completion(response)
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/Tests/EosioSwiftVaultSignatureProviderTests/EosioSwiftVaultSignatureProviderTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EosioSwiftVaultSignatureProviderTests.swift
3 | // EosioSwiftVaultSignatureProviderTests
4 | //
5 | // Created by Todd Bowden on 4/9/19.
6 | // Copyright (c) 2017-2019 block.one and its contributors. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import EosioSwiftVaultSignatureProvider
11 |
12 | class EosioSwiftVaultSignatureProviderTests: XCTestCase {
13 |
14 | override func setUp() {
15 | // Put setup code here. This method is called before the invocation of each test method in the class.
16 | }
17 |
18 | override func tearDown() {
19 | // Put teardown code here. This method is called after the invocation of each test method in the class.
20 | }
21 |
22 | func testExample() {
23 | // This is an example of a functional test case.
24 | // Use XCTAssert and related functions to verify your tests produce the correct results.
25 | }
26 |
27 | func testPerformanceExample() {
28 | // This is an example of a performance test case.
29 | self.measure {
30 | // Put the code you want to measure the time of here.
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/Tests/EosioSwiftVaultTests/EosioSwiftVaultTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EosioSwiftVaultTests.swift
3 | // EosioSwiftVaultTests
4 | //
5 | // Created by Todd Bowden on 3/22/19.
6 | // Copyright (c) 2017-2019 block.one and its contributors. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import EosioSwiftEcc
11 | @testable import EosioSwiftVault
12 |
13 | class EosioSwiftVaultTests: XCTestCase {
14 |
15 | override func setUp() {
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | }
22 |
23 | func testCompressUncompressR1KeysRoundTrip() {
24 | let keychain = Keychain(accessGroup: "")
25 | for _ in 0...100 {
26 | let key = keychain.createEllipticCurvePrivateKey(isPermanent: false)
27 | guard let uncompressedPubKey = key?.publicKey?.externalRepresentation else {
28 | return XCTFail("Not a valid key")
29 | }
30 |
31 | guard let compressedPubKey = uncompressedPubKey.compressedPublicKey else {
32 | return XCTFail("Not a valid key")
33 | }
34 |
35 | guard let uncompressedPubKey2 = try? keychain.uncompressedPublicKey(data: compressedPubKey) else {
36 | return XCTFail("uncompression error")
37 | }
38 | XCTAssertEqual(uncompressedPubKey, uncompressedPubKey2)
39 | }
40 | }
41 |
42 | func generatePrivateKeyBytes() -> Data? {
43 | var bytes = [UInt8](repeating: 0, count: 32)
44 | let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
45 | guard status == errSecSuccess else { return nil }
46 | return Data(bytes)
47 | }
48 |
49 | func testCompressUncompressK1KeysRoundTrip() {
50 | let keychain = Keychain(accessGroup: "")
51 | do {
52 | for _ in 0...100 {
53 | guard let privKey = generatePrivateKeyBytes() else {
54 | return XCTFail("Not a valid pk")
55 | }
56 | let uncompressedPubKey = try EccRecoverKey.recoverPublicKey(privateKey: privKey, curve: .k1)
57 | guard let compressedPubKey = uncompressedPubKey.compressedPublicKey else {
58 | return XCTFail("Not a valid key")
59 | }
60 | guard let uncompressedPubKey2 = try? keychain.uncompressedPublicKey(data: compressedPubKey, curve: "K1") else {
61 | return XCTFail("uncompression error")
62 | }
63 | XCTAssertEqual(uncompressedPubKey, uncompressedPubKey2)
64 | }
65 | } catch {
66 | return XCTFail(error.eosioError.reason)
67 | }
68 |
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | // LinuxMain.swift
2 | fatalError("Run the tests with `swift test --enable-test-discovery`.")
3 |
--------------------------------------------------------------------------------
/docs/EosioSwiftVault/Classes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |