-
Notifications
You must be signed in to change notification settings - Fork 297
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
sign: Support x509 signature type #3278
base: main
Are you sure you want to change the base?
Conversation
Hi @ueno. Thanks for your PR. I'm waiting for a ostreedev member to verify that this patch is reasonable to test. If it is, they should reply with Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
554697d
to
70035e3
Compare
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.
Thank you so much for this well written patch!
Can you elaborate a bit more on why specficially you chose to work on this now? The rationale makes sense, but...are you planning to actually use Ed448 somewhere?
if (pkey == NULL && public_key_size == OSTREE_SIGN_ED25519_PUBKEY_SIZE) | ||
pkey = EVP_PKEY_new_raw_public_key (EVP_PKEY_ED25519, NULL, public_key_buf, public_key_size); |
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'd be curious if in practice other key types happen to exactly match the ed25519 size...not saying you should spend a lot of time digging to find out, but if you happen to know offhand, that'd be interesting.
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 doubt it, but this is one of the reasons I think the PKCS8 handling should be in a separate backend and we should leave the existing raw ed25519 handling alone. Since signers and clients will need to be updated to handle the PKCS8 encoded keys, it might as well be "opt-in" in the form of a new signing type. Then the existing ed25519 signer can be left alone and there's no ambiguity about the key format it expects.
Certainly I think you'd want to have some common code for signing and verification once you've loaded the keys, though.
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 doubt it, but this is one of the reasons I think the PKCS8 handling should be in a separate backend
Yeah...this makes total sense to me. What do you think @ueno ?
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 agree; if we go that way, the following might be worth considering:
- Keys are not always restricted to particular signing parameters (e.g., hash in ECDSA); perhaps it might make sense to embed signing parameters in the signature itself in that case
- The current key format (one key per line) is not easy to deal with for longer keys (~2K with ML-DSA); maybe the new backend should support PEM/DER blocks instead (this could require additional API in OsteeSign)
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.
It's still in progress but I've updated the PR with a new signing backend "x509". As a next step, I'll try to add the PEM based keysfile and make it default for the new backend (if that sounds ok), and also reduce the code duplication between "ed25519" and "x509" backends.
I haven't reviewed the code yet, but this is awesome and how it should have been done initially. If you can inspect the key algorithm from a standard format, then there's no reason to specify what type of key it is. Again, I haven't looked at the changes, but my question now becomes what to do with the "sign types". On the one hand, I kind of want to get rid of it entirely since the only reason to specify the type is to know how to interpret the keys. But if the key type can be automatically determined, then there's no need for that. However, there's also the dummy sign type, which is just used for testing. I think the way I would handle this is:
|
I guess technically it's PKCS8 formatted private keys and X.509 SubjectPublicKeyInfo (SPKI) formatted public keys. So, |
Thank you for the reviews and comments so far!
My main motivation here is to prepare for the upcoming PQC transition. While encryption (key encapsulation) is the current priority as it affects forward secrecy, signatures will come next, and it would be nice if ostree could support PQC signatures transparently. |
Suggested in: ostreedev#3278 (comment) Signed-off-by: Daiki Ueno <dueno@redhat.com>
Got it! Makes sense...I think that's the kind of thing that's good to have in the commit message too. |
Suggested in: ostreedev#3278 (comment) Signed-off-by: Daiki Ueno <dueno@redhat.com>
@cgwalters @dbnicholson There is still room for refactoring, but this is mostly feature complete from my point of view. PQ signatures can be used through oqsprovider in both pure/hybrid schemes: # Create SPHINCS+ public and secret keys
$ openssl genpkey -provider oqsprovider -algorithm sphincssha2128fsimple -outform PEM -out sphincssha2128fsimple-sk.pem
$ openssl pkey -provider oqsprovider -outform PEM -pubout -in sphincssha2128fsimple-sk.pem -out sphincssha2128fsimple-pk.pem
# Set up an OSTree repo
$ mkdir repo work
$ cd work
$ ../ostree --repo=../repo init
# Make a signed commit
$ echo > a.txt
$ ../ostree --repo=../repo commit -b main -s "Signed with x509 module" --sign-from-file ../sphincssha2128fsimple-sk.pem --sign-type=x509
fac8d8df984644e6b814be093c3a5f60f746d90f1aacea020cd73dc22b2c33ce
$ COMMIT=$(../ostree --repo=../repo rev-parse main)
$ ../ostree --repo=../repo sign --verify --sign-type=x509 --keys-file=../sphincssha2128fsimple-pk.pem $COMMIT
x509: Signature verified successfully with key '302d300806062bce0f06040d0321007aac408c97612b9974785e2fcbcc35bfd4b31f7b95b0fc1026485dc8df66c95b' For hybrid schemes, e.g., Ed25519+ML-KEM (Dilithium), you can simply sign the same commit twice for each algorithm. |
dc7f0e8
to
9ec192a
Compare
/ok-to-test |
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.
Thanks so much for this! It overall looks really good! I mostly have nits and questions.
}; | ||
|
||
#define PEM_SUFFIX "-----" | ||
#define PEM_PREFIX_BEGIN "-----BEGIN " |
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.
OpenSSL really doesn't have something for this? A lot of new string parsing in C is pretty unfortunate.
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.
OpenSSL provides PEM_* functions indeed, though they don't fit in our use-case reading as many PEM blocks incrementally from a file (stream); PEM_read
expects a single complete PEM block, while PEM_read_bio
requires to implement a BIO method backed by GInputStream (that wouldn't be difficult, but cumbersome as it also requires to use correct memory allocators: OpenSSL vs. GLib).
tests/test-pem.c
Outdated
main (int argc, char **argv) | ||
{ | ||
g_test_init (&argc, &argv, NULL); | ||
g_test_add_func ("/ostree_read_pem_block", test_ostree_read_pem_block); |
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.
Thanks for unit testing this! We should also add some corrupted/invalid test cases...this type of code is very fuzzable and we do have a fuzzer wired up here https://github.com/google/oss-fuzz/tree/master/projects/ostree
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 added a couple more tests exercising invalid cases. Would it be OK to add fuzzing in a separate PR?
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.
Yep fuzzing is nonblocking
|
||
gsize n_elements; | ||
g_base64_decode_inplace (line, &n_elements); | ||
explicit_bzero (line + n_elements, len - n_elements); |
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.
This is worthy of the same comment you have in a similar place /* Don't leak the trailing encoded bytes */
later in the code (maybe also worthy of a shared helper function?).
But also...I don't fully grasp the threat model here...aren't the decoded bytes equally sensitive? In what cases would the trailing data in the malloc buffer be somehow accessible when the full buffer wouldn't?
Don't get me wrong, not arguing against it, but I would just like to understand a little bit more.
src/libostree/ostree-sign-ed25519.c
Outdated
return ret; | ||
|
||
/* Read the key itself */ | ||
/* base64 encoded key */ | ||
pk = g_variant_new_string (line); | ||
pk = g_variant_new_fixed_array (G_VARIANT_TYPE ("y"), g_bytes_get_data (blob, NULL), |
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.
Hmm isn't this...oh, I see ostree_sign_ed25519_add_pk
already accepted both strings and byte arrays.
src/libostree/ostree-sign-x509.c
Outdated
return FALSE; | ||
|
||
if (signatures == NULL) | ||
return glnx_throw (error, "x509: commit have no signatures of my type"); |
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.
Under what scenarios would this happen out of curiosity? Don't we just want g_assert (signatures != NULL);
?
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 tried this, but got an test failure:
ERROR: tests/test-signed-pull.sh - Bail out! OSTreeSign:ERROR:src/libostree/ostree-sign-x509.c:169:ostree_sign_x509_data_verify: assertion failed: (signatures != NULL)
Looks like ostree_sign_commit_verify
may call ostree_sign_data_verify
with NULL signatures
. Is there any case where this is supposed to succeed?
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.
Probably not but yeah let's continue to throw an error
But can you elaborate just a bit more - I'm just guessing here: did you do an audit of code shipped e.g. in RHEL linking to openssl? Do you happen to know about ostree specifically beforehand? |
Signed-off-by: Daiki Ueno <dueno@redhat.com>
Suggested in: ostreedev#3278 (comment) Signed-off-by: Daiki Ueno <dueno@redhat.com>
This defines a new interface OstreeBlobReader, which encapsulates the key file parsing logic. This would make it easy to support custom file formats such as PEM. Signed-off-by: Daiki Ueno <dueno@redhat.com>
This adds a new class OstreePemReader, which reads PEM blocks from an input stream. This would be useful for the "x509" signing backend, as the keys are typically stored in the PEM format. Signed-off-by: Daiki Ueno <dueno@redhat.com>
Signed-off-by: Daiki Ueno <dueno@redhat.com>
f070034
to
af338b8
Compare
Sorry, I keep meaning to give some time to reviewing this. The one thing I'll say now is that I don't think My suggested names would be:
|
Mild preference for |
I don't have strong opinion about the naming, but colleagues suggested On a slightly different note, while this PR only supports SubjectPublicKeyInfo (SPKI) based raw public keys (as in RFC 7250), I was wondering if it could eventually support X.509 certificates as well to take advantage of system trust store (either backed by p11-kit or file/directory based storage on |
After a second thought, I'm leaning to |
The current "ed25519" signing type assumes raw Ed25519 key format for both public and private keys. That requires custom processing of keys after generated with openssl tools, and also lacks cryptographic agility[1]; when Ed25519 becomes vulnerable, it would not be straightforward to migrate to other algorithms, such as post-quantum signature algorithms. This patch adds a new signature type "spki" which uses the X.509 SubjectPublicKeyInfo format for public keys. Keys in this format can easily be created with openssl tools and provide crypto agility as the format embeds algorithm identifier. Currently, the corresponding private keys shall be in the PKCS#8 format, while future extensions may support other format such as opaque key handles on a hardware token. The "spki" signature type prefers keys to be encoded in the PEM format on disk, while it still accepts base64 encoded keys when given through the command-line. 1. https://en.wikipedia.org/wiki/Cryptographic_agility Signed-off-by: Daiki Ueno <dueno@redhat.com>
af338b8
to
e83eda0
Compare
The current "ed25519" signing type assumes raw Ed25519 key format for
both public and private keys. That requires custom processing of keys
after generated with openssl tools, and also lacks cryptographic
agility[1]; when Ed25519 becomes vulnerable, it would not be
straightforward to migrate to other algorithms, such as post-quantum
signature algorithms.
This patch adds a new signature type "x509" to use the key formats
natively supported by OpenSSL (PKCS#8 and SubjectPublicKeyInfo) and
capable of embedding algorithm identifier in an X.509 format.
The "x509" signature type prefers keys to be encoded in the PEM
format on disk, while it still accepts base64 encoded keys when given
through the command-line.