Page MenuHome GnuPG

All s2k hardenings silently ignored when exporting private keys
Closed, ResolvedPublic

Description

Although i hardened all s2k options, they are silently ignored by doing a downgrade to old RFC4880-specs from 2007, when doing a private-key export. So the private key is only secured by AES-128 and SHA-1.

In 2015 you promised to fix this issue: https://dev.gnupg.org/T1800#84713

... but now under the "de-vs" mode aka "VS-NfD Modus", authorized for "VS-NfD" usage by the "BSI-VSA-10573" and "BSI-VSA-10584", this becomes an important security issue, when exporting private keys.

Here's a replay under "Gpg4Win 3.1.16" with "VS-NfD"-enforcement under "Windows 10":

In the log, you will see the enforced option "--openpgp" on export, that silently disables all "s2k"-hardenings and activates old RFC4880 conformity:

https://datatracker.ietf.org/doc/html/rfc4880#section-12.2
https://datatracker.ietf.org/doc/html/rfc4880#section-9.2
"... Implementations SHOULD implement AES-128 ..."

https://datatracker.ietf.org/doc/html/rfc4880#section-9.4
"... Implementations MUST implement SHA-1 ..."

For details see DEBUG-log of gpg-agent and gpg below:

Look for strings => !!!!!!!  LOOK HERE  !!!!!!!

To reproduce:

Run GnuPG (i.e. 2.2.28) in a client console on Windows 10 and create a private key with some hardenings for symmetric key encryption:

"gpg --s2k-cipher-algo AES256 --s2k-digest-algo SHA512 --s2k-mode 3 --s2k-count 65000000 --gen-key"

Do killall all kleopatra, gpg, gpg-agent, dirmngr processes ...

Use Open another admin console on Windows 10 and run gpg-agent in debug-mode:

"gpg-agent -v --debug-level expert --daemon"

Then use previously client console above on Windows 10 and run private key export of the previously generated key above, while monitoring the output on admin-console of gpg-agent:

"gpg --s2k-cipher-algo AES256 --s2k-digest-algo SHA512 --s2k-mode 3 --s2k-count 65000000 --export-secret-keys | gpg --list-packets"

... you will notice an export command like:
"EXPORT_KEY --openpgp --cache-nonce= ..."   <=== Downgrade to RFC4880 (although hardened in config / commandline) !!

Here you will see, that the output-crypto was downgraded to standard old "rfc4880-defaults" ...

  1. Details (gpg-agent output RSA PRIVATE-KEY Export request): ##############

C:\temp> gpg-agent -v --debug-level expert --daemon
gpg-agent[1116]: enabled debug flags: cache ipc
gpg-agent[1116]: Es wird auf Socket `C:\Users\xxxxxxxxxxx\AppData\Roaming\GnuPG\S.gpg-agent' gehört
gpg-agent[1116]: Es wird auf Socket `C:\Users\xxxxxxxxxxx\AppData\Roaming\GnuPG\S.gpg-agent.extra' gehört
gpg-agent[1116]: Es wird auf Socket `C:\Users\xxxxxxxxxxx\AppData\Roaming\GnuPG\S.gpg-agent.browser' gehört
gpg-agent[1116]: Es wird auf Socket `C:\Users\xxxxxxxxxxx\AppData\Roaming\GnuPG\S.gpg-agent.ssh' gehört
gpg-agent[1116]: gpg-agent (GnuPG) 2.2.28 started
gpg-agent[1116]: DBG: chan_0x00000290 -> OK Pleased to meet you
gpg-agent[1116]: DBG: chan_0x00000290 <- RESET
gpg-agent[1116]: DBG: chan_0x00000290 -> OK
gpg-agent[1116]: DBG: chan_0x00000290 <- GETINFO version
gpg-agent[1116]: DBG: chan_0x00000290 -> D 2.2.28
gpg-agent[1116]: DBG: chan_0x00000290 -> OK
gpg-agent[1116]: DBG: chan_0x00000290 <- OPTION allow-pinentry-notify
gpg-agent[1116]: DBG: chan_0x00000290 -> OK
gpg-agent[1116]: DBG: chan_0x00000290 <- OPTION agent-awareness=2.1.0
gpg-agent[1116]: DBG: chan_0x00000290 -> OK
gpg-agent[1116]: DBG: chan_0x00000290 <- GETINFO jent_active
gpg-agent[1116]: DBG: chan_0x00000290 -> OK
gpg-agent[1116]: DBG: chan_0x00000290 <- KEYWRAP_KEY --export
gpg-agent[1116]: DBG: chan_00000290 -> [ 44 20 af e5 15 32 b8 63 27 c1 5c b7 79 9d 29 c9 ...(2 byte(s) skipped) ]
gpg-agent[1116]: DBG: chan_0x00000290 -> OK
gpg-agent[1116]: DBG: chan_0x00000290 <- HAVEKEY XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX
gpg-agent[1116]: DBG: chan_0x00000290 -> OK
gpg-agent[1116]: DBG: chan_0x00000290 <- KEYINFO XXXXXXXXXXXXXXXX
gpg-agent[1116]: DBG: agent_get_cache 'XXXXXXXXXXXXXXXX'.0 (mode 2) ...
gpg-agent[1116]: DBG: ... miss
gpg-agent[1116]: DBG: chan_0x00000290 -> S KEYINFO XXXXXXXXXXXXXXXX D - - - P - - -
gpg-agent[1116]: DBG: chan_0x00000290 -> OK
gpg-agent[1116]: DBG: chan_0x00000290 <- SETKEYDESC Sie+benötigen+ein+Passwort,+um+den+geheimen+OpenPGP+Schlüssel+zu+exportieren:%0A%22test+tester+<test@tester.de>%22%0A4096-Bit+RSA+Schlüssel,+ID+8AA0EF152FA1B2DB,%0Aerzeugt+2021-12-07.%0A
gpg-agent[1116]: DBG: chan_0x00000290 -> OK !!!!!!! LOOK HERE !!!!!!!
gpg-agent[1116]: DBG: chan_0x00000290 <- EXPORT_KEY --openpgp XXXXXXXXXXXXXXXX
gpg-agent[1116]: starting a new PIN Entry
gpg-agent[1116]: DBG: connection to PIN entry established
gpg-agent[1116]: DBG: chan_0x00000290 -> INQUIRE PINENTRY_LAUNCHED 1332 qt 1.1.1 - - - - 0/0 -
gpg-agent[1116]: DBG: chan_0x00000290 <- END
gpg-agent[1116]: DBG: agent_put_cache 'XXXXXXXXXXXXXXXX'.0 (mode 0) requested ttl=0
gpg-agent[1116]: DBG: agent_put_cache 'XXXXXXXXXXXXXXXX'.0 (mode 5) requested ttl=120
gpg-agent[1116]: DBG: chan_0x00000290 -> S CACHE_NONCE 42E3B874198B86CC86244AC2
gpg-agent[1116]: DBG: chan_0x00000290 -> Confidential data not shown
gpg-agent[1116]: DBG: chan_0x00000290 -> Confidential data not shown
gpg-agent[1116]: DBG: chan_00000290 -> [ 44 20 24 a6 fc 2d b0 58 a6 3d f0 69 bb fb 74 be ...(22 byte(s) skipped) ]
gpg-agent[1116]: DBG: chan_0x00000290 -> OK
gpg-agent[1116]: DBG: chan_0x00000290 <- KEYINFO XXXXXXXXXXXXXXXX
gpg-agent[1116]: DBG: agent_get_cache 'XXXXXXXXXXXXXXXX'.0 (mode 2) ...
gpg-agent[1116]: DBG: ... miss
gpg-agent[1116]: DBG: chan_0x00000290 -> S KEYINFO XXXXXXXXXXXXXXXX D - - - P - - -
gpg-agent[1116]: DBG: chan_0x00000290 -> OK
gpg-agent[1116]: DBG: chan_0x00000290 <- SETKEYDESC Sie+benötigen+ein+Passwort,+um+den+geheimen+OpenPGP+Unterschlüssel+zu+exportieren:%0A%22test+tester+<test@tester.de>%22%0A4096-Bit+RSA+Schlüssel,+ID+XXXXXXXXXXXXXXXX,%0Aerzeugt+2021-12-07+(Hauptschlüssel-ID+XXXXXXXXXXXXXXXX).%0A
gpg-agent[1116]: DBG: chan_0x00000290 -> OK !!!!!!! LOOK HERE !!!!!!!
gpg-agent[1116]: DBG: chan_0x00000290 <- EXPORT_KEY --openpgp --cache-nonce=42E3B874198B86CC86244AC2 3B3A4D3DD3EC7E7ECCFE3FAEAD001BBE181E30DF
gpg-agent[1116]: DBG: agent_get_cache '42E3B874198B86CC86244AC2'.0 (mode 5) ...
gpg-agent[1116]: DBG: ... hit
gpg-agent[1116]: DBG: agent_put_cache 'XXXXXXXXXXXXXXXX'.0 (mode 5) requested ttl=120
gpg-agent[1116]: DBG: chan_0x00000290 -> S CACHE_NONCE 42E3B874198B86CC86244AC2
gpg-agent[1116]: DBG: chan_0x00000290 -> Confidential data not shown
gpg-agent[1116]: DBG: chan_0x00000290 -> Confidential data not shown
gpg-agent[1116]: DBG: chan_00000290 -> [ 44 20 b0 0f 61 38 20 51 64 7a fb f4 cc 04 98 50 ...(24 byte(s) skipped) ]
gpg-agent[1116]: DBG: chan_0x00000290 -> OK
gpg-agent[1116]: DBG: chan_0x00000290 <- [eof]
gpg-agent[1116]: DBG: agent_put_cache 'XXXXXXXXXXXXXXXX'.0 (mode 5) requested ttl=0

  1. Details (gpg output RSA PRIVATE-KEY 4096 Bit / SHA-512): ##############

C:\temp> gpg --s2k-cipher-algo AES256 --s2k-digest-algo SHA512 --s2k-mode 3 --s2k-count 65000000 --export-secret-keys | gpg --list-packets

off=0 ctb=95 tag=5 hlen=3 plen=1862

:secret key packet:

version 4, algo 1, created 1638902294, expires 0
pkey[0]: [4096 bits]
pkey[1]: [17 bits]
iter+salt S2K, algo: 7, SHA1 protection, hash: 2, salt: XXXXXXXXXXXXXXXX  <= !!!!!!!  LOOK HERE  !!!!!!!
protect count: 65011712 (255)
protect IV:  e4 7b de 46 29 39 1a 60 3f d4 1d cb 34 6e 24 df
skey[2]: [v4 protected]
keyid: 8AA0EF152FA1B2DB

off=1865 ctb=b4 tag=13 hlen=2 plen=28

:user ID packet: "test tester <test@tester.de>"

off=1895 ctb=89 tag=2 hlen=3 plen=596

:signature packet: algo 1, keyid XXXXXXXXXXXXXXXX

version 4, created 1638902294, md5len 0, sigclass 0x13
digest algo 10, begin of digest 3e 42
hashed subpkt 33 len 21 (issuer fpr v4 XXXXXXXXXXXXXXXX)
hashed subpkt 2 len 4 (sig created 2021-12-07)
hashed subpkt 27 len 1 (key flags: 03)
hashed subpkt 9 len 4 (key expires after 2y0d0h0m)
hashed subpkt 11 len 4 (pref-sym-algos: 9 8 7 3)
hashed subpkt 21 len 4 (pref-hash-algos: 10 9 8 11)
hashed subpkt 22 len 4 (pref-zip-algos: 2 3 1 0)
hashed subpkt 30 len 1 (features: 01)
hashed subpkt 23 len 1 (keyserver preferences: 80)
subpkt 16 len 8 (issuer key ID XXXXXXXXXXXXXXXX)
data: [4093 bits]

off=2494 ctb=9d tag=7 hlen=3 plen=1862

:secret sub key packet:

version 4, algo 1, created 1638902294, expires 0
pkey[0]: [4096 bits]
pkey[1]: [17 bits]
iter+salt S2K, algo: 7, SHA1 protection, hash: 2, salt: XXXXXXXXXXXXXXXX  <= !!!!!!!  LOOK HERE  !!!!!!!
protect count: 65011712 (255)
protect IV:  19 56 82 26 50 28 c5 40 09 34 fd cc 9f 3f cc 0f
skey[2]: [v4 protected]
keyid: 9F3C86D8EEEA01AF

off=4359 ctb=89 tag=2 hlen=3 plen=572

:signature packet: algo 1, keyid XXXXXXXXXXXXXXXX

version 4, created 1638902294, md5len 0, sigclass 0x18
digest algo 10, begin of digest 23 ed
hashed subpkt 33 len 21 (issuer fpr v4 XXXXXXXXXXXXXXXX)
hashed subpkt 2 len 4 (sig created 2021-12-07)
hashed subpkt 27 len 1 (key flags: 0C)
hashed subpkt 9 len 4 (key expires after 2y0d0h0m)
subpkt 16 len 8 (issuer key ID XXXXXXXXXXXXXXXX)
data: [4094 bits]

--- cut here ---

Best regards ...

Event Timeline

vitusb triaged this task as High priority.Jan 15 2022, 3:12 PM
vitusb created this task.
vitusb created this object in space S1 Public.
vitusb created this object with visibility "Subscribers".
vitusb created this object with edit policy "vitusb (vitusb)".
vitusb raised the priority of this task from High to Needs Triage.Jan 16 2022, 12:25 PM
vitusb changed the visibility from "Subscribers" to "Public (No Login Required)".
vitusb added a project: Bug Report.
vitusb renamed this task from All s2k hardenings silently ignored when doin an export of private keys to All s2k hardenings silently ignored when exporting private keys.Jan 16 2022, 2:10 PM
werner edited projects, added Not A Bug; removed Bug Report.

Sending a private key with just the local protection is not a good idea. It is better to export the key and then send it in an encrypted mail - for example in symmetric mode with a strong password.

As described in the the man page of gpg, the S2K options of gpg are only relevant to symmetric encryption and not protect a private key. It is unlikely that there will be an option to modify the S2K setting for exporting private keys before a new OpenPGP format for protecting a private key has been deployed.

Sending a private key with just the local protection is not a good idea.

That was not an option, but it is possible, because you have implemented this function ... so when this function is implemented, it should be implemented as secure as possible ...

It is better to export the key and then send it in an encrypted mail - for example in symmetric mode with a strong password.
As described in the the man page of gpg, the S2K options of gpg are only relevant to symmetric encryption and not protect a private key.

On the other way you promised to implement that:
https://dev.gnupg.org/T1800#84713
https://dev.gnupg.org/transactions/raw/PHID-XACT-TASK-pwqib3bttodlrht/

... or did i misunderstand that ?

It is unlikely that there will be an option to modify the S2K setting for exporting private keys before a new OpenPGP format for protecting a private key has been deployed.

Yes, i think that's the real reson ...

What is the current status of this issue?

I have offline master keys stored safely on paper for durability, as well as paper backups of important encryption keys to prevent data loss. This workflow depends on the ability to securely export secret keys in order to be viable. To that end, I studied all the gpg options and also applied the hardened s2k settings. However, it seems they are not being applied as expected.

It is better to export the key and then send it in an encrypted mail - for example in symmetric mode with a strong password.

This is not really compatible with the paperkey tool which expects standard PGP packets.

Paperkey is widely used to store master keys offline, allowing it to be loaded into a secure computer only when the relatively infrequent certification operations need to be performed. Combined with OpenPGP enabled secure cryptoprocessors such as smart cards and USB keys, this creates a workflow where gpg almost never handles key material directly, maximizing security.

Secure secret key export via this tool should be supported, with full support for hardened "non-standard" cryptography parameters.

Namely:

# Mode 3 = iterated and salted string-to-key
s2k-mode 3
s2k-cipher-algo AES256
s2k-digest-algo SHA512
s2k-count 65011712

It is unlikely that there will be an option to modify the S2K setting for exporting private keys before a new OpenPGP format for protecting a private key has been deployed.

When is that expected to happen?

In bug T1800 you said:

The agent calibrates the s2k count so that the KDF takes about 100ms.

This should be customizable, and indeed is customizable in other software. For example, KeePassXC which allows configuring these parameters so that key derivation takes as much as 5 seconds.

It is an open question whether gpg should be allowed to change the s2k options because the keys are a property of the agent and not of gpg.

Is it possible to configure this in the agent then?