Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New OpenPGP Specification #525

Open
wants to merge 46 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
25ae534
Initial implementation of Argon2 (ported from bc-java)
roughconsensusandrunningcode Feb 8, 2024
fa13755
New algorithms and packet types
roughconsensusandrunningcode Feb 10, 2024
5dcbac6
Padding Packet support
roughconsensusandrunningcode Feb 11, 2024
091b92e
Initial support for Version6 keys and signatures
roughconsensusandrunningcode Feb 13, 2024
182c2ae
basic creation of v6 public keys
roughconsensusandrunningcode Feb 14, 2024
31f2b98
check correct salt size in v6 signature packets
roughconsensusandrunningcode Feb 15, 2024
3edb702
refactored SecretKeyPacket (based on java version)
roughconsensusandrunningcode Feb 15, 2024
ad9b9b2
refactored S2k class (based on java version)
roughconsensusandrunningcode Feb 15, 2024
244eaa0
Packet criticality
roughconsensusandrunningcode Feb 22, 2024
683e76f
verify v6 key binding signatures
roughconsensusandrunningcode Feb 22, 2024
3105084
parse and verify v6 One-Pass Signatures
roughconsensusandrunningcode Feb 23, 2024
a613784
generate v6 signatures
roughconsensusandrunningcode Feb 28, 2024
ceaa293
test v6 inline signature generate and verify
roughconsensusandrunningcode Feb 29, 2024
4dfe4ad
Merge branch 'bcgit:master' into crypto-refresh
roughconsensusandrunningcode Feb 29, 2024
f4ac85c
doc comments
roughconsensusandrunningcode Feb 29, 2024
a8ce4b0
v5 keys and signature verification, tests on multiple v4+v6 signatures
roughconsensusandrunningcode Mar 2, 2024
6c933f2
generation of v6 OpenPGP keypairs
roughconsensusandrunningcode Mar 3, 2024
fd0da13
test adding encryption subkeys in v6 keyrings
roughconsensusandrunningcode Mar 3, 2024
cf27aea
small modifications in tests
roughconsensusandrunningcode Mar 4, 2024
a9c2208
"Version" and "Hash" Armor Header only on explicit request
roughconsensusandrunningcode Mar 5, 2024
b10b3f1
Merge branch 'bcgit:master' into crypto-refresh
roughconsensusandrunningcode Mar 7, 2024
f939a07
Review PgpSecretKey class
roughconsensusandrunningcode Mar 12, 2024
13b9d9f
Merge branch 'crypto-refresh' of https://github.com/roughconsensusand…
roughconsensusandrunningcode Mar 12, 2024
d7e7d09
Merge branch 'bcgit:master' into crypto-refresh
roughconsensusandrunningcode Mar 12, 2024
b0b65bc
Merge branch 'bcgit:master' into crypto-refresh
roughconsensusandrunningcode Mar 27, 2024
8554d8f
Merge branch 'bcgit:master' into crypto-refresh
roughconsensusandrunningcode Apr 9, 2024
e0ce83b
SKESK v6 decryption
roughconsensusandrunningcode Apr 13, 2024
32dbbd0
Merge branch 'argon2' into crypto-refresh
roughconsensusandrunningcode Apr 13, 2024
16e7329
Support for Argon2 s2k in SKESK and PgpSecretKey decryption
roughconsensusandrunningcode Apr 14, 2024
cb26579
V6 PKESK decryption
roughconsensusandrunningcode Apr 16, 2024
b063307
V4 SKESK + V1 SEIPD Encryption with Argon2 s2k
roughconsensusandrunningcode Apr 18, 2024
68d3821
V3 PKESK + V1 SEIPD Encryption with X25519 and X448
roughconsensusandrunningcode Apr 20, 2024
5e72362
AEAD Encryption - V6 PKESK, V6 SKESK, V2 SEIPD
roughconsensusandrunningcode Apr 24, 2024
b0848c0
Correct usage of RSA and ECDH in V6 PKESK
roughconsensusandrunningcode Apr 25, 2024
bdbb66c
Update references to "crypto-refresh" to point to RFC 9580.
roughconsensusandrunningcode Aug 16, 2024
a1496ef
Feature flag for Version 2 SEIPD
roughconsensusandrunningcode Sep 28, 2024
28db83e
Followup changes to Argon2 PR
peterdettman Oct 2, 2024
55361bd
Followup changes to Argon2 in OpenPGP
roughconsensusandrunningcode Oct 3, 2024
f9182de
Add XorBothTo methods to Nat(512)
peterdettman Oct 3, 2024
d396a19
Refactoring in Argon2
peterdettman Oct 3, 2024
6940300
Adapt PgpUtilities.DoMakeKeyFromPassPhrase to Argon2 Refactoring
roughconsensusandrunningcode Oct 5, 2024
1e57feb
Add various fingerprint-related methods in OpenPgp
peterdettman Apr 10, 2024
2805fb1
merge master branch
roughconsensusandrunningcode Dec 5, 2024
ea6a5b3
followup changes
roughconsensusandrunningcode Dec 5, 2024
8ad4e84
BitStrength for RFC 9580 pubkey algorithms
roughconsensusandrunningcode Dec 5, 2024
89e29ba
Merge branch 'bcgit:master' into crypto-refresh
roughconsensusandrunningcode Feb 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
V3 PKESK + V1 SEIPD Encryption with X25519 and X448
  • Loading branch information
roughconsensusandrunningcode committed Apr 20, 2024
commit 68d3821c2446e6b36fc23c638a835cd566b76581
157 changes: 128 additions & 29 deletions crypto/src/openpgp/PgpEncryptedDataGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

using System.Text;
using Org.BouncyCastle.Asn1.Cryptlib;
using Org.BouncyCastle.Asn1.EdEC;
using Org.BouncyCastle.Crypto;
Expand Down Expand Up @@ -46,16 +46,19 @@ private class PbeMethod
: EncMethod
{
private readonly S2k s2k;
private readonly int skeskVersion;

internal PbeMethod(
SymmetricKeyAlgorithmTag encAlgorithm,
S2k s2k,
KeyParameter key)
KeyParameter key,
int skeskVersion)
: base(PacketTag.SymmetricKeyEncryptedSessionKey)
{
this.encAlgorithm = encAlgorithm;
this.s2k = s2k;
this.key = key;
this.skeskVersion = skeskVersion;
}

public KeyParameter GetKey()
Expand Down Expand Up @@ -91,12 +94,14 @@ private class PubMethod
internal PgpPublicKey pubKey;
internal bool sessionKeyObfuscation;
internal byte[][] data;
private readonly int pkeskVersion;

internal PubMethod(PgpPublicKey pubKey, bool sessionKeyObfuscation)
internal PubMethod(PgpPublicKey pubKey, bool sessionKeyObfuscation, int pkeskVersion)
: base(PacketTag.PublicKeyEncryptedSession)
{
this.pubKey = pubKey;
this.sessionKeyObfuscation = sessionKeyObfuscation;
this.pkeskVersion = pkeskVersion;
}

public override void AddSessionInfo(
Expand All @@ -112,6 +117,86 @@ private byte[] EncryptSessionInfo(byte[] sessionInfo, SecureRandom random)
{
var cryptoPublicKey = pubKey.GetKey();

if (pubKey.Algorithm == PublicKeyAlgorithmTag.X25519 || pubKey.Algorithm == PublicKeyAlgorithmTag.X448)
{
IAsymmetricCipherKeyPairGenerator kpGen;
IRawAgreement agreement;
AsymmetricCipherKeyPair ephemeral;
byte[] ephPubEncoding;
IDigest digestForHkdf;
byte[] hkdfInfo;
SymmetricKeyAlgorithmTag wrappingAlgo;

if (pubKey.Algorithm == PublicKeyAlgorithmTag.X25519)
{
agreement = new X25519Agreement();
kpGen = new X25519KeyPairGenerator();
kpGen.Init(new X25519KeyGenerationParameters(random));
digestForHkdf = PgpUtilities.CreateDigest(HashAlgorithmTag.Sha256);
hkdfInfo = Encoding.ASCII.GetBytes("OpenPGP X25519");
wrappingAlgo = SymmetricKeyAlgorithmTag.Aes128;
ephemeral = kpGen.GenerateKeyPair();
ephPubEncoding = new byte[X25519PublicKeyParameters.KeySize];
((X25519PublicKeyParameters)ephemeral.Public).Encode(ephPubEncoding, 0);
}
else
{
// X448
agreement = new X448Agreement();
kpGen = new X448KeyPairGenerator();
kpGen.Init(new X448KeyGenerationParameters(random));
digestForHkdf = PgpUtilities.CreateDigest(HashAlgorithmTag.Sha512);
hkdfInfo = Encoding.ASCII.GetBytes("OpenPGP X448");
wrappingAlgo = SymmetricKeyAlgorithmTag.Aes256;
ephemeral = kpGen.GenerateKeyPair();
ephPubEncoding = new byte[X448PublicKeyParameters.KeySize];
((X448PublicKeyParameters)ephemeral.Public).Encode(ephPubEncoding, 0);
}

agreement.Init(ephemeral.Private);
byte[] sharedSecret = new byte[agreement.AgreementSize];
agreement.CalculateAgreement(cryptoPublicKey, sharedSecret, 0);

byte[] pubKeyMaterial = ((OctetArrayBcpgKey)pubKey.PublicKeyPacket.Key).GetKey();
byte[] ikm = Arrays.ConcatenateAll(ephPubEncoding, pubKeyMaterial, sharedSecret);
byte[] hkdfSalt = Array.Empty<byte>();
var hkdfParams = new HkdfParameters(ikm, hkdfSalt, hkdfInfo);
var hkdfGen = new HkdfBytesGenerator(digestForHkdf);
hkdfGen.Init(hkdfParams);
var hkdfOutput = new byte[PgpUtilities.GetKeySizeInOctets(wrappingAlgo)];
hkdfGen.GenerateBytes(hkdfOutput, 0, hkdfOutput.Length);

KeyParameter kek = ParameterUtilities.CreateKeyParameter("AES", hkdfOutput);
var wrapper = PgpUtilities.CreateWrapper(wrappingAlgo);
wrapper.Init(true, kek);

int offset = 0;
int length = sessionInfo.Length - 2; // no checksum for X25519 and X448 keys
// for X25519 and X448 keys the SymmetricKeyAlgorithmTag, when present (V3 PKESK)
// is not encrypted, is prepended to the ESK in plaintext
if (pkeskVersion == PublicKeyEncSessionPacket.Version3)
{
offset = 1;
length--;
}
var keyBytes = wrapper.Wrap(sessionInfo, offset, length);

byte[] esk;
using (var ms = new MemoryStream())
{
ms.Write(ephPubEncoding, 0, ephPubEncoding.Length);
if (pkeskVersion == PublicKeyEncSessionPacket.Version3)
{
// Unencrypted SymmetricKeyAlgorithmTag (V3 PKESK only)
ms.WriteByte(sessionInfo[0]);
}
ms.Write(keyBytes, 0, keyBytes.Length);
esk = ms.ToArray();
}

return esk;
}

if (pubKey.Algorithm != PublicKeyAlgorithmTag.ECDH)
{
IBufferedCipher c;
Expand Down Expand Up @@ -275,6 +360,16 @@ private byte[][] ProcessSessionInfo(byte[] encryptedSessionInfo)
case PublicKeyAlgorithmTag.ECDH:
data = new byte[1][]{ encryptedSessionInfo };
break;
case PublicKeyAlgorithmTag.X25519:
case PublicKeyAlgorithmTag.X448:
int ephemeralKeyLen = pubKey.Algorithm == PublicKeyAlgorithmTag.X25519 ? 32 : 56;
byte[] ephemeralKey = new byte[ephemeralKeyLen];
byte[] encryptedSessionKey = new byte[encryptedSessionInfo.Length - ephemeralKeyLen];
Array.Copy(encryptedSessionInfo, 0, ephemeralKey, 0, ephemeralKeyLen);
Array.Copy(encryptedSessionInfo, ephemeralKeyLen, encryptedSessionKey, 0, encryptedSessionKey.Length);

data = new byte[][] { ephemeralKey, encryptedSessionKey };
break;
default:
throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm);
}
Expand Down Expand Up @@ -306,20 +401,21 @@ public override void Encode(BcpgOutputStream pOut)
private readonly SymmetricKeyAlgorithmTag defAlgorithm;
private readonly SecureRandom rand;

public PgpEncryptedDataGenerator(
private readonly int skeskVersion;
private readonly int pkeskVersion;
private readonly int seipdVersion;

public PgpEncryptedDataGenerator(
SymmetricKeyAlgorithmTag encAlgorithm)
:this(encAlgorithm, CryptoServicesRegistrar.GetSecureRandom(), false, false)
{
this.defAlgorithm = encAlgorithm;
this.rand = CryptoServicesRegistrar.GetSecureRandom();
}

public PgpEncryptedDataGenerator(
SymmetricKeyAlgorithmTag encAlgorithm,
bool withIntegrityPacket)
{
this.defAlgorithm = encAlgorithm;
this.withIntegrityPacket = withIntegrityPacket;
this.rand = CryptoServicesRegistrar.GetSecureRandom();
: this(encAlgorithm, CryptoServicesRegistrar.GetSecureRandom(), false, withIntegrityPacket)
{
}

/// <summary>Existing SecureRandom constructor.</summary>
Expand All @@ -328,26 +424,17 @@ public PgpEncryptedDataGenerator(
public PgpEncryptedDataGenerator(
SymmetricKeyAlgorithmTag encAlgorithm,
SecureRandom random)
: this(encAlgorithm, random, false, false)
{
if (random == null)
throw new ArgumentNullException(nameof(random));

this.defAlgorithm = encAlgorithm;
this.rand = random;
}

/// <summary>Creates a cipher stream which will have an integrity packet associated with it.</summary>
public PgpEncryptedDataGenerator(
SymmetricKeyAlgorithmTag encAlgorithm,
bool withIntegrityPacket,
SecureRandom random)
:this(encAlgorithm, random, false, withIntegrityPacket)
{
if (random == null)
throw new ArgumentNullException(nameof(random));

this.defAlgorithm = encAlgorithm;
this.rand = random;
this.withIntegrityPacket = withIntegrityPacket;
}

/// <summary>Base constructor.</summary>
Expand All @@ -358,13 +445,24 @@ public PgpEncryptedDataGenerator(
SymmetricKeyAlgorithmTag encAlgorithm,
SecureRandom random,
bool oldFormat)
:this (encAlgorithm, random, oldFormat, false)
{
if (random == null)
throw new ArgumentNullException(nameof(random));
}

private PgpEncryptedDataGenerator(
SymmetricKeyAlgorithmTag encAlgorithm,
SecureRandom random,
bool oldFormat,
bool withIntegrityPacket)
{
this.rand = random ?? throw new ArgumentNullException(nameof(random));
this.defAlgorithm = encAlgorithm;
this.rand = random;
this.oldFormat = oldFormat;
this.withIntegrityPacket = withIntegrityPacket;

skeskVersion = SymmetricKeyEncSessionPacket.Version4;
pkeskVersion = PublicKeyEncSessionPacket.Version3;
seipdVersion = SymmetricEncIntegrityPacket.Version1;
}

/// <summary>Add a PBE encryption method to the encrypted object.</summary>
Expand Down Expand Up @@ -399,7 +497,7 @@ internal void DoAddMethod(byte[] rawPassPhrase, bool clearPassPhrase, HashAlgori
{
S2k s2k = PgpUtilities.GenerateS2k(s2kDigest, 0x60, rand);

methods.Add(new PbeMethod(defAlgorithm, s2k, PgpUtilities.DoMakeKeyFromPassPhrase(defAlgorithm, s2k, rawPassPhrase, clearPassPhrase)));
methods.Add(new PbeMethod(defAlgorithm, s2k, PgpUtilities.DoMakeKeyFromPassPhrase(defAlgorithm, s2k, rawPassPhrase, clearPassPhrase), skeskVersion));
}

/// <summary>Add a PBE encryption method to the encrypted object.</summary>
Expand Down Expand Up @@ -438,7 +536,8 @@ internal void DoAddMethod(byte[] rawPassPhrase, bool clearPassPhrase, S2k.Argon2
new PbeMethod(
defAlgorithm,
s2k,
PgpUtilities.DoMakeKeyFromPassPhrase(defAlgorithm, s2k, rawPassPhrase, clearPassPhrase)
PgpUtilities.DoMakeKeyFromPassPhrase(defAlgorithm, s2k, rawPassPhrase, clearPassPhrase),
skeskVersion
));
}

Expand All @@ -455,7 +554,7 @@ public void AddMethod(PgpPublicKey key, bool sessionKeyObfuscation)
throw new ArgumentException("passed in key not an encryption key!");
}

methods.Add(new PubMethod(key, sessionKeyObfuscation));
methods.Add(new PubMethod(key, sessionKeyObfuscation, pkeskVersion));
}

private void AddCheckSum(
Expand All @@ -478,7 +577,7 @@ private void AddCheckSum(
private byte[] CreateSessionInfo(SymmetricKeyAlgorithmTag algorithm, KeyParameter key)
{
int keyLength = key.KeyLength;
byte[] sessionInfo = new byte[keyLength + 3];
byte[] sessionInfo = new byte[keyLength + 3];
sessionInfo[0] = (byte)algorithm;
key.CopyTo(sessionInfo, 1, keyLength);
AddCheckSum(sessionInfo);
Expand Down Expand Up @@ -516,7 +615,7 @@ private Stream Open(
{
if (methods[0] is PbeMethod pbeMethod)
{
key = pbeMethod.GetKey();
key = pbeMethod.GetKey();
}
else if (methods[0] is PubMethod pubMethod)
{
Expand Down
Loading