<div dir="ltr"><div dir="ltr"><div>Hi Fraser,</div><div>Thanks for the response! <br></div><div> Regarding the poison extension, yes I was aware that it needed to be removed so the code already had it removed.  It was the order of things left inside tbsCert that I was concerned about since I used the existing delete method provided for the Extension class, which I wasn't sure if it'd preserve the order of the remaining extensions.  Thanks for confirming my suspicion.  I will double-check the order.</div><div><br></div><div>Also thanks for the input on how to handle failed CT log communication v.s. response verification failure.   I will address them separately as suggested.</div><div>Finally, nice catch with the missing data length!!  I'll add that and go from there.</div><div><br></div><div>thanks again!</div><div>Christina<br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Jun 1, 2020 at 7:31 PM Fraser Tweedale <<a href="mailto:ftweedal@redhat.com" target="_blank">ftweedal@redhat.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi Christina,<br>
<br>
Adding pki-devel@ for wider audience.  Comments below.<br>
<br>
On Mon, Jun 01, 2020 at 06:28:42PM -0700, Christina Fu wrote:<br>
> Hi Fraser,<br>
> Do you know how the signature returned in the SCT response could be<br>
> verified by the CA?<br>
> My thought is that the CA should somehow verify the CT response after<br>
> sending the add-pre-chain request and before signing the cert.  Since log<br>
> inclusion verification would not be feasible right after the request (the<br>
> SCT response is supposed to be just a "promise, according to the rfc),  I<br>
> ruled that out and intend to stay with just the following two verifications<br>
> on the response itself:<br>
> <br>
>    - checking if log id (CT log signer public key hash) returned in the CT<br>
>    response is correct<br>
>    - this I have no problem verifying<br>
>       - Verifying if the signature returned in the CT response is correct<br>
>       - this I can't seem to get it working.<br>
> <br>
> I put the verification work above in the method "verifySCT".<br>
> <a href="https://github.com/dogtagpki/pki/blob/master/base/ca/src/com/netscape/ca/CAService.java#L1209" rel="noreferrer" target="_blank">https://github.com/dogtagpki/pki/blob/master/base/ca/src/com/netscape/ca/CAService.java#L1209</a><br>
> What I am wondering is how this can be done properly.  Since the tbsCert is<br>
> not to contain the poison extension, the poison extension needs to be<br>
> removed by the CT server before it signs.  What if the order of the<br>
> extensions contained in the tbsCert gets changed in the process?<br>
> <br>
The poison extension must be removed, and care must be taken to keep<br>
everything else in the same order, and reserialise the parts in<br>
exactly the same way.<br>
<br>
> It seems that the response should contain the tbsCert that it signs (which<br>
> isn't per the rfc) or I am not sure how the CA could verify the signature.<br>
><br>
The response does not contain the TBCCertificate.  Both sides (log<br>
and client) remove the poison extension (and change nothing else),<br>
then both sides can sign the same data.<br>
<br>
> So the question now is if I should just leave out the check, unless you<br>
> have other suggestions.<br>
><br>
I do think we should verify the signature, to ensure the message was<br>
actually received by the log server we wanted and not an impostor.<br>
<br>
> Of course, I also could have missed something in my code.<br>
> <br>
The binary format is complex and it's easy to miss something.  After<br>
you implement removal of the poison extension, if it is still not<br>
working I will go over the code with a fine-tooth comb.<br>
<br>
I copied some of the code and left comments below, too.  Comments<br>
begin with `!!'.  I think I found one bug and a couple of possible<br>
improvements.<br>
<br>
> One last question, currently in the code, if verifySCT fails, I just<br>
> "continue" to process for next CT log.  Should this just bail out all<br>
> together or it's fine to continue? Or could this be a choice by the admin.<br>
> What do you think and why?<br>
> <a href="https://github.com/dogtagpki/pki/blob/master/base/ca/src/com/netscape/ca/CAService.java#L951" rel="noreferrer" target="_blank">https://github.com/dogtagpki/pki/blob/master/base/ca/src/com/netscape/ca/CAService.java#L951</a><br>
><br>
My line of thinking is this:<br>
<br>
- we should tolerate communication errors with log (perhaps<br>
  enqueuing the cert for a retry later)<br>
<br>
- but (assuming we implement it correctly) verifySCT failure is<br>
  indicative of something wrong with the log (e.g. key changed); it<br>
  is not a communication error and can be treated differently.<br>
<br>
- I think it's OK to fail hard.  Admins will likely want to know if<br>
  something is wrong with CT logging.<br>
<br>
- But in case we make a mistake, or an org needs issuance to<br>
  continue despite CT log misbehaviour, there should be a config<br>
  knob to allow this condition to be ignored.  "In case of<br>
  emergency..."<br>
<br>
<br>
> <br>
> thanks,<br>
> Christina<br>
<br>
    boolean verifySCT(CTResponse response, byte[] tbsCert, String logPublicKey)<br>
            throws Exception {<br>
<br>
        /* ... SNIP ... */<br>
<br>
        byte[] extensions = new byte[] {0, 0};<br>
!! although no extensions have been defined I think we should we take<br>
   extensions from the CT response to future-proof this code.  i.e.<br>
   decode the base64 data from the "extensions" field, and prepend the length.<br>
<br>
        // piece them together<br>
<br>
        int data_len = version.length + signature_type.length +<br>
                 timestamp.length + entry_type.length +<br>
                 issuer_key_hash.length + tbsCert.length + extensions.length;<br>
<br>
        logger.debug(method + " data_len = "+ data_len);<br>
        ByteArrayOutputStream ostream = new ByteArrayOutputStream();<br>
<br>
        ostream.write(version);<br>
        ostream.write(signature_type);<br>
        ostream.write(timestamp);<br>
<br>
        ostream.write(entry_type);<br>
        ostream.write(issuer_key_hash);<br>
        ostream.write(tbsCert);<br>
!! I believe you need to prepend the length of tbsCert as a<br>
   THREE-byte length field, because its definition is<br>
   `opaque TBSCertificate<1..2^24-1>;'<br>
<br>
        ostream.write(extensions);<br>
<br>
        byte[] data = ostream.toByteArray();<br>
<br>
        // Now, verify the signature<br>
        // Note: this part currently does not work; see method comment above<br>
<br>
        // cfu ToDo: interpret the alg bytes later; hardcode for now<br>
        Signature signer = Signature.getInstance("SHA256withEC", "Mozilla-JSS");<br>
        signer.initVerify(log_pubKey);<br>
        signer.update(data);<br>
!! We could call signer.update() multiple times instead of making an<br>
   intermediate ByteArrayOutputStream.  I do not care about the<br>
   performance, just whatever might simplify the routine.<br>
<br>
        if (!signer.verify(signature)) {<br>
            logger.error(method + "failed to verify SCT signature; pass for now");<br>
            // this method is not yet working;  Let this pass for now<br>
            // return false;<br>
        } else {<br>
            logger.debug(method + "SCT signature verified successfully");<br>
        }<br>
        logger.debug("verifySCT ends");<br>
<br>
        return true;<br>
    }<br>
<br>
<br>
<br>
Cheers,<br>
Fraser<br>
<br>
</blockquote></div></div>