-
Notifications
You must be signed in to change notification settings - Fork 273
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
Support custom signing implementations in Metadata.sign method #1263
Support custom signing implementations in Metadata.sign method #1263
Comments
A very basic implementation could look something like this: # in securesystemslib
class Signer:
@abc.abstractmethod
def sign(bytes: payload) -> Signature:
raise NotImplementedError
class SSlibSigner(Signer):
def __init__(self, sslib_private_key):
self.key = sslib_private_key
def sign(self, payload):
return sslib_keys.create_signature(self.key, payload)
class GPGSigner(Signer):
def __init__(self, gpg_keyid):
self.keyid = gpg_keyid
def sign(self, payload):
return sslib_gpg.create_signature(payload, self.keyid)
# in TUF
class Metadata:
def sign(sslib.Signer: signer):
signer.sign(self.signed.to_canonical_bytes())
# in Warehouse
class VaultSigner(Signer):
def sign(self, payload):
# ... you get the idea
metadata = tuf.Metadata(...)
metadata.sign(VaultSigner(...))
|
Great idea. I would add only that it might be useful to add in SSLib itself... |
Agreed. |
Thank you for the detailed issue @lukpueh. Bit of an aside, but do you imagine the GPG and HSM signing interfaces in securesystemslib becoming implementations of the interface we create here? That seems logical to me, but I'm not familiar with the GPG and HSM signing interfaces. |
I would suggest so, yes. Do you have reservations? |
No reservations, just checking whether my expectation was reasonable. Thank you. |
I'd like to add some ontological thoughts here. In particular, whether the What speaks against combining Signer and Key in one class: What speaks for it: Outlook I have already summarized the main tasks for keys (private and public!) in secure-systems-lab/securesystemslib#310, albeit with a focus on public keys, which need to implement serialization to and deserialization from TUF metadata format (secure-systems-lab/securesystemslib#308) and verification. Both may or may not be tied to the protocol that also implements the signing. @trishankatdatadog, maybe you can share your experience from implementing tuf-on-a-plane? |
I am asking myself does the I am not sure if we have a Maybe we can look the other way around and have a |
It's a good question. I didn't implement any signing interface, only verification interfaces. But, if it helps, what I found is that rather than designing on pen and paper, writing, trying, and feeling the code worked out very well. So try writing what comes naturally, and see if it makes sense? I know I sound like a mystic, but that's what worked for me. |
Thanks for your comments, @MVrachev and @trishankatdatadog!
Yes they are, but there are some variables that change for the signer but not for the key, such as padding, hashing, scheme, etc. To me it feels a bit odd to modify the key object, in order to use a different signing scheme, e.g.: k = SigningKey(...) # Initialize once e.g. with RSA secret exponent
k.scheme = "rsassa-pss-sha256"
k.sign(data) # Sign using one scheme
k.scheme = "rsa-pkcs1v15-sha512"
k.sign(data) # Sign using another scheme But maybe switching schemes during life time is just not a use case, and even then, the odd looks alone don't seem to be a strong argument against it. An alternative would be to accept these variables as a parameters to
Yes, but I think there is an argument for separating public and private key in the model. If they were combined, we'd often have half-empty objects, e.g. on the client where only the public parts are available. And even in the signing context we only really need the private portion. Furthermore, given that public keys are included in TUF metadata their class representation should look similar to the metadata representation for recognizability and have a focus on type/schema checking and serialization tasks. Whereas we care a lot less about what the private key class looks like as long as it can be used for signing. Does this make sense? I have a feeling that hands-on @trishankatdatadog might say, I'm overthinking this. :P |
This requires some meditation TBH. There are generic cryptographic algorithms and then there are specific schemes. What code do we expect delegators and delegatees to write? I would approach it that way. |
It makes sense and those are valid points if you ask me. Additional thinks we discussed with @jku:
Jussi looked into what is the returned value of some of the possible implementors of the
So, to summarize it seems achievable to return |
That does make sense. But then every class Metadata:
...
def sign(self, signer):
# NOTE: Let's assume signatures are still in the old/current dictionary format
self.signatures.append({
"keyid": signer.keyid,
"sig": signer.sign(self.signed.to_canonical_bytes())
}) I think your proposal makes the
Yeah, unfortunately these "other_headers" are required for verification. Although it might be possible to return them as part of the signature bytes and then parse upon verification. EDIT: FYI, this is where we parse GPG signature packets. |
...custom fields like the one on gpg signatures might also be an argument for |
It seems I misunderstand @lukpueh. Lukas seems to mean to combine
You have a point here. We can indeed create a |
I'm fine with
SGTM! :)
I'm a bit unsure about that. I think in most cases As a consequence a concrete |
Yes, you are right. Those arguments are logical. Today we had a quick discussion with @jku about the |
I think it was mostly that it seemed like an object isn't needed in this case at all (I'm assuming that usually when you sign something you are doing that to create metadata for the purpose of serializing it: an array of bytes is fine for that)? Also I don't know how the opposite case (verifying portions of metadata while de-serializing it) is going to look like and that seemed more relevant for a potential Signature object? As in how is Metadata API used with e.g. Vault in that case? |
@jku, this is roughly how canonicalization, signing/verifying and de/serialization currently play together:
Note that this might change in the future. Currently we are discussing an alternative protocol, which does not require parsing the full payload before verifying the signature (see Secure Systems Lab signing specification). Especially, in the light of that new proposal and the likely short-livedness of signature objects I agree with you that a class model might not be needed. However, I think a class helps to clearly define the required signature format, even if it only consists of two fields (keyid and signature bytes). And it also aligns with our decision in ADR 4 to create classes for all complex metadata attributes, e.g. in order to add self-validation behavior. |
I have pushed the initial version of the signing interface and the necessary TUF changes to support it in the prs linked just above this. comment. |
While working on SSlibSigner and GPGSigner I realized that if we want to store the necessary information to verify the signatures with For the SSlibSigner, we would need to add the key_dict: https://github.com/secure-systems-lab/securesystemslib/blob/dff4425e5663c58c954447a698efb17c4b23b0f8/securesystemslib/keys.py#L738 and for the GPGSigner, we would need to add the |
Because of the above problem, I am not sure if I can create a relationship between the Signature class and the GPGSignature. With the observations from the above comment, I made the the problem is that for GPGSignature I won't need the The logical solution will be to create a separate What should we do from here? @lukpueh? |
@MVrachev, I thought you agreed with my arguments above for not implementing verification as method of the |
Yes, we agreed that we would do the verification elsewhere. |
I will add a |
Description of issue or feature request:
Allow TUF integrators to easily use custom metadata signing implementations instead of the currently supported securesystemslib one.
An urgent use case for this is the PEP458/Warehouse integration, where metadata must be signed with keys stored in a Hashicorp Vault and the implementation is already available.
NOTE: This feature request only concerns the
tuf.api.Metadata.sign
method and not the legacy signing facilitieswrite
andwriteall
in repository tool.Current behavior:
tuf.api.Metadata.sign(key, ...)
callssecuresystemslib.keys.create_signature
with the passed sslib/json-formatted private key.Expected behavior:
tuf.api.Metadata.sign
takes an abstract signer parameter, which implements signing. A default signer may resolve to the currently usedsecuresystemslib.keys.create_signature
.Implementation considerations:
Different signers require completely different "keys", e.g. the currently used generic implementation requires an sslib/json-formatted private key, whereas signing implementations that don't have direct access to the key may only need a keyid (see GPG or HSM references below). As a consequence the key parameter in
Metadata.sign
should probably be encapsulated within the signer.The abstract signer interface must strictly define the format of the returned signature for metadata interoperability.
The abstract signer interface design should also keep in mind other key-related tasks, such as signature verification, public key export to sslib metadata format, and keyid generation.
securesystemslib.keys.create_signature
is already an abstract interface, where the json-formatted private key parameter format is generic enough to hold different key types and to choose one of many supported signing algorithms, identified by the key'sscheme
field. (see public key metadata format reference below). However, it is not suited to extend to custom implementations. A new abstract signer interface should integrate well with the existing securesystemslib infrastructure and/or aim to replace it. It should not become yet another co-existing interface and/or layer of abstraction (see public API overview reference below).Related work and references: (for the very ambitious reader)
securesystemslib key overview (to coordinate this feature request with the existing infrastructure)
Recently added abstract filesystem interface (for design inspiration) -- Support abstract files and directories #1009
@trishankatdatadog's Hashicorp Vault interface POC (as reference for PEP458/warehouse implementation and for design inspiration) -- VaultKey
Abstract
pyca/cryptograhpy
key interface with sign method (for design inspiration) -- RSAPrivateKey"Historical" external signing API feature request (has some interesting discussion and designs but targets legacy TUF codebase) -- External Signing API, or CCID/PIV Support #864
in-toto metadata model sign methods (alternative but less flexible approach to support different signing implementations)
The text was updated successfully, but these errors were encountered: