feat: add keychain-services
This commit is contained in:
201
__security/keychain-services/Cargo.lock
generated
Normal file
201
__security/keychain-services/Cargo.lock
generated
Normal file
@@ -0,0 +1,201 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"failure_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure_derive"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
|
||||
|
||||
[[package]]
|
||||
name = "keychain-services"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"keychain-services 0.1.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keychain-services"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"core-foundation",
|
||||
"failure",
|
||||
"failure_derive",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
|
||||
10
__security/keychain-services/Cargo.toml
Normal file
10
__security/keychain-services/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "keychain-services"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
keychain-services = {version = "0.1.1", path = "./keychain-services.rs"}
|
||||
|
||||
10
__security/keychain-services/keychain-services.rs/.gitignore
vendored
Normal file
10
__security/keychain-services/keychain-services.rs/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
@@ -0,0 +1,36 @@
|
||||
language: rust
|
||||
cache: cargo
|
||||
os: osx
|
||||
rust: stable
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
install:
|
||||
- rustup component add rustfmt-preview
|
||||
- rustup component add clippy-preview
|
||||
- command -v cargo-audit >/dev/null 2>&1 || cargo install cargo-audit
|
||||
|
||||
script:
|
||||
# build
|
||||
- cargo build --no-default-features
|
||||
- cargo build
|
||||
|
||||
# test
|
||||
- cargo test
|
||||
|
||||
# build (but do not run) interactive tests
|
||||
- cargo test --features=interactive-tests --no-run
|
||||
|
||||
# audit
|
||||
- cargo audit
|
||||
|
||||
# lint
|
||||
- cargo fmt --version
|
||||
- cargo fmt -- --check
|
||||
- cargo clippy --version
|
||||
- cargo clippy
|
||||
|
||||
# doc build
|
||||
- cargo doc --no-deps
|
||||
28
__security/keychain-services/keychain-services.rs/CHANGES.md
Normal file
28
__security/keychain-services/keychain-services.rs/CHANGES.md
Normal file
@@ -0,0 +1,28 @@
|
||||
## [0.1.1] (2018-12-10)
|
||||
|
||||
- Update to Rust 2018 edition ([#20])
|
||||
- Update links to use keychain-services.rs repo name ([#19])
|
||||
|
||||
## [0.1.0] (2018-11-06)
|
||||
|
||||
- Initial password support ([#17])
|
||||
- Factor related types into modules ([#16])
|
||||
- Remove `Sec*` prefix from all type names ([#15])
|
||||
- Implement keychain types and refactor builders ([#14])
|
||||
|
||||
## 0.0.2 (2018-11-01)
|
||||
|
||||
- Build and link to documentation.
|
||||
|
||||
## 0.0.1 (2018-10-31)
|
||||
|
||||
- Initial release
|
||||
|
||||
[0.1.1]: https://github.com/iqlusioninc/keychain-services.rs/pull/21
|
||||
[#20]: https://github.com/iqlusioninc/keychain-services.rs/pull/20
|
||||
[#19]: https://github.com/iqlusioninc/keychain-services.rs/pull/19
|
||||
[0.1.0]: https://github.com/iqlusioninc/keychain-services.rs/pull/18
|
||||
[#17]: https://github.com/iqlusioninc/keychain-services.rs/pull/17
|
||||
[#16]: https://github.com/iqlusioninc/keychain-services.rs/pull/16
|
||||
[#15]: https://github.com/iqlusioninc/keychain-services.rs/pull/15
|
||||
[#14]: https://github.com/iqlusioninc/keychain-services.rs/pull/14
|
||||
@@ -0,0 +1,74 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
education, socio-economic status, nationality, personal appearance, race,
|
||||
religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at [oss@iqlusion.io](mailto:oss@iqlusion.io).
|
||||
All complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
35
__security/keychain-services/keychain-services.rs/Cargo.toml
Normal file
35
__security/keychain-services/keychain-services.rs/Cargo.toml
Normal file
@@ -0,0 +1,35 @@
|
||||
[package]
|
||||
name = "keychain-services"
|
||||
description = """
|
||||
Rust access to macOS Keychain Services, including TouchID-guarded
|
||||
access to cryptographic keys stored in the Secure Enclave
|
||||
Processor (SEP).
|
||||
"""
|
||||
version = "0.1.2"
|
||||
authors = ["Tony Arcieri <tony@iqlusion.io>"]
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://keychain-services.rs/"
|
||||
documentation = "https://keychain-services.rs/docs/"
|
||||
repository = "https://github.com/iqlusioninc/keychain-services.rs/"
|
||||
readme = "README.md"
|
||||
categories = ["api-bindings", "authentication", "cryptography", "hardware-support"]
|
||||
keywords = ["ecdsa", "macos", "keychain", "touchid", "signatures"]
|
||||
edition = "2018"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
travis-ci = { repository = "iqlusioninc/keychain-services.rs" }
|
||||
|
||||
[dependencies]
|
||||
core-foundation = "0.7"
|
||||
failure = "0.1"
|
||||
failure_derive = "0.1"
|
||||
zeroize = "1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
ring = "0.13"
|
||||
tempfile = "3"
|
||||
untrusted = "0.6"
|
||||
|
||||
[features]
|
||||
interactive-tests = []
|
||||
201
__security/keychain-services/keychain-services.rs/LICENSE-APACHE
Normal file
201
__security/keychain-services/keychain-services.rs/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,20 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) iqlusion
|
||||
|
||||
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.
|
||||
15
__security/keychain-services/keychain-services.rs/Makefile
Normal file
15
__security/keychain-services/keychain-services.rs/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
target/doc/keychain_services:
|
||||
cargo rustdoc
|
||||
|
||||
docs: target/doc/keychain_services
|
||||
-git branch -D gh-pages
|
||||
git checkout --orphan gh-pages
|
||||
git reset README.md
|
||||
git reset --hard
|
||||
cp -r target/doc/* .
|
||||
cp -r keychain_services docs
|
||||
rm -rf target
|
||||
echo 'keychain-services.rs' > CNAME
|
||||
git add .
|
||||
git commit -m "Generate docs using 'make docs'"
|
||||
@echo "Use 'git push -f origin gh-pages' to deploy"
|
||||
103
__security/keychain-services/keychain-services.rs/README.md
Normal file
103
__security/keychain-services/keychain-services.rs/README.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# Keychain Services for Rust 🔐 <a href="https://www.iqlusion.io"><img src="https://storage.googleapis.com/iqlusion-prod-web-assets/img/logo/iqlusion-rings-sm.png" alt="iqlusion" width="24" height="24"></a>
|
||||
|
||||
[![Crate][crate-image]][crate-link]
|
||||
[![Build Status][build-image]][build-link]
|
||||
[![Apache 2.0 Licensed][license-image]][license-link]
|
||||
![Maintenance Status: Experimental][maintenance-image]
|
||||
|
||||
Rust binding for macOS Keychain Services, including TouchID-guarded access to
|
||||
cryptographic keys stored in the Secure Enclave Processor (SEP).
|
||||
|
||||
This binding aims to provide a thin wrapper using largely the same type names
|
||||
as Keychain Services itself, but also provide a safe, mostly idiomatic API
|
||||
which does not rely on e.g. Core Foundation types.
|
||||
|
||||
**NOTE:** This is an unofficial binding which is in no way affiliated with Apple!
|
||||
|
||||
[Documentation]
|
||||
|
||||
## Status
|
||||
|
||||
This crate is **experimental** and may have bugs/memory safety issues.
|
||||
*USE AT YOUR OWN RISK!*
|
||||
|
||||
Below is a rough outline of the Keychain Service API and what is supported
|
||||
by this crate:
|
||||
|
||||
- [ ] Keychains (`SecKeychain`)
|
||||
- [x] Creating keychains
|
||||
- [x] Deleting keychains
|
||||
- [ ] Open keychain (`SecKeychainOpen`)
|
||||
- [ ] Keychain status (`SecKeychainGetStatus`)
|
||||
- [ ] Keychain version (`SecKeychainGetVersion`)
|
||||
- [ ] Set default keychain (`SecKeychainSetDefault`)
|
||||
- [ ] Keychain Items (`SecKeychainItem`)
|
||||
- [x] Creating keychain items
|
||||
- [x] Fetching keychain items
|
||||
- [x] Getting keychain item attributes
|
||||
- [ ] Deleting keychain items
|
||||
- [ ] Certificates / Identities (`SecCertificate`)
|
||||
- [ ] Creating certificates
|
||||
- [ ] Deleting certificates
|
||||
- [ ] Querying certificates
|
||||
- [ ] Signing certificates
|
||||
- [x] Cryptographic keys (`SecKey`)
|
||||
- [x] Generating cryptographic keys
|
||||
- [x] Importing cryptographic keys
|
||||
- [x] Exporting cryptographic keys
|
||||
- [x] Deleting cryptographic keys
|
||||
- [x] Querying cryptographic keys
|
||||
- [x] Querying cryptographic key attributes
|
||||
- [x] Digital signatures (ECDSA/RSA)
|
||||
- [x] Encryption
|
||||
- [x] Passwords
|
||||
- [x] Creating passwords
|
||||
- [x] Querying passwords
|
||||
- [ ] Deleting passwords
|
||||
|
||||
## Tests
|
||||
|
||||
This crate has two suites of tests:
|
||||
|
||||
- Core: `cargo test` - run a minimal set of tests (e.g. in CI) that work
|
||||
everywhere, but don't cover all functionality.
|
||||
- Interactive: `cargo test --features=interactive-tests --no-run`
|
||||
compile tests which require user interactions, and additionally must be
|
||||
signed by macOS's code signing in order to work. See code signing notes.
|
||||
|
||||
## Code Signing
|
||||
|
||||
The Keychain Service API requires signed code to access much of its
|
||||
functionality. Accessing many APIs from an unsigned app will return
|
||||
an `ErrorKind::MissingEntitlement`.
|
||||
|
||||
Follow the instructions here to create a self-signed code signing certificate:
|
||||
<https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html>
|
||||
|
||||
You will need to use the [codesign] command-line utility (or XCode) to sign
|
||||
your code before it will be able to access most Keychain Services API
|
||||
functionality.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you shall be dual licensed as above, without any
|
||||
additional terms or conditions.
|
||||
|
||||
[crate-image]: https://img.shields.io/crates/v/keychain-services.svg
|
||||
[crate-link]: https://crates.io/crates/keychain-services
|
||||
[build-image]: https://travis-ci.org/iqlusioninc/keychain-services.rs.svg?branch=master
|
||||
[build-link]: https://travis-ci.org/iqlusioninc/keychain-services.rs
|
||||
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
|
||||
[license-link]: https://github.com/iqlusioninc/keychain-services.rs/blob/master/LICENSE-APACHE
|
||||
[maintenance-image]: https://img.shields.io/badge/maintenance-experimental-blue.svg
|
||||
[Documentation]: https://keychain-services.rs/docs/
|
||||
[codesign]: https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html#//apple_ref/doc/uid/TP40005929-CH4-SW4
|
||||
207
__security/keychain-services/keychain-services.rs/src/access.rs
Normal file
207
__security/keychain-services/keychain-services.rs/src/access.rs
Normal file
@@ -0,0 +1,207 @@
|
||||
//! Keychain item access control types: ACLs and policies around usage of
|
||||
//! private keys stored in the keychain.
|
||||
|
||||
use crate::{attr::AttrAccessible, error::Error, ffi::*};
|
||||
use core_foundation::{
|
||||
base::{kCFAllocatorDefault, CFOptionFlags, TCFType},
|
||||
error::CFErrorRef,
|
||||
};
|
||||
use std::{
|
||||
fmt::{self, Debug},
|
||||
ptr,
|
||||
};
|
||||
|
||||
/// Marker trait for types which can be used as `AccessControlFlags`.
|
||||
pub trait AccessControlFlag: Copy + Clone + Sized + Into<CFOptionFlags> {}
|
||||
|
||||
/// Constraints on keychain item access.
|
||||
///
|
||||
/// See "Constraints" topic under the "Topics" section of the
|
||||
/// `SecAccessControlCreateFlags` documentation at:
|
||||
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags>
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
pub enum AccessConstraint {
|
||||
/// Require either passcode or biometric auth (TouchID/FaceID).
|
||||
///
|
||||
/// Wrapper for `kSecAccessControlUserPresence`. See:
|
||||
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontroluserpresence>
|
||||
UserPresence,
|
||||
|
||||
/// Require biometric auth (TouchID/FaceID) from any enrolled user for this device.
|
||||
///
|
||||
/// Wrapper for `kSecAccessControlBiometryAny`. See:
|
||||
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontrolbiometryany>
|
||||
BiometryAny,
|
||||
|
||||
/// Require biometric auth (TouchID/FaceID) from the current user.
|
||||
///
|
||||
/// Wrapper for `kSecAccessControlBiometryCurrentSet`. See:
|
||||
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontrolbiometrycurrentset>
|
||||
BiometryCurrentSet,
|
||||
|
||||
/// Require device passcode.
|
||||
///
|
||||
/// Wrapper for `kSecAccessControlDevicePasscode`. See:
|
||||
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontroldevicepasscode>
|
||||
DevicePasscode,
|
||||
}
|
||||
|
||||
impl AccessControlFlag for AccessConstraint {}
|
||||
|
||||
impl From<AccessConstraint> for CFOptionFlags {
|
||||
fn from(constraint: AccessConstraint) -> CFOptionFlags {
|
||||
match constraint {
|
||||
AccessConstraint::UserPresence => 1,
|
||||
AccessConstraint::BiometryAny => 1 << 1,
|
||||
AccessConstraint::BiometryCurrentSet => 1 << 3,
|
||||
AccessConstraint::DevicePasscode => 1 << 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Conjunctions (and/or) on keychain item access.
|
||||
///
|
||||
/// See "Conjunctions" topic under the "Topics" section of the
|
||||
/// `SecAccessControlCreateFlags` documentation at:
|
||||
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags>
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
pub enum AccessConjunction {
|
||||
/// Require *all* constraints be satisfied.
|
||||
///
|
||||
/// Wrapper for `kSecAccessControlAnd`. See:
|
||||
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontroland>
|
||||
And,
|
||||
|
||||
/// Require *at least one* constraint must be satisfied.
|
||||
///
|
||||
/// Wrapper for `kSecAccessControlOr`. See:
|
||||
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontrolor>
|
||||
Or,
|
||||
}
|
||||
|
||||
impl AccessControlFlag for AccessConjunction {}
|
||||
|
||||
impl From<AccessConjunction> for CFOptionFlags {
|
||||
fn from(conjunction: AccessConjunction) -> CFOptionFlags {
|
||||
match conjunction {
|
||||
AccessConjunction::Or => 1 << 14,
|
||||
AccessConjunction::And => 1 << 15,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for keychain item access.
|
||||
///
|
||||
/// See "Additional Options" topic under the "Topics" section of the
|
||||
/// `SecAccessControlCreateFlags` documentation at:
|
||||
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags>
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
pub enum AccessOption {
|
||||
/// Require private key be stored in the device's Secure Enclave.
|
||||
///
|
||||
/// Wrapper for `kSecAccessControlPrivateKeyUsage`. See:
|
||||
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontrolprivatekeyusage>
|
||||
PrivateKeyUsage,
|
||||
|
||||
/// Generate encryption-key from an application-provided password.
|
||||
///
|
||||
/// Wrapper for `kSecAccessControlApplicationPassword`. See:
|
||||
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags/ksecaccesscontrolapplicationpassword>
|
||||
ApplicationPassword,
|
||||
}
|
||||
|
||||
impl AccessControlFlag for AccessOption {}
|
||||
|
||||
impl From<AccessOption> for CFOptionFlags {
|
||||
fn from(option: AccessOption) -> CFOptionFlags {
|
||||
match option {
|
||||
AccessOption::PrivateKeyUsage => 1 << 30,
|
||||
AccessOption::ApplicationPassword => 1 << 31,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Access control restrictions for a particular keychain item.
|
||||
///
|
||||
/// More information about restricting keychain items can be found at:
|
||||
/// <https://developer.apple.com/documentation/security/keychain_services/keychain_items/restricting_keychain_item_accessibility>
|
||||
///
|
||||
/// Wrapper for the `SecAccessControlCreateFlags` type:
|
||||
/// <https://developer.apple.com/documentation/security/secaccesscontrolcreateflags>
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct AccessControlFlags(CFOptionFlags);
|
||||
|
||||
impl AccessControlFlags {
|
||||
/// Create `SecAccessControlFlags` with no policy set
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Add an `AccessControlFlag` to this set of flags.
|
||||
// TODO: handle illegal combinations of flags?
|
||||
pub fn add<F: AccessControlFlag>(&mut self, flag: F) {
|
||||
self.0 |= flag.into();
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand syntax for when flags are all of the same type
|
||||
impl<'a, F> From<&'a [F]> for AccessControlFlags
|
||||
where
|
||||
F: AccessControlFlag,
|
||||
{
|
||||
fn from(flags: &[F]) -> AccessControlFlags {
|
||||
let mut result = AccessControlFlags::new();
|
||||
|
||||
for flag in flags {
|
||||
result.add(*flag)
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
declare_TCFType! {
|
||||
/// Access control policy (a.k.a. ACL) for a keychain item, combining both a
|
||||
/// set of `AccessControlFlags` and a `AttrAccessible` restriction.
|
||||
///
|
||||
/// Wrapper for the `SecAccessControl`/`SecAccessControlRef` types:
|
||||
/// <https://developer.apple.com/documentation/security/secaccesscontrolref>
|
||||
AccessControl, AccessControlRef
|
||||
}
|
||||
|
||||
impl_TCFType!(AccessControl, AccessControlRef, SecAccessControlGetTypeID);
|
||||
|
||||
impl AccessControl {
|
||||
/// Create a new `AccessControl` policy/ACL.
|
||||
///
|
||||
/// Wrapper for the `SecAccessControlCreateWithFlags()` function:
|
||||
/// <https://developer.apple.com/documentation/security/1394452-secaccesscontrolcreatewithflags>
|
||||
pub fn create_with_flags(
|
||||
protection: AttrAccessible,
|
||||
flags: AccessControlFlags,
|
||||
) -> Result<Self, Error> {
|
||||
let mut error: CFErrorRef = ptr::null_mut();
|
||||
|
||||
let result = unsafe {
|
||||
SecAccessControlCreateWithFlags(
|
||||
kCFAllocatorDefault,
|
||||
protection.as_CFString().as_CFTypeRef(),
|
||||
flags.0,
|
||||
&mut error,
|
||||
)
|
||||
};
|
||||
|
||||
if error.is_null() {
|
||||
Ok(unsafe { Self::wrap_under_create_rule(result) })
|
||||
} else {
|
||||
Err(error.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for AccessControl {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// TODO: display more information about `AccessControl`s
|
||||
write!(f, "SecAccessControl {{ ... }}")
|
||||
}
|
||||
}
|
||||
898
__security/keychain-services/keychain-services.rs/src/attr.rs
Normal file
898
__security/keychain-services/keychain-services.rs/src/attr.rs
Normal file
@@ -0,0 +1,898 @@
|
||||
//! Keychain item attributes (i.e. `SecAttr*`)
|
||||
|
||||
use crate::ffi::*;
|
||||
use core_foundation::{
|
||||
base::{CFType, TCFType, ToVoid},
|
||||
data::CFData,
|
||||
string::{CFString, CFStringRef},
|
||||
};
|
||||
use std::{
|
||||
ffi::c_void,
|
||||
fmt::{self, Debug, Display},
|
||||
str::{self, Utf8Error},
|
||||
};
|
||||
|
||||
/// Trait implemented by all `Attr*` types to simplify adding them to
|
||||
/// attribute dictionaries.
|
||||
pub(crate) trait TAttr {
|
||||
/// Get the `AttrKind` for this attribute.
|
||||
fn kind(&self) -> AttrKind;
|
||||
|
||||
/// Get a `CFType` object representing this attribute.
|
||||
fn as_CFType(&self) -> CFType;
|
||||
}
|
||||
|
||||
/// Enum of attribute types passed in parameter dictionaries. This wraps up
|
||||
/// access to framework constants which would otherwise be unsafe.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum AttrKind {
|
||||
/// Wrapper for the `kSecAttrAccessControl` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattraccesscontrol>
|
||||
AccessControl,
|
||||
|
||||
/// Wrapper for the `kSecAttrAccessible` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattraccessible>
|
||||
Accessible,
|
||||
|
||||
/// Wrapper for the `kSecAttrAccount` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattraccount>
|
||||
Account,
|
||||
|
||||
/// Wrapper for the `kSecAttrApplicationLabel` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrlabel>
|
||||
ApplicationLabel,
|
||||
|
||||
/// Wrapper for the `kSecAttrApplicationTag` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrapplicationtag>
|
||||
ApplicationTag,
|
||||
|
||||
/// Wrapper for the `kSecKeyDerive` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrcanderive>
|
||||
Derive,
|
||||
|
||||
/// Wrapper for the `kSecKeyDecrypt` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrcandecrypt>
|
||||
Decrypt,
|
||||
|
||||
/// Wrapper for the `kSecKeyEncrypt` attribute key. See:
|
||||
/// https://developer.apple.com/documentation/security/ksecattrcanencrypt>
|
||||
Encrypt,
|
||||
|
||||
/// Wrapper for the `kSecKeyExtractable` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrisextractable>
|
||||
Extractable,
|
||||
|
||||
/// Wrapper for the `kSecAttrKeyClass` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrkeyclass>
|
||||
KeyClass,
|
||||
|
||||
/// Wrapper for the `kSecAttrKeySizeInBits` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrkeysizeinbits>
|
||||
KeySizeInBits,
|
||||
|
||||
/// Wrapper for the `kSecAttrKeyType` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrkeytype>
|
||||
KeyType,
|
||||
|
||||
/// Wrapper for the `kSecAttrLabel` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrlabel>
|
||||
Label,
|
||||
|
||||
/// Wrapper for the `kSecAttrIsPermanent` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrispermanent>
|
||||
Permanent,
|
||||
|
||||
/// Wrapper for the `kSecAttrProtocol` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrprotocol>
|
||||
Protocol,
|
||||
|
||||
/// Wrapper for `kSecKeySensitive` attribute key. See
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrissensitive>
|
||||
Sensitive,
|
||||
|
||||
/// Wrapper for the `kSecAttrServer` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrserver>
|
||||
Server,
|
||||
|
||||
/// Wrapper for the `kSecAttrService` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrservice>
|
||||
Service,
|
||||
|
||||
/// Wrapper for the `kSecKeySign` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrcansign>
|
||||
Sign,
|
||||
|
||||
/// Wrapper for the `kSecAttrSynchronizable` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrsynchronizable>
|
||||
Synchronizable,
|
||||
|
||||
/// Wrapper for the `kSecAttrTokenID` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrtokenid>
|
||||
TokenId,
|
||||
|
||||
/// Wrapper for the `kSecKeyUnwrap` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrcanunwrap>
|
||||
Unwrap,
|
||||
|
||||
/// Wrapper for the `kSecKeyVerify` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrcanverify>
|
||||
Verify,
|
||||
|
||||
/// Wrapper for the `kSecKeyWrap` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrcanwrap>
|
||||
Wrap,
|
||||
}
|
||||
|
||||
impl AttrKind {
|
||||
/// Attempt to look up an attribute kind by its `SecKeychainAttrType`.
|
||||
// TODO: cache `SecKeychainAttrTypes`? e.g. as `lazy_static`
|
||||
pub(crate) fn from_tag(tag: SecKeychainAttrType) -> Option<Self> {
|
||||
let result = unsafe {
|
||||
if tag == SecKeychainAttrType::from(kSecAttrAccessControl) {
|
||||
AttrKind::AccessControl
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrAccessible) {
|
||||
AttrKind::Accessible
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrAccount) {
|
||||
AttrKind::Account
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrApplicationLabel) {
|
||||
AttrKind::ApplicationLabel
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrApplicationTag) {
|
||||
AttrKind::ApplicationTag
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrKeyClass) {
|
||||
AttrKind::KeyClass
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrKeySizeInBits) {
|
||||
AttrKind::KeySizeInBits
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrKeyType) {
|
||||
AttrKind::KeyType
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrIsPermanent) {
|
||||
AttrKind::Permanent
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrLabel) {
|
||||
AttrKind::Label
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrProtocol) {
|
||||
AttrKind::Protocol
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrServer) {
|
||||
AttrKind::Server
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrService) {
|
||||
AttrKind::Service
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrSynchronizable) {
|
||||
AttrKind::Synchronizable
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrTokenID) {
|
||||
AttrKind::TokenId
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrCanDerive) {
|
||||
AttrKind::Derive
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrCanDecrypt) {
|
||||
AttrKind::Decrypt
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrCanEncrypt) {
|
||||
AttrKind::Encrypt
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrCanSign) {
|
||||
AttrKind::Sign
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrCanVerify) {
|
||||
AttrKind::Verify
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrCanWrap) {
|
||||
AttrKind::Wrap
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrCanUnwrap) {
|
||||
AttrKind::Unwrap
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrIsExtractable) {
|
||||
AttrKind::Extractable
|
||||
} else if tag == SecKeychainAttrType::from(kSecAttrIsSensitive) {
|
||||
AttrKind::Sensitive
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SecKeychainAttrType> for AttrKind {
|
||||
fn from(tag: SecKeychainAttrType) -> Self {
|
||||
Self::from_tag(tag).unwrap_or_else(|| panic!("invalid SecKeychainAttrType tag: {:?}", tag))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AttrKind> for CFStringRef {
|
||||
fn from(attr: AttrKind) -> CFStringRef {
|
||||
unsafe {
|
||||
match attr {
|
||||
AttrKind::AccessControl => kSecAttrAccessControl,
|
||||
AttrKind::Accessible => kSecAttrAccessible,
|
||||
AttrKind::Account => kSecAttrAccount,
|
||||
AttrKind::ApplicationLabel => kSecAttrApplicationLabel,
|
||||
AttrKind::ApplicationTag => kSecAttrApplicationTag,
|
||||
AttrKind::Derive => kSecAttrCanDerive,
|
||||
AttrKind::Decrypt => kSecAttrCanDecrypt,
|
||||
AttrKind::Encrypt => kSecAttrCanEncrypt,
|
||||
AttrKind::Extractable => kSecAttrIsExtractable,
|
||||
AttrKind::KeyClass => kSecAttrKeyClass,
|
||||
AttrKind::KeySizeInBits => kSecAttrKeySizeInBits,
|
||||
AttrKind::KeyType => kSecAttrKeyType,
|
||||
AttrKind::Permanent => kSecAttrIsPermanent,
|
||||
AttrKind::Sensitive => kSecAttrIsSensitive,
|
||||
AttrKind::Sign => kSecAttrCanSign,
|
||||
AttrKind::Verify => kSecAttrCanVerify,
|
||||
AttrKind::Wrap => kSecAttrCanWrap,
|
||||
AttrKind::Unwrap => kSecAttrCanUnwrap,
|
||||
AttrKind::Label => kSecAttrLabel,
|
||||
AttrKind::Protocol => kSecAttrProtocol,
|
||||
AttrKind::Server => kSecAttrServer,
|
||||
AttrKind::Service => kSecAttrService,
|
||||
AttrKind::Synchronizable => kSecAttrSynchronizable,
|
||||
AttrKind::TokenId => kSecAttrTokenID,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl ToVoid<CFType> for AttrKind {
|
||||
fn to_void(&self) -> *const c_void {
|
||||
CFStringRef::from(*self).to_void()
|
||||
}
|
||||
}
|
||||
|
||||
/// Keychain item accessibility restrictions (from most to least restrictive).
|
||||
///
|
||||
/// More information about restricting keychain items can be found at:
|
||||
/// <https://developer.apple.com/documentation/security/keychain_services/keychain_items/restricting_keychain_item_accessibility>
|
||||
///
|
||||
/// Wrapper for the `kSecAttrAccessible` attribute key. See
|
||||
/// "Accessibility Values" section of "Item Attribute Keys and Values":
|
||||
/// <https://developer.apple.com/documentation/security/keychain_services/keychain_items/item_attribute_keys_and_values>
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum AttrAccessible {
|
||||
/// Device is unlocked and a passcode has been set on the device.
|
||||
/// <https://developer.apple.com/documentation/security/ksecattraccessiblewhenpasscodesetthisdeviceonly>
|
||||
WhenPasscodeSetThisDeviceOnly,
|
||||
|
||||
/// The device is unlocked (no passcode mandatory). Non-exportable.
|
||||
/// <https://developer.apple.com/documentation/security/ksecattraccessiblewhenunlockedthisdeviceonly>
|
||||
WhenUnlockedThisDeviceOnly,
|
||||
|
||||
/// The device is unlocked (no passcode mandatory).
|
||||
/// <https://developer.apple.com/documentation/security/ksecattraccessiblewhenunlocked>
|
||||
WhenUnlocked,
|
||||
|
||||
/// Permanently accessible after the device is first unlocked after boot.
|
||||
/// Non-exportable.
|
||||
/// <https://developer.apple.com/documentation/security/ksecattraccessibleafterfirstunlockthisdeviceonly>
|
||||
AfterFirstUnlockThisDeviceOnly,
|
||||
|
||||
/// Permanently accessible after the device is first unlocked after boot.
|
||||
/// <https://developer.apple.com/documentation/security/ksecattraccessibleafterfirstunlock>
|
||||
AfterFirstUnlock,
|
||||
|
||||
/// Item is always accessible on this device. Non-exportable.
|
||||
/// <https://developer.apple.com/documentation/security/ksecattraccessiblealwaysthisdeviceonly>
|
||||
AlwaysThisDeviceOnly,
|
||||
|
||||
/// Item is always accessible.
|
||||
/// <https://developer.apple.com/documentation/security/ksecattraccessiblealways>
|
||||
Always,
|
||||
}
|
||||
|
||||
impl AttrAccessible {
|
||||
/// Get pointer to an accessibility value to associate with the
|
||||
/// `kSecAttrAccessible` key for a keychain item
|
||||
pub fn as_CFString(self) -> CFString {
|
||||
unsafe {
|
||||
CFString::wrap_under_get_rule(match self {
|
||||
AttrAccessible::WhenPasscodeSetThisDeviceOnly => {
|
||||
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
|
||||
}
|
||||
AttrAccessible::WhenUnlockedThisDeviceOnly => {
|
||||
kSecAttrAccessibleWhenUnlockedThisDeviceOnly
|
||||
}
|
||||
AttrAccessible::WhenUnlocked => kSecAttrAccessibleWhenUnlocked,
|
||||
AttrAccessible::AfterFirstUnlockThisDeviceOnly => {
|
||||
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
|
||||
}
|
||||
AttrAccessible::AfterFirstUnlock => kSecAttrAccessibleAfterFirstUnlock,
|
||||
AttrAccessible::AlwaysThisDeviceOnly => kSecAttrAccessibleAlwaysThisDeviceOnly,
|
||||
AttrAccessible::Always => kSecAttrAccessibleAlways,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TAttr for AttrAccessible {
|
||||
fn kind(&self) -> AttrKind {
|
||||
AttrKind::Accessible
|
||||
}
|
||||
|
||||
fn as_CFType(&self) -> CFType {
|
||||
self.as_CFString().as_CFType()
|
||||
}
|
||||
}
|
||||
|
||||
/// Application-specific key labels, i.e. key fingerprints.
|
||||
///
|
||||
/// Not to be confused with `SecAttrApplicationTag` or `SecAttrLabel`, the
|
||||
/// `SecAttrApplicationLabel` value is useful for programatically looking up
|
||||
/// public/private key pairs, and is set to the hash of the public key, a.k.a.
|
||||
/// the public key fingerprint.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrApplicationLabel` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrapplicationlabel>
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct AttrApplicationLabel(pub(crate) CFData);
|
||||
|
||||
impl AttrApplicationLabel {
|
||||
/// Create a new application label from a byte slice
|
||||
pub fn new(bytes: &[u8]) -> Self {
|
||||
AttrApplicationLabel(CFData::from_buffer(bytes))
|
||||
}
|
||||
|
||||
/// Borrow this value as a byte slice
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for AttrApplicationLabel {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for AttrApplicationLabel {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let bytes = Vec::from(self.as_bytes());
|
||||
write!(f, "SecAttrApplicationLabel({:?})", bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8]> for AttrApplicationLabel {
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
Self::new(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl TAttr for AttrApplicationLabel {
|
||||
fn kind(&self) -> AttrKind {
|
||||
AttrKind::ApplicationLabel
|
||||
}
|
||||
|
||||
fn as_CFType(&self) -> CFType {
|
||||
self.0.as_CFType()
|
||||
}
|
||||
}
|
||||
|
||||
/// Application-specific tags for keychain items.
|
||||
///
|
||||
/// These should be unique for a specific item (i.e. named after its purpose
|
||||
/// and used as the "primary key" for locating a particular keychain item),
|
||||
/// and often use a reversed domain name-like syntax, e.g.
|
||||
/// `io.crates.PackageSigning`
|
||||
///
|
||||
/// Wrapper for the `kSecAttrApplicationTag` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrapplicationtag>
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct AttrApplicationTag(pub(crate) CFData);
|
||||
|
||||
impl AttrApplicationTag {
|
||||
/// Create a new application tag from a byte slice
|
||||
pub fn new(bytes: &[u8]) -> Self {
|
||||
AttrApplicationTag(CFData::from_buffer(bytes))
|
||||
}
|
||||
|
||||
/// Borrow the tag data as a byte slice
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Borrow the tag data as a `str` (if it is valid UTF-8)
|
||||
pub fn as_str(&self) -> Result<&str, Utf8Error> {
|
||||
str::from_utf8(self.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for AttrApplicationTag {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AttrApplicationTag {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", String::from_utf8_lossy(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8]> for AttrApplicationTag {
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
Self::new(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for AttrApplicationTag {
|
||||
fn from(string: &str) -> Self {
|
||||
Self::new(string.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl TAttr for AttrApplicationTag {
|
||||
fn kind(&self) -> AttrKind {
|
||||
AttrKind::ApplicationTag
|
||||
}
|
||||
|
||||
fn as_CFType(&self) -> CFType {
|
||||
self.0.as_CFType()
|
||||
}
|
||||
}
|
||||
|
||||
/// Human readable/meaningful labels for keychain items.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrLabel` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrlabel>
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct AttrLabel(pub(crate) CFString);
|
||||
|
||||
impl AttrLabel {
|
||||
/// Create a new label from a `&str`
|
||||
pub fn new(label: &str) -> Self {
|
||||
AttrLabel(CFString::new(label))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AttrLabel {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", &self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for AttrLabel {
|
||||
fn from(label: &str) -> Self {
|
||||
Self::new(label)
|
||||
}
|
||||
}
|
||||
|
||||
impl TAttr for AttrLabel {
|
||||
fn kind(&self) -> AttrKind {
|
||||
AttrKind::Label
|
||||
}
|
||||
|
||||
fn as_CFType(&self) -> CFType {
|
||||
self.0.as_CFType()
|
||||
}
|
||||
}
|
||||
|
||||
/// Classes of keys supported by Keychain Services (not to be confused with
|
||||
/// `SecClass`, `SecAttrClass` or `SecAttrKeyType`)
|
||||
///
|
||||
/// Wrapper for the `kSecAttrKeyClass` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrkeyclass>
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum AttrKeyClass {
|
||||
/// Public keys.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrKeyClassPublic` attribute value. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrkeyclasspublic>
|
||||
Public,
|
||||
|
||||
/// Private keys.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrKeyClassPrivate` attribute value. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrkeyclassprivate>
|
||||
Private,
|
||||
|
||||
/// Symmetric keys
|
||||
///
|
||||
/// Wrapper for the `kSecAttrKeyClassSymmetric` attribute value. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrkeyclasssymmetric>
|
||||
// TODO: support for symmetric encryption
|
||||
Symmetric,
|
||||
}
|
||||
|
||||
impl AttrKeyClass {
|
||||
/// Get `CFString` containing the `kSecAttrKeyClass` dictionary value for
|
||||
/// this particular `SecAttrKeyClass`.
|
||||
pub fn as_CFString(self) -> CFString {
|
||||
unsafe {
|
||||
CFString::wrap_under_get_rule(match self {
|
||||
AttrKeyClass::Public => kSecAttrKeyClassPublic,
|
||||
AttrKeyClass::Private => kSecAttrKeyClassPrivate,
|
||||
AttrKeyClass::Symmetric => kSecAttrKeyClassSymmetric,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TAttr for AttrKeyClass {
|
||||
fn kind(&self) -> AttrKind {
|
||||
AttrKind::KeyClass
|
||||
}
|
||||
|
||||
fn as_CFType(&self) -> CFType {
|
||||
self.as_CFString().as_CFType()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CFStringRef> for AttrKeyClass {
|
||||
fn from(string_ref: CFStringRef) -> AttrKeyClass {
|
||||
unsafe {
|
||||
if string_ref == kSecAttrKeyClassPublic {
|
||||
AttrKeyClass::Public
|
||||
} else if string_ref == kSecAttrKeyClassPrivate {
|
||||
AttrKeyClass::Private
|
||||
} else {
|
||||
AttrKeyClass::Symmetric
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a CFString> for AttrKeyClass {
|
||||
fn from(string: &'a CFString) -> AttrKeyClass {
|
||||
unsafe {
|
||||
if *string == CFString::wrap_under_get_rule(kSecAttrKeyClassPublic) {
|
||||
AttrKeyClass::Public
|
||||
} else if *string == CFString::wrap_under_get_rule(kSecAttrKeyClassPrivate) {
|
||||
AttrKeyClass::Private
|
||||
} else {
|
||||
AttrKeyClass::Symmetric
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Types of keys supported by Keychain Services (not to be confused with
|
||||
/// `AttrKeyClass`)
|
||||
///
|
||||
/// Wrapper for the `kSecAttrKeyType` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrkeytype>
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum AttrKeyType {
|
||||
/// AES algorithm.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrKeyTypeAES` attribute value. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrkeytypeaes>
|
||||
// TODO: support for AES encryption
|
||||
Aes,
|
||||
|
||||
/// RSA algorithm.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrKeyTypeRSA` attribute value. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrkeytypersa>
|
||||
Rsa,
|
||||
|
||||
/// Elliptic curve cryptography over the NIST curves (e.g. P-256)
|
||||
///
|
||||
/// Wrapper for the `kSecAttrKeyTypeECSECPrimeRandom` attribute value. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrkeytypeecsecprimerandom>
|
||||
EcSecPrimeRandom,
|
||||
}
|
||||
|
||||
impl AttrKeyType {
|
||||
/// Get `CFString` containing the `kSecAttrKeyType` dictionary value for
|
||||
/// this particular `SecAttrKeyType`.
|
||||
pub fn as_CFString(self) -> CFString {
|
||||
unsafe {
|
||||
CFString::wrap_under_get_rule(match self {
|
||||
AttrKeyType::Aes => kSecAttrKeyTypeAES,
|
||||
AttrKeyType::Rsa => kSecAttrKeyTypeRSA,
|
||||
AttrKeyType::EcSecPrimeRandom => kSecAttrKeyTypeECSECPrimeRandom,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TAttr for AttrKeyType {
|
||||
fn kind(&self) -> AttrKind {
|
||||
AttrKind::KeyType
|
||||
}
|
||||
|
||||
fn as_CFType(&self) -> CFType {
|
||||
self.as_CFString().as_CFType()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CFStringRef> for AttrKeyType {
|
||||
fn from(string_ref: CFStringRef) -> AttrKeyType {
|
||||
unsafe {
|
||||
if string_ref == kSecAttrKeyTypeECSECPrimeRandom {
|
||||
AttrKeyType::EcSecPrimeRandom
|
||||
} else if string_ref == kSecAttrKeyTypeRSA {
|
||||
AttrKeyType::Rsa
|
||||
} else {
|
||||
AttrKeyType::Aes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a CFString> for AttrKeyType {
|
||||
fn from(string: &'a CFString) -> AttrKeyType {
|
||||
unsafe {
|
||||
if *string == CFString::wrap_under_get_rule(kSecAttrKeyTypeECSECPrimeRandom) {
|
||||
AttrKeyType::EcSecPrimeRandom
|
||||
} else if *string == CFString::wrap_under_get_rule(kSecAttrKeyTypeRSA) {
|
||||
AttrKeyType::Rsa
|
||||
} else {
|
||||
AttrKeyType::Aes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Internet protocols optionally associated with `SecClass::InternetPassword`
|
||||
/// keychain items.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrProtocol` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrprotocol>
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum AttrProtocol {
|
||||
/// File Transfer Protocol
|
||||
FTP,
|
||||
|
||||
/// Client-side FTP account.
|
||||
FTPAccount,
|
||||
|
||||
/// Hypertext Transfer Protocol.
|
||||
HTTP,
|
||||
|
||||
/// Internet Relay Chat.
|
||||
IRC,
|
||||
|
||||
/// Network News Transfer Protocol.
|
||||
NNTP,
|
||||
|
||||
/// Post Office Protocol v3.
|
||||
POP3,
|
||||
|
||||
/// Simple Mail Transfer Protocol.
|
||||
SMTP,
|
||||
|
||||
/// SOCKS protocol.
|
||||
SOCKS,
|
||||
|
||||
/// Internet Message Access Protocol.
|
||||
IMAP,
|
||||
|
||||
/// Lightweight Directory Access Protocol.
|
||||
LDAP,
|
||||
|
||||
/// AFP over AppleTalk.
|
||||
AppleTalk,
|
||||
|
||||
/// AFP over TCP.
|
||||
AFP,
|
||||
|
||||
/// Telnet protocol.
|
||||
Telnet,
|
||||
|
||||
/// Secure Shell Protocol.
|
||||
SSH,
|
||||
|
||||
/// FTP over TLS/SSL.
|
||||
FTPS,
|
||||
|
||||
/// HTTP over TLS/SSL.
|
||||
HTTPS,
|
||||
|
||||
/// HTTP proxy.
|
||||
HTTPProxy,
|
||||
|
||||
/// HTTPS proxy.
|
||||
HTTPSProxy,
|
||||
|
||||
/// FTP proxy.
|
||||
FTPProxy,
|
||||
|
||||
/// Server Message Block protocol.
|
||||
SMB,
|
||||
|
||||
/// Real Time Streaming Protocol
|
||||
RTSP,
|
||||
|
||||
/// RTSP proxy.
|
||||
RTSPProxy,
|
||||
|
||||
/// DAAP protocol.
|
||||
DAAP,
|
||||
|
||||
/// Remote Apple Events.
|
||||
EPPC,
|
||||
|
||||
/// IPP protocol.
|
||||
IPP,
|
||||
|
||||
/// NNTP over TLS/SSL.
|
||||
NNTPS,
|
||||
|
||||
/// LDAP over TLS/SSL.
|
||||
LDAPS,
|
||||
|
||||
/// Telnet over TLS/SSL.
|
||||
TelnetS,
|
||||
|
||||
/// IMAP over TLS/SSL.
|
||||
IMAPS,
|
||||
|
||||
/// IRC over TLS/SSL.
|
||||
IRCS,
|
||||
|
||||
/// POP3 over TLS/SSL.
|
||||
POP3S,
|
||||
}
|
||||
|
||||
impl AttrProtocol {
|
||||
/// Get `CFString` containing the `kSecAttrProtocol` dictionary value for
|
||||
/// this particular `SecAttrProtocol`.
|
||||
pub fn as_CFString(self) -> CFString {
|
||||
unsafe {
|
||||
CFString::wrap_under_get_rule(match self {
|
||||
AttrProtocol::FTP => kSecAttrProtocolFTP,
|
||||
AttrProtocol::FTPAccount => kSecAttrProtocolFTPAccount,
|
||||
AttrProtocol::HTTP => kSecAttrProtocolHTTP,
|
||||
AttrProtocol::IRC => kSecAttrProtocolIRC,
|
||||
AttrProtocol::NNTP => kSecAttrProtocolNNTP,
|
||||
AttrProtocol::POP3 => kSecAttrProtocolPOP3,
|
||||
AttrProtocol::SMTP => kSecAttrProtocolSMTP,
|
||||
AttrProtocol::SOCKS => kSecAttrProtocolSOCKS,
|
||||
AttrProtocol::IMAP => kSecAttrProtocolIMAP,
|
||||
AttrProtocol::LDAP => kSecAttrProtocolLDAP,
|
||||
AttrProtocol::AppleTalk => kSecAttrProtocolAppleTalk,
|
||||
AttrProtocol::AFP => kSecAttrProtocolAFP,
|
||||
AttrProtocol::Telnet => kSecAttrProtocolTelnet,
|
||||
AttrProtocol::SSH => kSecAttrProtocolSSH,
|
||||
AttrProtocol::FTPS => kSecAttrProtocolFTPS,
|
||||
AttrProtocol::HTTPS => kSecAttrProtocolHTTPS,
|
||||
AttrProtocol::HTTPProxy => kSecAttrProtocolHTTPProxy,
|
||||
AttrProtocol::HTTPSProxy => kSecAttrProtocolHTTPSProxy,
|
||||
AttrProtocol::FTPProxy => kSecAttrProtocolFTPProxy,
|
||||
AttrProtocol::SMB => kSecAttrProtocolSMB,
|
||||
AttrProtocol::RTSP => kSecAttrProtocolRTSP,
|
||||
AttrProtocol::RTSPProxy => kSecAttrProtocolRTSPProxy,
|
||||
AttrProtocol::DAAP => kSecAttrProtocolDAAP,
|
||||
AttrProtocol::EPPC => kSecAttrProtocolEPPC,
|
||||
AttrProtocol::IPP => kSecAttrProtocolIPP,
|
||||
AttrProtocol::NNTPS => kSecAttrProtocolNNTPS,
|
||||
AttrProtocol::LDAPS => kSecAttrProtocolLDAPS,
|
||||
AttrProtocol::TelnetS => kSecAttrProtocolTelnetS,
|
||||
AttrProtocol::IMAPS => kSecAttrProtocolIMAPS,
|
||||
AttrProtocol::IRCS => kSecAttrProtocolIRCS,
|
||||
AttrProtocol::POP3S => kSecAttrProtocolPOP3S,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TAttr for AttrProtocol {
|
||||
fn kind(&self) -> AttrKind {
|
||||
AttrKind::Protocol
|
||||
}
|
||||
|
||||
fn as_CFType(&self) -> CFType {
|
||||
self.as_CFString().as_CFType()
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifiers for external storage tokens for cryptographic keys
|
||||
/// (i.e. Secure Enclave).
|
||||
///
|
||||
/// Wrapper for the `kSecAttrTokenID` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrtokenid>
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum AttrTokenId {
|
||||
/// Secure Enclave Processor (SEP), e.g. T1/T2 chip.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrTokenIDSecureEnclave` attribute value. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrtokenidsecureenclave>
|
||||
SecureEnclave,
|
||||
}
|
||||
|
||||
impl AttrTokenId {
|
||||
/// Get `CFString` containing the `kSecAttrTokenID` dictionary value for
|
||||
/// this particular `SecAttrTokenId`.
|
||||
pub fn as_CFString(self) -> CFString {
|
||||
unsafe {
|
||||
CFString::wrap_under_get_rule(match self {
|
||||
AttrTokenId::SecureEnclave => kSecAttrTokenIDSecureEnclave,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TAttr for AttrTokenId {
|
||||
fn kind(&self) -> AttrKind {
|
||||
AttrKind::TokenId
|
||||
}
|
||||
|
||||
fn as_CFType(&self) -> CFType {
|
||||
self.as_CFString().as_CFType()
|
||||
}
|
||||
}
|
||||
|
||||
/// Keychain Item Attribute Constants For Keys
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/security/keychain_services/keychain_items/1495743-keychain_item_attribute_constant>
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum KeyAttr {
|
||||
/// Wrapper for the `kSecKeyAlwaysSensitive` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyalwayssensitive>
|
||||
AlwaysSensitive,
|
||||
/// Wrapper for the `kSecKeyDerive` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyderive>
|
||||
CanDerive,
|
||||
/// Wrapper for the `kSecKeyDecrypt` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeydecrypt>
|
||||
CanDecrypt,
|
||||
/// Wrapper for the `kSecKeyEncrypt` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyencrypt>
|
||||
CanEncrypt,
|
||||
/// Wrapper for the `kSecKeySign` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeysign>
|
||||
CanSign,
|
||||
/// Wrapper for the `kSecKeyUnwrap` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyunwrap>
|
||||
CanUnwrap,
|
||||
/// Wrapper for the `kSecKeyVerify` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyverify>
|
||||
CanVerify,
|
||||
/// Wrapper for the `kSecKeyWrap` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeywrap>
|
||||
CanWrap,
|
||||
/// Wrapper for the `kSecKeyEffectiveKeySize` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyeffectivekeysize>
|
||||
EffectiveKeySize,
|
||||
/// Wrapper for the `kSecKeyEndDate` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyenddate>
|
||||
EndDate,
|
||||
/// Wrapper for the `kSecKeyExtractable` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyextractable>
|
||||
Extractable,
|
||||
/// Wrapper for the `kSecKeyModifiable` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeymodifiable>
|
||||
Modifiable,
|
||||
/// Wrapper for the `kSecKeyNeverExtractable` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyneverextractable>
|
||||
NeverExtractable,
|
||||
/// Wrapper for the `kSecKeyPermanent` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeypermanent>
|
||||
Permanent,
|
||||
/// Wrapper for the `kSecKeyPrivate` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyprivate>
|
||||
Private,
|
||||
/// Wrapper for the `kSecKeySensitive` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeysensitive>
|
||||
Sensitive,
|
||||
/// Wrapper for the `kSecKeyKeySizeInBits` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeykeysizeinbits>
|
||||
SizeInBits,
|
||||
/// Wrapper for the `kSecKeyStartDate` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeystartdate>
|
||||
StartDate,
|
||||
/// Wrapper for the `kSecKeyKeyType` attribute value see:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeykeytype>
|
||||
Type,
|
||||
}
|
||||
|
||||
impl KeyAttr {
|
||||
/// Get `CFString` containing the `kSecKeyAttr` dictionary value for
|
||||
/// this particular `SecKeyAttr`.
|
||||
pub fn as_CFString(self) -> CFString {
|
||||
unsafe {
|
||||
CFString::wrap_under_get_rule(match self {
|
||||
KeyAttr::AlwaysSensitive => kSecKeyAlwaysSensitive,
|
||||
KeyAttr::CanDerive => kSecKeyDerive,
|
||||
KeyAttr::CanDecrypt => kSecKeyDecrypt,
|
||||
KeyAttr::CanEncrypt => kSecKeyEncrypt,
|
||||
KeyAttr::CanSign => kSecKeySign,
|
||||
KeyAttr::CanUnwrap => kSecKeyUnwrap,
|
||||
KeyAttr::CanVerify => kSecKeyVerify,
|
||||
KeyAttr::CanWrap => kSecKeyWrap,
|
||||
KeyAttr::Extractable => kSecKeyExtractable,
|
||||
KeyAttr::EffectiveKeySize => kSecKeyEffectiveKeySize,
|
||||
KeyAttr::EndDate => kSecKeyEndDate,
|
||||
KeyAttr::Modifiable => kSecKeyModifiable,
|
||||
KeyAttr::NeverExtractable => kSecKeyNeverExtractable,
|
||||
KeyAttr::Permanent => kSecKeyPermanent,
|
||||
KeyAttr::Private => kSecKeyPrivate,
|
||||
KeyAttr::Sensitive => kSecKeySensitive,
|
||||
KeyAttr::SizeInBits => kSecKeyKeySizeInBits,
|
||||
KeyAttr::StartDate => kSecKeyStartDate,
|
||||
KeyAttr::Type => kSecKeyKeyType,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//! Ciphertext produced by this library.
|
||||
//!
|
||||
//! This type doesn't map directly to any type in the Keychain Services API,
|
||||
//! but instead provides a newtype for ciphertexts this binding produces.
|
||||
|
||||
use crate::key::KeyAlgorithm;
|
||||
|
||||
/// Cryptographic signatures
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Ciphertext {
|
||||
alg: KeyAlgorithm,
|
||||
bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Ciphertext {
|
||||
/// Create a new `Ciphertext`
|
||||
pub fn new(alg: KeyAlgorithm, bytes: Vec<u8>) -> Self {
|
||||
// TODO: restrict valid algorithms to encryption algorithms?
|
||||
Self { alg, bytes }
|
||||
}
|
||||
|
||||
/// Get the algorithm which produced this signature
|
||||
pub fn algorithm(&self) -> KeyAlgorithm {
|
||||
self.alg
|
||||
}
|
||||
|
||||
/// Borrow the ciphertext data as bytes
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
|
||||
/// Convert into a byte vector
|
||||
pub fn into_vec(self) -> Vec<u8> {
|
||||
self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Ciphertext {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ciphertext> for Vec<u8> {
|
||||
fn from(ciphertext: Ciphertext) -> Vec<u8> {
|
||||
ciphertext.into_vec()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
//! Builder for constructing a `CFDictionary` from attribute pairs.
|
||||
|
||||
use crate::{attr::TAttr, ffi::kSecClass, keychain::item};
|
||||
use core_foundation::{
|
||||
self,
|
||||
base::{CFType, TCFType},
|
||||
boolean::CFBoolean,
|
||||
number::CFNumber,
|
||||
string::{CFString, CFStringRef},
|
||||
};
|
||||
|
||||
/// All CFDictionary types we use follow this signature
|
||||
pub(crate) type Dictionary = core_foundation::dictionary::CFDictionary<CFType, CFType>;
|
||||
|
||||
/// Builder for attribute/parameter dictionaries we pass as arguments.
|
||||
// TODO: ensure there are no duplicate items, e.g. with `HashMap`/`BTreeMap`
|
||||
// storage and checking if the same key is added twice.
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub(crate) struct DictionaryBuilder(Vec<(CFType, CFType)>);
|
||||
|
||||
impl DictionaryBuilder {
|
||||
/// Create a new dictionary builder
|
||||
pub(crate) fn new() -> DictionaryBuilder {
|
||||
DictionaryBuilder(vec![])
|
||||
}
|
||||
|
||||
/// Add a key/value pair to the dictionary
|
||||
pub(crate) fn add<K, V>(&mut self, key: K, value: &V)
|
||||
where
|
||||
K: Into<CFStringRef>,
|
||||
V: TCFType,
|
||||
{
|
||||
self.0.push((
|
||||
unsafe { CFString::wrap_under_get_rule(key.into()) }.as_CFType(),
|
||||
value.as_CFType(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Add an attribute (i.e. `TSecAttr`) to the dictionary
|
||||
pub(crate) fn add_attr(&mut self, attr: &dyn TAttr) {
|
||||
self.add(attr.kind(), &attr.as_CFType())
|
||||
}
|
||||
|
||||
/// Add a key/value pair with a `bool` value to the dictionary
|
||||
pub(crate) fn add_boolean<K>(&mut self, key: K, value: bool)
|
||||
where
|
||||
K: Into<CFStringRef>,
|
||||
{
|
||||
self.add(key, &CFBoolean::from(value))
|
||||
}
|
||||
|
||||
/// Add a `keychain::item::Class` value to the dictionary
|
||||
pub(crate) fn add_class(&mut self, class: item::Class) {
|
||||
self.add(unsafe { kSecClass }, &class.as_CFString());
|
||||
}
|
||||
|
||||
/// Add a key/value pair with an `i64` value to the dictionary
|
||||
pub(crate) fn add_number<K>(&mut self, key: K, value: i64)
|
||||
where
|
||||
K: Into<CFStringRef>,
|
||||
{
|
||||
self.add(key, &CFNumber::from(value))
|
||||
}
|
||||
|
||||
/// Add a key/value pair with an `AsRef<str>` value to the dictionary
|
||||
pub(crate) fn add_string<K, V>(&mut self, key: K, value: V)
|
||||
where
|
||||
K: Into<CFStringRef>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
self.add(key, &CFString::from(value.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DictionaryBuilder> for Dictionary {
|
||||
fn from(builder: DictionaryBuilder) -> Dictionary {
|
||||
Dictionary::from_CFType_pairs(&builder.0)
|
||||
}
|
||||
}
|
||||
562
__security/keychain-services/keychain-services.rs/src/error.rs
Normal file
562
__security/keychain-services/keychain-services.rs/src/error.rs
Normal file
@@ -0,0 +1,562 @@
|
||||
//! Error types
|
||||
|
||||
use crate::ffi::*;
|
||||
use core_foundation::{
|
||||
base::{CFRelease, CFTypeRef, OSStatus, TCFType},
|
||||
error::{CFErrorCopyDescription, CFErrorGetCode, CFErrorGetDomain, CFErrorRef},
|
||||
string::CFString,
|
||||
};
|
||||
use failure::{Backtrace, Fail};
|
||||
use std::{
|
||||
fmt::{self, Display},
|
||||
io, ptr,
|
||||
};
|
||||
|
||||
/// No error occurred.
|
||||
/// <https://developer.apple.com/documentation/security/errsecsuccess>
|
||||
const errSecSuccess: OSStatus = 0;
|
||||
|
||||
/// Authentication and/or authorization failed.
|
||||
/// <https://developer.apple.com/documentation/security/errsecauthfailed>
|
||||
const errSecAuthFailed: OSStatus = -25293;
|
||||
|
||||
/// Buffer is too small.
|
||||
/// <https://developer.apple.com/documentation/security/errsecbuffertoosmall>
|
||||
const errSecBufferTooSmall: OSStatus = -25301;
|
||||
|
||||
/// Certificate chain creation attempt failed.
|
||||
/// <https://developer.apple.com/documentation/security/errseccreatechainfailed>
|
||||
const errSecCreateChainFailed: OSStatus = -25318;
|
||||
|
||||
/// Data too large for the given data type.
|
||||
/// <https://developer.apple.com/documentation/security/errsecdatatoolarge>
|
||||
const errSecDataTooLarge: OSStatus = -25302;
|
||||
|
||||
/// Data is not available.
|
||||
/// <https://developer.apple.com/documentation/security/errsecdatanotavailable>
|
||||
const errSecDataNotAvailable: OSStatus = -25316;
|
||||
|
||||
/// Data cannot be modified.
|
||||
/// <https://developer.apple.com/documentation/security/errsecdatanotmodifiable>
|
||||
const errSecDataNotModifiable: OSStatus = -25317;
|
||||
|
||||
/// Callback with the same name already exists.
|
||||
/// <https://developer.apple.com/documentation/security/errsecduplicatecallback>
|
||||
const errSecDuplicateCallback: OSStatus = -25297;
|
||||
|
||||
/// Item already exists.
|
||||
/// <https://developer.apple.com/documentation/security/errsecduplicateitem>
|
||||
const errSecDuplicateItem: OSStatus = -25299;
|
||||
|
||||
/// Keychain with the same name already exists.
|
||||
/// <https://developer.apple.com/documentation/security/errsecduplicatekeychain>
|
||||
const errSecDuplicateKeychain: OSStatus = -25296;
|
||||
|
||||
/// Base number for `OSStatus` values which map directly to errno values
|
||||
const errSecErrnoBase: OSStatus = 100_000;
|
||||
|
||||
/// Upper limit number for `OSStatus` values which map directly to errno values
|
||||
const errSecErrnoLimit: OSStatus = 100_255;
|
||||
|
||||
/// System is in a dark wake state - user interface cannot be displayed.
|
||||
/// <https://developer.apple.com/documentation/security/errsecindarkwake>
|
||||
const errSecInDarkWake: OSStatus = -25320;
|
||||
|
||||
/// Security Server interactions not allowed in this context.
|
||||
/// <https://developer.apple.com/documentation/security/errsecinteractionnotallowed>
|
||||
const errSecInteractionNotAllowed: OSStatus = -25308;
|
||||
|
||||
/// User interaction required.
|
||||
/// <https://developer.apple.com/documentation/security/errsecinteractionrequired>
|
||||
const errSecInteractionRequired: OSStatus = -25315;
|
||||
|
||||
/// Callback is invalid.
|
||||
/// <https://developer.apple.com/documentation/security/errsecinvalidcallback>
|
||||
const errSecInvalidCallback: OSStatus = -25298;
|
||||
|
||||
/// Item reference is invalid.
|
||||
/// <https://developer.apple.com/documentation/security/errsecinvaliditemref>
|
||||
const errSecInvalidItemRef: OSStatus = -25304;
|
||||
|
||||
/// Keychain is invalid.
|
||||
/// <https://developer.apple.com/documentation/security/errsecinvalidkeychain>
|
||||
const errSecInvalidKeychain: OSStatus = -25295;
|
||||
|
||||
/// Specified preference domain is not valid.
|
||||
/// <https://developer.apple.com/documentation/security/errsecinvalidprefsdomain>
|
||||
const errSecInvalidPrefsDomain: OSStatus = -25319;
|
||||
|
||||
/// Search reference is invalid.
|
||||
/// <https://developer.apple.com/documentation/security/errsecinvalidsearchref>
|
||||
const errSecInvalidSearchRef: OSStatus = -25305;
|
||||
|
||||
/// Item could not be found.
|
||||
/// <https://developer.apple.com/documentation/security/errsecitemnotfound>
|
||||
const errSecItemNotFound: OSStatus = -25300;
|
||||
|
||||
/// Invalid key size.
|
||||
/// <https://developer.apple.com/documentation/security/errseckeysizenotallowed>
|
||||
const errSecKeySizeNotAllowed: OSStatus = -25311;
|
||||
|
||||
/// Missing entitlement: keychain access disallowed because app is unsigned.
|
||||
/// <https://developer.apple.com/documentation/security/errsecmissingentitlement>
|
||||
const errSecMissingEntitlement: OSStatus = -34018;
|
||||
|
||||
/// Certificate module unavailable.
|
||||
/// <https://developer.apple.com/documentation/security/errsecnocertificatemodule>
|
||||
const errSecNoCertificateModule: OSStatus = -25313;
|
||||
|
||||
/// Default keychain does not exist.
|
||||
/// <https://developer.apple.com/documentation/security/errsecnodefaultkeychain>
|
||||
const errSecNoDefaultKeychain: OSStatus = -25307;
|
||||
|
||||
/// Policy module unavailable.
|
||||
/// <https://developer.apple.com/documentation/security/errsecnopolicymodule>
|
||||
const errSecNoPolicyModule: OSStatus = -25314;
|
||||
|
||||
/// Storage module unavailable.
|
||||
/// <https://developer.apple.com/documentation/security/errsecnostoragemodule>
|
||||
const errSecNoStorageModule: OSStatus = -25312;
|
||||
|
||||
/// Specified attribute does not exist.
|
||||
/// <https://developer.apple.com/documentation/security/errsecnosuchattr>
|
||||
const errSecNoSuchAttr: OSStatus = -25303;
|
||||
|
||||
/// Specified keychain item class does not exist.
|
||||
/// <https://developer.apple.com/documentation/security/errsecnosuchclass>
|
||||
const errSecNoSuchClass: OSStatus = -25306;
|
||||
|
||||
/// Specified keychain does not exist.
|
||||
/// <https://developer.apple.com/documentation/security/errsecnosuchkeychain>
|
||||
const errSecNoSuchKeychain: OSStatus = -25294;
|
||||
|
||||
/// Trust results not available.
|
||||
/// <https://developer.apple.com/documentation/security/errsecnotavailable>
|
||||
const errSecNotAvailable: OSStatus = -25291;
|
||||
|
||||
/// Can't perform given action on read-only item.
|
||||
/// <https://developer.apple.com/documentation/security/errsecreadonly>
|
||||
const errSecReadOnly: OSStatus = -25292;
|
||||
|
||||
/// Can't perform action on read-only attribute
|
||||
/// <https://developer.apple.com/documentation/security/errsecreadonlyattr>
|
||||
const errSecReadOnlyAttr: OSStatus = -25309;
|
||||
|
||||
/// Invalid version.
|
||||
/// <https://developer.apple.com/documentation/security/errsecwrongversion>
|
||||
const errSecWrongSecVersion: OSStatus = -25310;
|
||||
|
||||
/// Error type.
|
||||
///
|
||||
/// Wrapper for the `CFError` type:
|
||||
/// <https://developer.apple.com/documentation/corefoundation/cferror>
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
kind: ErrorKind,
|
||||
backtrace: Backtrace,
|
||||
description: String,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Create a new error of the given kind with the given description
|
||||
pub fn new<D>(kind: ErrorKind, description: &D) -> Self
|
||||
where
|
||||
D: ToString + ?Sized,
|
||||
{
|
||||
Error {
|
||||
kind,
|
||||
backtrace: Backtrace::new(),
|
||||
description: description.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an error from an `OSStatus` if the status is not success
|
||||
pub fn maybe_from_OSStatus(status: OSStatus) -> Option<Self> {
|
||||
if status == errSecSuccess {
|
||||
None
|
||||
} else {
|
||||
let kind = ErrorKind::from(status);
|
||||
let description = unsafe {
|
||||
CFString::wrap_under_create_rule(SecCopyErrorMessageString(status, ptr::null()))
|
||||
};
|
||||
|
||||
Some(Error::new(kind, &description))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the `ErrorKind` for this error
|
||||
pub fn kind(&self) -> &ErrorKind {
|
||||
&self.kind
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} ({})", &self.description, &self.kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl Fail for Error {
|
||||
fn cause(&self) -> Option<&dyn Fail> {
|
||||
None
|
||||
}
|
||||
|
||||
fn backtrace(&self) -> Option<&Backtrace> {
|
||||
Some(&self.backtrace)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CFErrorRef> for Error {
|
||||
/// Creates an `Error` with copies of all error data on the Rust heap.
|
||||
///
|
||||
/// Calls `CFRelease` on the provided `CFErrorRef`.
|
||||
fn from(error_ref: CFErrorRef) -> Error {
|
||||
let kind = ErrorKind::from(error_ref);
|
||||
let backtrace = Backtrace::new();
|
||||
let description =
|
||||
unsafe { CFString::wrap_under_create_rule(CFErrorCopyDescription(error_ref)) }
|
||||
.to_string();
|
||||
|
||||
// Free the error reference
|
||||
unsafe {
|
||||
CFRelease(error_ref as CFTypeRef);
|
||||
}
|
||||
|
||||
Error {
|
||||
kind,
|
||||
backtrace,
|
||||
description,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Kinds of errors.
|
||||
#[derive(Clone, Debug, Fail)]
|
||||
pub enum ErrorKind {
|
||||
/// Authentication and/or authorization failed.
|
||||
///
|
||||
/// Wrapper for the `errSecAuthFailed` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecauthfailed>
|
||||
#[fail(display = "authentication failed")]
|
||||
AuthFailed,
|
||||
|
||||
/// Buffer is too small.
|
||||
///
|
||||
/// Wrapper for the `errSecBufferTooSmall` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecbuffertoosmall>
|
||||
#[fail(display = "buffer too small")]
|
||||
BufferTooSmall,
|
||||
|
||||
/// Certificate chain creation attempt failed.
|
||||
///
|
||||
/// Wrapper for the `errSecCreateChainFailed` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errseccreatechainfailed>
|
||||
#[fail(display = "certificate chain creation attempt failed")]
|
||||
CreateChainFailed,
|
||||
|
||||
/// Data too large for the given data type.
|
||||
///
|
||||
/// Wrapper for the `errSecDataTooLarge` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecdatatoolarge>
|
||||
#[fail(display = "data too large")]
|
||||
DataTooLarge,
|
||||
|
||||
/// Data is not available.
|
||||
///
|
||||
/// Wrapper for the `errSecDataNotAvailable` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecdatanotavailable>
|
||||
#[fail(display = "data not available")]
|
||||
DataNotAvailable,
|
||||
|
||||
/// Data cannot be modified.
|
||||
///
|
||||
/// Wrapper for the `errSecDataNotModifiable` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecdatanotmodifiable>
|
||||
#[fail(display = "data not modifiable")]
|
||||
DataNotModifiable,
|
||||
|
||||
/// Callback with the same name already exists.
|
||||
///
|
||||
/// Wrapper for the `errSecDuplicateCallback` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecduplicatecallback>
|
||||
#[fail(display = "duplicate callback")]
|
||||
DuplicateCallback,
|
||||
|
||||
/// Item already exists.
|
||||
///
|
||||
/// Wrapper for the `errSecDuplicateItem` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecduplicateitem>
|
||||
#[fail(display = "duplicate item")]
|
||||
DuplicateItem,
|
||||
|
||||
/// Keychain with the same name already exists.
|
||||
///
|
||||
/// Wrapper for the `errSecDuplicateKeychain` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecduplicatekeychain>
|
||||
#[fail(display = "duplicate keychain")]
|
||||
DuplicateKeychain,
|
||||
|
||||
/// System is in a dark wake state - user interface cannot be displayed.
|
||||
///
|
||||
/// Wrapper for the `errSecInDarkWake` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecindarkwake>
|
||||
#[fail(display = "in dark wake")]
|
||||
InDarkWake,
|
||||
|
||||
/// Security Server interactions not allowed in this context.
|
||||
///
|
||||
/// Wrapper for the `errSecInteractionNotAllowed` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecinteractionnotallowed>
|
||||
#[fail(display = "interaction not allowed")]
|
||||
InteractionNotAllowed,
|
||||
|
||||
/// User interaction required.
|
||||
///
|
||||
/// Wrapper for the `errSecInteractionRequired` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecinteractionrequired>
|
||||
#[fail(display = "user interaction required")]
|
||||
InteractionRequired,
|
||||
|
||||
/// Callback is invalid.
|
||||
///
|
||||
/// Wrapper for the `errSecInvalidCallback` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecinvalidcallback>
|
||||
#[fail(display = "invalid callback")]
|
||||
InvalidCallback,
|
||||
|
||||
/// Item reference is invalid.
|
||||
///
|
||||
/// Wrapper for the `errSecInvalidItemRef` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecinvaliditemref>
|
||||
#[fail(display = "invalid item ref")]
|
||||
InvalidItemRef,
|
||||
|
||||
/// Keychain is invalid.
|
||||
///
|
||||
/// Wrapper for the `errSecInvalidKeychain` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecinvalidkeychain>
|
||||
#[fail(display = "invalid keychain")]
|
||||
InvalidKeychain,
|
||||
|
||||
/// Specified preference domain is not valid.
|
||||
///
|
||||
/// Wrapper for the `errSecInvalidPrefsDomain` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecinvalidprefsdomain>
|
||||
#[fail(display = "invalid preference domain")]
|
||||
InvalidPrefsDomain,
|
||||
|
||||
/// Search reference is invalid.
|
||||
///
|
||||
/// Wrapper for the `errSecInvalidSearchRef` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecinvalidsearchref>
|
||||
#[fail(display = "search ref is invalid")]
|
||||
InvalidSearchRef,
|
||||
|
||||
/// Item could not be found.
|
||||
///
|
||||
/// Wrapper for the `errSecItemNotFound` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecitemnotfound>
|
||||
#[fail(display = "item not found")]
|
||||
ItemNotFound,
|
||||
|
||||
/// Invalid key size.
|
||||
///
|
||||
/// Wrapper for the `errSecKeySizeNotAllowed` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errseckeysizenotallowed>
|
||||
#[fail(display = "key size not allowed")]
|
||||
KeySizeNotAllowed,
|
||||
|
||||
/// Required entitlement for accessing the keychain is missing. This error
|
||||
/// occurs when attempting to access certain keychain functionality from an
|
||||
/// application which is either unsigned or missing a required entitlement.
|
||||
///
|
||||
/// Wrapper for the `errSecMissingEntitlement` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecmissingentitlement>
|
||||
#[fail(display = "missing application entitlement (errSecMissingEntitlement)")]
|
||||
MissingEntitlement,
|
||||
|
||||
/// Certificate module unavailable.
|
||||
///
|
||||
/// Wrapper for the `errSecNoCertificateModule` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecnocertificatemodule>
|
||||
#[fail(display = "no certificate module")]
|
||||
NoCertificateModule,
|
||||
|
||||
/// Default keychain does not exist.
|
||||
///
|
||||
/// Wrapper for the `errSecNoDefaultKeychain` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecnodefaultkeychain>
|
||||
#[fail(display = "no default keychain")]
|
||||
NoDefaultKeychain,
|
||||
|
||||
/// Policy module unavailable.
|
||||
///
|
||||
/// Wrapper for the `errSecNoPolicyModule` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecnopolicymodule>
|
||||
#[fail(display = "no policy module")]
|
||||
NoPolicyModule,
|
||||
|
||||
/// Storage module unavailable.
|
||||
///
|
||||
/// Wrapper for the `errSecNoStorageModule` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecnostoragemodule>
|
||||
#[fail(display = "no storage module")]
|
||||
NoStorageModule,
|
||||
|
||||
/// Specified attribute does not exist.
|
||||
///
|
||||
/// Wrapper for the `errSecNoSuchAttr` status code. See:;
|
||||
/// <https://developer.apple.com/documentation/security/errsecnosuchattr>
|
||||
#[fail(display = "no such attr")]
|
||||
NoSuchAttr,
|
||||
|
||||
/// Specified keychain item class does not exist.
|
||||
///
|
||||
/// Wrapper for the `errSecNoSuchClass` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecnosuchclass>
|
||||
#[fail(display = "no such class")]
|
||||
NoSuchClass,
|
||||
|
||||
/// Specified keychain does not exist.
|
||||
///
|
||||
/// Wrapper for the `errSecNoSuchKeychain` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecnosuchkeychain>
|
||||
#[fail(display = "no such keychain")]
|
||||
NoSuchKeychain,
|
||||
|
||||
/// Trust results not available.
|
||||
///
|
||||
/// Wrapper for the `errSecNotAvailable` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecnotavailable>
|
||||
#[fail(display = "not available")]
|
||||
NotAvailable,
|
||||
|
||||
/// Can't perform given action on read-only item.
|
||||
///
|
||||
/// Wrapper for the `errSecReadOnly` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecreadonly>
|
||||
#[fail(display = "read-only")]
|
||||
ReadOnly,
|
||||
|
||||
/// Can't perform action on read-only attribute
|
||||
///
|
||||
/// Wrapper for the `errSecReadOnlyAttr` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecreadonlyattr>
|
||||
#[fail(display = "read-only attr")]
|
||||
ReadOnlyAttr,
|
||||
|
||||
/// Invalid version.
|
||||
///
|
||||
/// Wrapper for the `errSecWrongSecVersion` status code. See:
|
||||
/// <https://developer.apple.com/documentation/security/errsecwrongversion>
|
||||
#[fail(display = "wrong version")]
|
||||
WrongSecVersion,
|
||||
|
||||
/// Input/output errors.
|
||||
///
|
||||
/// Wrapper for errno codes we know/commonly encounter.
|
||||
#[fail(display = "I/O error ({:?})", kind)]
|
||||
Io {
|
||||
/// `std::io::ErrorKind` value representing the I/O error
|
||||
kind: io::ErrorKind,
|
||||
},
|
||||
|
||||
/// Errors returned from CoreFoundation.
|
||||
///
|
||||
/// Codes correspond to the return value of the `CFErrorGetCode` function.
|
||||
///
|
||||
/// For more information, see:
|
||||
/// <https://developer.apple.com/documentation/corefoundation/1494656-cferrorgetcode?language=objc>
|
||||
#[fail(display = "Core Foundation error (code: {}, domain: {})", code, domain)]
|
||||
CFError {
|
||||
/// Code identifying this type of `CFError`.
|
||||
///
|
||||
/// See `CFErrorGetCode()` for more information:
|
||||
/// <https://developer.apple.com/documentation/corefoundation/1494656-cferrorgetcode>
|
||||
code: i64,
|
||||
|
||||
/// Domain associated with this error.
|
||||
///
|
||||
/// See `CFErrorGetDomain()` for more information:
|
||||
/// <https://developer.apple.com/documentation/corefoundation/1494657-cferrorgetdomain>
|
||||
domain: String,
|
||||
},
|
||||
|
||||
/// Unix errno values we receive from the underlying OS
|
||||
// TODO: create `ErrorKind` variants for ones we commonly encounter?
|
||||
#[fail(display = "POSIX error (errno: {})", code)]
|
||||
Errno {
|
||||
/// Raw errno value
|
||||
code: u8,
|
||||
},
|
||||
|
||||
/// `OSStatus` codes which we can't otherwise decode.
|
||||
#[fail(display = "unknown OS error (code: {})", code)]
|
||||
OSError {
|
||||
/// OS error code
|
||||
code: i64,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<CFErrorRef> for ErrorKind {
|
||||
fn from(error_ref: CFErrorRef) -> ErrorKind {
|
||||
ErrorKind::CFError {
|
||||
code: unsafe { CFErrorGetCode(error_ref) } as i64,
|
||||
domain: unsafe { CFString::wrap_under_get_rule(CFErrorGetDomain(error_ref)) }
|
||||
.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OSStatus> for ErrorKind {
|
||||
fn from(status: OSStatus) -> ErrorKind {
|
||||
match status {
|
||||
errSecAuthFailed => ErrorKind::AuthFailed,
|
||||
errSecBufferTooSmall => ErrorKind::BufferTooSmall,
|
||||
errSecCreateChainFailed => ErrorKind::CreateChainFailed,
|
||||
errSecDataTooLarge => ErrorKind::DataTooLarge,
|
||||
errSecDataNotAvailable => ErrorKind::DataNotAvailable,
|
||||
errSecDataNotModifiable => ErrorKind::DataNotModifiable,
|
||||
errSecDuplicateCallback => ErrorKind::DuplicateCallback,
|
||||
errSecDuplicateItem => ErrorKind::DuplicateItem,
|
||||
errSecDuplicateKeychain => ErrorKind::DuplicateKeychain,
|
||||
errSecInDarkWake => ErrorKind::InDarkWake,
|
||||
errSecInteractionNotAllowed => ErrorKind::InteractionNotAllowed,
|
||||
errSecInteractionRequired => ErrorKind::InteractionRequired,
|
||||
errSecInvalidCallback => ErrorKind::InvalidCallback,
|
||||
errSecInvalidItemRef => ErrorKind::InvalidItemRef,
|
||||
errSecInvalidKeychain => ErrorKind::InvalidKeychain,
|
||||
errSecInvalidPrefsDomain => ErrorKind::InvalidPrefsDomain,
|
||||
errSecInvalidSearchRef => ErrorKind::InvalidSearchRef,
|
||||
errSecItemNotFound => ErrorKind::ItemNotFound,
|
||||
errSecKeySizeNotAllowed => ErrorKind::KeySizeNotAllowed,
|
||||
errSecMissingEntitlement => ErrorKind::MissingEntitlement,
|
||||
errSecNoCertificateModule => ErrorKind::NoCertificateModule,
|
||||
errSecNoDefaultKeychain => ErrorKind::NoDefaultKeychain,
|
||||
errSecNoPolicyModule => ErrorKind::NoPolicyModule,
|
||||
errSecNoStorageModule => ErrorKind::NoStorageModule,
|
||||
errSecNoSuchAttr => ErrorKind::NoSuchAttr,
|
||||
errSecNoSuchClass => ErrorKind::NoSuchClass,
|
||||
errSecNoSuchKeychain => ErrorKind::NoSuchKeychain,
|
||||
errSecNotAvailable => ErrorKind::NotAvailable,
|
||||
errSecReadOnly => ErrorKind::ReadOnly,
|
||||
errSecReadOnlyAttr => ErrorKind::ReadOnlyAttr,
|
||||
errSecWrongSecVersion => ErrorKind::WrongSecVersion,
|
||||
errSecErrnoBase..=errSecErrnoLimit => match (status - errSecErrnoBase) as u8 {
|
||||
1 => ErrorKind::Io {
|
||||
kind: io::ErrorKind::PermissionDenied,
|
||||
},
|
||||
2 => ErrorKind::Io {
|
||||
kind: io::ErrorKind::NotFound,
|
||||
},
|
||||
17 => ErrorKind::Io {
|
||||
kind: io::ErrorKind::AlreadyExists,
|
||||
},
|
||||
code => ErrorKind::Errno { code },
|
||||
},
|
||||
_ => ErrorKind::OSError {
|
||||
code: i64::from(status),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
443
__security/keychain-services/keychain-services.rs/src/ffi.rs
Normal file
443
__security/keychain-services/keychain-services.rs/src/ffi.rs
Normal file
@@ -0,0 +1,443 @@
|
||||
use core_foundation::{
|
||||
base::{CFAllocatorRef, CFIndex, CFOptionFlags, CFTypeID, CFTypeRef, OSStatus, TCFType},
|
||||
data::CFDataRef,
|
||||
dictionary::CFDictionaryRef,
|
||||
error::CFErrorRef,
|
||||
string::{CFString, CFStringRef},
|
||||
};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fmt::{self, Debug},
|
||||
os::raw::{c_char, c_void},
|
||||
ptr, slice, str,
|
||||
};
|
||||
|
||||
/// Four character codes used as identifiers. See:
|
||||
/// <https://developer.apple.com/documentation/kernel/fourcharcode>
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
pub(crate) struct FourCharacterCode(u32);
|
||||
|
||||
impl FourCharacterCode {
|
||||
fn as_bytes(&self) -> &[u8; 4] {
|
||||
unsafe { &*(self as *const FourCharacterCode as *const [u8; 4]) }
|
||||
}
|
||||
|
||||
fn as_str(&self) -> &str {
|
||||
str::from_utf8(self.as_bytes()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for FourCharacterCode {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for FourCharacterCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "FourCharacterCode({})", self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for FourCharacterCode {
|
||||
fn from(num: u32) -> FourCharacterCode {
|
||||
FourCharacterCode(num)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 4]> for FourCharacterCode {
|
||||
fn from(bytes: [u8; 4]) -> FourCharacterCode {
|
||||
Self::from(&bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8; 4]> for FourCharacterCode {
|
||||
fn from(bytes: &[u8; 4]) -> FourCharacterCode {
|
||||
let mut result: u32 = 0;
|
||||
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(bytes.as_ptr(), (&mut result as *mut u32) as *mut u8, 4);
|
||||
}
|
||||
|
||||
FourCharacterCode(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CFStringRef> for FourCharacterCode {
|
||||
fn from(string_ref: CFStringRef) -> FourCharacterCode {
|
||||
Self::from(&unsafe { CFString::wrap_under_get_rule(string_ref) })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a CFString> for FourCharacterCode {
|
||||
fn from(string: &'a CFString) -> FourCharacterCode {
|
||||
let string = Cow::from(string);
|
||||
assert_eq!(string.as_bytes().len(), 4);
|
||||
|
||||
let mut code = [0u8; 4];
|
||||
code.copy_from_slice(string.as_bytes());
|
||||
code.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Reference to an access control policy.
|
||||
///
|
||||
/// See `SecAccessControlRef` documentation:
|
||||
/// <https://developer.apple.com/documentation/security/secaccesscontrolref>
|
||||
pub(crate) type AccessControlRef = CFTypeRef;
|
||||
|
||||
/// Reference to a `Key`
|
||||
///
|
||||
/// See `SecKeyRef` documentation:
|
||||
/// <https://developer.apple.com/documentation/security/seckeyref>
|
||||
pub(crate) type KeyRef = CFTypeRef;
|
||||
|
||||
/// Reference to a `Keychain`
|
||||
///
|
||||
/// See `SecKeychainRef` documentation:
|
||||
/// <https://developer.apple.com/documentation/security/seckeychainref>
|
||||
pub(crate) type KeychainRef = CFTypeRef;
|
||||
|
||||
/// Reference to a `keychain::Item`
|
||||
///
|
||||
/// See `SecKeychainItemRef` documentation:
|
||||
/// <https://developer.apple.com/documentation/security/seckeychainitemref>
|
||||
pub(crate) type ItemRef = CFTypeRef;
|
||||
|
||||
/// Attribute type codes.
|
||||
///
|
||||
/// Wrapper for `SecKeychainAttrType`. See:
|
||||
/// <https://developer.apple.com/documentation/security/seckeychainattrtype>
|
||||
pub(crate) type SecKeychainAttrType = FourCharacterCode;
|
||||
|
||||
/// Individual keychain attribute.
|
||||
///
|
||||
/// Wrapper for the `SecKeychainAttribute` struct. See:
|
||||
/// <https://developer.apple.com/documentation/security/seckeychainattribute>
|
||||
#[repr(C)]
|
||||
pub(super) struct SecKeychainAttribute {
|
||||
tag: SecKeychainAttrType,
|
||||
length: u32,
|
||||
data: *mut u8,
|
||||
}
|
||||
|
||||
impl SecKeychainAttribute {
|
||||
/// Get the `FourCharacterCode` tag identifying this attribute's type
|
||||
pub(crate) fn tag(&self) -> SecKeychainAttrType {
|
||||
self.tag
|
||||
}
|
||||
|
||||
/// Get the data associated with this attribute as a byte slice.
|
||||
pub(crate) fn data(&self) -> Option<&[u8]> {
|
||||
if self.data.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { slice::from_raw_parts(self.data, self.length as usize) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// List of attributes (as returned from e.g. `SecKeychainItemCopyContent`).
|
||||
///
|
||||
/// NOTE: This type does not implement `Drop` as there are various ways it can
|
||||
/// be allocated/deallocated. The caller must take care to free it!
|
||||
///
|
||||
/// Wrapper for the `SecKeychainAttributeList` struct. See:
|
||||
/// <https://developer.apple.com/documentation/security/seckeychainattributelist>
|
||||
#[repr(C)]
|
||||
pub(super) struct SecKeychainAttributeList {
|
||||
count: u32,
|
||||
attr: *mut SecKeychainAttribute,
|
||||
}
|
||||
|
||||
impl SecKeychainAttributeList {
|
||||
/// Get an iterator over this attribute list.
|
||||
pub(crate) fn iter(&self) -> slice::Iter<SecKeychainAttribute> {
|
||||
self.as_slice().iter()
|
||||
}
|
||||
|
||||
/// Get a slice of `Attribute` values
|
||||
pub(crate) fn as_slice(&self) -> &[SecKeychainAttribute] {
|
||||
unsafe { slice::from_raw_parts(self.attr, self.count as usize) }
|
||||
}
|
||||
}
|
||||
|
||||
#[link(name = "Security", kind = "framework")]
|
||||
extern "C" {
|
||||
pub(crate) static kSecAttrAccessControl: CFStringRef;
|
||||
pub(crate) static kSecAttrAccessible: CFStringRef;
|
||||
pub(crate) static kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly: CFStringRef;
|
||||
pub(crate) static kSecAttrAccessibleWhenUnlockedThisDeviceOnly: CFStringRef;
|
||||
pub(crate) static kSecAttrAccessibleWhenUnlocked: CFStringRef;
|
||||
pub(crate) static kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly: CFStringRef;
|
||||
pub(crate) static kSecAttrAccessibleAfterFirstUnlock: CFStringRef;
|
||||
pub(crate) static kSecAttrAccessibleAlwaysThisDeviceOnly: CFStringRef;
|
||||
pub(crate) static kSecAttrAccessibleAlways: CFStringRef;
|
||||
pub(crate) static kSecAttrAccount: CFStringRef;
|
||||
pub(crate) static kSecAttrApplicationLabel: CFStringRef;
|
||||
pub(crate) static kSecAttrApplicationTag: CFStringRef;
|
||||
pub(crate) static kSecAttrCanEncrypt: CFStringRef;
|
||||
pub(crate) static kSecAttrCanDecrypt: CFStringRef;
|
||||
pub(crate) static kSecAttrCanDerive: CFStringRef;
|
||||
pub(crate) static kSecAttrCanSign: CFStringRef;
|
||||
pub(crate) static kSecAttrCanVerify: CFStringRef;
|
||||
pub(crate) static kSecAttrCanWrap: CFStringRef;
|
||||
pub(crate) static kSecAttrCanUnwrap: CFStringRef;
|
||||
pub(crate) static kSecAttrIsExtractable: CFStringRef;
|
||||
pub(crate) static kSecAttrIsPermanent: CFStringRef;
|
||||
pub(crate) static kSecAttrIsSensitive: CFStringRef;
|
||||
pub(crate) static kSecAttrKeyClass: CFStringRef;
|
||||
pub(crate) static kSecAttrKeyClassPublic: CFStringRef;
|
||||
pub(crate) static kSecAttrKeyClassPrivate: CFStringRef;
|
||||
pub(crate) static kSecAttrKeyClassSymmetric: CFStringRef;
|
||||
pub(crate) static kSecAttrKeyType: CFStringRef;
|
||||
pub(crate) static kSecAttrKeyTypeAES: CFStringRef;
|
||||
pub(crate) static kSecAttrKeyTypeRSA: CFStringRef;
|
||||
pub(crate) static kSecAttrKeyTypeECSECPrimeRandom: CFStringRef;
|
||||
pub(crate) static kSecAttrKeySizeInBits: CFStringRef;
|
||||
pub(crate) static kSecAttrLabel: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocol: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolFTP: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolFTPAccount: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolHTTP: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolIRC: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolNNTP: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolPOP3: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolSMTP: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolSOCKS: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolIMAP: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolLDAP: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolAppleTalk: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolAFP: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolTelnet: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolSSH: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolFTPS: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolHTTPS: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolHTTPProxy: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolHTTPSProxy: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolFTPProxy: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolSMB: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolRTSP: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolRTSPProxy: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolDAAP: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolEPPC: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolIPP: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolNNTPS: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolLDAPS: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolTelnetS: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolIMAPS: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolIRCS: CFStringRef;
|
||||
pub(crate) static kSecAttrProtocolPOP3S: CFStringRef;
|
||||
pub(crate) static kSecAttrServer: CFStringRef;
|
||||
pub(crate) static kSecAttrService: CFStringRef;
|
||||
pub(crate) static kSecAttrSynchronizable: CFStringRef;
|
||||
pub(crate) static kSecAttrTokenID: CFStringRef;
|
||||
pub(crate) static kSecAttrTokenIDSecureEnclave: CFStringRef;
|
||||
pub(crate) static kSecClass: CFStringRef;
|
||||
pub(crate) static kSecClassGenericPassword: CFStringRef;
|
||||
pub(crate) static kSecClassInternetPassword: CFStringRef;
|
||||
pub(crate) static kSecClassCertificate: CFStringRef;
|
||||
pub(crate) static kSecClassKey: CFStringRef;
|
||||
pub(crate) static kSecClassIdentity: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardX963SHA1AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardX963SHA224AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardX963SHA256AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardX963SHA384AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardX963SHA512AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA224AESGCM:
|
||||
CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA256AESGCM:
|
||||
CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA384AESGCM:
|
||||
CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA512AESGCM:
|
||||
CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA224AESGCM:
|
||||
CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA256AESGCM:
|
||||
CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA384AESGCM:
|
||||
CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA512AESGCM:
|
||||
CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorX963SHA1AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorX963SHA224AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorX963SHA256AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorX963SHA384AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECIESEncryptionCofactorX963SHA512AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDSASignatureRFC4754: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDSASignatureDigestX962: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDSASignatureDigestX962SHA1: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDSASignatureDigestX962SHA224: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDSASignatureDigestX962SHA256: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDSASignatureDigestX962SHA384: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDSASignatureDigestX962SHA512: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDSASignatureMessageX962SHA1: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDSASignatureMessageX962SHA224: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDSASignatureMessageX962SHA256: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDSASignatureMessageX962SHA384: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDSASignatureMessageX962SHA512: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeCofactor: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeStandard: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA1: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA224: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA256: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA384: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA512: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA224: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA256: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA384: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA512: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSAEncryptionRaw: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSAEncryptionPKCS1: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA1: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA224: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA256: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA384: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA512: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA1AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA224AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA256AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA384AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureRaw: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPKCS1v15Raw: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA224: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA384: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA512: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA1: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA224: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA384: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA512: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPSSSHA1: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPSSSHA224: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPSSSHA256: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPSSSHA384: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureDigestPSSSHA512: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePSSSHA1: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePSSSHA224: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePSSSHA256: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePSSSHA384: CFStringRef;
|
||||
pub(crate) static kSecKeyAlgorithmRSASignatureMessagePSSSHA512: CFStringRef;
|
||||
pub(crate) static kSecKeyAlwaysSensitive: CFStringRef;
|
||||
pub(crate) static kSecKeyDecrypt: CFStringRef;
|
||||
pub(crate) static kSecKeyDerive: CFStringRef;
|
||||
pub(crate) static kSecKeyEffectiveKeySize: CFStringRef;
|
||||
pub(crate) static kSecKeyEncrypt: CFStringRef;
|
||||
pub(crate) static kSecKeyEndDate: CFStringRef;
|
||||
pub(crate) static kSecKeyExtractable: CFStringRef;
|
||||
pub(crate) static kSecKeyKeySizeInBits: CFStringRef;
|
||||
pub(crate) static kSecKeyKeyType: CFStringRef;
|
||||
pub(crate) static kSecKeyModifiable: CFStringRef;
|
||||
pub(crate) static kSecKeyNeverExtractable: CFStringRef;
|
||||
pub(crate) static kSecKeyPermanent: CFStringRef;
|
||||
pub(crate) static kSecKeyPrivate: CFStringRef;
|
||||
pub(crate) static kSecKeySensitive: CFStringRef;
|
||||
pub(crate) static kSecKeySign: CFStringRef;
|
||||
pub(crate) static kSecKeyStartDate: CFStringRef;
|
||||
pub(crate) static kSecKeyUnwrap: CFStringRef;
|
||||
pub(crate) static kSecKeyVerify: CFStringRef;
|
||||
pub(crate) static kSecKeyWrap: CFStringRef;
|
||||
pub(crate) static kSecMatchLimit: CFStringRef;
|
||||
pub(crate) static kSecMatchLimitOne: CFStringRef;
|
||||
pub(crate) static kSecMatchLimitAll: CFStringRef;
|
||||
pub(crate) static kSecPrivateKeyAttrs: CFStringRef;
|
||||
pub(crate) static kSecReturnRef: CFStringRef;
|
||||
pub(crate) static kSecUseKeychain: CFStringRef;
|
||||
pub(crate) static kSecUseOperationPrompt: CFStringRef;
|
||||
pub(crate) static kSecValueData: CFStringRef;
|
||||
|
||||
pub(crate) fn SecAccessControlCreateWithFlags(
|
||||
allocator: CFAllocatorRef,
|
||||
protection: CFTypeRef,
|
||||
flags: CFOptionFlags,
|
||||
error: *mut CFErrorRef,
|
||||
) -> CFTypeRef;
|
||||
pub(crate) fn SecAccessControlGetTypeID() -> CFTypeID;
|
||||
pub(crate) fn SecCopyErrorMessageString(
|
||||
status: OSStatus,
|
||||
reserved: *const c_void,
|
||||
) -> CFStringRef;
|
||||
pub(crate) fn SecItemAdd(attributes: CFDictionaryRef, result: *mut CFTypeRef) -> OSStatus;
|
||||
pub(crate) fn SecItemDelete(attributes: CFDictionaryRef) -> OSStatus;
|
||||
pub(crate) fn SecItemCopyMatching(query: CFDictionaryRef, result: *mut CFTypeRef) -> OSStatus;
|
||||
pub(crate) fn SecKeyCopyAttributes(key: KeyRef) -> CFDictionaryRef;
|
||||
pub(crate) fn SecKeyCreateWithData(
|
||||
keyData: CFDataRef,
|
||||
attributes: CFDictionaryRef,
|
||||
error: *mut CFErrorRef,
|
||||
) -> KeyRef;
|
||||
pub(crate) fn SecKeyCopyExternalRepresentation(
|
||||
key: KeyRef,
|
||||
error: *mut CFErrorRef,
|
||||
) -> CFDataRef;
|
||||
pub(crate) fn SecKeyCreateSignature(
|
||||
key: KeyRef,
|
||||
algorithm: CFTypeRef,
|
||||
data_to_sign: CFDataRef,
|
||||
error: *mut CFErrorRef,
|
||||
) -> CFDataRef;
|
||||
pub(crate) fn SecKeyVerifySignature(
|
||||
key: KeyRef,
|
||||
algorithm: CFTypeRef,
|
||||
data_to_verify: CFDataRef,
|
||||
signature: CFDataRef,
|
||||
error: *mut CFErrorRef,
|
||||
) -> u8;
|
||||
pub(crate) fn SecKeyCreateEncryptedData(
|
||||
key: KeyRef,
|
||||
algorithm: CFTypeRef,
|
||||
plaintext: CFDataRef,
|
||||
error: *mut CFErrorRef,
|
||||
) -> CFDataRef;
|
||||
pub(crate) fn SecKeyCreateDecryptedData(
|
||||
key: KeyRef,
|
||||
algorithm: CFTypeRef,
|
||||
ciphertext: CFDataRef,
|
||||
error: *mut CFErrorRef,
|
||||
) -> CFDataRef;
|
||||
pub(crate) fn SecKeyGeneratePair(
|
||||
parameters: CFDictionaryRef,
|
||||
publicKey: *mut KeyRef,
|
||||
privateKey: *mut KeyRef,
|
||||
) -> OSStatus;
|
||||
pub(crate) fn SecKeyCreateRandomKey(
|
||||
parameters: CFDictionaryRef,
|
||||
error: *mut CFErrorRef,
|
||||
) -> KeyRef;
|
||||
pub(crate) fn SecKeyIsAlgorithmSupported(
|
||||
key: KeyRef,
|
||||
operationType: CFIndex,
|
||||
algorithm: CFTypeRef,
|
||||
) -> u8;
|
||||
pub(crate) fn SecKeyCopyPublicKey(privatekey: KeyRef) -> KeyRef;
|
||||
pub(crate) fn SecKeyGetTypeID() -> CFTypeID;
|
||||
pub(crate) fn SecKeychainCopyDefault(keychain: *mut KeychainRef) -> OSStatus;
|
||||
pub(crate) fn SecKeychainCreate(
|
||||
path_name: *const c_char,
|
||||
password_length: u32,
|
||||
password: *const c_char,
|
||||
prompt_user: bool,
|
||||
initial_access: CFTypeRef,
|
||||
keychain: *mut KeychainRef,
|
||||
) -> OSStatus;
|
||||
pub(crate) fn SecKeychainDelete(keychain_or_array: KeychainRef) -> OSStatus;
|
||||
pub(crate) fn SecKeychainGetTypeID() -> CFTypeID;
|
||||
pub(crate) fn SecKeychainItemGetTypeID() -> CFTypeID;
|
||||
pub(crate) fn SecKeychainItemCopyContent(
|
||||
item_ref: ItemRef,
|
||||
itemClass: *mut FourCharacterCode,
|
||||
attr_list: *mut SecKeychainAttributeList,
|
||||
data_length: *mut u32,
|
||||
data_out: *mut *mut c_void,
|
||||
) -> OSStatus;
|
||||
pub(crate) fn SecKeychainItemFreeContent(
|
||||
attr_list: *mut SecKeychainAttributeList,
|
||||
data: *mut c_void,
|
||||
) -> OSStatus;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
use crate::ffi::*;
|
||||
use core_foundation::{base::TCFType, string::CFString};
|
||||
|
||||
/// Classes of keychain items supported by Keychain Services
|
||||
/// (not to be confused with `SecAttrClass` or `SecType`)
|
||||
///
|
||||
/// Wrapper for the `kSecClass` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecclass>
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Class {
|
||||
/// Generic password items.
|
||||
///
|
||||
/// Wrapper for the `kSecClassGenericPassword` attribute value. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecclassgenericpassword>
|
||||
GenericPassword,
|
||||
|
||||
/// Internet passwords.
|
||||
///
|
||||
/// Wrapper for the `kSecClassInternetPassword` attribute value. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecclassinternetpassword>
|
||||
InternetPassword,
|
||||
|
||||
/// Certificates.
|
||||
///
|
||||
/// Wrapper for the `kSecClassCertificate` attribute value. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecclasscertificate>
|
||||
Certificate,
|
||||
|
||||
/// Cryptographic keys.
|
||||
///
|
||||
/// Wrapper for the `kSecClassKey` attribute value. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecclasskey>
|
||||
Key,
|
||||
|
||||
/// Identities.
|
||||
///
|
||||
/// Wrapper for the `kSecClassIdentity` attribute value. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecclassidentity>
|
||||
Identity,
|
||||
}
|
||||
|
||||
impl Class {
|
||||
/// Attempt to look up an attribute kind by its `FourCharacterCode`.
|
||||
// TODO: cache `FourCharacterCodes`? e.g. as `lazy_static`
|
||||
pub(crate) fn from_tag(tag: FourCharacterCode) -> Option<Self> {
|
||||
let result = unsafe {
|
||||
if tag == FourCharacterCode::from(kSecClassGenericPassword) {
|
||||
Class::GenericPassword
|
||||
} else if tag == FourCharacterCode::from(kSecClassInternetPassword) {
|
||||
Class::InternetPassword
|
||||
} else if tag == FourCharacterCode::from(kSecClassCertificate) {
|
||||
Class::Certificate
|
||||
} else if tag == FourCharacterCode::from(kSecClassKey) {
|
||||
Class::Key
|
||||
} else if tag == FourCharacterCode::from(kSecClassIdentity) {
|
||||
Class::Identity
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some(result)
|
||||
}
|
||||
/// Get `CFString` containing the `kSecClass` dictionary value for
|
||||
/// this particular `SecClass`.
|
||||
pub fn as_CFString(self) -> CFString {
|
||||
unsafe {
|
||||
CFString::wrap_under_get_rule(match self {
|
||||
Class::GenericPassword => kSecClassGenericPassword,
|
||||
Class::InternetPassword => kSecClassInternetPassword,
|
||||
Class::Certificate => kSecClassCertificate,
|
||||
Class::Key => kSecClassKey,
|
||||
Class::Identity => kSecClassIdentity,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FourCharacterCode> for Class {
|
||||
fn from(tag: FourCharacterCode) -> Self {
|
||||
Self::from_tag(tag).unwrap_or_else(|| panic!("invalid SecItemClass tag: {:?}", tag))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
//! Items stored in a keychain (e.g. certificates, keys, passwords)
|
||||
|
||||
mod class;
|
||||
mod password;
|
||||
mod query;
|
||||
|
||||
pub use self::{class::*, password::*, query::*};
|
||||
use crate::{attr::AttrKind, error::*, ffi::*};
|
||||
use core_foundation::base::TCFType;
|
||||
use std::{mem, os::raw::c_void, ptr, slice};
|
||||
|
||||
declare_TCFType! {
|
||||
/// Items stored in the keychain.
|
||||
///
|
||||
/// Wrapper for the `SecKeychainItem`/`SecKeychainItemRef` types:
|
||||
/// <https://developer.apple.com/documentation/security/seckeychainitemref>
|
||||
Item, ItemRef
|
||||
}
|
||||
|
||||
impl_TCFType!(Item, ItemRef, SecKeychainItemGetTypeID);
|
||||
|
||||
impl Item {
|
||||
/// Get the class of this item
|
||||
pub fn class(&self) -> Class {
|
||||
let mut result = FourCharacterCode::from(b"NULL");
|
||||
|
||||
Error::maybe_from_OSStatus(unsafe {
|
||||
SecKeychainItemCopyContent(
|
||||
self.as_concrete_TypeRef(),
|
||||
&mut result,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
result.into()
|
||||
}
|
||||
|
||||
/// Get the raw data associated with this keychain item
|
||||
pub(crate) fn data(&self) -> Result<Vec<u8>, Error> {
|
||||
let result_ptr: *mut u8 = ptr::null_mut();
|
||||
let mut length = 0;
|
||||
|
||||
let status = unsafe {
|
||||
SecKeychainItemCopyContent(
|
||||
self.as_concrete_TypeRef(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
&mut length,
|
||||
&mut (result_ptr as *mut c_void),
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(e) = Error::maybe_from_OSStatus(status) {
|
||||
Err(e)
|
||||
} else if result_ptr.is_null() {
|
||||
Err(Error::new(
|
||||
ErrorKind::MissingEntitlement,
|
||||
"SecKeychainItemCopyContent refused to return data",
|
||||
))
|
||||
} else {
|
||||
// Copy the data into a vector we've allocated
|
||||
let result = Vec::from(unsafe { slice::from_raw_parts(result_ptr, length as usize) });
|
||||
|
||||
// Free the original data
|
||||
Error::maybe_from_OSStatus(unsafe {
|
||||
SecKeychainItemFreeContent(ptr::null_mut(), result_ptr as *mut c_void)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an attribute of this item as a `String`.
|
||||
// TODO: handle attribute types other than `String`?
|
||||
pub(crate) fn attribute(&self, attr_kind: AttrKind) -> Result<String, Error> {
|
||||
let mut attrs = unsafe { self.attributes() }?;
|
||||
|
||||
let result = attrs
|
||||
.iter()
|
||||
.find(|attr| {
|
||||
if let Some(kind) = AttrKind::from_tag(attr.tag()) {
|
||||
kind == attr_kind
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.map(|attr| String::from_utf8(attr.data().unwrap().into()).unwrap());
|
||||
|
||||
Error::maybe_from_OSStatus(unsafe {
|
||||
SecKeychainItemFreeContent(&mut attrs, ptr::null_mut())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
result.ok_or_else(|| {
|
||||
Error::new(
|
||||
ErrorKind::NoSuchAttr,
|
||||
&format!("missing attribute {:?}", attr_kind),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the attributes of a keychain item. Note that this does not handle
|
||||
/// deallocating the attribute list so the caller must take care to do so.
|
||||
unsafe fn attributes(&self) -> Result<SecKeychainAttributeList, Error> {
|
||||
let mut result: SecKeychainAttributeList = mem::zeroed();
|
||||
|
||||
let status = SecKeychainItemCopyContent(
|
||||
self.as_concrete_TypeRef(),
|
||||
ptr::null_mut(),
|
||||
&mut result,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
);
|
||||
|
||||
if let Some(e) = Error::maybe_from_OSStatus(status) {
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
use crate::{attr::*, dictionary::DictionaryBuilder, error::Error, ffi::*, keychain::*};
|
||||
use std::str;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// Generic passwords
|
||||
pub struct GenericPassword(Item);
|
||||
|
||||
impl GenericPassword {
|
||||
/// Create a new generic password item in the given keychain.
|
||||
pub fn create(
|
||||
keychain: &Keychain,
|
||||
service: &str,
|
||||
account: &str,
|
||||
password: &str,
|
||||
) -> Result<Self, Error> {
|
||||
let mut attrs = DictionaryBuilder::new();
|
||||
attrs.add_class(item::Class::GenericPassword);
|
||||
attrs.add_string(AttrKind::Service, service);
|
||||
attrs.add_string(AttrKind::Account, account);
|
||||
attrs.add_string(unsafe { kSecValueData }, password);
|
||||
|
||||
Ok(GenericPassword(keychain.add_item(attrs)?))
|
||||
}
|
||||
|
||||
/// Find a generic password in the given keychain.
|
||||
pub fn find(keychain: &Keychain, service: &str, account: &str) -> Result<Self, Error> {
|
||||
let mut attrs = DictionaryBuilder::new();
|
||||
attrs.add_class(item::Class::GenericPassword);
|
||||
attrs.add_string(AttrKind::Service, service);
|
||||
attrs.add_string(AttrKind::Account, account);
|
||||
|
||||
Ok(GenericPassword(keychain.find_item(attrs)?))
|
||||
}
|
||||
|
||||
/// Get the account this password is associated with
|
||||
pub fn account(&self) -> Result<String, Error> {
|
||||
self.0.attribute(AttrKind::Account)
|
||||
}
|
||||
|
||||
/// Get the service this password is associated with
|
||||
pub fn service(&self) -> Result<String, Error> {
|
||||
self.0.attribute(AttrKind::Service)
|
||||
}
|
||||
|
||||
/// Get the raw password value
|
||||
pub fn password(&self) -> Result<PasswordData, Error> {
|
||||
Ok(PasswordData(self.0.data()?))
|
||||
}
|
||||
}
|
||||
|
||||
/// Internet passwords
|
||||
pub struct InternetPassword(Item);
|
||||
|
||||
impl InternetPassword {
|
||||
/// Create a new Internet password item in the given keychain.
|
||||
pub fn create(
|
||||
keychain: &Keychain,
|
||||
server: &str,
|
||||
account: &str,
|
||||
password: &str,
|
||||
) -> Result<Self, Error> {
|
||||
let mut attrs = DictionaryBuilder::new();
|
||||
attrs.add_class(item::Class::InternetPassword);
|
||||
attrs.add_string(AttrKind::Server, server);
|
||||
attrs.add_string(AttrKind::Account, account);
|
||||
attrs.add_string(unsafe { kSecValueData }, password);
|
||||
|
||||
Ok(InternetPassword(keychain.add_item(attrs)?))
|
||||
}
|
||||
|
||||
/// Find an Internet password in the given keychain.
|
||||
pub fn find(
|
||||
keychain: &Keychain,
|
||||
server: &str,
|
||||
account: &str,
|
||||
protocol: Option<AttrProtocol>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut attrs = DictionaryBuilder::new();
|
||||
attrs.add_class(item::Class::InternetPassword);
|
||||
attrs.add_string(AttrKind::Server, server);
|
||||
attrs.add_string(AttrKind::Account, account);
|
||||
|
||||
if let Some(proto) = protocol {
|
||||
attrs.add_attr(&proto);
|
||||
}
|
||||
|
||||
Ok(InternetPassword(keychain.find_item(attrs)?))
|
||||
}
|
||||
|
||||
/// Get the account this password is associated with
|
||||
pub fn account(&self) -> Result<String, Error> {
|
||||
self.0.attribute(AttrKind::Account)
|
||||
}
|
||||
|
||||
/// Get the service this password is associated with
|
||||
pub fn server(&self) -> Result<String, Error> {
|
||||
self.0.attribute(AttrKind::Server)
|
||||
}
|
||||
|
||||
/// Get the raw password value
|
||||
pub fn password(&self) -> Result<PasswordData, Error> {
|
||||
Ok(PasswordData(self.0.data()?))
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around password data that ensures it is cleared from memory after
|
||||
/// being used.
|
||||
#[derive(Clone)]
|
||||
pub struct PasswordData(Vec<u8>);
|
||||
|
||||
impl PasswordData {
|
||||
/// Borrow the password as a byte slice
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
|
||||
/// Borrow the password as a `str` (if valid UTF-8), panicking if the
|
||||
/// UTF-8 conversion fails.
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.try_as_str().expect("password contained invalid UTF-8")
|
||||
}
|
||||
|
||||
/// Borrow the password as a `str` (if valid UTF-8), returning a
|
||||
/// `Utf8Error` if the UTF-8 conversion failed.
|
||||
pub fn try_as_str(&self) -> Result<&str, str::Utf8Error> {
|
||||
str::from_utf8(self.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for PasswordData {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PasswordData {
|
||||
fn drop(&mut self) {
|
||||
self.0.zeroize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
//! Query the keychain, looking for particular items
|
||||
|
||||
use crate::{attr::*, dictionary::DictionaryBuilder, ffi::*};
|
||||
use core_foundation::{
|
||||
base::{CFType, TCFType},
|
||||
number::CFNumber,
|
||||
string::CFString,
|
||||
};
|
||||
|
||||
/// Limit the number of matched items to one or an unlimited number.
|
||||
///
|
||||
/// Wrapper for the `kSecMatchLimit` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecmatchlimit>
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum MatchLimit {
|
||||
/// Match exactly one item.
|
||||
///
|
||||
/// Wrapper for the `kSecMatchLimitOne` attribute value. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecmatchlimitone>
|
||||
One,
|
||||
|
||||
/// Match the specified number of items.
|
||||
///
|
||||
/// Equivalent to passing a `CFNumberRef` as the value for
|
||||
/// `kSecMatchLimit`. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecmatchlimit>
|
||||
Number(usize),
|
||||
|
||||
/// Match an unlimited number of items.
|
||||
///
|
||||
/// Wrapper for the `kSecMatchLimitAll` attribute value. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecmatchlimitall>
|
||||
All,
|
||||
}
|
||||
|
||||
impl MatchLimit {
|
||||
/// Get `CFType` containing the `kSecMatchLimit` dictionary value for
|
||||
/// this particular `SecMatchLimit`.
|
||||
pub fn as_CFType(self) -> CFType {
|
||||
match self {
|
||||
MatchLimit::One => {
|
||||
unsafe { CFString::wrap_under_get_rule(kSecMatchLimitOne) }.as_CFType()
|
||||
}
|
||||
MatchLimit::Number(n) => CFNumber::from(n as i64).as_CFType(),
|
||||
MatchLimit::All => {
|
||||
unsafe { CFString::wrap_under_get_rule(kSecMatchLimitAll) }.as_CFType()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Query builder for locating particular keychain items.
|
||||
///
|
||||
/// For more information, see "Search Attribute Keys and Values":
|
||||
/// <https://developer.apple.com/documentation/security/keychain_services/keychain_items/search_attribute_keys_and_values>
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Query(DictionaryBuilder);
|
||||
|
||||
impl Query {
|
||||
/// Create a new keychain item query builder
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Query for keychain items with the provided `SecAttrApplicationLabel`
|
||||
/// (not to be confused with a `SecAttrLabel`), i.e. the hash/fingerprint
|
||||
/// of a public key in the keychain.
|
||||
///
|
||||
/// Both the private and public key in a keypair have a
|
||||
/// `SecAttrApplicationLabel` set to the public key's fingerprint.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrApplicationLabel` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrlabel>
|
||||
pub fn application_label<L: Into<AttrApplicationLabel>>(mut self, label: L) -> Self {
|
||||
self.0.add_attr(&label.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Query for keychain items with the provided `SecAttrApplicationTag`.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrApplicationTag` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrapplicationtag>
|
||||
pub fn application_tag<T>(mut self, tag: T) -> Self
|
||||
where
|
||||
T: Into<AttrApplicationTag>,
|
||||
{
|
||||
self.0.add_attr(&tag.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Query for keys with the given `SecAttrKeyClass`.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrKeyClass` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrkeyclass>
|
||||
pub fn key_class(mut self, key_class: AttrKeyClass) -> Self {
|
||||
self.0.add_attr(&key_class);
|
||||
self
|
||||
}
|
||||
|
||||
/// Query for keys with the given `SecAttrKeyType`.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrKeyType` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrkeytype>
|
||||
pub fn key_type(mut self, key_type: AttrKeyType) -> Self {
|
||||
self.0.add_attr(&key_type);
|
||||
self
|
||||
}
|
||||
|
||||
/// Query for a particular (human-meaningful) label on keys
|
||||
///
|
||||
/// Wrapper for the `kSecAttrLabel` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrlabel>
|
||||
pub fn label<L: Into<AttrLabel>>(mut self, label: L) -> Self {
|
||||
self.0.add_attr(&label.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Query for keys which are or not permanent members of the default keychain.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrIsPermanent` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrispermanent>
|
||||
pub fn permanent(mut self, value: bool) -> Self {
|
||||
self.0.add_boolean(AttrKind::Permanent, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Query for keys which are or are not synchronizable.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrSynchronizable` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrsynchronizable>
|
||||
pub fn synchronizable(mut self, value: bool) -> Self {
|
||||
self.0.add_boolean(AttrKind::Synchronizable, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Query for keys which are or are not sensitive.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrIsSensitive` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrissensitive>
|
||||
pub fn sensitive(mut self, value: bool) -> Self {
|
||||
self.0.add_boolean(AttrKind::Sensitive, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Query for keys stored in an external token i.e. the
|
||||
/// Secure Enclave Processor (SEP).
|
||||
///
|
||||
/// Wrapper for the `kSecAttrTokenID` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrtokenid>
|
||||
pub fn token_id(mut self, value: AttrTokenId) -> Self {
|
||||
self.0.add_attr(&value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Prompt the user with the given custom message when using keys returned
|
||||
/// from this query.
|
||||
///
|
||||
/// Wrapper for the `kSecUseOperationPrompt`. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecuseoperationprompt>
|
||||
pub fn use_operation_prompt(mut self, value: &str) -> Self {
|
||||
self.0.add_string(unsafe { kSecUseOperationPrompt }, value);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Query> for DictionaryBuilder {
|
||||
fn from(params: Query) -> DictionaryBuilder {
|
||||
params.0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
use crate::ffi::*;
|
||||
use core_foundation::{base::TCFType, string::CFString};
|
||||
|
||||
/// Cryptographic algorithms for use with keys stored in the keychain.
|
||||
///
|
||||
/// Wrapper for `SecKeyAlgorithm`. See:
|
||||
/// <https://developer.apple.com/documentation/security/seckeyalgorithm>
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum KeyAlgorithm {
|
||||
/// Elliptic Curve Encryption Standard X963
|
||||
ECIESEncryptionStandardX963SHA1AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Standard X963
|
||||
ECIESEncryptionStandardX963SHA224AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Standard X963
|
||||
ECIESEncryptionStandardX963SHA256AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Standard X963
|
||||
ECIESEncryptionStandardX963SHA384AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Standard X963
|
||||
ECIESEncryptionStandardX963SHA512AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Standard Variable IVX963
|
||||
ECIESEncryptionStandardVariableIVX963SHA224AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Standard Variable IVX963
|
||||
ECIESEncryptionStandardVariableIVX963SHA256AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Standard Variable IVX963
|
||||
ECIESEncryptionStandardVariableIVX963SHA384AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Standard Variable IVX963
|
||||
ECIESEncryptionStandardVariableIVX963SHA512AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Cofactor Variable IVX963
|
||||
ECIESEncryptionCofactorVariableIVX963SHA224AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Cofactor Variable IVX963
|
||||
ECIESEncryptionCofactorVariableIVX963SHA256AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Cofactor Variable IVX963
|
||||
ECIESEncryptionCofactorVariableIVX963SHA384AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Cofactor Variable IVX963
|
||||
ECIESEncryptionCofactorVariableIVX963SHA512AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Cofactor X963
|
||||
ECIESEncryptionCofactorX963SHA1AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Cofactor X963
|
||||
ECIESEncryptionCofactorX963SHA224AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Cofactor X963
|
||||
ECIESEncryptionCofactorX963SHA256AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Cofactor X963
|
||||
ECIESEncryptionCofactorX963SHA384AESGCM,
|
||||
|
||||
/// Elliptic Curve Encryption Cofactor X963
|
||||
ECIESEncryptionCofactorX963SHA512AESGCM,
|
||||
|
||||
/// Elliptic Curve Signature RFC4754
|
||||
ECDSASignatureRFC4754,
|
||||
|
||||
/// Elliptic Curve Signature Digest X962
|
||||
ECDSASignatureDigestX962,
|
||||
|
||||
/// Elliptic Curve Signature Digest X962
|
||||
ECDSASignatureDigestX962SHA1,
|
||||
|
||||
/// Elliptic Curve Signature Digest X962
|
||||
ECDSASignatureDigestX962SHA224,
|
||||
|
||||
/// Elliptic Curve Signature Digest X962
|
||||
ECDSASignatureDigestX962SHA256,
|
||||
|
||||
/// Elliptic Curve Signature Digest X962
|
||||
ECDSASignatureDigestX962SHA384,
|
||||
|
||||
/// Elliptic Curve Signature Digest X962
|
||||
ECDSASignatureDigestX962SHA512,
|
||||
|
||||
/// Elliptic Curve Signature Message X962
|
||||
ECDSASignatureMessageX962SHA1,
|
||||
|
||||
/// Elliptic Curve Signature Digest X962
|
||||
ECDSASignatureMessageX962SHA224,
|
||||
|
||||
/// Elliptic Curve Signature Digest X962
|
||||
ECDSASignatureMessageX962SHA256,
|
||||
|
||||
/// Elliptic Curve Signature Digest X962
|
||||
ECDSASignatureMessageX962SHA384,
|
||||
|
||||
/// Elliptic Curve Signature Digest X962
|
||||
ECDSASignatureMessageX962SHA512,
|
||||
|
||||
/// Elliptic Curve Key Exchange
|
||||
ECDHKeyExchangeCofactor,
|
||||
|
||||
/// Elliptic Curve Key Exchange
|
||||
ECDHKeyExchangeStandard,
|
||||
|
||||
/// Elliptic Curve Key Exchange
|
||||
ECDHKeyExchangeCofactorX963SHA1,
|
||||
|
||||
/// Elliptic Curve Key Exchange
|
||||
ECDHKeyExchangeStandardX963SHA1,
|
||||
|
||||
/// Elliptic Curve Key Exchange
|
||||
ECDHKeyExchangeCofactorX963SHA224,
|
||||
|
||||
/// Elliptic Curve Key Exchange
|
||||
ECDHKeyExchangeCofactorX963SHA256,
|
||||
|
||||
/// Elliptic Curve Key Exchange
|
||||
ECDHKeyExchangeCofactorX963SHA384,
|
||||
|
||||
/// Elliptic Curve Key Exchange
|
||||
ECDHKeyExchangeCofactorX963SHA512,
|
||||
|
||||
/// Elliptic Curve Key Exchange
|
||||
ECDHKeyExchangeStandardX963SHA224,
|
||||
|
||||
/// Elliptic Curve Key Exchange
|
||||
ECDHKeyExchangeStandardX963SHA256,
|
||||
|
||||
/// Elliptic Curve Key Exchange
|
||||
ECDHKeyExchangeStandardX963SHA384,
|
||||
|
||||
/// Elliptic Curve Key Exchange
|
||||
ECDHKeyExchangeStandardX963SHA512,
|
||||
|
||||
/// RSA Encryption
|
||||
RSAEncryptionRaw,
|
||||
|
||||
/// RSA Encryption
|
||||
RSAEncryptionPKCS1,
|
||||
/// RSA Encryption OAEP
|
||||
RSAEncryptionOAEPSHA1,
|
||||
|
||||
/// RSA Encryption OAEP
|
||||
RSAEncryptionOAEPSHA224,
|
||||
|
||||
/// RSA Encryption OAEP
|
||||
RSAEncryptionOAEPSHA256,
|
||||
|
||||
/// RSA Encryption OAEP
|
||||
RSAEncryptionOAEPSHA384,
|
||||
|
||||
/// RSA Encryption OAEP
|
||||
RSAEncryptionOAEPSHA512,
|
||||
|
||||
/// RSA Encryption OAEP AES-GCM
|
||||
RSAEncryptionOAEPSHA1AESGCM,
|
||||
|
||||
/// RSA Encryption OAEP AES-GCM
|
||||
RSAEncryptionOAEPSHA224AESGCM,
|
||||
|
||||
/// RSA Encryption OAEP AES-GCM
|
||||
RSAEncryptionOAEPSHA256AESGCM,
|
||||
|
||||
/// RSA Encryption OAEP AES-GCM
|
||||
RSAEncryptionOAEPSHA384AESGCM,
|
||||
|
||||
/// RSA Encryption OAEP AES-GCM
|
||||
RSAEncryptionOAEPSHA512AESGCM,
|
||||
|
||||
/// RSA Signature Raw
|
||||
RSASignatureRaw,
|
||||
|
||||
/// RSA Signature Digest PKCS1v15
|
||||
RSASignatureDigestPKCS1v15Raw,
|
||||
|
||||
/// RSA Signature Digest PKCS1v15
|
||||
RSASignatureDigestPKCS1v15SHA1,
|
||||
|
||||
/// RSA Signature Digest PKCS1v15
|
||||
RSASignatureDigestPKCS1v15SHA224,
|
||||
|
||||
/// RSA Signature Digest PKCS1v15
|
||||
RSASignatureDigestPKCS1v15SHA256,
|
||||
|
||||
/// RSA Signature Digest PKCS1v15
|
||||
RSASignatureDigestPKCS1v15SHA384,
|
||||
|
||||
/// RSA Signature Digest PKCS1v15
|
||||
RSASignatureDigestPKCS1v15SHA512,
|
||||
|
||||
/// RSA Signature Message PKCS1v15
|
||||
RSASignatureMessagePKCS1v15SHA1,
|
||||
|
||||
/// RSA Signature Digest PKCS1v15
|
||||
RSASignatureMessagePKCS1v15SHA224,
|
||||
|
||||
/// RSA Signature Digest PKCS1v15
|
||||
RSASignatureMessagePKCS1v15SHA256,
|
||||
|
||||
/// RSA Signature Digest PKCS1v15
|
||||
RSASignatureMessagePKCS1v15SHA384,
|
||||
|
||||
/// RSA Signature Digest PKCS1v15
|
||||
RSASignatureMessagePKCS1v15SHA512,
|
||||
|
||||
/// RSA Signature Digest PSS
|
||||
RSASignatureDigestPSSSHA1,
|
||||
|
||||
/// RSA Signature Digest PSS
|
||||
RSASignatureDigestPSSSHA224,
|
||||
|
||||
/// RSA Signature Digest PSS
|
||||
RSASignatureDigestPSSSHA256,
|
||||
|
||||
/// RSA Signature Digest PSS
|
||||
RSASignatureDigestPSSSHA384,
|
||||
|
||||
/// RSA Signature Digest PSS
|
||||
RSASignatureDigestPSSSHA512,
|
||||
|
||||
/// RSA Signature Message PSS
|
||||
RSASignatureMessagePSSSHA1,
|
||||
|
||||
/// RSA Signature Message PSS
|
||||
RSASignatureMessagePSSSHA224,
|
||||
|
||||
/// RSA Signature Message PSS
|
||||
RSASignatureMessagePSSSHA256,
|
||||
|
||||
/// RSA Signature Message PSS
|
||||
RSASignatureMessagePSSSHA384,
|
||||
|
||||
/// RSA Signature Message PSS
|
||||
RSASignatureMessagePSSSHA512,
|
||||
}
|
||||
|
||||
impl KeyAlgorithm {
|
||||
/// Get `CFString` containing the `kSecKeyAlgorithm` dictionary value for
|
||||
/// a particular cryptographic algorithm.
|
||||
pub fn as_CFString(self) -> CFString {
|
||||
unsafe {
|
||||
CFString::wrap_under_get_rule(match self {
|
||||
KeyAlgorithm::ECIESEncryptionStandardX963SHA1AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionStandardX963SHA1AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionStandardX963SHA224AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionStandardX963SHA224AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionStandardX963SHA256AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionStandardX963SHA256AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionStandardX963SHA384AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionStandardX963SHA384AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionStandardX963SHA512AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionStandardX963SHA512AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionStandardVariableIVX963SHA224AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA224AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionStandardVariableIVX963SHA256AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA256AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionStandardVariableIVX963SHA384AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA384AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionStandardVariableIVX963SHA512AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA512AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionCofactorVariableIVX963SHA224AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA224AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionCofactorVariableIVX963SHA256AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA256AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionCofactorVariableIVX963SHA384AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA384AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionCofactorVariableIVX963SHA512AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionCofactorVariableIVX963SHA512AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionCofactorX963SHA1AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionCofactorX963SHA1AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionCofactorX963SHA224AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionCofactorX963SHA224AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionCofactorX963SHA256AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionCofactorX963SHA256AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionCofactorX963SHA384AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionCofactorX963SHA384AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECIESEncryptionCofactorX963SHA512AESGCM => {
|
||||
kSecKeyAlgorithmECIESEncryptionCofactorX963SHA512AESGCM
|
||||
}
|
||||
KeyAlgorithm::ECDSASignatureRFC4754 => kSecKeyAlgorithmECDSASignatureRFC4754,
|
||||
KeyAlgorithm::ECDSASignatureDigestX962 => kSecKeyAlgorithmECDSASignatureDigestX962,
|
||||
KeyAlgorithm::ECDSASignatureDigestX962SHA1 => {
|
||||
kSecKeyAlgorithmECDSASignatureDigestX962SHA1
|
||||
}
|
||||
KeyAlgorithm::ECDSASignatureDigestX962SHA224 => {
|
||||
kSecKeyAlgorithmECDSASignatureDigestX962SHA224
|
||||
}
|
||||
KeyAlgorithm::ECDSASignatureDigestX962SHA256 => {
|
||||
kSecKeyAlgorithmECDSASignatureDigestX962SHA256
|
||||
}
|
||||
KeyAlgorithm::ECDSASignatureDigestX962SHA384 => {
|
||||
kSecKeyAlgorithmECDSASignatureDigestX962SHA384
|
||||
}
|
||||
KeyAlgorithm::ECDSASignatureDigestX962SHA512 => {
|
||||
kSecKeyAlgorithmECDSASignatureDigestX962SHA512
|
||||
}
|
||||
KeyAlgorithm::ECDSASignatureMessageX962SHA1 => {
|
||||
kSecKeyAlgorithmECDSASignatureMessageX962SHA1
|
||||
}
|
||||
KeyAlgorithm::ECDSASignatureMessageX962SHA224 => {
|
||||
kSecKeyAlgorithmECDSASignatureMessageX962SHA224
|
||||
}
|
||||
KeyAlgorithm::ECDSASignatureMessageX962SHA256 => {
|
||||
kSecKeyAlgorithmECDSASignatureMessageX962SHA256
|
||||
}
|
||||
KeyAlgorithm::ECDSASignatureMessageX962SHA384 => {
|
||||
kSecKeyAlgorithmECDSASignatureMessageX962SHA384
|
||||
}
|
||||
KeyAlgorithm::ECDSASignatureMessageX962SHA512 => {
|
||||
kSecKeyAlgorithmECDSASignatureMessageX962SHA512
|
||||
}
|
||||
KeyAlgorithm::ECDHKeyExchangeCofactor => kSecKeyAlgorithmECDHKeyExchangeCofactor,
|
||||
KeyAlgorithm::ECDHKeyExchangeStandard => kSecKeyAlgorithmECDHKeyExchangeStandard,
|
||||
KeyAlgorithm::ECDHKeyExchangeCofactorX963SHA1 => {
|
||||
kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA1
|
||||
}
|
||||
KeyAlgorithm::ECDHKeyExchangeStandardX963SHA1 => {
|
||||
kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1
|
||||
}
|
||||
KeyAlgorithm::ECDHKeyExchangeCofactorX963SHA224 => {
|
||||
kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA224
|
||||
}
|
||||
KeyAlgorithm::ECDHKeyExchangeCofactorX963SHA256 => {
|
||||
kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA256
|
||||
}
|
||||
KeyAlgorithm::ECDHKeyExchangeCofactorX963SHA384 => {
|
||||
kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA384
|
||||
}
|
||||
KeyAlgorithm::ECDHKeyExchangeCofactorX963SHA512 => {
|
||||
kSecKeyAlgorithmECDHKeyExchangeCofactorX963SHA512
|
||||
}
|
||||
KeyAlgorithm::ECDHKeyExchangeStandardX963SHA224 => {
|
||||
kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA224
|
||||
}
|
||||
KeyAlgorithm::ECDHKeyExchangeStandardX963SHA256 => {
|
||||
kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA256
|
||||
}
|
||||
KeyAlgorithm::ECDHKeyExchangeStandardX963SHA384 => {
|
||||
kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA384
|
||||
}
|
||||
KeyAlgorithm::ECDHKeyExchangeStandardX963SHA512 => {
|
||||
kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA512
|
||||
}
|
||||
KeyAlgorithm::RSAEncryptionRaw => kSecKeyAlgorithmRSAEncryptionRaw,
|
||||
KeyAlgorithm::RSAEncryptionPKCS1 => kSecKeyAlgorithmRSAEncryptionPKCS1,
|
||||
KeyAlgorithm::RSAEncryptionOAEPSHA1 => kSecKeyAlgorithmRSAEncryptionOAEPSHA1,
|
||||
KeyAlgorithm::RSAEncryptionOAEPSHA224 => kSecKeyAlgorithmRSAEncryptionOAEPSHA224,
|
||||
KeyAlgorithm::RSAEncryptionOAEPSHA256 => kSecKeyAlgorithmRSAEncryptionOAEPSHA256,
|
||||
KeyAlgorithm::RSAEncryptionOAEPSHA384 => kSecKeyAlgorithmRSAEncryptionOAEPSHA384,
|
||||
KeyAlgorithm::RSAEncryptionOAEPSHA512 => kSecKeyAlgorithmRSAEncryptionOAEPSHA512,
|
||||
KeyAlgorithm::RSAEncryptionOAEPSHA1AESGCM => {
|
||||
kSecKeyAlgorithmRSAEncryptionOAEPSHA1AESGCM
|
||||
}
|
||||
KeyAlgorithm::RSAEncryptionOAEPSHA224AESGCM => {
|
||||
kSecKeyAlgorithmRSAEncryptionOAEPSHA224AESGCM
|
||||
}
|
||||
KeyAlgorithm::RSAEncryptionOAEPSHA256AESGCM => {
|
||||
kSecKeyAlgorithmRSAEncryptionOAEPSHA256AESGCM
|
||||
}
|
||||
KeyAlgorithm::RSAEncryptionOAEPSHA384AESGCM => {
|
||||
kSecKeyAlgorithmRSAEncryptionOAEPSHA384AESGCM
|
||||
}
|
||||
KeyAlgorithm::RSAEncryptionOAEPSHA512AESGCM => {
|
||||
kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM
|
||||
}
|
||||
KeyAlgorithm::RSASignatureRaw => kSecKeyAlgorithmRSASignatureRaw,
|
||||
KeyAlgorithm::RSASignatureDigestPKCS1v15Raw => {
|
||||
kSecKeyAlgorithmRSASignatureDigestPKCS1v15Raw
|
||||
}
|
||||
KeyAlgorithm::RSASignatureDigestPKCS1v15SHA1 => {
|
||||
kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1
|
||||
}
|
||||
KeyAlgorithm::RSASignatureDigestPKCS1v15SHA224 => {
|
||||
kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA224
|
||||
}
|
||||
KeyAlgorithm::RSASignatureDigestPKCS1v15SHA256 => {
|
||||
kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256
|
||||
}
|
||||
KeyAlgorithm::RSASignatureDigestPKCS1v15SHA384 => {
|
||||
kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA384
|
||||
}
|
||||
KeyAlgorithm::RSASignatureDigestPKCS1v15SHA512 => {
|
||||
kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA512
|
||||
}
|
||||
KeyAlgorithm::RSASignatureMessagePKCS1v15SHA1 => {
|
||||
kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA1
|
||||
}
|
||||
KeyAlgorithm::RSASignatureMessagePKCS1v15SHA224 => {
|
||||
kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA224
|
||||
}
|
||||
KeyAlgorithm::RSASignatureMessagePKCS1v15SHA256 => {
|
||||
kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256
|
||||
}
|
||||
KeyAlgorithm::RSASignatureMessagePKCS1v15SHA384 => {
|
||||
kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA384
|
||||
}
|
||||
KeyAlgorithm::RSASignatureMessagePKCS1v15SHA512 => {
|
||||
kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA512
|
||||
}
|
||||
KeyAlgorithm::RSASignatureDigestPSSSHA1 => {
|
||||
kSecKeyAlgorithmRSASignatureDigestPSSSHA1
|
||||
}
|
||||
KeyAlgorithm::RSASignatureDigestPSSSHA224 => {
|
||||
kSecKeyAlgorithmRSASignatureDigestPSSSHA224
|
||||
}
|
||||
KeyAlgorithm::RSASignatureDigestPSSSHA256 => {
|
||||
kSecKeyAlgorithmRSASignatureDigestPSSSHA256
|
||||
}
|
||||
KeyAlgorithm::RSASignatureDigestPSSSHA384 => {
|
||||
kSecKeyAlgorithmRSASignatureDigestPSSSHA384
|
||||
}
|
||||
KeyAlgorithm::RSASignatureDigestPSSSHA512 => {
|
||||
kSecKeyAlgorithmRSASignatureDigestPSSSHA512
|
||||
}
|
||||
KeyAlgorithm::RSASignatureMessagePSSSHA1 => {
|
||||
kSecKeyAlgorithmRSASignatureMessagePSSSHA1
|
||||
}
|
||||
KeyAlgorithm::RSASignatureMessagePSSSHA224 => {
|
||||
kSecKeyAlgorithmRSASignatureMessagePSSSHA224
|
||||
}
|
||||
KeyAlgorithm::RSASignatureMessagePSSSHA256 => {
|
||||
kSecKeyAlgorithmRSASignatureMessagePSSSHA256
|
||||
}
|
||||
KeyAlgorithm::RSASignatureMessagePSSSHA384 => {
|
||||
kSecKeyAlgorithmRSASignatureMessagePSSSHA384
|
||||
}
|
||||
KeyAlgorithm::RSASignatureMessagePSSSHA512 => {
|
||||
kSecKeyAlgorithmRSASignatureMessagePSSSHA512
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
//! Keys stored in macOS Keychain Services.
|
||||
|
||||
mod algorithm;
|
||||
mod operation;
|
||||
mod pair;
|
||||
|
||||
pub use self::{algorithm::*, operation::*, pair::*};
|
||||
use crate::{
|
||||
attr::*,
|
||||
ciphertext::Ciphertext,
|
||||
dictionary::{Dictionary, DictionaryBuilder},
|
||||
error::Error,
|
||||
ffi::*,
|
||||
keychain::item::{self, MatchLimit},
|
||||
signature::Signature,
|
||||
};
|
||||
use core_foundation::{
|
||||
base::{CFIndexConvertible, CFTypeRef, TCFType},
|
||||
data::{CFData, CFDataRef},
|
||||
error::CFErrorRef,
|
||||
string::{CFString, CFStringRef},
|
||||
};
|
||||
use std::{
|
||||
fmt::{self, Debug},
|
||||
ptr,
|
||||
};
|
||||
|
||||
declare_TCFType! {
|
||||
/// Object which represents a cryptographic key.
|
||||
///
|
||||
/// Wrapper for the `SecKey`/`SecKeyRef` types:
|
||||
/// <https://developer.apple.com/documentation/security/seckeyref>
|
||||
Key, KeyRef
|
||||
}
|
||||
|
||||
impl_TCFType!(Key, KeyRef, SecKeyGetTypeID);
|
||||
|
||||
impl Key {
|
||||
/// Find a `Key` in the keyring using the given `ItemQuery`.
|
||||
///
|
||||
/// Wrapper for `SecItemCopyMatching`. See:
|
||||
/// <https://developer.apple.com/documentation/security/1398306-secitemcopymatching>
|
||||
pub fn find(query: item::Query) -> Result<Self, Error> {
|
||||
let mut params = DictionaryBuilder::from(query);
|
||||
params.add(unsafe { kSecClass }, &item::Class::Key.as_CFString());
|
||||
params.add(unsafe { kSecMatchLimit }, &MatchLimit::One.as_CFType());
|
||||
params.add_boolean(unsafe { kSecReturnRef }, true);
|
||||
|
||||
let mut result: KeyRef = ptr::null_mut();
|
||||
let status = unsafe {
|
||||
SecItemCopyMatching(
|
||||
Dictionary::from(params).as_concrete_TypeRef(),
|
||||
&mut result as &mut CFTypeRef,
|
||||
)
|
||||
};
|
||||
|
||||
// Return an error if the status was unsuccessful
|
||||
if let Some(e) = Error::maybe_from_OSStatus(status) {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
Ok(unsafe { Key::wrap_under_create_rule(result) })
|
||||
}
|
||||
|
||||
/// Get the `AttrApplicationLabel` for this `Key`.
|
||||
pub fn application_label(&self) -> Option<AttrApplicationLabel> {
|
||||
self.attributes()
|
||||
.find(AttrKind::ApplicationLabel)
|
||||
.map(|tag| {
|
||||
AttrApplicationLabel(unsafe {
|
||||
CFData::wrap_under_get_rule(tag.as_CFTypeRef() as CFDataRef)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the `AttrApplicationTag` for this `Key`.
|
||||
pub fn application_tag(&self) -> Option<AttrApplicationTag> {
|
||||
self.attributes().find(AttrKind::ApplicationTag).map(|tag| {
|
||||
AttrApplicationTag(unsafe {
|
||||
CFData::wrap_under_get_rule(tag.as_CFTypeRef() as CFDataRef)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the `AttrLabel` for this `Key`.
|
||||
pub fn label(&self) -> Option<AttrLabel> {
|
||||
self.attributes().find(AttrKind::Label).map(|label| {
|
||||
AttrLabel(unsafe { CFString::wrap_under_get_rule(label.as_CFTypeRef() as CFStringRef) })
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the `AttrKeyClass` for this `Key`.
|
||||
pub fn class(&self) -> Option<AttrKeyClass> {
|
||||
self.attributes()
|
||||
.find(AttrKind::KeyClass)
|
||||
.map(|class| AttrKeyClass::from(class.as_CFTypeRef() as CFStringRef))
|
||||
}
|
||||
|
||||
/// Get the `AttrKeyType` for this `Key`.
|
||||
pub fn key_type(&self) -> Option<AttrKeyType> {
|
||||
self.attributes()
|
||||
.find(AttrKind::KeyType)
|
||||
.map(|keytype| AttrKeyType::from(keytype.as_CFTypeRef() as CFStringRef))
|
||||
}
|
||||
|
||||
/// Determine whether a key is suitable for an operation using a certain algorithm
|
||||
///
|
||||
/// Wrapper for the `SecKeyIsAlgorithmSupported` function. See:
|
||||
/// <https://developer.apple.com/documentation/security/1644057-seckeyisalgorithmsupported>
|
||||
pub fn is_supported(&self, operation: KeyOperation, alg: KeyAlgorithm) -> bool {
|
||||
let res = unsafe {
|
||||
SecKeyIsAlgorithmSupported(
|
||||
self.as_concrete_TypeRef(),
|
||||
operation.to_CFIndex(),
|
||||
alg.as_CFString().as_CFTypeRef(),
|
||||
)
|
||||
};
|
||||
res == 1
|
||||
}
|
||||
|
||||
/// Create a cryptographic signature of the given data using this key.
|
||||
///
|
||||
/// Wrapper for the `SecKeyCreateSignature` function. See:
|
||||
/// <https://developer.apple.com/documentation/security/1643916-seckeycreatesignature>
|
||||
pub fn sign(&self, alg: KeyAlgorithm, data: &[u8]) -> Result<Signature, Error> {
|
||||
let mut error: CFErrorRef = ptr::null_mut();
|
||||
let signature = unsafe {
|
||||
SecKeyCreateSignature(
|
||||
self.as_concrete_TypeRef(),
|
||||
alg.as_CFString().as_CFTypeRef(),
|
||||
CFData::from_buffer(data).as_concrete_TypeRef(),
|
||||
&mut error,
|
||||
)
|
||||
};
|
||||
|
||||
if error.is_null() {
|
||||
let bytes = unsafe { CFData::wrap_under_create_rule(signature) }.to_vec();
|
||||
Ok(Signature::new(alg, bytes))
|
||||
} else {
|
||||
Err(error.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies the cryptographic signature of the given data using this key.
|
||||
///
|
||||
/// Wrapper for the `SecKeyVerifySignature` function. See:
|
||||
/// <https://developer.apple.com/documentation/security/1643715-seckeyverifysignature>
|
||||
pub fn verify(&self, signed_data: &[u8], signature: &Signature) -> Result<bool, Error> {
|
||||
let mut error: CFErrorRef = ptr::null_mut();
|
||||
let result = unsafe {
|
||||
SecKeyVerifySignature(
|
||||
self.as_concrete_TypeRef(),
|
||||
signature.algorithm().as_CFString().as_CFTypeRef(),
|
||||
CFData::from_buffer(signed_data).as_concrete_TypeRef(),
|
||||
CFData::from_buffer(signature.as_bytes()).as_concrete_TypeRef(),
|
||||
&mut error,
|
||||
)
|
||||
};
|
||||
|
||||
if error.is_null() {
|
||||
Ok(result == 0x1)
|
||||
} else {
|
||||
Err(error.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Encrypts a block of data using a public key and specified algorithm
|
||||
///
|
||||
/// Wrapper for the `SecKeyCreateEncryptedData` function. See:
|
||||
/// <https://developer.apple.com/documentation/security/1643957-seckeycreateencrypteddata>
|
||||
pub fn encrypt(&self, alg: KeyAlgorithm, plaintext: &[u8]) -> Result<Ciphertext, Error> {
|
||||
let mut error: CFErrorRef = ptr::null_mut();
|
||||
let ciphertext = unsafe {
|
||||
SecKeyCreateEncryptedData(
|
||||
self.as_concrete_TypeRef(),
|
||||
alg.as_CFString().as_CFTypeRef(),
|
||||
CFData::from_buffer(plaintext).as_concrete_TypeRef(),
|
||||
&mut error,
|
||||
)
|
||||
};
|
||||
|
||||
if error.is_null() {
|
||||
let bytes = unsafe { CFData::wrap_under_create_rule(ciphertext) }.to_vec();
|
||||
Ok(Ciphertext::new(alg, bytes))
|
||||
} else {
|
||||
Err(error.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Decrypts a block of data using a private key and specified algorithm
|
||||
///
|
||||
/// Wrapper for the `SecKeyCreateDecryptedData` function. See:
|
||||
/// <https://developer.apple.com/documentation/security/1644043-seckeycreatedecrypteddata>
|
||||
pub fn decrypt(&self, ciphertext: Ciphertext) -> Result<Vec<u8>, Error> {
|
||||
let mut error: CFErrorRef = ptr::null_mut();
|
||||
let plaintext = unsafe {
|
||||
SecKeyCreateDecryptedData(
|
||||
self.as_concrete_TypeRef(),
|
||||
ciphertext.algorithm().as_CFString().as_CFTypeRef(),
|
||||
CFData::from_buffer(ciphertext.as_ref()).as_concrete_TypeRef(),
|
||||
&mut error,
|
||||
)
|
||||
};
|
||||
|
||||
if error.is_null() {
|
||||
let bytes = unsafe { CFData::wrap_under_create_rule(plaintext) }.to_vec();
|
||||
Ok(bytes)
|
||||
} else {
|
||||
Err(error.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete this key from the keychain
|
||||
///
|
||||
/// Wrapper for `SecItemDelete` function. See:
|
||||
/// <https://developer.apple.com/documentation/security/1395547-secitemdelete>
|
||||
pub fn delete(self) -> Result<(), Error> {
|
||||
let mut query = DictionaryBuilder::new();
|
||||
let key_class = self.class().unwrap();
|
||||
query.add(unsafe { kSecClass }, &item::Class::Key.as_CFString());
|
||||
query.add(unsafe { kSecAttrKeyClass }, &key_class.as_CFString());
|
||||
if key_class == AttrKeyClass::Public {
|
||||
query.add(unsafe { kSecAttrKeyType }, &self.key_type().unwrap().as_CFString());
|
||||
query.add(
|
||||
unsafe { kSecAttrApplicationTag },
|
||||
&self.application_tag().unwrap().as_CFType(),
|
||||
);
|
||||
} else if key_class == AttrKeyClass::Private {
|
||||
query.add(
|
||||
unsafe { kSecAttrApplicationLabel },
|
||||
&self.application_label().unwrap().as_CFType(),
|
||||
);
|
||||
query.add_boolean(unsafe { kSecReturnRef }, true);
|
||||
}
|
||||
let status = unsafe { SecItemDelete(Dictionary::from(query).as_concrete_TypeRef()) };
|
||||
if let Some(e) = Error::maybe_from_OSStatus(status) {
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Export this key as an external representation.
|
||||
///
|
||||
/// If the key is not exportable the operation will fail (e.g. if it
|
||||
/// was generated inside of the Secure Enclave, or if the "Extractable"
|
||||
/// flag is set to NO).
|
||||
///
|
||||
/// The data returned depends on the key type:
|
||||
///
|
||||
/// - RSA: PKCS#1 format
|
||||
/// - EC: ANSI X9.63 bytestring:
|
||||
/// - Public key: `04 || X || Y`
|
||||
/// - Private key: Concatenation of public key with big endian encoding
|
||||
/// of the secret scalar, i.e. `04 || X || Y || K`
|
||||
///
|
||||
/// All representations use fixed-size integers with leading zeroes.
|
||||
///
|
||||
/// Wrapper for the `SecKeyCopyExternalRepresentation` function. See:
|
||||
/// <https://developer.apple.com/documentation/security/1643698-seckeycopyexternalrepresentation>
|
||||
pub fn to_external_representation(&self) -> Result<Vec<u8>, Error> {
|
||||
let mut error: CFErrorRef = ptr::null_mut();
|
||||
let data =
|
||||
unsafe { SecKeyCopyExternalRepresentation(self.as_concrete_TypeRef(), &mut error) };
|
||||
|
||||
if error.is_null() {
|
||||
Ok(unsafe { CFData::wrap_under_create_rule(data) }.to_vec())
|
||||
} else {
|
||||
Err(error.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores a key from an external representation of that key.
|
||||
///
|
||||
/// Wrapper for the `SecKeyCreateWithData` function. See:
|
||||
/// <https://developer.apple.com/documentation/security/1643701-seckeycreatewithdata>
|
||||
pub fn from_external_representation(params: RestoreKeyParams) -> Result<Self, Error> {
|
||||
let mut error: CFErrorRef = ptr::null_mut();
|
||||
let data = unsafe {
|
||||
SecKeyCreateWithData(
|
||||
CFData::from_buffer(params.as_bytes()).as_concrete_TypeRef(),
|
||||
params.attributes().as_concrete_TypeRef(),
|
||||
&mut error,
|
||||
)
|
||||
};
|
||||
|
||||
if error.is_null() {
|
||||
Ok(unsafe { Key::wrap_under_create_rule(data) })
|
||||
} else {
|
||||
Err(error.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch attributes for this `Key`.
|
||||
///
|
||||
/// Wrapper for `SecKeyCopyAttributes`. See:
|
||||
/// <https://developer.apple.com/documentation/security/1643699-seckeycopyattributes>
|
||||
fn attributes(&self) -> Dictionary {
|
||||
unsafe { Dictionary::wrap_under_get_rule(SecKeyCopyAttributes(self.as_concrete_TypeRef())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Key {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"SecKey {{ application_label: {:?}, application_tag: {:?}, label: {:?} }}",
|
||||
self.application_label(),
|
||||
self.application_tag(),
|
||||
self.label()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use core_foundation::base::{CFIndex, CFIndexConvertible};
|
||||
|
||||
use self::KeyOperation::*;
|
||||
/// Types of operations that a cryptographic key can perform
|
||||
///
|
||||
/// Wrapper for `SecKeyOperationType`. See:
|
||||
/// <https://developer.apple.com/documentation/security/seckeyoperationtype>
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum KeyOperation {
|
||||
/// Decrypt operation
|
||||
Decrypt,
|
||||
/// Encrypt operation
|
||||
Encrypt,
|
||||
/// KeyExchange operation
|
||||
KeyExchange,
|
||||
/// Sign operation
|
||||
Sign,
|
||||
/// Verify operation
|
||||
Verify,
|
||||
}
|
||||
|
||||
impl CFIndexConvertible for KeyOperation {
|
||||
fn to_CFIndex(self) -> CFIndex {
|
||||
let i = match self {
|
||||
Decrypt => 3,
|
||||
Encrypt => 2,
|
||||
KeyExchange => 4,
|
||||
Sign => 0,
|
||||
Verify => 1,
|
||||
};
|
||||
i as CFIndex
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
use super::*;
|
||||
use crate::{access::AccessControl, dictionary::*, error::Error};
|
||||
use core_foundation::base::TCFType;
|
||||
use std::ptr;
|
||||
|
||||
/// Public key pairs (i.e. public and private key) stored in the keychain.
|
||||
#[derive(Debug)]
|
||||
pub struct KeyPair {
|
||||
/// Public key
|
||||
pub public_key: Key,
|
||||
|
||||
/// Private key
|
||||
pub private_key: Key,
|
||||
}
|
||||
|
||||
impl KeyPair {
|
||||
/// An asymmetric cryptographic key pair is composed of a public and a private key that are generated together.
|
||||
/// The public key can be distributed freely, but keep the private key secret.
|
||||
/// One or both may be stored in a keychain for safekeeping.
|
||||
///
|
||||
/// Wrapper for the `SecKeyCreateRandomKey` function see:
|
||||
/// <https://developer.apple.com/documentation/security/1823694-seckeycreaterandomkey>
|
||||
pub fn create(params: KeyPairGenerateParams) -> Result<KeyPair, Error> {
|
||||
let mut error: CFErrorRef = ptr::null_mut();
|
||||
let private_key_ref: KeyRef = unsafe {
|
||||
SecKeyCreateRandomKey(Dictionary::from(params).as_concrete_TypeRef(), &mut error)
|
||||
};
|
||||
if private_key_ref.is_null() {
|
||||
Err(error.into())
|
||||
} else {
|
||||
let public_key_ref = unsafe { SecKeyCopyPublicKey(private_key_ref) };
|
||||
assert!(!public_key_ref.is_null());
|
||||
assert!(!private_key_ref.is_null());
|
||||
|
||||
Ok(unsafe {
|
||||
KeyPair {
|
||||
public_key: Key::wrap_under_create_rule(public_key_ref),
|
||||
private_key: Key::wrap_under_create_rule(private_key_ref),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a public/private `KeyPair` using the given
|
||||
/// `GeneratePairParams`.
|
||||
///
|
||||
/// Wrapper for the `SecKeyGeneratePair` function. See:
|
||||
/// <https://developer.apple.com/documentation/security/1395339-seckeygeneratepair>
|
||||
pub fn generate(params: KeyPairGenerateParams) -> Result<KeyPair, Error> {
|
||||
let mut public_key_ref: KeyRef = ptr::null_mut();
|
||||
let mut private_key_ref: KeyRef = ptr::null_mut();
|
||||
|
||||
let status = unsafe {
|
||||
SecKeyGeneratePair(
|
||||
Dictionary::from(params).as_concrete_TypeRef(),
|
||||
&mut public_key_ref,
|
||||
&mut private_key_ref,
|
||||
)
|
||||
};
|
||||
|
||||
// Return an error if the status was unsuccessful
|
||||
if let Some(e) = Error::maybe_from_OSStatus(status) {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
assert!(!public_key_ref.is_null());
|
||||
assert!(!private_key_ref.is_null());
|
||||
|
||||
Ok(unsafe {
|
||||
KeyPair {
|
||||
public_key: Key::wrap_under_create_rule(public_key_ref),
|
||||
private_key: Key::wrap_under_create_rule(private_key_ref),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for key generation parameters (passed to the underlying
|
||||
/// `SecKeyGeneratePair` function)
|
||||
///
|
||||
/// For more information on generating cryptographic keys in a keychain, see:
|
||||
/// <https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/generating_new_cryptographic_keys>
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct KeyPairGenerateParams {
|
||||
key_type: AttrKeyType,
|
||||
key_size: usize,
|
||||
attrs: DictionaryBuilder,
|
||||
}
|
||||
|
||||
impl KeyPairGenerateParams {
|
||||
/// Create a new `GeneratePairParams`
|
||||
pub fn new(key_type: AttrKeyType, key_size: usize) -> Self {
|
||||
Self {
|
||||
key_type,
|
||||
key_size,
|
||||
attrs: <_>::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the access control policy (a.k.a. ACL) for the `Key`.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrAccessControl` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattraccesscontrol>
|
||||
pub fn access_control(mut self, access_control: &AccessControl) -> Self {
|
||||
self.attrs.add(AttrKind::AccessControl, access_control);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set a tag (private, application-specific identifier) on this key.
|
||||
/// Tags are useful as the "primary key" for looking up keychain items.
|
||||
///
|
||||
/// Wrapper for `kSecAttrApplicationTag` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrapplicationtag>
|
||||
pub fn application_tag<T>(mut self, tag: T) -> Self
|
||||
where
|
||||
T: Into<AttrApplicationTag>,
|
||||
{
|
||||
self.attrs.add_attr(&tag.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether this key can be used in a key derivation operation
|
||||
///
|
||||
/// Wrapper for the `kSecKeyDerive` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyderive>
|
||||
pub fn can_derive(mut self, value: bool) -> Self {
|
||||
self.attrs.add_boolean(AttrKind::Derive, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether this key can be used in a decrypt operation.
|
||||
///
|
||||
/// Wrapper for the `kSecKeyDecrypt` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeydecrypt>
|
||||
pub fn can_decrypt(mut self, value: bool) -> Self {
|
||||
self.attrs.add_boolean(AttrKind::Decrypt, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether this key can be used in a encrypt operation.
|
||||
///
|
||||
/// Wrapper for the `kSecKeyEncrypt` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyencrypt>
|
||||
pub fn can_encrypt(mut self, value: bool) -> Self {
|
||||
self.attrs.add_boolean(AttrKind::Encrypt, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether this key can be used in a signing operation.
|
||||
///
|
||||
/// Wrapper for the `kSecKeySign` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeysign>
|
||||
pub fn can_sign(mut self, value: bool) -> Self {
|
||||
self.attrs.add_boolean(AttrKind::Sign, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether this key can be used to verify a signatures.
|
||||
///
|
||||
/// Wrapper for the `kSecKeyVerify` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyverify>
|
||||
pub fn can_verify(mut self, value: bool) -> Self {
|
||||
self.attrs.add_boolean(AttrKind::Verify, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether this key can be used to wrap another key.
|
||||
///
|
||||
/// Wrapper for the `kSecKeyWrap` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeywrap>
|
||||
pub fn can_wrap(mut self, value: bool) -> Self {
|
||||
self.attrs.add_boolean(AttrKind::Wrap, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether this key can be used to unwrap another key.
|
||||
///
|
||||
/// Wrapper for the `kSecKeyUnwrap` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyunwrap>
|
||||
pub fn can_unwrap(mut self, value: bool) -> Self {
|
||||
self.attrs.add_boolean(AttrKind::Unwrap, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set a key's cryptographic class.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrKeyClass` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrkeyclass>
|
||||
pub fn key_class(mut self, value: AttrKeyClass) -> Self {
|
||||
self.attrs.add(AttrKind::KeyClass, &value.as_CFString());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether this key can be extractable when wrapped
|
||||
///
|
||||
/// Wrapper for the `kSecKeyExtractable` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/kseckeyextractable>
|
||||
pub fn extractable(mut self, value: bool) -> Self {
|
||||
self.attrs.add_boolean(AttrKind::Extractable, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether this key is stored permanently in the keychain (default: false).
|
||||
///
|
||||
/// Wrapper for the `kSecAttrIsPermanent` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrispermanent>
|
||||
pub fn permanent(mut self, value: bool) -> Self {
|
||||
self.attrs.add_boolean(AttrKind::Permanent, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether this key can be wrapped with NONE algorithm. True
|
||||
/// means it cannot be wrapped with NONE, false means it can.
|
||||
///
|
||||
/// Wrapper for `kSecKeySensitive` attribute key. See
|
||||
/// <https://developer.apple.com/documentation/security/kseckeysensitive>
|
||||
pub fn sensitive(mut self, value: bool) -> Self {
|
||||
self.attrs.add_boolean(AttrKind::Sensitive, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set a string label on this key. SecAttrLabels are useful for providing
|
||||
/// additional descriptions or context on keys.
|
||||
///
|
||||
/// Wrapper for the `kSecAttrLabel` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrlabel>
|
||||
pub fn label<L: Into<AttrLabel>>(mut self, label: L) -> Self {
|
||||
self.attrs.add_attr(&label.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether this key can be synchronized with other devices owned by
|
||||
/// the same account (default: false).
|
||||
///
|
||||
/// Wrapper for the `kSecAttrSynchronizable` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrsynchronizable>
|
||||
pub fn synchronizable(mut self, value: bool) -> Self {
|
||||
self.attrs.add_boolean(AttrKind::Synchronizable, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Store this key in an external token i.e. Secure Enclave Processor (SEP).
|
||||
///
|
||||
/// Wrapper for the `kSecAttrTokenID` attribute key. See:
|
||||
/// <https://developer.apple.com/documentation/security/ksecattrtokenid>
|
||||
pub fn token_id(mut self, value: AttrTokenId) -> Self {
|
||||
self.attrs.add_attr(&value);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KeyPairGenerateParams> for Dictionary {
|
||||
fn from(params: KeyPairGenerateParams) -> Dictionary {
|
||||
let mut result = DictionaryBuilder::new();
|
||||
result.add_attr(¶ms.key_type);
|
||||
result.add_number(AttrKind::KeySizeInBits, params.key_size as i64);
|
||||
result.add(
|
||||
unsafe { kSecPrivateKeyAttrs },
|
||||
&Dictionary::from(params.attrs),
|
||||
);
|
||||
result.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for restoring a key from an external representation of that key parameters
|
||||
/// (passed to the underlying `SecKeyCreateWithData` function).
|
||||
///
|
||||
/// The key must have already been imported or generated.
|
||||
///
|
||||
/// For more information on restoring cryptographic keys in keychain, see
|
||||
/// <https://developer.apple.com/documentation/security/1643701-seckeycreatewithdata>
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RestoreKeyParams {
|
||||
/// The category the key fits (public, private, or symmetric)
|
||||
pub key_class: AttrKeyClass,
|
||||
/// Data representing the key. The format of the data depends on the type of key
|
||||
/// being created.
|
||||
///
|
||||
/// - RSA: PKCS#1 format
|
||||
/// - EC: ANSI X9.63 bytestring:
|
||||
/// - Public key: `04 || X || Y`
|
||||
/// - Private key: Concatenation of public key with big endian encoding
|
||||
/// of the secret scalar, i.e. `04 || X || Y || K`
|
||||
///
|
||||
/// All representations use fixed-size integers with leading zeroes.
|
||||
pub key_data: Vec<u8>,
|
||||
/// The type of key algorithm
|
||||
pub key_type: AttrKeyType,
|
||||
}
|
||||
|
||||
impl RestoreKeyParams {
|
||||
/// Return the attributes that will be used to restore the key
|
||||
pub fn attributes(&self) -> Dictionary {
|
||||
let mut result = DictionaryBuilder::new();
|
||||
result.add_attr(&self.key_type);
|
||||
result.add(AttrKind::KeyClass, &self.key_class.as_CFString());
|
||||
result.add_number(AttrKind::KeySizeInBits, (self.key_data.len() * 8) as i64);
|
||||
result.into()
|
||||
}
|
||||
|
||||
/// Return the `key_data` as a slice
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.key_data.as_slice()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
//! Keychains
|
||||
|
||||
pub mod item;
|
||||
pub mod key;
|
||||
|
||||
use self::item::MatchLimit;
|
||||
pub use self::{item::Item, key::Key};
|
||||
use crate::dictionary::*;
|
||||
use crate::error::Error;
|
||||
use crate::ffi::*;
|
||||
use core_foundation::base::{CFTypeRef, TCFType};
|
||||
use std::{ffi::CString, os::raw::c_char, os::unix::ffi::OsStrExt, path::Path, ptr};
|
||||
|
||||
declare_TCFType! {
|
||||
/// Keychains which store cryptographic keys, passwords, and other secrets.
|
||||
///
|
||||
/// Wrapper for the `SecKeychain`/`SecKeychainRef` types:
|
||||
/// <https://developer.apple.com/documentation/security/seckeychainref>
|
||||
Keychain, KeychainRef
|
||||
}
|
||||
|
||||
impl_TCFType!(Keychain, KeychainRef, SecKeychainGetTypeID);
|
||||
|
||||
impl Keychain {
|
||||
/// Find the default keychain. Returns an `Error` result with a kind of
|
||||
/// `ErrorKind::NoDefaultKeychain` if there is no default keychain.
|
||||
///
|
||||
/// This is a non-panicking alternative to `Keychain::default()`.
|
||||
///
|
||||
/// Wrapper for the `SecKeychainCopyDefault` function. See:
|
||||
/// <https://developer.apple.com/documentation/security/1400743-seckeychaincopydefault>
|
||||
pub fn find_default() -> Result<Keychain, Error> {
|
||||
let mut result: KeychainRef = ptr::null_mut();
|
||||
let status = unsafe { SecKeychainCopyDefault(&mut result) };
|
||||
|
||||
if let Some(e) = Error::maybe_from_OSStatus(status) {
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(unsafe { Keychain::wrap_under_create_rule(result) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new keychain. Accepts a path where the new keychain will be
|
||||
/// located along with an optional password. If no password is given, the
|
||||
/// user will be prompted for a password.
|
||||
///
|
||||
/// Wrapper for the `SecKeychainCreate` function. See:
|
||||
/// <https://developer.apple.com/documentation/security/1401214-seckeychaincreate>
|
||||
pub fn create(path: &Path, password: Option<&str>) -> Result<Keychain, Error> {
|
||||
let path_cstring = CString::new(path.as_os_str().as_bytes()).unwrap();
|
||||
let mut result: KeychainRef = ptr::null_mut();
|
||||
|
||||
let status = match password {
|
||||
Some(pw) => unsafe {
|
||||
SecKeychainCreate(
|
||||
path_cstring.as_ptr() as *const c_char,
|
||||
pw.len() as u32,
|
||||
pw.as_bytes().as_ptr() as *const c_char,
|
||||
false,
|
||||
ptr::null(),
|
||||
&mut result,
|
||||
)
|
||||
},
|
||||
None => unsafe {
|
||||
SecKeychainCreate(
|
||||
path_cstring.as_ptr() as *const c_char,
|
||||
0,
|
||||
ptr::null(),
|
||||
true,
|
||||
ptr::null(),
|
||||
&mut result,
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(e) = Error::maybe_from_OSStatus(status) {
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(unsafe { Keychain::wrap_under_create_rule(result) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete this keychain.
|
||||
///
|
||||
/// Wrapper for the `SecKeychainDelete` function. See:
|
||||
/// <https://developer.apple.com/documentation/security/1395206-seckeychaindelete>
|
||||
pub fn delete(self) -> Result<(), Error> {
|
||||
let status = unsafe { SecKeychainDelete(self.as_concrete_TypeRef()) };
|
||||
|
||||
if let Some(e) = Error::maybe_from_OSStatus(status) {
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Find an item in this keychain.
|
||||
///
|
||||
/// This is a private method we wrap using builders for querying various
|
||||
/// keychain item types.
|
||||
///
|
||||
/// Wrapper for `SecItemCopyMatching`. See:
|
||||
/// <https://developer.apple.com/documentation/security/1398306-secitemcopymatching>
|
||||
fn find_item(&self, mut attrs: DictionaryBuilder) -> Result<Item, Error> {
|
||||
attrs.add(unsafe { kSecMatchLimit }, &MatchLimit::One.as_CFType());
|
||||
attrs.add_boolean(unsafe { kSecReturnRef }, true);
|
||||
|
||||
let mut result: ItemRef = ptr::null_mut();
|
||||
let status = unsafe {
|
||||
SecItemCopyMatching(
|
||||
Dictionary::from(attrs).as_concrete_TypeRef(),
|
||||
&mut result as &mut CFTypeRef,
|
||||
)
|
||||
};
|
||||
|
||||
// Return an error if the status was unsuccessful
|
||||
if let Some(e) = Error::maybe_from_OSStatus(status) {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
Ok(unsafe { Item::wrap_under_create_rule(result) })
|
||||
}
|
||||
|
||||
/// Add an item to this keychain.
|
||||
///
|
||||
/// This is a private method we wrap using builders for various keychain
|
||||
/// item types.
|
||||
///
|
||||
/// Wrapper for the `SecItemAdd` function. See:
|
||||
/// <https://developer.apple.com/documentation/security/1401659-secitemadd>
|
||||
fn add_item(&self, mut attrs: DictionaryBuilder) -> Result<Item, Error> {
|
||||
attrs.add(unsafe { kSecUseKeychain }, self);
|
||||
attrs.add_boolean(unsafe { kSecReturnRef }, true);
|
||||
|
||||
let mut result: ItemRef = ptr::null_mut();
|
||||
let status =
|
||||
unsafe { SecItemAdd(Dictionary::from(attrs).as_concrete_TypeRef(), &mut result) };
|
||||
|
||||
if let Some(e) = Error::maybe_from_OSStatus(status) {
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(unsafe { Item::wrap_under_create_rule(result) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Keychain {
|
||||
fn default() -> Keychain {
|
||||
Self::find_default().expect("no default keychain available")
|
||||
}
|
||||
}
|
||||
67
__security/keychain-services/keychain-services.rs/src/lib.rs
Normal file
67
__security/keychain-services/keychain-services.rs/src/lib.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
//! macOS Keychain Services wrapper for accessing the system and user's
|
||||
//! cryptographic keychains, as well as keys stored in the Secure Enclave
|
||||
//! Processor (SEP).
|
||||
//!
|
||||
//! This crate provides a thin, low-level binding with a safe, mostly idiomatic
|
||||
//! Rust API. Ideally however, it should be wrapped up in higher level, easy-to-use
|
||||
//! libraries, as the API it presents is rather complicated and arcane.
|
||||
//!
|
||||
//! For more information on Keychain Services`, see:
|
||||
//! <https://developer.apple.com/documentation/security/keychain_services/keychains>
|
||||
//!
|
||||
//! ## Code Signing
|
||||
//!
|
||||
//! The Keychain Service API requires signed code to access much of its
|
||||
//! functionality. Accessing many APIs from an unsigned app will return
|
||||
//! an error with a kind of `ErrorKind::MissingEntitlement`.
|
||||
//!
|
||||
//! Follow the instructions here to create a self-signed code signing certificate:
|
||||
//! <https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html>
|
||||
//!
|
||||
//! You will need to use the [codesign] command-line utility (or XCode) to sign
|
||||
//! your code before it will be able to access most Keychain Services API
|
||||
//! functionality. When you sign, you will need an entitlements file which
|
||||
//! grants access to the Keychain Services API. Below is an example:
|
||||
//!
|
||||
//! ```xml
|
||||
//! <?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>keychain-access-groups</key>
|
||||
//! <array>
|
||||
//! <string>$(AppIdentifierPrefix)com.example.MyApplication</string>
|
||||
//! </array>
|
||||
//! </dict>
|
||||
//! </plist>
|
||||
//! ```
|
||||
//!
|
||||
//! [codesign]: https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html#//apple_ref/doc/uid/TP40005929-CH4-SW4
|
||||
|
||||
#![crate_name = "keychain_services"]
|
||||
#![crate_type = "rlib"]
|
||||
#![allow(non_snake_case, non_upper_case_globals)]
|
||||
#![deny(warnings, missing_docs, unused_import_braces, unused_qualifications)]
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
compile_error!("This crate presently only compiles on macOS (see GH issue #5 for iOS support)");
|
||||
|
||||
#[macro_use]
|
||||
extern crate core_foundation;
|
||||
|
||||
mod access;
|
||||
mod attr;
|
||||
mod ciphertext;
|
||||
mod dictionary;
|
||||
mod error;
|
||||
mod ffi;
|
||||
pub mod keychain;
|
||||
mod signature;
|
||||
|
||||
pub use crate::access::*;
|
||||
pub use crate::attr::*;
|
||||
pub use crate::ciphertext::*;
|
||||
pub use crate::error::*;
|
||||
pub use crate::key::*;
|
||||
pub use crate::keychain::*;
|
||||
pub use crate::signature::*;
|
||||
@@ -0,0 +1,48 @@
|
||||
//! Signatures produced by this library.
|
||||
//!
|
||||
//! This type doesn't map directly to any type in the Keychain Services API,
|
||||
//! but instead provides a newtype for signatures this binding produces.
|
||||
|
||||
use crate::key::KeyAlgorithm;
|
||||
|
||||
/// Cryptographic signatures
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Signature {
|
||||
alg: KeyAlgorithm,
|
||||
bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Signature {
|
||||
/// Create a new `Signature`
|
||||
pub(crate) fn new(alg: KeyAlgorithm, bytes: Vec<u8>) -> Self {
|
||||
// TODO: restrict valid algorithms to signature algorithms?
|
||||
Self { alg, bytes }
|
||||
}
|
||||
|
||||
/// Get the algorithm which produced this signature
|
||||
pub fn algorithm(&self) -> KeyAlgorithm {
|
||||
self.alg
|
||||
}
|
||||
|
||||
/// Borrow the signature data as bytes
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
|
||||
/// Convert into a byte vector
|
||||
pub fn into_vec(self) -> Vec<u8> {
|
||||
self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Signature {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signature> for Vec<u8> {
|
||||
fn from(sig: Signature) -> Vec<u8> {
|
||||
sig.into_vec()
|
||||
}
|
||||
}
|
||||
176
__security/keychain-services/keychain-services.rs/tests/core.rs
Normal file
176
__security/keychain-services/keychain-services.rs/tests/core.rs
Normal file
@@ -0,0 +1,176 @@
|
||||
//! Core suite of tests which should work on all supported macOS platforms.
|
||||
//!
|
||||
//! This suite is mainly intended to run in CI. See `tests/interactive.rs`
|
||||
//! for notes on how to run the full test suite.
|
||||
|
||||
use keychain_services::*;
|
||||
|
||||
const TEST_MESSAGE: &[u8] = b"Embed confidential information in items that you store in a keychain";
|
||||
|
||||
/// Soft ECDSA key support
|
||||
#[test]
|
||||
fn generate_and_sign_with_generate_ecdsa_keys() {
|
||||
let acl =
|
||||
AccessControl::create_with_flags(AttrAccessible::WhenUnlocked, Default::default()).unwrap();
|
||||
|
||||
let generate_params =
|
||||
KeyPairGenerateParams::new(AttrKeyType::EcSecPrimeRandom, 256).access_control(&acl);
|
||||
|
||||
let keypair = KeyPair::generate(generate_params).unwrap();
|
||||
|
||||
let public_key_bytes = keypair.public_key.to_external_representation().unwrap();
|
||||
|
||||
let signature = keypair
|
||||
.private_key
|
||||
.sign(KeyAlgorithm::ECDSASignatureMessageX962SHA256, TEST_MESSAGE)
|
||||
.unwrap();
|
||||
|
||||
ring::signature::verify(
|
||||
&ring::signature::ECDSA_P256_SHA256_ASN1,
|
||||
untrusted::Input::from(&public_key_bytes),
|
||||
untrusted::Input::from(TEST_MESSAGE),
|
||||
untrusted::Input::from(signature.as_ref()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let res = keypair.public_key.verify(TEST_MESSAGE, &signature);
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
let res = keypair.public_key.verify(&[0u8, 0u8], &signature);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
/// Soft ECDSA key support with new functions
|
||||
#[test]
|
||||
fn generate_and_sign_with_create_ecdsa_keys() {
|
||||
let acl =
|
||||
AccessControl::create_with_flags(AttrAccessible::WhenUnlocked, Default::default()).unwrap();
|
||||
|
||||
let generate_params =
|
||||
KeyPairGenerateParams::new(AttrKeyType::EcSecPrimeRandom, 256).access_control(&acl);
|
||||
|
||||
let keypair = KeyPair::create(generate_params).unwrap();
|
||||
|
||||
let public_key_bytes = keypair.public_key.to_external_representation().unwrap();
|
||||
|
||||
let signature = keypair
|
||||
.private_key
|
||||
.sign(KeyAlgorithm::ECDSASignatureMessageX962SHA256, TEST_MESSAGE)
|
||||
.unwrap();
|
||||
|
||||
ring::signature::verify(
|
||||
&ring::signature::ECDSA_P256_SHA256_ASN1,
|
||||
untrusted::Input::from(&public_key_bytes),
|
||||
untrusted::Input::from(TEST_MESSAGE),
|
||||
untrusted::Input::from(signature.as_ref()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let res = keypair.public_key.verify(TEST_MESSAGE, &signature);
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
let res = keypair.public_key.verify(&[0u8, 0u8], &signature);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
/// Soft ECDSA key create from external representation
|
||||
#[test]
|
||||
fn export_and_import_ecdsa_keys() {
|
||||
let acl =
|
||||
AccessControl::create_with_flags(AttrAccessible::WhenUnlocked, Default::default()).unwrap();
|
||||
|
||||
let generate_params =
|
||||
KeyPairGenerateParams::new(AttrKeyType::EcSecPrimeRandom, 256).access_control(&acl);
|
||||
|
||||
let keypair = KeyPair::create(generate_params).unwrap();
|
||||
|
||||
let public_key_bytes = keypair.public_key.to_external_representation().unwrap();
|
||||
|
||||
let restore_params = RestoreKeyParams {
|
||||
key_type: AttrKeyType::EcSecPrimeRandom,
|
||||
key_data: public_key_bytes.clone(),
|
||||
key_class: AttrKeyClass::Public,
|
||||
};
|
||||
|
||||
let res = Key::from_external_representation(restore_params);
|
||||
|
||||
assert!(res.is_ok());
|
||||
let public_key = res.unwrap();
|
||||
let pub1bytes = public_key.application_tag().map(|t| t.as_bytes().to_vec());
|
||||
let pub2bytes = keypair
|
||||
.public_key
|
||||
.application_tag()
|
||||
.map(|t| t.as_bytes().to_vec());
|
||||
assert_eq!(pub1bytes, pub2bytes);
|
||||
|
||||
let restore_params = RestoreKeyParams {
|
||||
key_type: AttrKeyType::EcSecPrimeRandom,
|
||||
key_data: public_key_bytes,
|
||||
key_class: AttrKeyClass::Private,
|
||||
};
|
||||
|
||||
let res = Key::from_external_representation(restore_params);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_and_use_rsa_keys() {
|
||||
let acl =
|
||||
AccessControl::create_with_flags(AttrAccessible::WhenUnlocked, Default::default()).unwrap();
|
||||
|
||||
let generate_params = KeyPairGenerateParams::new(AttrKeyType::Rsa, 2048).access_control(&acl);
|
||||
|
||||
let keypair = KeyPair::create(generate_params).unwrap();
|
||||
|
||||
let signature = keypair
|
||||
.private_key
|
||||
.sign(KeyAlgorithm::RSASignatureMessagePSSSHA256, TEST_MESSAGE)
|
||||
.unwrap();
|
||||
|
||||
let public_key_bytes = keypair.public_key.to_external_representation().unwrap();
|
||||
|
||||
let res = ring::signature::verify(
|
||||
&ring::signature::RSA_PSS_2048_8192_SHA256,
|
||||
untrusted::Input::from(&public_key_bytes),
|
||||
untrusted::Input::from(TEST_MESSAGE),
|
||||
untrusted::Input::from(signature.as_ref()),
|
||||
);
|
||||
assert!(res.is_ok());
|
||||
|
||||
let res = keypair.public_key.verify(TEST_MESSAGE, &signature);
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
let res = keypair.public_key.verify(&[0u8, 0u8], &signature);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encrypt_and_decrypt_rsa_keys() {
|
||||
let acl =
|
||||
AccessControl::create_with_flags(AttrAccessible::WhenUnlocked, Default::default()).unwrap();
|
||||
|
||||
let generate_params = KeyPairGenerateParams::new(AttrKeyType::Rsa, 2048).access_control(&acl);
|
||||
|
||||
let keypair = KeyPair::create(generate_params).unwrap();
|
||||
|
||||
let ciphertext = keypair
|
||||
.public_key
|
||||
.encrypt(KeyAlgorithm::RSAEncryptionOAEPSHA256, TEST_MESSAGE)
|
||||
.unwrap();
|
||||
|
||||
let res = keypair.private_key.decrypt(ciphertext);
|
||||
assert!(res.is_ok());
|
||||
assert_eq!(res.unwrap(), TEST_MESSAGE);
|
||||
let ciphertext = Ciphertext::new(KeyAlgorithm::RSAEncryptionOAEPSHA256, vec![0u8, 0u8]);
|
||||
let res = keypair.private_key.decrypt(ciphertext);
|
||||
assert!(res.is_err());
|
||||
|
||||
assert!(!keypair
|
||||
.private_key
|
||||
.is_supported(KeyOperation::Encrypt, KeyAlgorithm::RSAEncryptionOAEPSHA256));
|
||||
let res = keypair
|
||||
.private_key
|
||||
.encrypt(KeyAlgorithm::RSAEncryptionOAEPSHA256, TEST_MESSAGE);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
#![cfg(feature = "interactive-tests")]
|
||||
|
||||
//! Interactive tests intended to be manually run by a person.
|
||||
//!
|
||||
//! These tests require a signed `target/debug/interactive-*` executable in
|
||||
//! order to pass. To sign the test executable, you'll first need to
|
||||
//! create a self-signed code signing certificate, see the
|
||||
//! "To obtain a self-signed certificate using Certificate Assistant"
|
||||
//! section of:
|
||||
//!
|
||||
//! <https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html>
|
||||
|
||||
// TODO: these tests presently fail (possibly due to a codesigning issue?)
|
||||
|
||||
use keychain_services::*;
|
||||
use tempfile::TempDir;
|
||||
|
||||
const TEST_PASSWORD: &str = "test password. do not really use";
|
||||
|
||||
/// Creates a temporary keychain in a temporary directory
|
||||
struct TempKeychain {
|
||||
pub dir: TempDir,
|
||||
pub keychain: Keychain,
|
||||
}
|
||||
|
||||
/// Create a temporary keychain we can use for testing
|
||||
fn temp_keychain() -> TempKeychain {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let keychain =
|
||||
Keychain::create(&dir.path().join("test-keychain"), Some(TEST_PASSWORD)).unwrap();
|
||||
|
||||
TempKeychain { dir, keychain }
|
||||
}
|
||||
|
||||
/// Generate a `key::Pair` for testing purposes
|
||||
fn generate_keypair(tag: &str, label: &str) -> KeyPair {
|
||||
let acl =
|
||||
AccessControl::create_with_flags(AttrAccessible::WhenUnlocked, Default::default()).unwrap();
|
||||
|
||||
let generate_params = KeyPairGenerateParams::new(AttrKeyType::EcSecPrimeRandom, 256)
|
||||
.access_control(&acl)
|
||||
.application_tag(tag)
|
||||
.label(label)
|
||||
.permanent(true);
|
||||
|
||||
KeyPair::generate(generate_params).unwrap()
|
||||
}
|
||||
|
||||
/// Queries for secret keys
|
||||
#[test]
|
||||
fn key_query() {
|
||||
let keypair = generate_keypair(
|
||||
"rs.keychain-services.test.integration.query",
|
||||
"keychain-services.rs integration test query key",
|
||||
);
|
||||
|
||||
let private_key_query = keychain::item::Query::new()
|
||||
.key_class(AttrKeyClass::Private)
|
||||
.key_type(AttrKeyType::EcSecPrimeRandom)
|
||||
.application_label(keypair.public_key.application_label().unwrap());
|
||||
|
||||
let private_key = Key::find(private_key_query).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
keypair.private_key.application_label(),
|
||||
private_key.application_label()
|
||||
);
|
||||
}
|
||||
|
||||
/// Passwords
|
||||
#[test]
|
||||
fn store_and_retrieve_passwords() {
|
||||
let tmp = temp_keychain();
|
||||
let service = "example.com";
|
||||
let account = "example";
|
||||
|
||||
let keychain_item =
|
||||
keychain::item::GenericPassword::create(&tmp.keychain, service, account, TEST_PASSWORD)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(keychain_item.service().unwrap(), service);
|
||||
assert_eq!(keychain_item.account().unwrap(), account);
|
||||
assert_eq!(keychain_item.password().unwrap().as_str(), TEST_PASSWORD);
|
||||
}
|
||||
|
||||
///
|
||||
#[test]
|
||||
fn key_delete() {
|
||||
let acl =
|
||||
AccessControl::create_with_flags(AttrAccessible::WhenUnlocked, Default::default()).unwrap();
|
||||
|
||||
let generate_params = KeyPairGenerateParams::new(AttrKeyType::EcSecPrimeRandom, 256).access_control(&acl)
|
||||
.permanent(true);
|
||||
|
||||
let keypair = KeyPair::generate(generate_params).unwrap();
|
||||
let res = keypair.private_key.delete();
|
||||
println!("{:?}", res);
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?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>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)rs.keychain-services.tests</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
34
__security/keychain-services/src/main.rs
Normal file
34
__security/keychain-services/src/main.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use keychain_services::{AccessControl, AttrAccessible, AttrKeyClass,
|
||||
AttrKeyType, Key, keychain, KeyPair, KeyPairGenerateParams};
|
||||
|
||||
fn main() {
|
||||
let keypair = generate_keypair(
|
||||
"rs.keychain-services.test.integration.query",
|
||||
"keychain-services.rs integration test query key",
|
||||
);
|
||||
|
||||
let private_key_query = keychain::item::Query::new()
|
||||
.key_class(AttrKeyClass::Private)
|
||||
.key_type(AttrKeyType::EcSecPrimeRandom)
|
||||
.application_label(keypair.public_key.application_label().unwrap());
|
||||
|
||||
let private_key = Key::find(private_key_query).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
keypair.private_key.application_label(),
|
||||
private_key.application_label()
|
||||
);
|
||||
}
|
||||
|
||||
fn generate_keypair(tag: &str, label: &str) -> KeyPair {
|
||||
let acl =
|
||||
AccessControl::create_with_flags(AttrAccessible::WhenUnlocked, Default::default()).unwrap();
|
||||
|
||||
let generate_params = KeyPairGenerateParams::new(AttrKeyType::EcSecPrimeRandom, 256)
|
||||
.access_control(&acl)
|
||||
.application_tag(tag)
|
||||
.label(label)
|
||||
.permanent(true);
|
||||
|
||||
KeyPair::generate(generate_params).unwrap()
|
||||
}
|
||||
10
__security/keychain-services/test.entitlements
Normal file
10
__security/keychain-services/test.entitlements
Normal file
@@ -0,0 +1,10 @@
|
||||
<?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>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)rs.keychain-services.tests</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
Reference in New Issue
Block a user