Skip to content

Commit

Permalink
Verify on encrypted data searches in correct lists of keys. Additiona…
Browse files Browse the repository at this point in the history
…l minor refactorings.
  • Loading branch information
Liam-Rougoor committed Oct 14, 2023
1 parent 64c46cf commit fc73229
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 29 deletions.
36 changes: 33 additions & 3 deletions PgpCore.Tests/UnitTests/UnitTestsSync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Org.BouncyCastle.Bcpg;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Xunit;
Expand Down Expand Up @@ -358,7 +356,6 @@ public void Decrypt300MbFile_DecryptEncryptedFileWithMemoryUsageLessThan50Mb(Key
// Arrange
long memoryCap = 50 * 1024 * 1024;
long startPeakWorkingSet = Process.GetCurrentProcess().PeakWorkingSet64;

TestFactory testFactory = new TestFactory();
testFactory.Arrange(keyType, fileType);
EncryptionKeys encryptionKeys = new EncryptionKeys(testFactory.PublicKeyFileInfo);
Expand Down Expand Up @@ -1897,6 +1894,39 @@ public void Verify_VerifyEncryptedAndSignedStream(KeyType keyType)
// Teardown
testFactory.Teardown();
}

[Fact]
public void Verify_VerifyEncryptedAndSignedStreamForMultipleKeys()
{
// Arrange
TestFactory testFactory = new TestFactory();
testFactory.Arrange(KeyType.Known, FileType.Known);
EncryptionKeys encryptionKeys = new EncryptionKeys(testFactory.PublicKeyStream, testFactory.PrivateKeyStream, testFactory.Password);
PGP pgp = new PGP(encryptionKeys);

// Act
long[] keyIdsInPublicKeyRing = encryptionKeys.PublicKeyRings.First().PgpPublicKeyRing.GetPublicKeys()
.Where(key => key.IsEncryptionKey).Select(key => key.KeyId).ToArray();
foreach (long keyId in keyIdsInPublicKeyRing)
{
encryptionKeys.UseEncrytionKey(keyId);
using (Stream inputFileStream = testFactory.ContentStream)
using (Stream outputFileStream = File.Create(testFactory.EncryptedContentFilePath))
pgp.EncryptStreamAndSign(inputFileStream, outputFileStream);

bool verified = false;

using (Stream inputFileStream = testFactory.EncryptedContentStream)
verified = pgp.VerifyStream(inputFileStream);

// Assert
Assert.True(testFactory.EncryptedContentFileInfo.Exists);
Assert.True(verified);
}

// Teardown
testFactory.Teardown();
}

[Theory]
[InlineData(KeyType.Generated)]
Expand Down
11 changes: 5 additions & 6 deletions PgpCore/EncryptionKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ public void UseEncrytionKey(long keyId)
{
foreach (PgpPublicKeyRingWithPreferredKey publicKeyRing in PublicKeyRings)
{
publicKeyRing.UseEncryptionKey(keyId);
publicKeyRing.UsePreferredEncryptionKey(keyId);
}
}

Expand Down Expand Up @@ -409,14 +409,13 @@ private void
else
{
// Need to consume the stream into a list before it is closed (can happen because of lazy instantiation).
PgpPublicKeyRing[] publicKeyRingsConsumed = publicKeyRings.ToArray();
_publicKeyRingsWithPreferredKey = new Lazy<IEnumerable<PgpPublicKeyRingWithPreferredKey>>(() => publicKeyRingsConsumed.Select(keyRing => new PgpPublicKeyRingWithPreferredKey(keyRing)));
_publicKeyRingsWithPreferredKey = new Lazy<IEnumerable<PgpPublicKeyRingWithPreferredKey>>(() => publicKeyRings.Select(keyRing => new PgpPublicKeyRingWithPreferredKey(keyRing)).ToArray());
_masterKey = new Lazy<PgpPublicKey>(() =>
Utilities.FindMasterKey(publicKeyRingsConsumed.First()));
Utilities.FindMasterKey(publicKeyRings.First()));
_encryptKeys = new Lazy<IEnumerable<PgpPublicKey>>(() =>
publicKeyRingsConsumed.Select(Utilities.FindBestEncryptionKey).ToArray());
publicKeyRings.Select(Utilities.FindBestEncryptionKey).ToArray());
_verificationKeys = new Lazy<IEnumerable<PgpPublicKey>>(() =>
publicKeyRingsConsumed.Select(Utilities.FindBestVerificationKey).ToArray());
publicKeyRings.Select(Utilities.FindBestVerificationKey).ToArray());
}

if (_secretKeys != null)
Expand Down
16 changes: 7 additions & 9 deletions PgpCore/PGP.cs
Original file line number Diff line number Diff line change
Expand Up @@ -552,8 +552,9 @@ public async Task EncryptStreamAsync(Stream inputStream, Stream outputStream, bo

PgpEncryptedDataGenerator pk =
new PgpEncryptedDataGenerator(SymmetricKeyAlgorithm, withIntegrityCheck, new SecureRandom());
foreach (PgpPublicKey publicKey in EncryptionKeys.EncryptKeys)
foreach (PgpPublicKeyRingWithPreferredKey publicKeyRing in EncryptionKeys.PublicKeyRings)
{
PgpPublicKey publicKey = publicKeyRing.PreferredEncryptionKey ?? publicKeyRing.DefaultEncryptionKey;
pk.AddMethod(publicKey);
}

Expand Down Expand Up @@ -672,11 +673,7 @@ public void EncryptStream(Stream inputStream, Stream outputStream, bool armor =

foreach (PgpPublicKeyRingWithPreferredKey publicKeyRing in EncryptionKeys.PublicKeyRings)
{
PgpPublicKey publicKey = publicKeyRing.PreferredKey ?? Utilities.FindBestEncryptionKey(publicKeyRing.PgpPublicKeyRing);
pk.AddMethod(publicKey);
}
foreach (PgpPublicKey publicKey in EncryptionKeys.EncryptKeys)
{
PgpPublicKey publicKey = publicKeyRing.PreferredEncryptionKey ?? publicKeyRing.DefaultEncryptionKey;
pk.AddMethod(publicKey);
}

Expand Down Expand Up @@ -5674,7 +5671,7 @@ private Task<VerificationResult> VerifyAsync(Stream inputStream)
var keyIdToVerify = publicKeyEncryptedData.KeyId;
// If we encounter an encrypted packet, verify with the encryption keys used instead
// TODO does this even make sense? maybe throw exception instead, or try to decrypt first
verified = Utilities.FindPublicKey(keyIdToVerify, EncryptionKeys.EncryptKeys, out PgpPublicKey _);
verified = Utilities.FindPublicKeyInKeyRings(keyIdToVerify, EncryptionKeys.PublicKeyRings.Select(keyRing => keyRing.PgpPublicKeyRing), out PgpPublicKey _);

}
else if (pgpObject is PgpOnePassSignatureList onePassSignatureList)
Expand Down Expand Up @@ -5817,7 +5814,7 @@ private VerificationResult Verify(Stream inputStream)

// If we encounter an encrypted packet, verify the encryption key used instead
// TODO does this even make sense? maybe throw exception instead, or try to decrypt first
if (Utilities.FindPublicKey(keyIdToVerify, EncryptionKeys.EncryptKeys, out PgpPublicKey _))
if (Utilities.FindPublicKeyInKeyRings(keyIdToVerify, EncryptionKeys.PublicKeyRings.Select(keyRing => keyRing.PgpPublicKeyRing), out PgpPublicKey _))
{
verified = true;
}
Expand Down Expand Up @@ -6130,8 +6127,9 @@ private Stream ChainEncryptedOut(Stream outputStream, bool withIntegrityCheck)
var encryptedDataGenerator =
new PgpEncryptedDataGenerator(SymmetricKeyAlgorithm, withIntegrityCheck, new SecureRandom());

foreach (PgpPublicKey publicKey in EncryptionKeys.EncryptKeys)
foreach (PgpPublicKeyRingWithPreferredKey publicKeyRing in EncryptionKeys.PublicKeyRings)
{
PgpPublicKey publicKey = publicKeyRing.PreferredEncryptionKey ?? publicKeyRing.DefaultEncryptionKey;
encryptedDataGenerator.AddMethod(publicKey);
}

Expand Down
35 changes: 24 additions & 11 deletions PgpCore/PgpPublicKeyRingWithPreferredKey.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,45 @@
using System.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using Org.BouncyCastle.Bcpg.OpenPgp;

namespace PgpCore
{
/// <summary>
/// A wrapper class for <see cref="PgpPublicKeyRing"/> that also keeps track of a preferred <see cref="PgpPublicKey"/> to be used for encryption.
/// </summary>
public class PgpPublicKeyRingWithPreferredKey
{
public PgpPublicKeyRing PgpPublicKeyRing { get; set; }
public PgpPublicKey PreferredKey { get; private set; } = null;
public PgpPublicKey PreferredEncryptionKey { get; private set; } = null;
public PgpPublicKey DefaultEncryptionKey => _defaultEncryptionKey.Value;

private Lazy<PgpPublicKey> _defaultEncryptionKey;
private Lazy<IEnumerable<PgpPublicKey>> _encryptionKeys;

public PgpPublicKeyRingWithPreferredKey(PgpPublicKeyRing publicKeyRing)
{
PgpPublicKeyRing = publicKeyRing;
_defaultEncryptionKey = new Lazy<PgpPublicKey>(() => Utilities.FindBestEncryptionKey(PgpPublicKeyRing));
_encryptionKeys = new Lazy<IEnumerable<PgpPublicKey>>(() => PgpPublicKeyRing.GetPublicKeys().Where(key => key.IsEncryptionKey));
}

public PgpPublicKeyRingWithPreferredKey(PgpPublicKeyRing publicKeyRing, long preferredKeyId)

/// <summary>
/// Try to find the key with the given keyId and set it as the preferred encryption key.
/// If no key is found, the preferred key is not changed.
/// </summary>
/// <param name="keyId">The keyId to find.</param>
public void UsePreferredEncryptionKey(long? keyId)
{
PgpPublicKeyRing = publicKeyRing;
UseEncryptionKey(preferredKeyId);
PreferredEncryptionKey = _encryptionKeys.Value.FirstOrDefault(key => key.KeyId == keyId) ?? PreferredEncryptionKey;
}

/// <summary>
/// This method will try to find the key with the given keyId and set it as the preferred key.
/// If it cannot find the key, it will not change the preferred key.
/// Clear the preferred encryption key.
/// </summary>
/// <param name="keyId">The keyId to find.</param>
public void UseEncryptionKey(long keyId)
public void ClearPreferredEncryptionKey()
{
PreferredKey = PgpPublicKeyRing.GetPublicKeys().FirstOrDefault(key => key.KeyId == keyId && key.IsEncryptionKey) ?? PreferredKey;
PreferredEncryptionKey = null;
}
}
}
15 changes: 15 additions & 0 deletions PgpCore/Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,21 @@ public static bool FindPublicKey(long keyId, IEnumerable<PgpPublicKey> verificat
return foundKeys.Any();
}

public static bool FindPublicKeyInKeyRings(long keyId, IEnumerable<PgpPublicKeyRing> publicKeyRings,
out PgpPublicKey verificationKey)
{
verificationKey = null;

foreach (PgpPublicKeyRing publicKeyRing in publicKeyRings)
{
var verificationKeys = publicKeyRing.GetPublicKeys();
if (FindPublicKey(keyId, verificationKeys, out verificationKey))
return true;
}

return false;
}

private static async Task PipeFileContentsAsync(FileInfo file, Stream pOut, int bufSize)
{
using (FileStream inputStream = file.OpenRead())
Expand Down

0 comments on commit fc73229

Please sign in to comment.