Understanding the DNIe, Part III: Hashing and signing
Here I come with yet another post about the DNIe. In the previous posts, we have seen how the device authentication procedure works and how to use the resulting keys to perform secure messaging. Now it's time to see how to ask the device to perform a hash on the input data and how to perform electronic signatures on it.
I'll start off with the description of the standard and continue with an explanation on how the DNIe drivers do it. Yes, you are reading it right, they use different APDUs than the ones defined in CWA14890, at least in the OpenSC module I'm using as a base for this analysis.
Electronic signatures in CWA14890
According to the CWA14890 specifications, in order to ask the card to compute an electronic signature one has to load a hash value into the card. The hash value can be computed completely on card, partially on card or completely off card.
This means you can choose between sending the plaintext data to the card and let it hash it, compute part of the hash outside the card and send an intermediate result together with the rest of the data or compute the complete hash on yourself. This is done with the following APDUs (copy-paste from the standard):
- On card hashing
- Hashing partially on card
- Hashing off-card
PSO( INS = ‘2A’, // PERFORM SECURITY OPERATION
P1 = ‘90’, // return hash code if required by Le
P2 = ‘80’, // plain value
data = plain data to be hashed)
PSO( INS = ‘2A’, // PERFORM SECURITY OPERATION
P1 = ‘90’, // return hash code if required by Le
P2 = ‘A0’, // input template for hash computation
data = ‘90’ L90 <intermediate hash result> || ‘80’ L80 <rest of the data> ;
PSO( INS = ‘2A’, // PERFORM SECURITY OPERATION
P1 = ‘90’, // return hash code if required by Le
P2 = ‘A0’, // input template for hash computation
data = ‘90’ L90 <hash value>
Now, once the card has a working hash, you can ask it to compute an electronic signature with this APDU:
PSO( INS = ‘2A’, // PERFORM SECURITY OPERATION
P1 = ‘9E’, // COMPUTE DIGITAL SIGNATURE
P2 = ‘9A’, // data to be signed
data = absent) // data already in ICC
In our case, the DNIe requires these messages to be sent using secure messaging. Further, it requires to perform user authentication (i.e. sending the PIN) before the signature command is issued. Also, before calling the PSO - Compute Digital Signature command we should select the desired private key with a Manage Security Environment command.
Therefore, the sequence of commands to request a signature from the card after establishing a secure channel could be something like this:
- VERIFY: wrap(0C 20 00 00 Lc PIN)
- Manage Security Environment command: wrap(0C 22 41 B6 Lc 84 L84 <keyid>)
- Hash command: wrap(0C 2A 90 80 Lc <plain data>)
- Sign command: wrap(0C 2A 9E 9A 00)
Where wrap means wrapping the APDU using Secure Messaging. This means encrypting the data and adding a MAC to authenticate the APDU header and the data.
Signing: the libopensc-dnie way
As I was saying, the procedure described above complies with the CWA14890 specs. And there I was trying to get it working using the APDUs defined in CWA14890 and scanning through my logs, when I realized that I could not find those APDUs anywhere in the logs! WTF? Could they be doing things in a different way?
As I was puzzled by the logs, I decided to go to the source of these APDUs: the binary driver.
Loading that driver into IDA Pro, I could easily find the function that implements the signature process. Reversing that function a bit, one can find out that the following communication happens when a signature is requested:
- The data to be signed (direct input to the RSA signature operation, padding performed outside the card if needed) is loaded into the card with this command: wrap(9C 58 00 Lc <data>)
- A signature is requested by the card with the following command: wrap(9C 5A 80 <keyRef> 00). Here <keyRef> is 01 for your authentication key and 02 for signing key.
- The initial response is parsed and more data is obtained using Get Response if needed (i.e. if SW1=0x61, then there are SW2 bytes available).
So there we got it, they are actually using something that does not adhere to the CWA14890 standard! I'm quite sure I must have been doing something wrong when trying to get it working the CWA14890 way, but since I don't have the complete documentation I found this way much easier... although it required some reversing with the help of IDA and the sources for OpenSC.
Further work
With the information I've dumped into these three posts on the DNIe, anyone interested should be able to understand how this device works and how electronic signatures are requested from it.
Some more work would need to be done in order to completely understand the interface the device provides and to make a fully compliant open source driver. As far as I know, most of the work left now should be related to understanding the file system structure, and of course implementing all this in an open source driver if desired.
On my side, this completes the analysis of the device for now. I'll conclude this series of posts with an article giving tips on how to find some of the information needed to implement the communication, including some links to source code that might be useful.
Unfortunately I can't share my code because part of it has been reused from non-public sources. Anyway, I think the information available in these posts together with the key for device authentication is all you need.
April 27th, 2010 - 18:57
Great great job!
You are now closer to your initial target 😉
April 28th, 2010 - 21:42
Hi,
Nice blog. Just a mail to propose an exchange of links. I discovered your site by tweeter. http://infond.blogspot.com
PS: please don’t validate this commentary
Bests,
t0ka7a