Well. There are two proposals. If we go with the subkey proposal, we'd not implement the simple protocol, solving that issue.
> Signer is setup to have the whole input in memory at once
It's fine.
> This text says very little about validating trusted keys. A signature where you just take the pubkey from the input
That would be silly. APT would maintain a list of allowed public primary keys. The spec hints at that with "Clients shall verify all signatures from supported algorithms in the Release file, whether a given key is considered trusted or not."
> No obvious affordance for additional signed metadata, which is useful for forensics. So, for example, if a project has multiple parties with access to the keys they may want to tag which user(s) authorized the signature or included a timestamp. If their signing is performed by a HSM (e.g. something like USB armory that allows no user access) the consistency of their metadata could be preserved. This could probably be kludged on by requiring the metadata just be inside the payload somewhere.
We don't have a use for that. The feature set of GPG is already over the top.
> It's not entirely clear to me that it's supported to hand out multiple subkeys to different parties all with the same serial, and they stay all equally valid until a key is observed with a newer serial (at which point all subkeys need to be updated)
Yeah, the serial exists to revoke all priorily issued subkeys, effectively.
You've blatantly forgotten the implication of TOFU regarding new debian keys. Without a way to certify debian's keys, there's no way to verify that installation media is correct any more. With PGP, there was the web of trust. Now there is TLS at best, no?
It seems the proposed specification lacks a crucial addition of keysigning.
Avoiding that concrete exploit.
The lack of domain separation design flaw remains. :)
As typical for design flaws it may not be meaningfully exploitable at any given time. Doesn't mean it isn't a design flaw.
This is annoying, but ultimately I guess not super relevant, as it needs to verify with APT, and that's about it in terms of requirements. And APT doesn't change it's crypto backend all the time, and we don't need to care about alternative implementations. It does mean we can't switch the crypto library inside APT I guess.
Does ed448 have the same issue? Do we have hardware tokens that support ed448?
The C++ code right now uses libnettle to verify signatures for the PoC.
I do feel their discussion about not using separators to avoid coding string splitting actually makes it more complicated to code around variable lengths.
(I'm finding that to be a great reading on validation)
But that's not what that sentence means at all. It meant that APT might start just with support for ed25519, as IIRC I was told that ed448 is overkill right now.
I successfully have signed and verified signatures using Ed448 using the branch of python-aptsign for it.
> I do feel their discussion about not using separators to avoid coding string splitting actually makes it more complicated to code around variable lengths.
I don't follow
like in C++, I can
std::string algo, encoded;
in >> algo >> encoded;
if (algo == ed25519) base64_decode(encoded, ed25519_pointer); return std::move(ed25519_pointer); else if (algo == ed448) base64_decode(encoded, ed448_pointer); return std::move(ed448_pointer);
Where the _pointer are std::unique_ptr of Ed25519Signature and Ed448Signature which are each subclasses of Signature, and the return type hence std::unique_ptr<Signature>.
Both of these options have different lengths, and if both were supported you've have to split based on.. magic?
It's been 20 years since I looked at C++, so I don't understand how you're splitting the public key from the signature given they are one concatenated blob, but that could just be me.
In C++ the blob can just be directly decoded into a struct, though. e.g. the initial format looked like this:
struct signature
{
uint8_t primary_key[ED25519_KEY_SIZE] = {};
struct
{
uint8_t pubkey[ED25519_KEY_SIZE];
unaligned_little<uint64_t> expiry; // seconds since epoch
unaligned_little<uint32_t> serial; // serial number
} subkey;
uint8_t subkey_signature[ED25519_SIGNATURE_SIZE] = {};
uint8_t signature[ED25519_SIGNATURE_SIZE] = {};
bool decode(std::string_view view);
}
and then signature::decode effectively just needs to call a base64_decode(this, sizeof(*this), view.data(), view.size()).
(given a base64 decode function that allowed a fixed size output buffer)You can see there are magic unaligned integer types in there so that the whole thing is not architecture specific.
The entire sample is in
https://gist.github.com/julian-klode/4514ce39d3dc62647b502e5...
Obviously it does not fully match the current spec.
If you wanted to support both structs, you could just try decoding as one or the other, or change the spec to use ed25519 and ed25519-subkey algorithm names :D
Signer is setup to have the whole input in memory at once. The protocol design makes it tricky to do otherwise without using two passes at best. This could be avoided if anyone cared, e.g. using a signing scheme with prehashing (which has its own obscure tradeoffs).
This text says very little about validating trusted keys. A signature where you just take the pubkey from the input and don't validate it is essentially just a really computationally expensive hash and doesn't itself provide security. The text about validating all signatures regardless of trusted or not could be read as saying that you just accept stuff with valid signature regardless if they were being signed by the Evil League of Evil instead of an approved source. :)
It uses ed25519 but doesn't do anything about inconsistent validation criteria ( https://hdevalence.ca/blog/2020-10-04-its-25519am ), different implementations consider non-overlapping sets of signatures valid. If you want to have automated klaxons on signature failures, this ought to be addressed for the same reason as "repositories do not fail for a subset of users". (though it's not fatal in this application as it is in consensus systems).
The format will be a little clunky if used with post-quantum signatures, e.g. sphincs+ would have a couple kilobytes of signature data stuffed on a line. I don't have a real recommendation there other than saying you should probably have test vectors that have some couple-kilobyte-long examples.
No obvious affordance for additional signed metadata, which is useful for forensics. So, for example, if a project has multiple parties with access to the keys they may want to tag which user(s) authorized the signature or included a timestamp. If their signing is performed by a HSM (e.g. something like USB armory that allows no user access) the consistency of their metadata could be preserved. This could probably be kludged on by requiring the metadata just be inside the payload somewhere.
I like that there are subkeys with expiration. It's not entirely clear to me that it's supported to hand out multiple subkeys to different parties all with the same serial, and they stay all equally valid until a key is observed with a newer serial (at which point all subkeys need to be updated)? "Subkeys may reuse/share serial numbers provided" is confusing on this point. Good security practices suggest that if there are multiple places that can sign each should get its own subkey (so if there is a compromise you can isolate it), and this might mean that there are multiple subkeys in parallel all valid for a given trusted master.
Personally, I'm a little disappointed to see a proposal for repository signing that doesn't include affordances for something like opentimestamps or a certificate-transparency like facility (e.g. to validate that parties aren't making backdoored alternative versions for private distribution). But I guess these could be overlaid as additional signature types and the idea is just to phase out pgp, not advance the security state of the art forward.
Authors of this proposal should be aware the NSA has made a formal recommendation against the use of ECC with curves smaller than 384-bits. There are plenty of applications where compatibility, performance, or computational constraints make it attractive to stay with 256 bit options. But if I were doing something entirely greenfield where a double size key/signature would be a non-issue, I'd be really tempted to use ed448 instead: If nothing else, at least if there are problems in the future you at least didn't violate existing credible advice. :) (Really I'd want to use sphincs+ for something like this, but it's probably too premature). Considering how close to security theater package signing often is in practice, I'm not sure if it's worth worrying too much that the underlying cryptosystem might be stronger-- it's not the limiting factor.