yess
enables splitting secrets into shares using a threshold schema that requires e.g. 3 out of 4 shares to successfully recombine. These shares are furthermore encrypted using the PIV interface of compatible Yubikeys. This enables workflows where shares are protected by physical devices that are hard to clone and can be protected through additional physical security measures (e.g. safes). Since only a subset of devices is neccessary to recombine, operations are still possible even if a devices breaks or get lost.
DO NOT USE THIS TOOL FOR ++ANY++ PURPOSE YET - REVIEW(S) ARE STILL PENDING
Three Yubikeys yk1, yk2 and yk3 have been prepared in advance (using the yubikey-piv-tool
, ykman
or the Yubikey Manager GUI) by
- enabling the PIV interface (enabled by default) and creating "Key Management" (encryption) certificates (currently only ECC keys are supported)
- optionally disabling the OTP interface (enabled by default) in order to prevent accidently "typing" in OTP codes - this (or USB extension cords) should be considered when dealing with very small (e.g. USB-C) form factors or "nano" keys
Next a secret is piped into yess
like this: echo my-secret | yess split --parts 3 --threshold 2 > result.json
. yess
now asks the user to insert the Yubikeys one-by-one and enter their respective PINs. After this succeeds, yess
outputs a metadata file like this on stdout
:
{
"parts": [
{
"device": "A",
"expiry": "2021-02-25T00:00:00Z",
"issuer": "CN=acme inc",
"publicKey": "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAECgXCraDGX1xN8HfvOpGAPY2Jmp56bRBWLE0vIVxk4CsyDnPyiWF3Vq3gI1KsWaMZxyXRk+mUprPbbu32pUEv4/a9b7zYwte8lsL4n9DS92EKZbkqxSEa4Xd2kI2klZlz",
"serial": 1,
"share": "U/VNWIT1+ZqYwbwanJ/5FZITpP2xBQM2QQilK7uunh2K6gSRvcxnmFNtShebbh+9Xxd4dPZ+U3aqKx3IT3FSFtZL",
"subject": "CN=mr. a"
},
{
"device": "B",
"expiry": "2021-02-26T00:00:00Z",
"issuer": "CN=bcme inc",
"publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEs8WjfkQMzZaaCj7UltEtzLDJwdox1QhFPMQBDqJN0EhT/egUfo+2gC4ibWGpH8PsKrJKJP+F3OIQcX0ZTbUNVg==",
"serial": 2,
"share": "k9YI2Yzpr5gTYtuyu1giI5oeWSmFSOVxx82QinbCJJFRANuN4TvBKyQHsedca2ZrAYGm59ci1ZeE1A3F7MVP",
"subject": "CN=ms. b"
},
{
"device": "C",
"expiry": "2021-02-27T00:00:00Z",
"issuer": "CN=ccme inc",
"publicKey": "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEWW5+qZV4rgceOFHw/M/kxpE/5DrQyben5vDwM0cxNCt2dpoNIksQloDnrE58gVVl0kKl5zXQ7zUNYsWLr//rveBHiFEVcYhZOiahMELPa0QqPWJR0+50kCxJ3G9btKbX",
"serial": 3,
"share": "6aWlG3fx2dpf6AeSnX7UMlFkdF0aBB6+nMRubqTCZloXvIXT+2spOu0nLs4EcOL3ChhUwv9wJodssUrI",
"subject": "CN=mrs. c"
}
],
"threshold": 2
}
Next the metadata is piped into yess
like this: cat result.json | yess combine
. yess
presents the list of candidate devices and asks the user to insert at least 2 Yubikeys (= the threshold from above) out of this list one-by-one and enter their respective PINs. After this succeeds, yess
outputs the secret on stdout
.
Operating on
- secret s
- parts p=3
- threshold t=2
Currently only ECC keys are supported.
- Apply SHA3-256 hash on s and concat resulting hash h to s, yielding sh
- Split sh into p parts using Shamir Secret Sharing with p and t, yielding p times shp (shps)
- For each shp
- generate ephemeral ECC keypair ekp / eks matching the curve of the device public key (dkp)
- perform key exchange with eks and dkp, yielding shared ephemeral key sk
- derive a 32-bit key dk from sk using SHA3-256
- encrypt shp using a NacL secretbox, with dk as key and zero as nonce (since keys are ephemeral anyways) yielding shpe
- store shpe and the public key pk of ek in metadata to allow for later recovery
- For each shpe
- recover sk by calling
Decrypt
on device using ekp - derive dk from sk using SHA3-256
- decrypt shpe using a NacL secretbox, with dk as key and zero as nonce (since keys are ephemeral anyways) yielding shp
- recover sk by calling
- As soon as t has been passed, attempt a Shamir Secret Sharing recovery and continue with the loop if that fails
- Split sh into s and h and verify that the SHA3-256 hash of s is equal to h and continue with loop if that fails
If no failure occurs, the secret s has been recovered.