DOI: 10.5281/zenodo.3982271
title: CVE-2020-12399: research data and tooling

description: This dataset and software tools are for reproducing the research results related to CVE-2020-12399, resulting from the manuscript "Déjà vu: Side-channel analysis of Mozilla's NSS", to appear at ACM CCS 2020. The data is from a remote timing attack against the NSS v3.51 implementation of DSA signing. The client machine was a 3.1 GHz 64-bit Intel i5-2400 CPU (Sandy Bridge). The server machine was a Raspberry Pi 3 Model B plus board containing a 1.4 GHz 64-bit quad-core Cortex-A53 processor. The client and server were connected by a Cisco 9300 series enterprise switch over Gbit Ethernet. The data contains pow(2,18) samples. The data was used to produce Figure 1 in the paper and contains all the remote timing attack data from Section 4. Data description The file <code>remote_timings.json</code> contains a single JSON array. Each entry is a dictionary representation of one digital signature. A description of the dictionary fields follows. <code>p</code>: prime (DSA parameter). <code>q</code>: generator order (DSA parameter). <code>g</code>: generator (DSA parameter). <code>x</code>: the DSA private key. <code>y</code>: the corresponding public key. <code>r</code>: first component of the DSA signature. <code>s</code>: second component of the DSA signature. <code>k</code>: the ground truth nonce generated during DSA signing. <code>k_len</code>: the ground truth number of bits in said nonce. <code>msg</code>: message digitally signed. <code>h</code>: SHA-256 hash of said message. (Truncated to the same bitlen as q.) <code>id</code>: ignored. <code>latency</code>: the measured wall clock time (CPU clock cycles) to produce the digital signature. Prerequisites <pre><code>sudo apt install openssl python3-ijson xxd jq</code></pre> Data setup Extract the JSON: <pre><code>tar xf remote_timings_rpi.tar.gz</code></pre> Key setup Generate the public key (<code>public.pem</code> here) from the provided private key (<code>private.pem</code> here): <pre><code>$ openssl pkey -in private.pem -pubout -out public.pem</code></pre> Examine the keys if you want. <pre><code>$ openssl pkey -in private.pem -text -noout $ openssl pkey -in public.pem -text -noout -pubin</code></pre> Example: Verify key material <pre><code>$ openssl pkey -in private.pem -text -noout Private-Key: (2048 bit) priv: 1f:87:68:eb:57:e1:f4:f1:29:a6:c8:ca:03:c8:db: 49:1d:8e:2b:81:bd:72:92:64:0c:1c:d6:6d pub: 00:9d:fa:bc:47:00:cb:11:fa:51:45:c1:bd:b1:88: 2d:dd:a2:79:5b:c3:43:0a:af:bb:83:e2:d5:84:d1: 07:01:ab:f9:ae:76:2d:dd:f2:a5:75:f5:3e:94:4d: 3b:c6:f6:ce:17:c6:60:09:5b:49:3d:cb:a0:db:ec: 29:91:85:8b:c3:f5:6c:6a:3c:01:87:12:85:ae:fc: 9e:bf:67:81:1b:1d:b1:9d:12:bd:79:8c:54:08:48: 11:13:6d:ab:b0:16:ef:11:4a:27:a7:0a:80:b3:db: 72:c1:cc:1e:e8:4a:39:b7:00:ca:97:b7:3a:6e:e9: 25:22:2e:5c:57:ee:62:be:23:d0:5e:53:a3:9f:05: d4:7d:7f:b5:b6:cb:4b:27:90:14:79:72:a5:43:97: c6:6a:7d:f7:32:b3:67:58:90:fc:c3:65:34:57:89: 1b:43:28:68:43:24:12:5e:f1:43:76:3c:e9:bc:9c: 5d:7d:ae:d6:3a:31:32:ca:df:a4:07:88:a2:55:6e: a4:8c:da:13:c8:30:b7:2a:1c:23:0f:32:da:9e:7f: e1:f7:3d:2d:1c:58:f5:1d:f2:7d:fb:67:45:8d:dd: 84:eb:83:c4:b0:00:a6:c2:09:b0:48:48:f9:4e:a8: d7:ab:e1:c6:e8:bf:5c:fa:e3:f2:cd:c6:f1:e7:f2: 2c:90 P: 00:e5:4e:f4:32:f8:4a:ec:28:3c:dd:32:a8:05:e3: 5a:fa:a5:81:47:98:d9:a7:94:ba:34:b0:f9:7b:20: c5:fb:52:12:3e:82:d7:6e:6f:f5:50:be:5e:9f:df: 82:9b:4e:0c:9d:a2:9f:3f:0a:f3:72:c2:55:7c:46: 6e:fe:48:00:88:b6:4e:4f:9b:19:8c:98:3b:71:42: 56:d2:b4:1c:47:69:6e:fc:f0:e6:26:04:0e:e2:63: ed:06:0f:fb:a8:a9:94:73:e1:41:e0:6b:5a:b4:d9: 86:cd:7b:46:d3:39:ba:18:13:da:f2:3a:7b:dc:41: 21:83:e8:0d:25:13:31:90:5d:bd:82:41:9b:ea:6b: 8a:ba:8a:48:b1:1d:d2:3d:5e:c4:1b:29:5e:7f:b6: 56:1b:e6:91:65:ec:84:82:c2:f6:a1:b0:14:1b:0b: 08:d8:2b:2a:06:17:d7:2a:9b:c3:aa:fb:28:26:14: 3f:5d:0a:48:1a:48:45:c0:fd:ea:ec:90:6c:ec:93: c8:af:a3:31:4b:3a:d8:cd:20:ae:8f:14:58:26:49: 18:1f:7a:99:c9:da:c3:f0:76:b8:52:8d:eb:b2:e2: 98:6b:a5:47:15:c3:ff:c8:e7:6c:d3:db:c7:fb:4c: 36:3e:15:eb:45:e1:4a:5d:01:ed:3b:87:f7:69:c1: 31:59 Q: 00:ca:6d:df:fc:7b:96:2e:35:30:27:4f:1f:cf:57: 2f:e9:4c:40:97:53:a1:fa:d0:89:56:8d:2c:25 G: 43:26:04:66:b3:80:c3:3f:8d:f5:5a:29:79:58:7a: 0b:8c:72:b9:cb:23:61:5d:c1:45:c5:38:7f:33:4e: 93:63:75:8a:b0:44:61:8f:59:df:fd:2f:3f:1f:22: 73:66:ba:53:65:53:2a:57:5b:d9:40:34:be:4c:78: 22:4a:bf:94:5d:23:15:65:66:e1:1f:6b:93:12:00: f0:ac:f5:64:0d:6d:6c:a3:eb:26:83:6d:68:95:e0: 2c:bf:75:62:fa:5f:95:0f:b0:40:68:ce:66:3b:58: ed:c1:63:e3:d8:35:5c:cc:db:b8:12:e6:62:e4:63: b6:29:e0:86:75:79:bc:95:27:74:d1:fd:94:b9:7f: 6e:57:b4:e5:39:a2:15:41:94:3f:47:90:43:a5:da: dd:08:a4:92:c5:bf:ef:34:4e:2e:7e:82:5c:07:0e: dc:5d:6b:79:10:04:53:cc:b2:8e:bd:65:61:80:49: ad:c7:dd:5f:5a:9b:74:ae:bc:e0:49:f1:ad:4c:1e: 8f:4e:9d:39:e9:fe:57:4d:39:b7:ba:69:03:e3:7e: 4d:0d:9b:65:c3:55:77:ff:2c:86:27:21:c7:3e:60: a3:23:a5:e8:7e:0d:29:15:1c:5e:04:91:91:25:03: f3:97:77:6c:11:24:34:58:c9:ec:b7:ca:ce:74:cd: a7</code></pre> This shows the keys indeed match (JSON <code>x,y</code>, above <code>priv,pub</code>): <pre><code>$ grep --max-count=1 '"x"' remote_timings.json "x": "0x1F8768EB57E1F4F129A6C8CA03C8DB491D8E2B81BD7292640C1CD66D", $ grep --max-count=1 '"y"' remote_timings.json "y": "0x9DFABC4700CB11FA5145C1BDB1882DDDA2795BC3430AAFBB83E2D584D10701ABF9AE762DDDF2A575F53E944D3BC6F6CE17C660095B493DCBA0DBEC2991858BC3F56C6A3C01871285AEFC9EBF67811B1DB19D12BD798C54084811136DABB016EF114A27A70A80B3DB72C1CC1EE84A39B700CA97B73A6EE925222E5C57EE62BE23D05E53A39F05D47D7FB5B6CB4B2790147972A54397C66A7DF732B3675890FCC3653457891B4328684324125EF143763CE9BC9C5D7DAED63A3132CADFA40788A2556EA48CDA13C830B72A1C230F32DA9E7FE1F73D2D1C58F51DF27DFB67458DDD84EB83C4B000A6C209B04848F94EA8D7ABE1C6E8BF5CFAE3F2CDC6F1E7F22C90",</code></pre> This shows the DSA parameters match (JSON <code>p,q,g</code>, above <code>P,Q,G</code>): <pre><code>$ grep --max-count=1 '"p"' remote_timings.json "p": "0xE54EF432F84AEC283CDD32A805E35AFAA5814798D9A794BA34B0F97B20C5FB52123E82D76E6FF550BE5E9FDF829B4E0C9DA29F3F0AF372C2557C466EFE480088B64E4F9B198C983B714256D2B41C47696EFCF0E626040EE263ED060FFBA8A99473E141E06B5AB4D986CD7B46D339BA1813DAF23A7BDC412183E80D251331905DBD82419BEA6B8ABA8A48B11DD23D5EC41B295E7FB6561BE69165EC8482C2F6A1B0141B0B08D82B2A0617D72A9BC3AAFB2826143F5D0A481A4845C0FDEAEC906CEC93C8AFA3314B3AD8CD20AE8F14582649181F7A99C9DAC3F076B8528DEBB2E2986BA54715C3FFC8E76CD3DBC7FB4C363E15EB45E14A5D01ED3B87F769C13159", $ grep --max-count=1 '"q"' remote_timings.json "q": "0xCA6DDFFC7B962E3530274F1FCF572FE94C409753A1FAD089568D2C25", $ grep --max-count=1 '"g"' remote_timings.json "g": "0x43260466B380C33F8DF55A2979587A0B8C72B9CB23615DC145C5387F334E9363758AB044618F59DFFD2F3F1F227366BA5365532A575BD94034BE4C78224ABF945D23156566E11F6B931200F0ACF5640D6D6CA3EB26836D6895E02CBF7562FA5F950FB04068CE663B58EDC163E3D8355CCCDBB812E662E463B629E0867579BC952774D1FD94B97F6E57B4E539A21541943F479043A5DADD08A492C5BFEF344E2E7E825C070EDC5D6B79100453CCB28EBD65618049ADC7DD5F5A9B74AEBCE049F1AD4C1E8F4E9D39E9FE574D39B7BA6903E37E4D0D9B65C35577FF2C862721C73E60A323A5E87E0D29151C5E0491912503F397776C11243458C9ECB7CACE74CDA7",</code></pre> Example: Extract a single entry Here we use the python script <code>pickone.py</code> to extract the entry at index 2 (starting from 0). <pre><code>$ python3 pickone.py remote_timings.json 2 | jq . &gt; 2.json $ cat 2.json { "latency": "399901598", "y": "0x9DFABC4700CB11FA5145C1BDB1882DDDA2795BC3430AAFBB83E2D584D10701ABF9AE762DDDF2A575F53E944D3BC6F6CE17C660095B493DCBA0DBEC2991858BC3F56C6A3C01871285AEFC9EBF67811B1DB19D12BD798C54084811136DABB016EF114A27A70A80B3DB72C1CC1EE84A39B700CA97B73A6EE925222E5C57EE62BE23D05E53A39F05D47D7FB5B6CB4B2790147972A54397C66A7DF732B3675890FCC3653457891B4328684324125EF143763CE9BC9C5D7DAED63A3132CADFA40788A2556EA48CDA13C830B72A1C230F32DA9E7FE1F73D2D1C58F51DF27DFB67458DDD84EB83C4B000A6C209B04848F94EA8D7ABE1C6E8BF5CFAE3F2CDC6F1E7F22C90", "g": "0x43260466B380C33F8DF55A2979587A0B8C72B9CB23615DC145C5387F334E9363758AB044618F59DFFD2F3F1F227366BA5365532A575BD94034BE4C78224ABF945D23156566E11F6B931200F0ACF5640D6D6CA3EB26836D6895E02CBF7562FA5F950FB04068CE663B58EDC163E3D8355CCCDBB812E662E463B629E0867579BC952774D1FD94B97F6E57B4E539A21541943F479043A5DADD08A492C5BFEF344E2E7E825C070EDC5D6B79100453CCB28EBD65618049ADC7DD5F5A9B74AEBCE049F1AD4C1E8F4E9D39E9FE574D39B7BA6903E37E4D0D9B65C35577FF2C862721C73E60A323A5E87E0D29151C5E0491912503F397776C11243458C9ECB7CACE74CDA7", "h": "0xC7DEAC64C95157992CB0D77CF944CB107C756F3E30D1C49C0C48A6EA", "k": "0x742A7562E2A192996440AE2A4FDF5D37E1A532E1E6A50BCA3964BBDA", "q": "0xCA6DDFFC7B962E3530274F1FCF572FE94C409753A1FAD089568D2C25", "p": "0xE54EF432F84AEC283CDD32A805E35AFAA5814798D9A794BA34B0F97B20C5FB52123E82D76E6FF550BE5E9FDF829B4E0C9DA29F3F0AF372C2557C466EFE480088B64E4F9B198C983B714256D2B41C47696EFCF0E626040EE263ED060FFBA8A99473E141E06B5AB4D986CD7B46D339BA1813DAF23A7BDC412183E80D251331905DBD82419BEA6B8ABA8A48B11DD23D5EC41B295E7FB6561BE69165EC8482C2F6A1B0141B0B08D82B2A0617D72A9BC3AAFB2826143F5D0A481A4845C0FDEAEC906CEC93C8AFA3314B3AD8CD20AE8F14582649181F7A99C9DAC3F076B8528DEBB2E2986BA54715C3FFC8E76CD3DBC7FB4C363E15EB45E14A5D01ED3B87F769C13159", "s": "0x79FD73D901BB077D14334D8CC714804577515A1E0ADC9F995BB7534C", "r": "0x61F949D772E22EA9EFBB36442BC229767B28BE2A8061FA7339AFDDC8", "msg": "0x318198301A06092A864886F70D010903310D060B2A864886F70D0109100104301C06092A864886F70D010905310F170D3230303432343131333135375A302B060B2A864886F70D010910020C311C301A30183016041470AD64D33E65A855E6C332AA52736F71D58E7527302F06092A864886F70D0109043122042090C90BFC7A8C459EDB5AF58A8878EE826B6FD02A20E2BAAF2C73984FA380FDD2", "x": "0x1F8768EB57E1F4F129A6C8CA03C8DB491D8E2B81BD7292640C1CD66D", "id": "335451053151725", "k_len": "223" }</code></pre> Example: Dump message to binary file Extract the <code>msg</code> field from the target JSON and dump it as binary. <pre><code>$ sed -n 's/^ "msg": "0x\(.*\)",$/\1/p' 2.json | xxd -r -p &gt; 2.msg $ xxd -g1 2.msg 00000000: 31 81 98 30 1a 06 09 2a 86 48 86 f7 0d 01 09 03 1..0...*.H...... 00000010: 31 0d 06 0b 2a 86 48 86 f7 0d 01 09 10 01 04 30 1...*.H........0 00000020: 1c 06 09 2a 86 48 86 f7 0d 01 09 05 31 0f 17 0d ...*.H......1... 00000030: 32 30 30 34 32 34 31 31 33 31 35 37 5a 30 2b 06 200424113157Z0+. 00000040: 0b 2a 86 48 86 f7 0d 01 09 10 02 0c 31 1c 30 1a .*.H........1.0. 00000050: 30 18 30 16 04 14 70 ad 64 d3 3e 65 a8 55 e6 c3 0.0...p.d.&gt;e.U.. 00000060: 32 aa 52 73 6f 71 d5 8e 75 27 30 2f 06 09 2a 86 2.Rsoq..u'0/..*. 00000070: 48 86 f7 0d 01 09 04 31 22 04 20 90 c9 0b fc 7a H......1". ....z 00000080: 8c 45 9e db 5a f5 8a 88 78 ee 82 6b 6f d0 2a 20 .E..Z...x..ko.* 00000090: e2 ba af 2c 73 98 4f a3 80 fd d2 ...,s.O....</code></pre> Note the <code>xxd</code> output matches the <code>msg</code> byte string from the target JSON. Example: Dump hash to binary file Extract the <code>h</code> field from the target JSON and dump it as binary. <pre><code>$ sed -n 's/^ "h": "0x\(.*\)",$/\1/p' 2.json | xxd -r -p &gt; 2.hash $ xxd -g1 2.hash 00000000: c7 de ac 64 c9 51 57 99 2c b0 d7 7c f9 44 cb 10 ...d.QW.,..|.D.. 00000010: 7c 75 6f 3e 30 d1 c4 9c 0c 48 a6 ea |uo&gt;0....H..</code></pre> Note the <code>xxd</code> output matches the <code>h</code> byte string from the target JSON. Example: Hash is consistent <pre><code>$ sha256sum 2.msg c7deac64c95157992cb0d77cf944cb107c756f3e30d1c49c0c48a6ea809e6d58 2.msg</code></pre> Note the first 28 bytes of <code>sha256sum</code> output match the <code>h</code> byte string from the target JSON. (DSA maps to GF(q) with truncation.) Example: Dump signature to DER The <code>hex2der.sh</code> script takes as an argument the target JSON filename, and outputs the DER-encoded DSA signature to stdout by extracting the <code>r</code> and <code>s</code> fields from the target JSON. <pre><code>$ ./hex2der.sh 2.json &gt; 2.der $ openssl asn1parse -in 2.der -inform DER 0:d=0 hl=2 l= 60 cons: SEQUENCE 2:d=1 hl=2 l= 28 prim: INTEGER :61F949D772E22EA9EFBB36442BC229767B28BE2A8061FA7339AFDDC8 32:d=1 hl=2 l= 28 prim: INTEGER :79FD73D901BB077D14334D8CC714804577515A1E0ADC9F995BB7534C</code></pre> Note the <code>asn1parse</code> output contains a sequence with two integers, matching the <code>r</code> and <code>s</code> fields from the target JSON. Example: Verify the signature (post-hash) We use <code>pkeyutl</code> here to verify the raw hash directly. <pre><code>$ openssl pkeyutl -in 2.hash -inkey public.pem -pubin -verify -sigfile 2.der Signature Verified Successfully</code></pre> Note it fails for other hashes (messages), a fundamental security property for digital signatures: <pre><code>$ dd if=/dev/urandom of=bad.hash bs=1 count=28 28+0 records in 28+0 records out 28 bytes copied, 0.000647097 s, 43.3 kB/s $ openssl pkeyutl -in bad.hash -inkey public.pem -pubin -verify -sigfile 2.der Signature Verification Failure</code></pre> Example: Verify the signature (pre-hash) We use <code>dgst</code> here to verify by recomputing the hash. <pre><code>$ openssl dgst -sha256 -verify public.pem -signature 2.der 2.msg Verified OK</code></pre> Example: Message analysis The <code>msg</code> JSON field is an RFC 3161 Time Stamp Request. You can examine it: <pre><code>$ openssl asn1parse -in 2.msg -inform DER 0:d=0 hl=3 l= 152 cons: SET 3:d=1 hl=2 l= 26 cons: SEQUENCE 5:d=2 hl=2 l= 9 prim: OBJECT :contentType 16:d=2 hl=2 l= 13 cons: SET 18:d=3 hl=2 l= 11 prim: OBJECT :id-smime-ct-TSTInfo 31:d=1 hl=2 l= 28 cons: SEQUENCE 33:d=2 hl=2 l= 9 prim: OBJECT :signingTime 44:d=2 hl=2 l= 15 cons: SET 46:d=3 hl=2 l= 13 prim: UTCTIME :200424113157Z 61:d=1 hl=2 l= 43 cons: SEQUENCE 63:d=2 hl=2 l= 11 prim: OBJECT :id-smime-aa-signingCertificate 76:d=2 hl=2 l= 28 cons: SET 78:d=3 hl=2 l= 26 cons: SEQUENCE 80:d=4 hl=2 l= 24 cons: SEQUENCE 82:d=5 hl=2 l= 22 cons: SEQUENCE 84:d=6 hl=2 l= 20 prim: OCTET STRING [HEX DUMP]:70AD64D33E65A855E6C332AA52736F71D58E7527 106:d=1 hl=2 l= 47 cons: SEQUENCE 108:d=2 hl=2 l= 9 prim: OBJECT :messageDigest 119:d=2 hl=2 l= 34 cons: SET 121:d=3 hl=2 l= 32 prim: OCTET STRING [HEX DUMP]:90C90BFC7A8C459EDB5AF58A8878EE826B6FD02A20E2BAAF2C73984FA380FDD2</code></pre> Example: Statistics The <code>stats.py</code> script shows how to extract the desired fields from the JSON. It computes the median latency over each nonce bit length. <pre><code>$ python3 stats.py remote_timings.json Len Median 205 121.2 206 121.2 207 120.6 208 138.3 209 122.9 210 123.0 211 123.0 212 123.0 213 125.0 214 125.0 215 125.0 216 125.0 217 127.1 218 127.1 219 127.1 220 127.1 221 129.1 222 129.1 223 129.1 224 129.1</code></pre> You can verify these medians are consistent with Figure 1 in the paper. The <code>stats.py</code> script can be easily modified for more advanced analysis. Credits Some parts borrowed from this artifact. Authors Sohaib ul Hassan (Tampere University, Tampere, Finland) Iaroslav Gridin (Tampere University, Tampere, Finland) Ignacio M. Delgado-Lozano (Tampere University, Tampere, Finland) Cesar Pereida García (Tampere University, Tampere, Finland) Jesús-Javier Chi-Domínguez (Tampere University, Tampere, Finland) Alejandro Cabrera Aldaya (Tampere University, Tampere, Finland) Billy Bob Brumley (Tampere University, Tampere, Finland) Funding This project has received funding from the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 804476). License This project is distributed under MIT license.