On Tue, Oct 10, 2023 at 12:07:51PM +0200, David Bürgin wrote: > When you inspect the following key with ‘openssl pkey’, it works fine: > > -----BEGIN PUBLIC KEY----- > MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9oaro18Mt4FITtXvhy/v2N0d0 > aQJ285MgstG5QSgvFnXA+7Bww20hnLQZD4vOZbeIhdu4g5s8S6LWczqswDjVyD97 > 9j+RcZM+JcnHPEIvkn7YCKYnM3mvSQKmeRtm9kDVL0waKf+iZ5ZDYiLcfXCSIDnT > 2SMxp3D9UNEfEZDMoQIDAQABMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDK > A5hv8tKZBw3cx+j0NMrbsOY5QfoUtxGeXjmGI89q63iFxBdSgrJW5wpthZfHcVHl > roPW885ToeSrEdyUIVCokR7L8PP7Up0PGXUDPIFCQB7+jVV8ezLyxHSLGT81u7Be > el5ybAgsal/GmhpeQXcEpnYpiqVcHL3XTlY8+34EQQIDAQAB > -----END PUBLIC KEY----- > > However, there is something odd about it, openssl seems to be > interested only in the first half of the key data: > > Well, that key actually contains two, concatenated > SubjectPublicKeyInfos! I noticed this when I first used a different > library, and processing failed. The data between the PEM "BEGIN" and "END" markers is supposed to be a block of base64 text that contains an ASN.1 DER encoding of the object in question. In this case we see, that indeed there are two back-to-back DER SPKI structures: $ openssl asn1parse <<EOF -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9oaro18Mt4FITtXvhy/v2N0d0 aQJ285MgstG5QSgvFnXA+7Bww20hnLQZD4vOZbeIhdu4g5s8S6LWczqswDjVyD97 9j+RcZM+JcnHPEIvkn7YCKYnM3mvSQKmeRtm9kDVL0waKf+iZ5ZDYiLcfXCSIDnT 2SMxp3D9UNEfEZDMoQIDAQABMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDK A5hv8tKZBw3cx+j0NMrbsOY5QfoUtxGeXjmGI89q63iFxBdSgrJW5wpthZfHcVHl roPW885ToeSrEdyUIVCokR7L8PP7Up0PGXUDPIFCQB7+jVV8ezLyxHSLGT81u7Be el5ybAgsal/GmhpeQXcEpnYpiqVcHL3XTlY8+34EQQIDAQAB -----END PUBLIC KEY----- EOF 0:d=0 hl=3 l= 159 cons: SEQUENCE 3:d=1 hl=2 l= 13 cons: SEQUENCE 5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption 16:d=2 hl=2 l= 0 prim: NULL 18:d=1 hl=3 l= 141 prim: BIT STRING 162:d=0 hl=3 l= 159 cons: SEQUENCE 165:d=1 hl=2 l= 13 cons: SEQUENCE 167:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption 178:d=2 hl=2 l= 0 prim: NULL 180:d=1 hl=3 l= 141 prim: BIT STRING Because ASN.1 objects have an explicit length, it is possible to process just the first 162 bytes (3 byte header + 159 data), without checking that this represents the entire content of the base64-encoded input. > • Is openssl right in accepting this key? Why does it use only the > first one? OpenSSL "pkey" application (and perhaps more generally, the underlying PEM_read_bio_PUBKEY(3)) did not check that the entire input buffer was consumed, so only the first structure is processed. The only plausible outcomes are to extract the first structure and ignore excess data, or to the reject the input due to the excess data. Note that the PEM layer relies on the "d2i" functions to read the DER-encoded data, and the "d2i" functions support reading a portion of an input buffer, filling in the decoded structure and returning a pointer to the tail of the input stream, this supports reading of arrays of DER-encoded objects, or just a single DER encoded object in a larger "packet". So what happens here, is that the PEM layer does not compare the "d2i" end pointer with the end of the input buffer and the "trailing data" is simply ignored. The check that's "missing" would be similar to: https://github.com/vdukhovni/postfix/blob/master/postfix/src/tls/tls_certkey.c#L208-L214 > • Is the other library wrong in rejecting this key? I don't think it is wrong to be strict. > • Do relevant RFCs say something about such a ‘concatenated’ format? I haven't seen anything that clearly prohibits ignoring excess input, but there is certainly no expectation that it would be used. It can either be rejected or ignored. I don't see any explicit rules about this in: https://datatracker.ietf.org/doc/html/rfc7468 Perhaps I did not read it closely enough, or the relevant normative rules are in some other document. -- Viktor.