Goal
Last time I ran out of time implementing an OpenPGP credential, because I had to implement a bunch of scaffolding for executing programs in a convenient manner, and a SOP implementation in particular.
- Add tooling for running a SOP implementation to encrypt and decrypt data.
Plan
- Add a wrapper around
std::process::Command
to make it easy to run a command and inspect the result easier. - Implement a type for convenient execution of a SOP implementation.
Notes
A command runner
- The Rust standard library has the
std::process::Command
module for executing a program. It works well. - I find the least convenient part of using
Command
is to analyze the result and to deal with the various failure modes. It's not just "program ended with a non-zero exit code", there's so many other ways in which this can fail. - I have previously written a little Rust module to show how to handle
errors from
Command
. I will steal that to add a module to theobnam
crate. I'll try to make the module as self-contained as possible so I can make use of it other projects. Some day I may find a crate where I can put it so that I don't copy it from project to project. But right now that is premature. - Copied the module and made some improvements. At some point this will need a proper test suite, but I'll worry about that later. It'll probably be tricky.
- The module,
src/runner.rsx
in the source tree, gets asrc::process::Command
that has already been set up, runs that, and inspects the results. The big difference withCommand
is that it treats a non-zero exit code as an error.
A SOP abstraction
- The SOP specification is an IETF draft.
- To use SOP, one needs OpenPGP keys and certificates in files. From those keys, one can extract certificates. It seems convenient to only require the user to provide a key file and have Obnam extract the certificate.
- The basic operations we care about are:
sop extract-cert < KEY > CERT
sop encrypt CERT < PLAINTEXT > CIPHERTEXT
sop decrypt KEY < CIPHERTEXT > PLAINTEXT
- I will define a type
Sop
:
pub struct OpenPgpKey {...}
pub struct OpenPgpCert {...}
pub struct Sop {...}
impl Sop {
pub fn new(command: &OsStr) -> Self {...}
pub fn extract_cert(&self, key: &OpenPgpKey) -> Result<OpenPgpCert, SopError> {...}
pub fn encrypt(&self, certs: &[OpenPgpKey]) -> Result<Vec<u8>, SopError> {...}
pub fn decrypt(&self, key: &OpenPgpKey) -> Result<Vec<u8>, SopError> {...}
}
- The types for OpenPGP keys and certificates are there for type safety.
- Implemented certificate extraction.
- Implemented encryption.
- Tried to implement decryption, but having trouble getting it to
work.
sqop
says "Invalid data type". But it works when I run it manually. - Oh! It works if I feed the encrypted file to
decrypt
, not the plaintext file. That should not have been difficult. - Added verification scenarios for
obnam sop extract-cert
,encrypt
, anddecrypt
.
Tidy up
- Tidied up the history of today's branch.
- Created a patch for the Radicle repository.
- Waited for CI run to succeed. It did.
- Merged patch branch to
main
. - Pushed
main
to Radicle.
Summary
Today was mostly about creating building blocks to make OpenPGP credentials easier to implement. I got that done.
As an extra bonus twist, added an OpenPGP key to the Obnam subplot, to see how many people complain that I distribute a private key in Git.
Comments?
If you have feedback on this development session, please use the following fediverse thread: https://toot.liw.fi/@liw/114805474268611738.