keyformat.txt (wk 2001-12-18)
-----------------------------


Some notes on the format of the secret keys used with gpg-agent.

Location of keys
================
The secret keys[1] are stored on a per file basis in a directory below
the ~/.gnupg home directory.  This directory is named

   private-keys-v1.d

and should have permissions 700.

The secret keys are stored in files with a name matching the
hexadecimal representation of the keygrip[2].

Unprotected Private Key Format
==============================
The content of the file is an S-Expression like the ones used with
Libgcrypt.  Here is an example of an unprotected file:

(private-key
 (rsa
  (n #00e0ce9..[some bytes not shown]..51#)
  (e #010001#)
  (d #046129F..[some bytes not shown]..81#)
  (p #00e861b..[some bytes not shown]..f1#)
  (q #00f7a7c..[some bytes not shown]..61#)
  (u #304559a..[some bytes not shown]..9b#)
 )
 (created-at timestamp)
 (uri http://foo.bar x-foo:whatever_you_want)
 (comment whatever)
)

"comment", "created-at" and "uri" are optional.  "comment" is
currently used to keep track of ssh key comments. "created-at" is used
to keep track of the creation time stamp used with OpenPGP keys; it is
optional but required for some operations to calculate the fingerprint
of the key.  This timestamp should be a string with the number of
seconds since Epoch or an ISO time string (yyyymmddThhmmss).

Actually this form should not be used for regular purposes and only
accepted by gpg-agent with the configuration option:
--allow-non-canonical-key-format.  The regular way to represent the
keys is in canonical representation[3]:

(private-key
   (rsa
    (n #00e0ce9..[some bytes not shown]..51#)
    (e #010001#)
    (d #046129F..[some bytes not shown]..81#)
    (p #00e861b..[some bytes not shown]..f1#)
    (q #00f7a7c..[some bytes not shown]..61#)
    (u #304559a..[some bytes not shown]..9b#)
   )
   (uri http://foo.bar x-foo:whatever_you_want)
)  


Protected Private Key Format
==============================
A protected key is like this:

(protected-private-key
   (rsa
    (n #00e0ce9..[some bytes not shown]..51#)
    (e #010001#)
    (protected mode (parms) encrypted_octet_string)
    (protected-at <isotimestamp>)
   )
   (uri http://foo.bar x-foo:whatever_you_want)
   (comment whatever)
)  


In this scheme the encrypted_octet_string is encrypted according to
the algorithm described after the keyword protected; most protection
algorithms need some parameters, which are given in a list before the
encrypted_octet_string.  The result of the decryption process is a
list of the secret key parameters.  The protected-at expression is
optional; the isotimestamp is 15 bytes long (e.g. "19610711T172000").

The only available protection mode for now is

  openpgp-s2k3-sha1-aes-cbc

which describes an algorithm using using AES in CBC mode for
encryption, SHA-1 for integrity protection and the String to Key
algorithm 3 from OpenPGP (rfc2440).

Example:

(protected openpgp-s2k3-sha1-aes-cbc
  ((sha1 16byte_salt no_of_iterations) 16byte_iv)
  encrypted_octet_string
)

The encrypted_octet string should yield this S-Exp (in canonical
representation) after decryption:

(
 (
  (d #046129F..[some bytes not shown]..81#)
  (p #00e861b..[some bytes not shown]..f1#)
  (q #00f7a7c..[some bytes not shown]..61#)
  (u #304559a..[some bytes not shown]..9b#) 
 ) 
 (hash sha1 #...[hashvalue]...#)
)

For padding reasons, random bytes are appended to this list - they can
easily be stripped by looking for the end of the list. 

The hash is calculated on the concatenation of the public key and
secret key parameter lists: i.e it is required to hash the
concatenation of these 6 canonical encoded lists for RSA, including
the parenthesis, the algorithm keyword and (if used) the protected-at
list.

(rsa
 (n #00e0ce9..[some bytes not shown]..51#)
 (e #010001#)
 (d #046129F..[some bytes not shown]..81#)
 (p #00e861b..[some bytes not shown]..f1#)
 (q #00f7a7c..[some bytes not shown]..61#)
 (u #304559a..[some bytes not shown]..9b#)
 (protected-at "18950523T000000")
)

After decryption the hash must be recalculated and compared against
the stored one - If they don't match the integrity of the key is not
given.


Shadowed Private Key Format
============================
To keep track of keys stored on IC cards we use a third format for
private kyes which are called shadow keys as they are only a reference
to keys stored on a token:

(shadowed-private-key
   (rsa
    (n #00e0ce9..[some bytes not shown]..51#)
    (e #010001#)
    (shadowed protocol (info))
   )
   (uri http://foo.bar x-foo:whatever_you_want)
   (comment whatever)
)  

The currently used protocol is "ti-v1" (token info version 1).  The
second list with the information has this layout:

(card_serial_number id_string_of_key)

More items may be added to the list.






Notes:
======
[1] I usually use the terms private and secret key exchangeable but prefer the
term secret key because it can be visually be better distinguished
from the term public key.

[2] The keygrip is a unique identifier for a key pair, it is
independent of any protocol, so that the same key can be used with
different protocols.  PKCS-15 calls this a subjectKeyHash; it can be
calculated using Libgcrypt's gcry_pk_get_keygrip ().

[3] Even when canonical representation are required we will show the
S-expression here in a more readable representation.