# gpgsm: Wrong length when parsing octetstring in constructed encoding + definite length
a.k.a. "Cannot import QuoVadis-issued PKCS#12", a.k.a. "Failed to decrypt encrypted certificate".
TLDR: the length of the data value seems to be calculated to wrongly include header fields
when constructed encoding and definite length are used.
## Short steps to reproduce:
1. Get a PKCS#12 archive (certificate + private key) with certain choices of BER encoding.
2. Run `gpgsm --import test.pfx` (or try to import it in the Kleopatra UI).
Versions:
```
$ gpgsm --version
gpgsm (GnuPG) 2.2.19
libgcrypt 1.8.5
libksba 1.3.5-unknown
[...]
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.3 LTS
Release: 20.04
Codename: focal
$ uname -a
Linux thore 5.4.0-96-generic #109-Ubuntu SMP Wed Jan 12 16:49:16 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
```
## Expected behaviour:
Certificate is imported.
## Actual behaviour:
The certificate import fails:
```
$ gpgsm --import test.pfx
gpgsm: enabled debug flags: ipc
gpgsm: DBG: chan_4 <- OK Pleased to meet you, process 25716
gpgsm: DBG: connection to agent established
gpgsm: DBG: chan_4 -> RESET
gpgsm: DBG: chan_4 <- OK
gpgsm: DBG: chan_4 -> OPTION ttyname=/dev/pts/6
gpgsm: DBG: chan_4 <- OK
gpgsm: DBG: chan_4 -> OPTION ttytype=xterm-256color
gpgsm: DBG: chan_4 <- OK
gpgsm: DBG: chan_4 -> OPTION display=:0
gpgsm: DBG: chan_4 <- OK
gpgsm: DBG: chan_4 -> OPTION xauthority=/tmp/xauth-1000-_0
gpgsm: DBG: chan_4 <- OK
gpgsm: DBG: chan_4 -> OPTION putenv=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
gpgsm: DBG: chan_4 <- OK
gpgsm: DBG: chan_4 -> OPTION lc-ctype=en_US.UTF-8
gpgsm: DBG: chan_4 <- OK
gpgsm: DBG: chan_4 -> OPTION lc-messages=en_US.UTF-8
gpgsm: DBG: chan_4 <- OK
gpgsm: DBG: chan_4 -> GETINFO version
gpgsm: DBG: chan_4 <- D 2.2.19
gpgsm: DBG: chan_4 <- OK
gpgsm: DBG: chan_4 -> OPTION allow-pinentry-notify
gpgsm: DBG: chan_4 <- OK
gpgsm: DBG: chan_4 -> GET_PASSPHRASE --data -- X X X Please+enter+the+passphrase+to+unprotect+the+PKCS#12+object.
gpgsm: DBG: chan_4 <- INQUIRE PINENTRY_LAUNCHED 25718 gnome3 1.1.0 /dev/pts/6 xterm-256color :0
gpgsm: DBG: chan_4 -> END
gpgsm: DBG: chan_4 <- D test
gpgsm: DBG: chan_4 <- OK
gpgsm: 2376 bytes of 3DES encrypted text
gpgsm: 1632 bytes of RC2 encrypted text
gpgsm: decryption failed; trying charset 'ISO-8859-1'
gpgsm: decryption failed; trying charset 'ISO-8859-15'
gpgsm: decryption failed; trying charset 'ISO-8859-2'
gpgsm: decryption failed; trying charset 'ISO-8859-3'
gpgsm: decryption failed; trying charset 'ISO-8859-4'
gpgsm: decryption failed; trying charset 'ISO-8859-5'
gpgsm: decryption failed; trying charset 'ISO-8859-6'
gpgsm: decryption failed; trying charset 'ISO-8859-7'
gpgsm: decryption failed; trying charset 'ISO-8859-8'
gpgsm: decryption failed; trying charset 'ISO-8859-9'
gpgsm: decryption failed; trying charset 'KOI8-R'
gpgsm: decryption failed; trying charset 'IBM437'
gpgsm: decryption failed; trying charset 'IBM850'
gpgsm: decryption failed; trying charset 'EUC-JP'
gpgsm: decryption failed; trying charset 'BIG5'
gpgsm: encryptedData error at "outer.outer.seq", offset 2
gpgsm: possibly bad passphrase given
gpgsm: error at "bag.encryptedData", offset 2585
gpgsm: error parsing or decrypting the PKCS#12 file
gpgsm: total number processed: 0
secmem usage: 2400/16384 bytes in 1 blocks
```
Yes, there is a cleartext password in this log, and it is "test".
## Long steps to reproduce
I got my hands on such a specifically-formatted archive through ETH Zurich's
PKI portal, which in turn obtains them from QuoVadis.
This is an issue, because it makes it rather hard to obtain a test archive that
contains test keys (we would need to jump through hoops at QuoVadis support).
I also didn't find an easy way to reproduce the ASN1 structure of this archive
with openssl.
Therefore throughout this report I use a `test.pfx` created as follows:
1. Disassemble: `der2ascii -i quovadis-production.pfx -o test.ascii` (from [0]).
2. `der2ascii -i openssl-self-signed.pfx -o self.ascii`
3. Manually fiddle with the hex values in the OCTET_STRINGs and
copy the values from `self.ascii` over to `test.ascii`.
4. Reassemble: `ascii2der -i test.ascii -o test.pfx`
This way, we preserve the ASN1 structure of the PFX QuoVadis created.
But now it contains non-confidential, self-signed test data, protected
with the password "test".
Note that since we use ascii2der all the length fields in the BER are set correctly.
The final MAC is NOT correct.
The hashes that integrity-protect the key and cert bags are correct.
You can see this in the log (`gpgsm: 2376 bytes of 3DES encrypted text`, i.e. the
keybag can be decrypted), or by running `pk12util -l test.pfx` (from [1]).
So for the purpose of reproducing the problem, this should suffice.
If you need a better pfx file to reproduce this, I can *privately* provide my revoked,
but-still-production pfx file.
### Digging into the problem
Looking into the structure of the `test.pfx`:
```
$ openssl asn1parse -i -inform der -in test.pfx -strparse 30
0:d=0 hl=4 l=4243 cons: SEQUENCE
4:d=1 hl=4 l=2532 cons: SEQUENCE
8:d=2 hl=2 l= 9 prim: OBJECT :pkcs7-data
19:d=2 hl=4 l=2517 cons: cont [ 0 ]
23:d=3 hl=4 l=2513 prim: OCTET STRING [HEX DUMP]:308209CD30....6500720074
2540:d=1 hl=4 l=1703 cons: SEQUENCE
2544:d=2 hl=2 l= 9 prim: OBJECT :pkcs7-encryptedData
2555:d=2 hl=4 l=1688 cons: cont [ 0 ]
2559:d=3 hl=4 l=1684 cons: SEQUENCE
2563:d=4 hl=2 l= 1 prim: INTEGER :00
2566:d=4 hl=4 l=1677 cons: SEQUENCE
2570:d=5 hl=2 l= 9 prim: OBJECT :pkcs7-data
2581:d=5 hl=2 l= 28 cons: SEQUENCE
2583:d=6 hl=2 l= 10 prim: OBJECT :pbeWithSHA1And40BitRC2-CBC
2595:d=6 hl=2 l= 14 cons: SEQUENCE
2597:d=7 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:5024C63D9644A885
2607:d=7 hl=2 l= 2 prim: INTEGER :0800
2611:d=5 hl=4 l=1632 cons: cont [ 0 ]
2615:d=6 hl=4 l= 500 prim: OCTET STRING [HEX DUMP]:B243AD2C37....1C9890FD3F
3119:d=6 hl=4 l=1124 prim: OCTET STRING [HEX DUMP]:F76CD48CE0....3854B5AC26
```
If you step down at offset 30+27=57, you will see the 2376 bytes of the keybag that gpgsm
successfully decrypted.
At offset 30+2615 is the data of the certificate. As seen in the log above, gpgsm
tries to decrypt "1632 bytes of RC2 encrypted text".
This is wrong, the cert bag is only 1624 bytes long.
Inspecting the original `openssl-self-signed.pfx` confirms this (note that here
openssl swapped the order of the cert and key bags):
```
$ openssl asn1parse -i -inform der -in openssl-self-signed.pfx -strparse 30
0:d=0 hl=4 l=4200 cons: SEQUENCE
4:d=1 hl=4 l=1695 cons: SEQUENCE
8:d=2 hl=2 l= 9 prim: OBJECT :pkcs7-encryptedData
19:d=2 hl=4 l=1680 cons: cont [ 0 ]
23:d=3 hl=4 l=1676 cons: SEQUENCE
27:d=4 hl=2 l= 1 prim: INTEGER :00
30:d=4 hl=4 l=1669 cons: SEQUENCE
34:d=5 hl=2 l= 9 prim: OBJECT :pkcs7-data
45:d=5 hl=2 l= 28 cons: SEQUENCE
47:d=6 hl=2 l= 10 prim: OBJECT :pbeWithSHA1And40BitRC2-CBC
59:d=6 hl=2 l= 14 cons: SEQUENCE
61:d=7 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:5024C63D9644A885
71:d=7 hl=2 l= 2 prim: INTEGER :0800
75:d=5 hl=4 l=1624 prim: cont [ 0 ]
1703:d=1 hl=4 l=2497 cons: SEQUENCE
1707:d=2 hl=2 l= 9 prim: OBJECT :pkcs7-data
1718:d=2 hl=4 l=2482 cons: cont [ 0 ]
1722:d=3 hl=4 l=2478 prim: OCTET STRING [HEX DUMP]:[omitted]
```
The extra 8 bytes seem to come from the two identifier and length octets that come as
part of the constructed encoding with definite length.
Others can parse this correctly:
1. pk12util [1] from Mozilla's NSS can read it (For my QuoVadis production PFX there is no MAC warning.):
```
$ sudo apt install libnss3-tools # on Ubuntu 20.04 it installs 2:3.49.1-1ubuntu1.6
[...]
$ pk12util -l test.pfx
Enter password for PKCS12 file: # test
pk12util: PKCS12 decode not verified: SEC_ERROR_PKCS12_INVALID_MAC: Unable to import. Invalid MAC. Incorrect password or corrupt file.
[...PFX content...]
```
2. openssl can read it too:
```
$ openssl version
OpenSSL 1.1.1f 31 Mar 2020
$ openssl pkcs12 -in test.pfx
Enter Import Password: # test
[...PFX content...]
```
I attached the `openssl-self-signed.pfx` that I generated myself, as well as the
`test.pfx` that contains the cert and key from the `openssl-self-signed.pfx`
but mapped onto the ASN1 structure as QuoVadis created it for my ETH PFX archive.
{F3132290}
{F3132293}
Let me know if you need more information.
[0] https://github.com/google/der-ascii
[1] https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/tools/NSS_Tools_pk12util