OpenPGP Version 6 Support

Requirements: IPWorks OpenPGP

Introduction

IPWorks OpenPGP is a comprehensive suite of components that implements the OpenPGP standard for encryption and decryption, as well as key generation and management.

This guide will focus on implementing the latest support for OpenPGP v6. Before continuing, it is recommended to download the latest version of IPWorks OpenPGP to follow along with this guide. This guide also assumes that you are familiar with the updates and new behavior related to OpenPGP v4, as described in the upgrade section of our OpenPGP guide here.

Contents

OpenPGP Keys

There have been a variety of additions to OpenPGP keys, specifically related to the KeyMgr component. This section details the recent additions regarding key creation and management.

Key Version

As of IPWorks OpenPGP 2024, OpenPGP v6 keys are now supported. As a result, the new KeyVersion config has been exposed, allowing users to specify the key version when calling CreateKey. Note that by default, this config is set to 4. To create an OpenPGP v6 key, this config may be set to 6. Additionally, a new field, Version, has been exposed to the Key type. For example:

keymgr1.Config("KeyVersion=6"); keymgr1.CreateKey("test", "test"); Console.WriteLine("Key Version: " + keymgr1.Key.Version);

When a key is used in other components, such as the OpenPGP component, the version of the created signature or encrypted message is dependent entirely upon the version of the key performing the operation. For example, if a v6 key is used during signing, a v6 signature is produced. Additionally, the operations the key may be used for depends upon the version of the key and signature/message.

Note: Many additional sections in this article are regarding detailed options for key creation. Ultimately, to create an OpenPGP v6 key, only the KeyVersion config must be set appropriately. While additional configs may be utilized when creating a key, the component will choose appropriate values if these configs are left unspecified. Please see below for the relevant default values of various configs, assuming only the version is selected.

Key Version KeyUsage PublicKeyAlgorithm Curve SubKeyUsageSubKeyAlgorithm SubKeyCurve
4 0x0F EdDSA Ed25519 0x0C ECDH Curve25519
6 0x0F Ed25519 Ed25519 0x0C X25519 Curve25519

User Ids

In OpenPGP v6, a key may or may not have a UserId, the field is optional. As a result, when calling CreateKey for v6 keys, it is possible to specify an empty string for the user Id. For example:

keymgr1.Config("KeyVersion=6"); //key with user Id keymgr1.CreateKey("user1", "test1"); //key without user Id keymgr1.CreateKey("", "test2");

Note that when creating an OpenPGP v4 key, a UserId is required.

Key Ids and Fingerprints

In many cases, a user Id can identify or select a specific Key. For v6 keys without a user Id, the Fingerprint or KeyId should be used as the key's identifier instead. It is typically recommended to use the key's fingerprint as the identifier instead of the key Id, as it is possible (though still unlikely) for keys to have the same key Id. Note for OpenPGP v6 keys, the KeyId corresponds to the first 8 bytes of the key's Fingerprint.

Additionally, certain methods in the KeyMgr component require a key's user Id (e.g., AddRevoker). If a v6 key is created without a user Id, the key's fingerprint or key Id should also be used in place of the user Id. Please see below for an example utilizing the key's fingerprint and key Id.

keymgr1.Config("KeyVersion=6"); keymgr1.CreateKey("user1", "test1"); string fingerprint1 = keymgr1.Key.Fingerprint; keymgr1.CreateKey("user2", "test2"); string fingerprint2 = keymgr1.Key.Fingerprint; // Select the first key keymgr1.Key.Fingerprint = fingerprint1; Console.WriteLine(keymgr1.Key.UserId); // user1 // Select the second key keymgr1.Key.Fingerprint = fingerprint2; Console.WriteLine(keymgr1.Key.UserId); // user2 // Add revoker with fingerprint2 keymgr1.AddRevoker(fingerprint2);

Public Key Algorithms

When creating a key, the key's public key algorithm may be set using the PublicKeyAlgorithm configuration setting. The new default value of the PublicKeyAlgorithm config is automatic. For OpenPGP v6 keys, this corresponds to a public key algorithm of Ed25519.

It's important to note that not all supported public key algorithms are valid for OpenPGP v6. For convenience, please refer to the below table for a full list of supported public key algorithms for both OpenPGP v4 and v6.

Public Key AlgorithmSupported for OpenPGP v4Supported for OpenPGP v6
RSA
DSA
ECDSA
EdDSA
Ed25519
Ed448
RSA-Legacy

Note when creating a v6 RSA key, the PublicKeyLength config must be at least 3072 (default).

Subkey Algorithms

When creating a key with CreateKey, or creating a subkey directly with CreateSubkey, the SubKeyAlgorithm configuration setting can be set to specify the subkey's public key algorithm. Like the public key algorithms, not all subkey algorithms are valid for OpenPGP v6. For convenience, please refer to the below table for a full list of supported subkey algorithms for both OpenPGP v4 and OpenPGP v6.

Public Key AlgorithmSupported for OpenPGP v4Supported for OpenPGP v6
RSA
DSA
ElGamal
ECDSA
ECDH
EdDSA
Ed25519
Ed448
X25519
X448

Note when creating a v6 RSA subkey, the PublicKeyLength config must be at least 3072 (default). Additionally, when creating a v6 ECDH subkey, the SubKeyCurve cannot be set to Curve25519.

AEAD and Argon2 Support

The latest version introduces AEAD and Argon2 support. This support can be utilized when directly creating a key, during symmetric encryption, and message encryption.

Encryption Algorithms

The following AEAD encryption algorithms have been exposed throughout various components:

  • AES256-OCB
  • AES192-OCB
  • AES128-OCB
  • AES256-GCM
  • AES192-GCM
  • AES128-GCM

These algorithms may be utilized in a few different scenarios. Please see below for additional details.

Message Encryption

The AEAD encryption algorithms may be utilized when performing message encryption (e.g., using the OpenPGP component). These algorithms may be specified using the EncryptingAlgorithm property. When encrypting a message, the AEADChunkSizeExp config has been introduced, which specifies the exponent used to calculate the plaintext chunk size for encryption (default value of 6). Please see below for a simple example.

openpgp1.RecipientKeys.Add(new Key(pubKey, "test")); // some random public key openpgp1.EncryptingAlgorithm = "AES256-OCB"; openpgp1.Config("AEADChunkSizeExp=8") // Plaintext is split into chunks of size 2^8 bytes (in this case, only one chunk) openpgp1.InputMessage = "Hello, World!"; openpgp1.Encrypt();

Symmetric Encryption

The mentioned AEAD algorithms may be utilized during symmetric encryption (e.g., using the OpenPGP component), assuming the Argon2 algorithm is enabled. Enabling Argon2 is only valid if an AEAD algorithm is specified. If an AEAD encryption algorithm is specified, but Argon2 is disabled, the AEAD algorithm will have no effect during symmetric encryption but will still apply during message encryption. For more information regarding Argon2 support, please see here, or see below for a simple example:

// Enable symmetric encryption openpgp1.Config("SymmetricPassphrase=test"); openpgp1.Config("UseArgon2=true"); // must specify AEAD algorithm openpgp1.EncryptingAlgorithm = "AES256-OCB"; // required since UseArgon2=true openpgp1.InputMessage = "Hello, World!"; openpgp1.Encrypt();

Key Derivation

For use with the KeyMgr component, the AEAD encryption algorithms may be specified when creating a key, and have been exposed through the KeyEncryptionAlgorithm config. Like with symmetric encryption, the mentioned AEAD algorithms may be utilized during key derivation, assuming the Argon2 algorithm is enabled. Enabling Argon2 is only valid if an AEAD algorithm is specified. In KeyMgr, it is only intended to use these algorithms together (unlike OpenPGP, where an AEAD algorithm may be utilized for message encryption). Either an AEAD algorithm is specified and Argon2 is enabled, or a non-AEAD algorithm is specified and Argon2 is disabled. For more information regarding Argon2 support, please see here, or see below for a simple example:

keymgr1.Config("KeyVersion=6"); keymgr1.Config("KeyEncryptionAlgorithm=AES256-OCB"); keymgr1.Config("UseArgon2=true"); // required keymgr1.Create("test", "test");

Argon2 Support

The Argon2 algorithm has been exposed for use when performing passphrase-based key derivation. This algorithm is relevant when creating a key in the KeyMgr component, as well as when performing symmetric encryption in the OpenPGP component (and elsewhere). The use of the Argon2 algorithm can be enabled and tuned with the addition of the following configurations:

  • UseArgon2
  • Argon2Iterations
  • Argon2Parallelism
  • Argon2MemorySizeExp

UseArgon2: This config enables the use of the Argon2 algorithm for use during passphrase-based key derivation. By default, this config is disabled. If enabled, an AEAD encryption algorithm must be specified by the component. The below configuration settings may be used to tune the Argon2 algorithm, and are only valid if UseArgon2 is enabled.

Argon2Iterations: This config specifies the number of iterations performed by the Argon2 algorithm during passphrase-based key derivation. The default value of this config is 3, and valid values range from 1 to 2^(32)-1.

Argon2Parallelism: This config specifies the degree of parallelism used by the Argon2 algorithm during passphrase-based key derivation. It may be used to tune the running time independently of the memory size (see Argon2MemorySizeExp below). The default value of this config is 4, and valid values range from 1 to 2^(24)-1.

Argon2MemorySizeExp: This config specifies the exponent used to calculate memory size for use by the Argon2 algorithm during passphrase-based key derivation. The default value of this config is 16, and valid values range from 3 to 31. The memory size (in KB) is calculated as 2^exp, where exp is the value of this config. For example, the default memory size would be 2^(16) = 65,536 KB.

Note that the memory size must be an integer number of kilobytes ranging from 8*p to 2^(32)-1, where p is the value of Argon2Parallelism. Therefore, this config must be an integer number ranging from 3+ceil(log2(p)) to 31. For example, if Argon2Parallelism is set to 8, valid values of Argon2MemorySizeExp range from 6 to 31.

See below for a simple example of utilizing these configs when creating a key using the KeyMgr component.

// Using default values keymgr1.Config("KeyEncryptionAlgorithm=AES256-OCB"); keymgr1.Config("KeyVersion=6"); keymgr1.Config("UseArgon2=true"); keymgr1.CreateKey("test", "test"); // Tuning Argon2 algorithm keymgr1.Config("KeyEncryptionAlgorithm=AES256-OCB"); keymgr1.Config("KeyVersion=6"); keymgr1.Config("UseArgon2=true"); keymgr1.Config("Argon2Iterations=1"); keymgr1.Config("Argon2Parallelism=4"); // Argon2MemorySizeExp must be within the range 5 to 31 keymgr1.Config("Argon2MemorySizeExp=22"); keymgr1.CreateKey("test", "test");

We appreciate your feedback.  If you have any questions, comments, or suggestions about this article please contact our support team at kb@nsoftware.com.