Support ssh-rsa public key format (RFC 4253)
Currently the RSA class only supports the PKCS#1 (RFC 3447) standard. This is perfect for almost all cases except for when interfacing with SSH. RFC 4253 reverses the two public key parameters which makes using Botan with SSH keys impossible (without extensive workarounds).
This could be solved in a couple ways:
- Create a special SSHRSA_PublicKey class that extends the RSA_PublicKey classes. Invert the n and e reading and writing as necessary, including the fingerprint.
- Add provisions to the existing RSA_PublicKey class to handle an algorithm identifier of "ssh-rsa" differently than "rsa".
- Create stand-alone helper functions that use existing RSA_PublicKey but ensure the n and e are read/wrote in the correct order.
I am willing to do the legwork to make this possible, which is preferred by @randombit?
Since SSH format is completely specific to SSH, and only supports a subset of the algorithms implemented, I would prefer to keep it out of the main public key code, so 1/2 seem not good.
3 is fine, but I think there is a cleaner approach - write a generic SSHPublicKey type which can encapsulate any SSH key type. Internally it holds a unique_ptr<Public_Key> of the decoded type. It would look something like
SSH_Format_Reader rdr(buf); // see TLS_Data_Reader for similar
m_tag = rdr.get_string(buf);
if(m_tag == "ssh-rsa") {
BigInt e = rdr.get_mpint();
BigInt n = rdr.get_mpint();
m_key = std::make_unique<Public_Key>(new RSA_PublicKey(n, e));
}
else if(m_tag == "ssh-ecdsa") {
...
}
else throw Decoding_Error(...);
[Feel free to just support RSA if that is all you need - this still gives us a place to hang support for ECDSA or Ed25519 later]
Then you can similarly implement re-encoding, fingerprint generation, or whatever else SSH-specific functionality that is useful. You could also (if needed/desired) implement on SSHPublicKey operations like verify that verify signatures using the SSH specific formatting and rules.
This is all a very different approach from how PK algorithms work in the rest of the library, where each key type has a distinct class, and cryptographic operations are mediated through classes like PK_Verifier. But, it's how in retrospect the rest of the library probably should have been done which is why I recommended a similar course of action for PGP in https://github.com/randombit/botan/issues/2499#issuecomment-729837778
Excellent idea, this is actually how we have it implemented in keepassxc currently. Although not for any Cryptographic operations, we just read the key to hand over to pageant or ssh-agent. Still a pain in the butt with a lot of overhead code. Otherwise Botan reduced our codebase by 3,500 lines!