swh alter remove should produce backups with what has been removed from the archive. These backups should contain enough information to restore removed data in case of a mistake. Because they might contain sensitive data, they should be encrypted using a public encryption key. (The decryption key will be split between multiple entities.)
Design a format for the recovery bundles (!2 (merged))
$age-plugin-yubikey ✨ Let's get your YubiKey set up for age! ✨This tool can create a new age identity in a free slot of your YubiKey.It will generate an identity file that you can use with an age client,along with the corresponding recipient. You can also do this directlywith: age-plugin-yubikey --generateIf you are already using a YubiKey with age, you can select an existingslot to recreate its corresponding identity file and recipient.When asked below to select an option, use the up/down arrow keys tomake your choice, or press [Esc] or [q] to quit.🔑 Select a YubiKey: Yubico YubiKey OTP+FIDO+CCID 00 00 (Serial: 5229836)🕳️ Select a slot for your age identity: Slot 1 (Empty)📛 Name this identity [age identity TAG_HEX]: 🔤 Select a PIN policy: Never (A PIN is NOT required to decrypt)👆 Select a touch policy: Cached (A physical touch is required for decryption, and is cached for 15 seconds)Generate new identity in slot 1? yes🎲 Generating key...Enter PIN for YubiKey with serial 5229836 (default is 123456): [hidden]🔏 Generating certificate...👆 Please touch the YubiKey📝 File name to write this identity to: age-yubikey-identity-c3fa08e1.txt✅ Done! This YubiKey identity is ready to go.🔑 Here's your shiny new YubiKey recipient: age1yubikey1q2e37f74zzazz75mtggzql3at66pegemfnul0dtd7axctahljkvsqezscaqHere are some example things you can do with it:- Encrypt a file to this identity: $cat foo.txt | rage -r age1yubikey1q2e37f74zzazz75mtggzql3at66pegemfnul0dtd7axctahljkvsqezscaq -o foo.txt.age- Decrypt a file with this identity: $cat foo.txt.age | rage -d-i age-yubikey-identity-c3fa08e1.txt > foo.txt- Recreate the identity file: $age-plugin-yubikey -i--serial 5229836 --slot 1 > age-yubikey-identity-c3fa08e1.txt- Recreate the recipient: $age-plugin-yubikey -l--serial 5229836 --slot 1💭 Remember: everything breaks, have a backup plan for when this YubiKey does.
With the above settings, running rage -d -i age-yubikey-identity-c3fa08e1.txt will make the YubiKey blink. A touch will enable the decryption to perform. No messages appear on the console though.
$shamir create -S deadbeefdeadbeefdeadbeefdeafbeef 2of2Using master secret: deadbeefdeadbeefdeadbeefdeafbeefGroup 1 of 1 - 2 of 2 shares required:clock recover academic acid disaster tension regular human grin force faint museum satoshi listen royal stadium river oasis decent benefitclock recover academic agency course unkind traffic traveler lyrics armed august firefly empty cards listen darkness dream easy source spray
Encrypting them for two different YubiKeys:
echo -n "clock recover academic acid disaster tension regular human grin force faint museum satoshi listen royal stadium river oasis decent benefit" | rage -r age1yubikey1q2e37f74zzazz75mtggzql3at66pegemfnul0dtd7axctahljkvsqezscaq -o share-c3fa08e1.ageecho -n "clock recover academic agency course unkind traffic traveler lyrics armed august firefly empty cards listen darkness dream easy source spray" | rage -r age1yubikey1q04k5fs8cz6kypt7vjetl2gc2qtpz9lyzpxrvv2agzt6h8n3awmzk9sgd8v -o share-4c1bc5f1.age
decrypt.py:
importsubprocessdefmain():mnemonic_str_1=subprocess.check_output(["/home/lunar/.cargo/bin/rage","-d","-i","age-yubikey-identity-c3fa08e1.txt","share-c3fa08e1.age"]).decode('utf-8')mnemonic_str_2=subprocess.check_output(["/home/lunar/.cargo/bin/rage","-d","-i","age-yubikey-identity-4c1bc5f1.txt","share-4c1bc5f1.age"]).decode('utf-8')print(recover([mnemonic_str_1,mnemonic_str_2]).hex())defrecover(share_mnemonic_strs):fromshamir_mnemonic.recoveryimportRecoveryStatefromshamir_mnemonic.shareimportSharerecovery_state=RecoveryState()formnemonic_strinshare_mnemonic_strs:share=Share.from_mnemonic(mnemonic_str)recovery_state.add_share(share)assertrecovery_state.is_complete()# no passphrase has been set so leave it emptypassphrase=b""returnrecovery_state.recover(passphrase)if__name__=="__main__":main()