-
Notifications
You must be signed in to change notification settings - Fork 395
Add containers_image_sequoia build tag to do simple signing verification using Sequoia-PGP, and add a signature/simplesequoia implementation #2876
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
Conversation
This adds a new OpenPGP signing mechanism based Sequoia-PGP[1]. As Sequoia-PGP is written in Rust and doesn't provide a stable C FFI, this integration includes a minimal shared library as a "point solution". To build, first follow the instruction in signature/sequoia/README.md and install `libimage_sequoia.so*` into the library path, and then build with the following command from the top-level directory: $ make BUILDTAGS="btrfs_noversion containers_image_sequoia" Note also that, for testing on Fedora, crypto-policies settings might need to be modified to explictly allow SHA-1 and 1024 bit RSA, as the testing keys in signature/fixtures are using those legacy algorithms. 1. https://sequoia-pgp.org/ Signed-off-by: Daiki Ueno <dueno@redhat.com>
8c6899b to
97955fb
Compare
| return Ok(()); | ||
| } | ||
| Err(verification_error) => { | ||
| signature_errors.push(verification_error.to_string()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a matter of taste, but instead of storing errors as a string, I would keep the error types and let the caller format them:
diff --git a/signature/internal/sequoia/rust/src/signature.rs b/signature/internal/sequoia/rust/src/signature.rs
index 014d4f5f..d0ba6cd3 100644
--- a/signature/internal/sequoia/rust/src/signature.rs
+++ b/signature/internal/sequoia/rust/src/signature.rs
@@ -169,6 +169,7 @@ impl<'a> SequoiaMechanism<'a> {
let h = Helper {
certstore: self.certstore.clone(),
signer: Default::default(),
+ errors: Default::default(),
};
let mut v = VerifierBuilder::from_bytes(signature)?.with_policy(&self.policy, None, h)?;
@@ -190,6 +191,7 @@ impl<'a> SequoiaMechanism<'a> {
struct Helper<'a> {
certstore: Arc<sequoia_cert_store::CertStore<'a>>,
signer: Option<openpgp::Cert>,
+ errors: Vec<openpgp::Error>,
}
impl<'a> VerificationHelper for Helper<'a> {
@@ -205,7 +207,6 @@ impl<'a> VerificationHelper for Helper<'a> {
}
fn check(&mut self, structure: MessageStructure) -> openpgp::Result<()> {
- let mut signature_errors: Vec<String> = Vec::new();
for layer in structure {
match layer {
MessageLayer::Compression { algo } => log::info!("Compressed using {}", algo),
@@ -219,7 +220,7 @@ impl<'a> VerificationHelper for Helper<'a> {
log::info!("Encrypted using {}", sym_algo);
}
}
- MessageLayer::SignatureGroup { ref results } => {
+ MessageLayer::SignatureGroup { results } => {
for result in results {
match result {
Ok(good_checksum) => {
@@ -227,19 +228,14 @@ impl<'a> VerificationHelper for Helper<'a> {
return Ok(());
}
Err(verification_error) => {
- signature_errors.push(verification_error.to_string());
+ self.errors.push(openpgp::Error::from(verification_error));
}
}
}
}
}
}
- let err = match signature_errors.len() {
- 0 => anyhow::anyhow!("No valid signature"),
- 1 => anyhow::anyhow!("{}", &signature_errors[0]),
- _ => anyhow::anyhow!("Multiple signature errors: [{}]", signature_errors.join(", ")),
- };
- Err(err)
+ Err(anyhow::anyhow!("No valid signature"))
}
}I also wonder if we might want to record all signers if there are multiple signatures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would keep the error types and let the caller format them:
Thanks, that might very well be cleaner. It would require a somewhat tight coupling with the caller — the caller would specifically need to know that it should ignore the returned “no valid signature” error.
I also wonder if we might want to record all signers if there are multiple signatures.
For myself, I think I need to study / understand what is the precise semantics of MessageStructure, whether it is possible to have multiple signatures, and what does that mean.
… and whether it is relevant to designing a stable C API, which is the ~most urgent work right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would keep the error types and let the caller format them:
Thanks, that might very well be cleaner. It would require a somewhat tight coupling with the caller — the caller would specifically need to know that it should ignore the returned “no valid signature” error.
Actually that's the intention behind this suggestion: if the errors are converted into a string at the callee, useful information that could be retrieved through VerificationError may be lost, or it would require parsing of the error strings later.
I also wonder if we might want to record all signers if there are multiple signatures.
For myself, I think I need to study / understand what is the precise semantics of
MessageStructure, whether it is possible to have multiple signatures, and what does that mean.… and whether it is relevant to designing a stable C API, which is the ~most urgent work right now.
MessageStructure can certainly have multiple signatures, though the current c/image API seems to only expect a single signature to be valid. For future extension with PQ/T multiple signatures, it might make sense to provide a control on which signature has to be valid and which can be ignored.
As for the C API, GPGME provides a similar abstraction with gpgme_verify_result_t, which also supports multiple signatures.
23e7645 to
7e16175
Compare
efcf53d to
461beea
Compare
signature/simplesequoia/signer.go
Outdated
| func WithPassphrase(passphrase string) Option { | ||
| return func(s *simpleSequoiaSigner) error { | ||
| // The gpgme implementation can’t use passphrase with \n; reject it here for consistent behavior. | ||
| // FIXME: We don’t need it in this API at all, but the "\n" check exists in the current call stack. That should go away. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Branch sequoia-refactor-SignDockerManifest, but that can wait.
| return nil, fmt.Errorf("Signing not supported: %w", err) | ||
| } | ||
|
|
||
| // Ideally, we should look up (and unlock?) the key at this point already. FIXME: is that possible? Anyway, low-priority. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, if needed we could reorganize the Rust API to allow that.
83975b0 to
e19a9d0
Compare
| sig := C.go_sequoia_sign( | ||
| m.mechanism, | ||
| cKeyIdentity, | ||
| cPassphrase, | ||
| (*C.uchar)(unsafe.Pointer(unsafe.SliceData(input))), | ||
| C.size_t(len(input)), | ||
| &cerr, | ||
| ) | ||
| if sig == nil { | ||
| defer C.go_sequoia_error_free(cerr) | ||
| return nil, errors.New(C.GoString(cerr.message)) | ||
| } | ||
| defer C.go_sequoia_signature_free(sig) | ||
| var size C.size_t | ||
| cData := C.go_sequoia_signature_get_data(sig, &size) | ||
| if size > C.size_t(C.INT_MAX) { | ||
| return nil, errors.New("overflow") | ||
| } | ||
| return C.GoBytes(unsafe.Pointer(cData), C.int(size)), nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mention it because we have been burned by this many times before and I haven't looked into the sequoia code to know for sure but is the sequoia library fully thread safe. And by that I mean is it using any thread local memory or other things that depend on the current thread? Because the go runtime can schedule us around in any thread at any time between these C calls which means go_sequoia_sign() and go_sequoia_signature_get_data() may get called from different threads so the underlying library must be able to handle this correctly
i.e. a simple example on how things can go wrong easily:
coreos/go-systemd@d1df97f
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Go side, this PR now uses a sync.Once to load the library. And afterwards, everything is supposed to go through a Go *SigningMechanism / Rust SequoiaMechanism, where serialization of operations on a single mechanism is up to the caller.
I have no idea whether Sequoia is internally consistently thread-safe; I do see some uses of OnceLock on static variables, so it is at least not naively unaware.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
serialization of operations on a single mechanism is up to the caller.
My concern was not so much about serialization but more about the use of stuff like thread local storage or other per thread things like a namespace for example. Between each C.somefunc call the go runtime can schedule us the code onto another thread. But because that is super unlikely normal testing will not uncover this most likely. It could end up being a very hard to find race condition later on. See the linked commit where the use of dlerror() causes some bugs because it stores the error in thread local memory.
I am not saying there is a problem here but just that we should check. I am sure @ueno or the other sequoia folks would know this
f7dbbbe to
7871386
Compare
0f89fa3 to
cb2c674
Compare
| @@ -0,0 +1,200 @@ | |||
| /* | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
signature/internal/sequoia/*.[ch] are auto-generated in https://github.com/ueno/podman-sequoia/tree/main/go/sequoia , only copied into this repo.
- Use the correct library name - Allow specifying a library path at compile time Example usage: > make BUILDTAGS="btrfs_noversion containers_image_sequoia" SEQUOIA_SONAME_DIR=$(pwd)/signature/internal/sequoia/rust/target/release Signed-off-by: Miloslav Trmač <mitr@redhat.com>
... to match the recent more specific subkey validation semantics. Should not change behavior. Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Should not change test behavior, merely to be more similar to the rest of the codebase. Signed-off-by: Miloslav Trmač <mitr@redhat.com>
…Mechanism Signed-off-by: Miloslav Trmač <mitr@redhat.com>
It can be handled in callers - and it's better to do based on .Init() outcomes. Signed-off-by: Miloslav Trmač <mitr@redhat.com>
- Allow using c/image without loading sequoia if GO_SEQUOIA_ENABLE_DLOPEN is used, and nothing uses the Sequoia code at runtime. - Allow adding ~independent users of signature/internal/sequoia, without having to coordinate to call Init at least once. We will add signature/simplesequoia to benefit from this. Signed-off-by: Miloslav Trmač <mitr@redhat.com>
We can't reasonably unit-test the $HOME variant; tested manually. Signed-off-by: Miloslav Trmač <mitr@redhat.com>
By default, the Rust logging output is silently discarded. Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Add missing tests, or add comments where that's not practical. Should not change behavior. Signed-off-by: Miloslav Trmač <mitr@redhat.com>
…ner API Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Should not change behavior. Signed-off-by: Miloslav Trmač <mitr@redhat.com>
CI images come from containers/automation_images#411 . Signed-off-by: Miloslav Trmač <mitr@redhat.com>
cb2c674 to
8cec77c
Compare
Sure, thanks for the reminder. I’ll move things after the dust settles. |
Luap99
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, I rather get this merged by EOD before the monorepo move to avoid having to redo all commits against the new repo structure.
Automation wise it means we must port the rawhide tests to the new repo but we can do this after the initial repo creation.
cc @jankaluza
lsm5
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, I rather get this merged by EOD before the monorepo move to avoid having to redo all commits against the new repo structure.
LGTM in that case
signature/internal/sequoia, add Go bindings for https://github.com/ueno/podman-sequoia . (Parts of the middle C code are auto-generated by that project.)containers_image_sequoiabuild tag:signature/simplesequoiaAPI for creating simple-signing signatures using Sequoia-PGPGNUPGHOME, are unaffected.containers_image_sequoiabuild tag; currently that requires Rawhide.Based on original contribution in #2569, with many thanks to @ueno .
DO NOT MERGE: Includes UNMERGED #2905 .