Is this a bad certificate? (Having nine-bit long key usage extension)

Ingela Andin ingela.andin@REDACTED
Wed Sep 22 12:28:28 CEST 2021


Hi  Jesse!

Thank you very much for sending me the test data. I now know why it used to
work and is no longer working.  Indeed it has to do with re-encoding a
decode certificate however the place that is failing is not in the ssl
application code but in the public_key applications code, although it is a
change in the ssl application that causes the situation. Even if the root
cause is that the certificate is wrongly encoded in the first place.

So the first patch I made should not be needed as in this case we are
verifying that a possible sent ROOT cert matches the ROOT cert that we
trust. And if it is the same they will have the same possible incorrect DER
encoding protected by the signature and they will match.  That is we have
not performed an decode and then encoded it back again.

My PR, we will want to have to be able to tolerate such certificates (in a
different scenario than yours)  but we will need to add some additional
commit to handle your case.  So the change that made it fail is
optimization effort to avoid decoding certs multiple times.  Although, I
realized when looking into this issue, actually  instead creates a need for
encoding the certs again.  So before the public_key application got the
certs in DER and now they get them decoded. I think the "sane" thing will
be to allow public_key to also accept a list with both encoded (as received
from peer) and decoded.  This will avoid all extra encode and decode needs,
and allow for tolerance of some decoding errors in certificates that can be
"worked around".

Always these interop tradeoffs, well this will be best for performance
also. I will add it to my PR.

Regards Ingela Erlang/OTP team - Ericsson AB



Den ons 22 sep. 2021 kl 00:21 skrev Jesse Bickel - NOAA Affiliate <
jesse.bickel@REDACTED>:

> I apologize for the delayed response. Thank you all for working on this
> issue even though it may be more of a gnutls issue.
>
> Kenneth Lundin wrote:
> > In this case it is the encoding of KeyUsage which is the problem, it is
> questionable if it is correctly encoded by GnuTLS.
> >
> > It is declared like this:
> > KeyUsage ::= BIT STRING {
> >      digitalSignature        (0),
> >      nonRepudiation          (1),
> >      keyEncipherment         (2),
> >      dataEncipherment        (3),
> >      keyAgreement            (4),
> >      keyCertSign             (5),
> >      cRLSign                 (6),
> >      encipherOnly            (7),
> >      decipherOnly            (8) }
> >
> > And encoded like this by GnuTLS (as I understand it from Jesses initial
> description)
> > <<3,3,7,16#A0,0>> with the interpretation of bytes and bits like this:
> > Byte 0: Value 3:  The UNIVERSAL tag for BIT STRING
> > Byte 1: Value 3: Length = 3
> > Byte 2: Value 7: Number of unused bits in last octet = 7
> > Byte 3: Value 16#A0: (2#10100000) The first 8 bits of the bitstring with
> bit (0) digitalSignature and bit (2) keyEncipherment set
> > Byte 4: Value 0: The first bit is zero and the 7 remaining bits are
> unused
> >
> > If we read the standard docs below I interpret this as 1) we have a BIT
> STRING with a NamedBitList (22.7 in X.680 applies) and then we apply 11.2.1
> and
> > especially 11.2.2 from X.690 which says that trailing zero bits should
> be removed before encoding which I don't think has been done in the above
> case.
> >
> > So I think GnuTLS might be wrong here in that it does not encode
> according to DER.
>
> Yes, this is consistent with what I see. Gnutls keeps the trailing zero
> bits while openssl strips the trailing zero bits. But both tolerate each
> others' differing representations (for better or worse).
>
> > The encoding that Erlang/OTP is using which I claim is according to DER
> is like this:
> > <<3,2,5,16#A0>> with the interpretation of bytes and bits like this:
> > Byte 0: Value 3:  The UNIVERSAL tag for BIT STRING
> > Byte 1: Value 2: Length = 2
> > Byte 2: Value 5: Number of unused bits in last octet = 5
> > Byte 3: Value 16#A0: (2#10100000) The first 3 bits of the bitstring with
> bit (0) digitalSignature and bit (2) keyEncipherment set
> >                         , the remaining 5 bits are unused.
> > This encoding is removing all trailing zero bits which make the encoding
> use as few bytes as possible
> >
> > From the standard in X.690 and X.680 we have :
> >
> > X.690
> > 11.2 Unused bits
> > 11.2.1 Each unused bit in the final octet of the encoding of a bit
> string value shall be set to zero.
> > 11.2.2 Where Rec. ITU-T X.680 | ISO/IEC 8824-1, 22.7, applies, the
> bitstring shall have all trailing 0 bits removed before
> > it is encoded.
> > NOTE 1 – In the case where a size constraint has been applied, the
> abstract value delivered by a decoder to the application will be on e
> > of those satisfying the size constraint and differing from the
> transmitted value only in the number of trailing 0 bits.
> > NOTE 2 – If a bitstring value has no 1 bits, then an encoder shall
> encode the value with a length of 1 and an initial octet set to 0
> >
> > X.680
> > 22.7 When a "NamedBitList" is used in defining a bitstring type ASN.1
> encoding rules are free to add (or remove)
> > arbitrarily any trailing 0 bits to (or from) values that are being
> encoded or decoded. Application designers should ISO/IEC 8824-1:2021 (E)
> > Rec. ITU-T X.680 (02/2021) 43
> > therefore ensure that different semantics are not associated with such
> values which differ only in the number of trailing
> > 0 bits.
> >
> > This is just one case where the intention with DER does not hold since
> many certificates created also with OpenSSL does not follow the standard
> (The ASN.1
> > specs) for example when it comes to the encoding of CountryName (there
> are a few other data fields more which are problematic). The CountryName is
> > sometimes encoded as Printable STRING and sometimes as UTF8 STRING and
> the length constraint of 2
> > is also violated in some occasions. As the UNIVERSAL Tag for these types
> differ the encoded bytes will be different.
> > The Erlang/OTP SSL implementation have been forced to accept those "non
> confirming to standard" encoding because otherwise the interoperability
> with other
> > servers and clients would have been to limited.
>
> I am not as familiar with interpreting the x.680/x.690 documents but I
> think you make a strong case. It would make sense to have exactly one
> correct encoding of a certificate such that one always gets exactly the
> same signature, for example.
>
> > The solution is to never trust that a certificate which is decoded up to
> its internal Erlang representation and then encoded back again will result
> in
> > exactly the same sequence of bytes as in the original.
> >
> > /Kenneth
> >
> >  Bram Verburg <bram.verburg@REDACTED> wrote:
> >
> >  Hi Ingela,
> >
> >  I was looking into this too, and I noticed that this particular leaf
> certificate, once decoded as an OTPCertificate record, does not encode back
> to
> >  the same DER value (which is likely due to the unusual representation
> of the KeyUsage extension in the original file):
> >
> >  1> {ok, PEM} = file:read_file("cert.pem").
>
> >  {ok,<<"-----BEGIN
> CERTIFICATE-----\nMIIEfDCCAuSgAwIBAgIUdcWg+bxobl7OWSOjbNxyaXTnqx8wDQYJKoZIhvcNAQEL\nBQAwMTEXMBUGA1U"...>>}
> >  2> DER = element(2, hd(public_key:pem_decode(PEM))).
> >  <<48,130,4,124,48,130,2,228,160,3,2,1,2,2,20,117,197,160,
> >    249,188,104,110,94,206,89,35,163,108,220,...>>
> >  3> DER = public_key:pkix_encode('OTPCertificate',
> public_key:pkix_decode_cert(DER, otp), otp).
> >  ** exception error: no match of right hand side value
> <<48,130,4,123,48,130,2,227,160,3,2,1,2,2,20,117,197,160,
> >
> 249,188,104,110,94,206,89,35,163,108,220,...>>
> >
> >  (That would presumably mean the original DER representation is not
> really proper DER, as I believe ASN.1's DER encoding is supposed to always
> produce
> >  the same (canonical) byte sequence, in order to allow for reliable
> signature verification...)
> >
> >  I haven't quite pinned it down, but this commit
> https://github.com/erlang/otp/pull/4941/commits/80f9b71323c9c5a3bc111fcbc5c6c5ee497e27a6
> that was
> >  included in 24.0.4 seems to sometimes reconstruct a DER representation
> from an OTPCertificate record, and if that DER value is passed to
> >  public_key:pkix_verify/2 instead of the original DER representation,
> the signature won't match and ssl will throw the reported error.
> >
> >  Hope that might help,
> >
> >  Bram
> >
> >
> >  Ingela Andin <ingela.andin@REDACTED> wrote:
> >
> >  Hello again!
> >
> >  I thought of a possible cause  of your strange problem. If the
> ROOT-cert is sent as part of the chain (optional in the protocol).  We must
> check
> >  that it is a certificate that we trust, part of our "trust store". This
> was done by  a match of the DER-version of the cert and we should match
> >  the decode  structure instead.
> >
> >  In such cases the following patch ought to solve your problem.  Can you
> please test it? It is based on the latest maint!
>
> I tried this to no avail but we see there is a later patch at
> https://github.com/erlang/otp/pull/5222 which I assume is related. When I
> tried this patch a few minutes ago, I still get this:
> ** exception error: no match of right hand side value
> {error,{tls_alert,{bad_certificate,"TLS client: In state certify at
> ssl_handshake.erl:1989 generated CLIENT ALERT: Fatal - Bad Certificate\n"}}}
>
> Ingela, I directly emailed you the dummy/throwaway/test key corresponding
> to the leaf certificate that I posted earlier and also the commands my
> teammate found to help reproduce the issue. I hope this helps, thank you
> for your help too.
>
> Best,
>
> Jesse Bickel
>
> --
> Contractor, ERT, Inc.
> Federal Affiliation: NWC/OWP/NOAA/DOC
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20210922/3cfeffa9/attachment.htm>


More information about the erlang-questions mailing list