I agree on "AN politely speaking is not really perfect". In fact, I found the description in the uboot documentation clearer (doc/imx/habv4/).
I decided to describe in quite some detail why we need to use two CSFs to generate an encrypt-then-sign image (that is the protocol that I use) and why we need to transcribe the nonce/MAC from one binary CSF to the other. I know you already know a lot of what I'm going to describe, but I hope to clarify some aspects and I hope it's useful for someone else.
I have attached two CSFs (spl_enc.csf and spl_sign_enc.csf) and the corresponding binary csf dumps interpreted by hab_csf_parser (spl_enc.bin.tags, spl_sign_enc.bin.tags) that reports the HAB commands present in the binary CSF. Note, they report only the commands and values part of the commands, not the indirect data like signatures and certificates.
The general scheme involves, to simplify, using the first csf to sign and then encrypt the image, then discarding the signature; and the second csf to sign the encrypted image, discarding the encryption produced (the encryption of the already encrypted image).
This strange sequence is forced by the fact that the HAB executes the commands in the CSF in the order they appear. And since for the chosen protocol I want to first authenticate the encrypted image and then decrypt it, it is necessary that in the binary CSF the image authentication command comes before the decryption one.
If you take a look at the two .csf and the two .tags using diff, you will see that they are very very similar. In particular the two .tags differ only for the nonce/MAC HAB tag values.
In the following, we first execute "cst -i spl_enc.csf -o spl_enc.bin" and then "cst -i spl_sign_enc.csf -o spl_sign_enc.bin".
We suppose to have in input a "flash.bin" image produced by mkimage and to know the useful offsets in the image (csf and blob offsets).
The CST program reads and executes the command in the input spl_enc.csf:
- [Install SRK] generates the HAB command that indicates where the SRK table, inserted by the CST itself, is into the binary CSF.
- [Install CSFK] generates the HAB command that indicates where the CSF1_1 certificate, inserted by the CST itself, is into the binary CSF.
- [Authenticate CSF] generates the HAB command (HAB_CMD_AUT_DAT). The command also indicates where the CSF signature is into the binary CSF. Because the CST is building the CSF, the CST delays the generation of the CSF signature.
- [Install Key] Reads the certificate IMG1_1. Generates the HAB command to assign the verification index and where the IMG1_1 certificate is into the binary CSF.
- [Authenticate Data] Uses the IMG1_1 private key to sign the specified block(s) from "flash.bin" file and generate the HAB command (HAB_CMD_AUT_DAT with PCL=HAB_PCL_CMS) and the computed signature.
- [Install Secret Key] Generates a random symmetric key dek.bin and notes the address where the blob will be put, and generates the related HAB command to install the key.
- [Decrypt Data] Applies the AEAD scheme. Using the dek key encrypts the specified block(s) read from "flash-spl-enc.bin" calculating also the MAC, and generates the authenticated-decrypt HAB command (HAB_CMD_AUT_DAT with PCL=HAB_PCL_AEAD). "flash-spl-enc.bin" is initially a copy of "flash.bin". After this command the "flash-spl-enc.bin" has these block(s) ciphered. These blocks are a subset of the blocks in the previous [Authenticate Data] command.
- At end, the CST inserts the certificates, as a series of HAB_TAG_CSF commands, then generates the HAB_TAG_SIG command that represents the image signature followed by the HAB_TAG_MAC command that indicates the nonce and MAC values, and finally inserts the HAB_TAG_SIG command for the CSF signature.
The CST produces a file ("spl_enc.bin") that is the binary CSF containing these HAB commands that has generated. The attached "spl_enc.bin.tags" is a human readable dump of the commands present in the file.
NOTE: it seems that the binary CSF ends with the CSF signature in CST3.3.2, while ends with nonce/MAC in the CST3.3.1. The current NXP documentation assumes that nonce/MAC is at the end of binary CSF.
After producing the blob from the dek, we put the blob and the spl_enc.bin into flash-spl-enc.bin at the calculated offsets, obtaining a final image.
If we fed this image to the HAB, it will execute the commands as given in the exact order in which they were generated by the CST. But it will fail. In fact the HAB (simplifying):
- Autenticates the CSF. It is OK, the CSF is in plaintext.
- Autenticates the image. It fails, because the CST signed the blocks as dictated by the [Authenticate Data] above, before crypting a subset of the same blocks, as directed by the [Decrypt Data] command.
At this point we have a correctly ciphered image with a wrong signature.
To get a correct image we need the signature calculated including the encrypted blocks. To achieve this we can run the CST once again passing it an identical CSF, but replacing "flash.bin" with "flash-spl-enc.bin", and replacing "flash-spl-enc.bin" with a dummy copy ("flash-spl-enc-dummy.bin"). So we have the CST calculate the signature on an image that includes the encrypted blocks. We are also careful not to overwrite the dek already generated, indicating in the new CSF "dek_dummy.bin". See the attached spl_sign_enc.csf.
Now the CST generates the same sequence but the signature value is now calculated including the encrypted blocks. In this way, the generated binary csf (spl_sign_enc.bin) should be identical to the spl_enc.bin, except for the signature value.
We now put this spl_sign_enc.bin into flash-spl-enc.bin at usual offset, obtaining a new final image.
We feed this new image to the HAB and ... it fails again!
In fact the "HAB" (always simplifying):
- Autenticates the CSF. It is OK, the CSF is in plaintext.
- Autenticates the image. It is OK, now the signature is computed over the correct dataset.
- Decrypt Data. Applies the AEAD scheme. Uses the dek in the blob to compute the MAC over encrypted blocks. Uses the dek to decrypt the blocks. Check the MAC. The check fails for mismatch on the MAC and the ROM will not executes the code.
NOTE: the HAB maybe always decrypts the blocks regardless of MAC mismatch, to avoid oracle attacks. I don't know if it does that, I have not tested it.
What is happening is that the MAC in the spl_sign_enc.bin is incorrect because it has been calculated over the already ciphered blocks and not on the original blocks, and moreover with a different random nonce. (Incidentally this means that the MAC is doing its job). Thus, the spl_sign_enc.bin contains a correct signature but a wrong nonce/MAC.
At this point we have a correctly encrypted image (done by first CSF) with a correct signature (done by the second) but with a bad MAC because this is the MAC calculated during the second encryption that we discarded and is not related to the first, right encryption, done by the first CSF.
To fix this, we just need to copy the original nonce/MAC from spl_enc.bin into spl_sign_enc.bin, and assemble the image again. To know the position of the nonce/MAC we can search the HAB_TAG_MAC in the binary csf generated by the CST, independently by the CST version.
Done. Now the HAB will authenticate and decrypt the image correctly.
I wish you happy holidays,
I.