Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

YubiKey plugin #25

Closed
wants to merge 11 commits into from
Closed

YubiKey plugin #25

wants to merge 11 commits into from

Conversation

str4d
Copy link
Owner

@str4d str4d commented Dec 2, 2019

Discuss this draft on the age-dev mailing list thread!

Current draft specification:

age PIV identities use ECC P-256 keys, generated on the hardware token, with certificates that never expire.

For hardware tokens that support PIN and/or touch policies (such as YubiKeys), the default PIN policy is "once per session", and the default touch policy is "every decryption".

A PIV recipient has the following form:

Bech32("age1piv", SEC-1-C(public key))

where SEC-1-C is the 33-byte compressed SEC-1 encoding.

A PIV recipient line is of the form:

-> piv encode(SHA-256(recipient)[:4]) encode(SEC-1-C(ECDH(ephemeral secret, p256-basepoint)))\n
encode(encrypt[HKDF[salt, label](ECDH(ephemeral secret, public key))](file key))\n

where ephemeral secret is a random scalar within the scalar field of P-256 and MUST be new for every file key, salt is SEC-1-C(ECDH(ephemeral secret, basepoint)) || SEC-1-C(public key), and label is age-encryption.org/v1/piv.

A YubiKey "identity" is managed locally as a key stub with the following form:

Uppercase(Bech32("age-yubikey-stub-", serial || slot || SHA-256(recipient)[:4]))

The stub may exist in files alongside age X25519 secret keys, and is similarly passed to the age / rage binary with the -i flag.

Note that the common tag in the recipient line and key stub means that recipients can trivially identify whether they can decrypt a particular recipient line, at the cost of making recipients linkable across different encrypted files.


Usage example (responses out-of-date):

$ cargo install --path . --features yubikey
$ rage-keygen --yubikey -o keystub.txt
Enter PIN for YubiKey with serial 12345678: [hidden]
Use the up/down arrow keys to select a PIV slot (q to quit): Retired(R1) (Empty)
Select a PIN policy: Once   (A PIN is required once per session, if set)
Select a touch policy: Always (A physical touch is required for every decryption),
Generate new key in Retired(R1) slot? yes
$ cat keystub.txt 
# created: 2019-12-07T23:13:09Z
# yubikey:A_y2TWFFIZ8AhuFCjpxGzt_qiZrwMfEyDG6M8fqgG3ET
AGE_YUBIKEY_STUB_00bc614e_9d_6ecc74ff
$ echo "YubiKey FTW!" | rage -o test.age -r yubikey:A_y2TWFFIZ8AhuFCjpxGzt_qiZrwMfEyDG6M8fqgG3ET
$ rage -d -i keystub.txt test.age
Enter PIN for YubiKey with serial 12345678: [hidden]
YubiKey FTW!

@codecov
Copy link

codecov bot commented Dec 2, 2019

Codecov Report

Merging #25 into master will decrease coverage by 6.79%.
The diff coverage is 8.78%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #25      +/-   ##
==========================================
- Coverage   39.64%   32.85%   -6.80%     
==========================================
  Files          24       33       +9     
  Lines        2023     2624     +601     
==========================================
+ Hits          802      862      +60     
- Misses       1221     1762     +541     
Impacted Files Coverage Δ
age-core/src/format.rs 82.22% <ø> (-0.76%) ⬇️
age-plugin-yubikey/src/format.rs 0.00% <0.00%> (ø)
age-plugin-yubikey/src/main.rs 0.00% <0.00%> (ø)
age-plugin-yubikey/src/p256.rs 0.00% <0.00%> (ø)
age-plugin-yubikey/src/plugin.rs 0.00% <0.00%> (ø)
age-plugin/examples/age-plugin-unencrypted.rs 0.00% <0.00%> (ø)
age-plugin/src/identity.rs 0.00% <0.00%> (ø)
age/src/cli_common.rs 0.00% <0.00%> (ø)
age/src/lib.rs 100.00% <ø> (ø)
age/src/plugin.rs 0.00% <0.00%> (ø)
... and 23 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9f274b6...f8a6f4d. Read the comment docs.

@str4d str4d force-pushed the yubikey branch 5 times, most recently from 462075e to 65ad1ae Compare December 8, 2019 16:55
@str4d str4d force-pushed the yubikey branch 7 times, most recently from de4f7be to d21c9ca Compare December 18, 2019 17:52
@str4d str4d changed the title Native Yubikey support Native PIV / Yubikey support Dec 23, 2019
@str4d str4d force-pushed the yubikey branch 3 times, most recently from a128496 to 0e16ffa Compare December 29, 2019 05:23
@str4d
Copy link
Owner Author

str4d commented Jan 7, 2020

UX notes from demoing the current draft implementation to @FiloSottile:

  • I'm going to turn this from a native part of rage into the first age plugin (so that age can use this for YubiKey support prior to having a Go implementation).
    • This will require figuring out how to handle the common PIV part vs the YubiKey-specific part within a plugin system. This is likely to be a common issue, so we should think about it while designing the plugin system.
  • YubiKey setup should not be in rage-keygen (which should only be for X25519). Instead make this a separate binary, e.g. rage-yubikey.
    • This will probably be the same binary that is used in the plugin system (enabling the plugin API with e.g. --age-plugin).
  • Setup UX improvements:
    • Define a way to unambiguously mark slots as generated for age use (i.e. an age-global magic string in the Common Name, rather than just rage-keygen).
    • For slots with existing keys containing the magic string (and with the correct algorithm), show created date, public key.
    • For slots that do not match the magic or algorithm, keep the existing information, but add something like "NOT AN AGE KEY" to the output.
    • Remove the special "Key Management" slot to simplify the UI.
    • Rename "Retired(N)" to "Slot N".

@jstasiak
Copy link

Hey, a person looking for a simple encryption tool with hardware key support here – this looks like a great feature for an already nice tool.

Is there a plan to support uploading existing keys to a YubiKey? Is it even possible? Here's my use case for this: generate a keypair on an airgapped computer booted from some live CD distro, then copy it to a YubiKey device and create a password-protected backup for storage on a usb stick for extra safety in case I lose or damage my hardware key. This way my regular machine never sees the private key yet I have it securely backed up just in case.

@tarcieri
Copy link

@jstasiak yes, that can be done with import_private_key, although it hasn't been tested and really needs some refactoring

@str4d
Copy link
Owner Author

str4d commented Jan 22, 2020

That would be a very useful feature! Once the aforementioned refactor into a plugin has occurred, it will be much easier to support this kind of customized key setup for a particular recipient type.

@str4d str4d force-pushed the yubikey branch 2 times, most recently from 4376c5d to 7258d07 Compare January 26, 2020 16:19
@jstasiak
Copy link

jstasiak commented Feb 1, 2020

BTW which YubiKeys this is expected to be compatible with?

@str4d
Copy link
Owner Author

str4d commented Feb 1, 2020

Any that support secp256r1 for PIV, and that the yubikey-piv crate supports. Currently that means YubiKey 4 series and 5 series (including Nano and USB C).

@str4d str4d force-pushed the yubikey branch 3 times, most recently from e2aa4ff to ad99744 Compare February 2, 2020 13:45
@gitirabassi
Copy link

Is there a specific reason this is blocked? Can I help?

@str4d
Copy link
Owner Author

str4d commented Apr 7, 2020

@gitirabassi This is currently blocked on the design of the age plugin system (which is how we are going to deploy this), because the recipient and stub formats will be changing as a result. I've reworked this PR locally on top of the draft plugin interface (#99), and the next step is exercising the plugin API and ironing out the kinks.

@str4d str4d force-pushed the yubikey branch 2 times, most recently from e154c13 to ad6e48e Compare August 30, 2020 16:53
@str4d str4d changed the title Native PIV / Yubikey support YubiKey plugin Aug 30, 2020
YubiKeys are managed as age identities via a "stub" that indicates the
slot to be used on a particular YubiKey. The stub can be placed
alongside any age keys, and tells rage that it should attempt to decrypt
matching YubiKey recipient lines.
Generates stubs for existing keys, and new keys for empty slots.
@str4d
Copy link
Owner Author

str4d commented Aug 30, 2020

I've rebased this PR on #99, and this is now working as a plugin. However, it adds a default requirement for anyone building from the rage repo on Unix to have libpcsclite installed, so I think I will move the age-plugin-yubikey crate into a separate repository.

@str4d
Copy link
Owner Author

str4d commented Aug 30, 2020

Migrated to str4d/age-plugin-yubikey#1.

@str4d str4d closed this Aug 30, 2020
@str4d str4d deleted the yubikey branch August 30, 2020 22:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants