<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>