diff --git a/crypto/src/bcpg/AeadAlgorithmTag.cs b/crypto/src/bcpg/AeadAlgorithmTag.cs
new file mode 100644
index 000000000..632f88838
--- /dev/null
+++ b/crypto/src/bcpg/AeadAlgorithmTag.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ public enum AeadAlgorithmTag : byte
+ {
+ Eax = 1, // EAX (IV len: 16 octets, Tag len: 16 octets)
+ Ocb = 2, // OCB (IV len: 15 octets, Tag len: 16 octets)
+ Gcm = 3, // GCM (IV len: 12 octets, Tag len: 16 octets)
+ }
+}
diff --git a/crypto/src/bcpg/AeadEncDataPacket.cs b/crypto/src/bcpg/AeadEncDataPacket.cs
new file mode 100644
index 000000000..2c7ec97f6
--- /dev/null
+++ b/crypto/src/bcpg/AeadEncDataPacket.cs
@@ -0,0 +1,77 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /**
+ * Packet representing AEAD encrypted data. At the moment this appears to exist in the following
+ * expired draft only, but it's appearing despite this.
+ *
+ * @ref https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-rfc4880bis-04#section-5.16
+ */
+ public class AeadEncDataPacket
+ : InputStreamPacket
+ {
+ private readonly byte m_version;
+ private readonly SymmetricKeyAlgorithmTag m_algorithm;
+ private readonly AeadAlgorithmTag m_aeadAlgorithm;
+ private readonly byte m_chunkSize;
+ private readonly byte[] m_iv;
+
+ public AeadEncDataPacket(BcpgInputStream bcpgIn)
+ : base(bcpgIn)
+ {
+ m_version = (byte)bcpgIn.ReadByte();
+ if (m_version != 1)
+ throw new ArgumentException("wrong AEAD packet version: " + m_version);
+
+ m_algorithm = (SymmetricKeyAlgorithmTag)bcpgIn.ReadByte();
+ m_aeadAlgorithm = (AeadAlgorithmTag)bcpgIn.ReadByte();
+ m_chunkSize = (byte)bcpgIn.ReadByte();
+
+ m_iv = new byte[GetIVLength(m_aeadAlgorithm)];
+ bcpgIn.ReadFully(m_iv);
+ }
+
+ public AeadEncDataPacket(SymmetricKeyAlgorithmTag algorithm, AeadAlgorithmTag aeadAlgorithm, int chunkSize,
+ byte[] iv)
+ : base(null)
+ {
+ m_version = 1;
+ m_algorithm = algorithm;
+ m_aeadAlgorithm = aeadAlgorithm;
+ m_chunkSize = (byte)chunkSize;
+ m_iv = Arrays.Clone(iv);
+ }
+
+ public byte Version => m_version;
+
+ public SymmetricKeyAlgorithmTag Algorithm => m_algorithm;
+
+ public AeadAlgorithmTag AeadAlgorithm => m_aeadAlgorithm;
+
+ public int ChunkSize => m_chunkSize;
+
+ public byte[] GetIV()
+ {
+ return m_iv;
+ }
+
+ public static int GetIVLength(AeadAlgorithmTag aeadAlgorithm)
+ {
+ switch (aeadAlgorithm)
+ {
+ case AeadAlgorithmTag.Eax:
+ return 16;
+ case AeadAlgorithmTag.Ocb:
+ return 15;
+ case AeadAlgorithmTag.Gcm:
+ return 12;
+ default:
+ throw new ArgumentException("unknown mode: " + aeadAlgorithm);
+ }
+ }
+ }
+}
diff --git a/crypto/src/bcpg/ArmoredInputStream.cs b/crypto/src/bcpg/ArmoredInputStream.cs
index b493d04fc..eacba17dd 100644
--- a/crypto/src/bcpg/ArmoredInputStream.cs
+++ b/crypto/src/bcpg/ArmoredInputStream.cs
@@ -304,7 +304,7 @@ namespace Org.BouncyCastle.Bcpg
{
c = input.ReadByte();
}
- while (c == ' ' || c == '\t' || c == '\f' || c == '\u000B'); // \u000B ~ \v
+ while (c == ' ' || c == '\t' || c == '\f' || c == '\v');
if (c >= 128)
throw new IOException("invalid armor");
diff --git a/crypto/src/bcpg/BcpgOutputStream.cs b/crypto/src/bcpg/BcpgOutputStream.cs
index 3bae7e383..29ed9e22a 100644
--- a/crypto/src/bcpg/BcpgOutputStream.cs
+++ b/crypto/src/bcpg/BcpgOutputStream.cs
@@ -19,6 +19,7 @@ namespace Org.BouncyCastle.Bcpg
}
private Stream outStr;
+ private bool useOldFormat;
private byte[] partialBuffer;
private int partialBufferLength;
private int partialPower;
@@ -27,26 +28,28 @@ namespace Org.BouncyCastle.Bcpg
/// <summary>Create a stream representing a general packet.</summary>
/// <param name="outStr">Output stream to write to.</param>
- public BcpgOutputStream(
- Stream outStr)
+ public BcpgOutputStream(Stream outStr)
+ : this(outStr, false)
{
- if (outStr == null)
- throw new ArgumentNullException("outStr");
-
- this.outStr = outStr;
}
- /// <summary>Create a stream representing an old style partial object.</summary>
+ /// <summary>Base constructor specifying whether or not to use packets in the new format wherever possible.
+ /// </summary>
/// <param name="outStr">Output stream to write to.</param>
- /// <param name="tag">The packet tag for the object.</param>
- public BcpgOutputStream(
- Stream outStr,
- PacketTag tag)
+ /// <param name="newFormatOnly"><c>true</c> if use new format packets, <c>false</c> if backwards compatible
+ /// preferred.</param>
+ public BcpgOutputStream(Stream outStr, bool newFormatOnly)
{
- if (outStr == null)
- throw new ArgumentNullException("outStr");
+ this.outStr = outStr ?? throw new ArgumentNullException(nameof(outStr));
+ this.useOldFormat = !newFormatOnly;
+ }
- this.outStr = outStr;
+ /// <summary>Create a stream representing an old style partial object.</summary>
+ /// <param name="outStr">Output stream to write to.</param>
+ /// <param name="tag">The packet tag for the object.</param>
+ public BcpgOutputStream(Stream outStr, PacketTag tag)
+ {
+ this.outStr = outStr ?? throw new ArgumentNullException(nameof(outStr));
this.WriteHeader(tag, true, true, 0);
}
@@ -55,16 +58,9 @@ namespace Org.BouncyCastle.Bcpg
/// <param name="tag">Packet tag.</param>
/// <param name="length">Size of chunks making up the packet.</param>
/// <param name="oldFormat">If true, the header is written out in old format.</param>
- public BcpgOutputStream(
- Stream outStr,
- PacketTag tag,
- long length,
- bool oldFormat)
+ public BcpgOutputStream(Stream outStr, PacketTag tag, long length, bool oldFormat)
{
- if (outStr == null)
- throw new ArgumentNullException("outStr");
-
- this.outStr = outStr;
+ this.outStr = outStr ?? throw new ArgumentNullException(nameof(outStr));
if (length > 0xFFFFFFFFL)
{
@@ -84,15 +80,9 @@ namespace Org.BouncyCastle.Bcpg
/// <param name="outStr">Output stream to write to.</param>
/// <param name="tag">Packet tag.</param>
/// <param name="length">Size of chunks making up the packet.</param>
- public BcpgOutputStream(
- Stream outStr,
- PacketTag tag,
- long length)
+ public BcpgOutputStream(Stream outStr, PacketTag tag, long length)
{
- if (outStr == null)
- throw new ArgumentNullException("outStr");
-
- this.outStr = outStr;
+ this.outStr = outStr ?? throw new ArgumentNullException(nameof(outStr));
this.WriteHeader(tag, false, false, length);
}
@@ -100,15 +90,9 @@ namespace Org.BouncyCastle.Bcpg
/// <param name="outStr">Output stream to write to.</param>
/// <param name="tag">Packet tag.</param>
/// <param name="buffer">Buffer to use for collecting chunks.</param>
- public BcpgOutputStream(
- Stream outStr,
- PacketTag tag,
- byte[] buffer)
+ public BcpgOutputStream(Stream outStr, PacketTag tag, byte[] buffer)
{
- if (outStr == null)
- throw new ArgumentNullException("outStr");
-
- this.outStr = outStr;
+ this.outStr = outStr ?? throw new ArgumentNullException(nameof(outStr));
this.WriteHeader(tag, false, true, 0);
this.partialBuffer = buffer;
@@ -120,15 +104,13 @@ namespace Org.BouncyCastle.Bcpg
}
if (partialPower > 30)
- {
throw new IOException("Buffer cannot be greater than 2^30 in length.");
- }
+
this.partialBufferLength = 1 << partialPower;
this.partialOffset = 0;
}
- private void WriteNewPacketLength(
- long bodyLen)
+ private void WriteNewPacketLength(long bodyLen)
{
if (bodyLen < 192)
{
@@ -378,29 +360,28 @@ namespace Org.BouncyCastle.Bcpg
(byte)n);
}
- public void WritePacket(
- ContainedPacket p)
+ public void WritePacket(ContainedPacket p)
{
p.Encode(this);
}
- internal void WritePacket(
- PacketTag tag,
- byte[] body,
- bool oldFormat)
+ internal void WritePacket(PacketTag tag, byte[] body)
+ {
+ WritePacket(tag, body, useOldFormat);
+ }
+
+ internal void WritePacket(PacketTag tag, byte[] body, bool oldFormat)
{
this.WriteHeader(tag, oldFormat, false, body.Length);
this.Write(body);
}
- public void WriteObject(
- BcpgObject bcpgObject)
+ public void WriteObject(BcpgObject bcpgObject)
{
bcpgObject.Encode(this);
}
- public void WriteObjects(
- params BcpgObject[] v)
+ public void WriteObjects(params BcpgObject[] v)
{
foreach (BcpgObject o in v)
{
diff --git a/crypto/src/bcpg/ExperimentalPacket.cs b/crypto/src/bcpg/ExperimentalPacket.cs
index 36a254be1..3d413db09 100644
--- a/crypto/src/bcpg/ExperimentalPacket.cs
+++ b/crypto/src/bcpg/ExperimentalPacket.cs
@@ -5,34 +5,24 @@ namespace Org.BouncyCastle.Bcpg
{
/// <remarks>Basic packet for an experimental packet.</remarks>
public class ExperimentalPacket
- : ContainedPacket //, PublicKeyAlgorithmTag
+ : ContainedPacket
{
- private readonly PacketTag tag;
- private readonly byte[] contents;
+ private readonly PacketTag m_tag;
+ private readonly byte[] m_contents;
- internal ExperimentalPacket(
- PacketTag tag,
- BcpgInputStream bcpgIn)
+ internal ExperimentalPacket(PacketTag tag, BcpgInputStream bcpgIn)
{
- this.tag = tag;
-
- this.contents = bcpgIn.ReadAll();
+ m_tag = tag;
+ m_contents = bcpgIn.ReadAll();
}
- public PacketTag Tag
- {
- get { return tag; }
- }
+ public PacketTag Tag => m_tag;
- public byte[] GetContents()
- {
- return (byte[]) contents.Clone();
- }
+ public byte[] GetContents() => (byte[])m_contents.Clone();
- public override void Encode(
- BcpgOutputStream bcpgOut)
+ public override void Encode(BcpgOutputStream bcpgOut)
{
- bcpgOut.WritePacket(tag, contents, true);
+ bcpgOut.WritePacket(m_tag, m_contents);
}
}
}
diff --git a/crypto/src/bcpg/HashAlgorithmTags.cs b/crypto/src/bcpg/HashAlgorithmTags.cs
index 96c009153..a13725de6 100644
--- a/crypto/src/bcpg/HashAlgorithmTags.cs
+++ b/crypto/src/bcpg/HashAlgorithmTags.cs
@@ -15,5 +15,15 @@ namespace Org.BouncyCastle.Bcpg
Sha384 = 9, // SHA-384
Sha512 = 10, // SHA-512
Sha224 = 11, // SHA-224
+ Sha3_256 = 12, // SHA3-256
+ Sha3_512 = 14, // SHA3-512
+
+ MD4 = 301,
+ Sha3_224 = 312, // SHA3-224
+ Sha3_256_Old = 313, //SHA3-256
+ Sha3_384 = 314, // SHA3-384
+ Sha3_512_Old = 315, // SHA3-512
+
+ SM3 = 326, // SM3
}
}
diff --git a/crypto/src/bcpg/IBcpgKey.cs b/crypto/src/bcpg/IBcpgKey.cs
index 275461772..86832eb8b 100644
--- a/crypto/src/bcpg/IBcpgKey.cs
+++ b/crypto/src/bcpg/IBcpgKey.cs
@@ -2,6 +2,7 @@ using System;
namespace Org.BouncyCastle.Bcpg
{
+ // TODO[api] Extend IEncodable
/// <remarks>Base interface for a PGP key.</remarks>
public interface IBcpgKey
{
diff --git a/crypto/src/bcpg/IUserDataPacket.cs b/crypto/src/bcpg/IUserDataPacket.cs
new file mode 100644
index 000000000..1519b5007
--- /dev/null
+++ b/crypto/src/bcpg/IUserDataPacket.cs
@@ -0,0 +1,6 @@
+namespace Org.BouncyCastle.Bcpg
+{
+ public interface IUserDataPacket
+ {
+ }
+}
diff --git a/crypto/src/bcpg/InputStreamPacket.cs b/crypto/src/bcpg/InputStreamPacket.cs
index c45efab7b..d4946ebf2 100644
--- a/crypto/src/bcpg/InputStreamPacket.cs
+++ b/crypto/src/bcpg/InputStreamPacket.cs
@@ -5,8 +5,7 @@ namespace Org.BouncyCastle.Bcpg
{
private readonly BcpgInputStream bcpgIn;
- public InputStreamPacket(
- BcpgInputStream bcpgIn)
+ public InputStreamPacket(BcpgInputStream bcpgIn)
{
this.bcpgIn = bcpgIn;
}
diff --git a/crypto/src/bcpg/MarkerPacket.cs b/crypto/src/bcpg/MarkerPacket.cs
index 4dc4b5a83..f2c450793 100644
--- a/crypto/src/bcpg/MarkerPacket.cs
+++ b/crypto/src/bcpg/MarkerPacket.cs
@@ -7,18 +7,16 @@ namespace Org.BouncyCastle.Bcpg
: ContainedPacket
{
// "PGP"
- byte[] marker = { (byte)0x50, (byte)0x47, (byte)0x50 };
+ private readonly byte[] marker = { (byte)0x50, (byte)0x47, (byte)0x50 };
- public MarkerPacket(
- BcpgInputStream bcpgIn)
+ public MarkerPacket(BcpgInputStream bcpgIn)
{
bcpgIn.ReadFully(marker);
}
- public override void Encode(
- BcpgOutputStream bcpgOut)
+ public override void Encode(BcpgOutputStream bcpgOut)
{
- bcpgOut.WritePacket(PacketTag.Marker, marker, true);
+ bcpgOut.WritePacket(PacketTag.Marker, marker);
}
}
}
diff --git a/crypto/src/bcpg/OnePassSignaturePacket.cs b/crypto/src/bcpg/OnePassSignaturePacket.cs
index 70f0a2207..4efae1d63 100644
--- a/crypto/src/bcpg/OnePassSignaturePacket.cs
+++ b/crypto/src/bcpg/OnePassSignaturePacket.cs
@@ -81,7 +81,7 @@ namespace Org.BouncyCastle.Bcpg
pOut.WriteByte((byte)nested);
}
- bcpgOut.WritePacket(PacketTag.OnePassSignature, bOut.ToArray(), true);
+ bcpgOut.WritePacket(PacketTag.OnePassSignature, bOut.ToArray());
}
}
}
diff --git a/crypto/src/bcpg/PublicKeyEncSessionPacket.cs b/crypto/src/bcpg/PublicKeyEncSessionPacket.cs
index f0b1577ec..25d297890 100644
--- a/crypto/src/bcpg/PublicKeyEncSessionPacket.cs
+++ b/crypto/src/bcpg/PublicKeyEncSessionPacket.cs
@@ -105,7 +105,7 @@ namespace Org.BouncyCastle.Bcpg
}
}
- bcpgOut.WritePacket(PacketTag.PublicKeyEncryptedSession, bOut.ToArray(), true);
+ bcpgOut.WritePacket(PacketTag.PublicKeyEncryptedSession, bOut.ToArray());
}
}
}
diff --git a/crypto/src/bcpg/PublicKeyPacket.cs b/crypto/src/bcpg/PublicKeyPacket.cs
index 40c696a37..b3b5d1600 100644
--- a/crypto/src/bcpg/PublicKeyPacket.cs
+++ b/crypto/src/bcpg/PublicKeyPacket.cs
@@ -115,10 +115,9 @@ namespace Org.BouncyCastle.Bcpg
return bOut.ToArray();
}
- public override void Encode(
- BcpgOutputStream bcpgOut)
+ public override void Encode(BcpgOutputStream bcpgOut)
{
- bcpgOut.WritePacket(PacketTag.PublicKey, GetEncodedContents(), true);
+ bcpgOut.WritePacket(PacketTag.PublicKey, GetEncodedContents());
}
}
}
diff --git a/crypto/src/bcpg/PublicSubkeyPacket.cs b/crypto/src/bcpg/PublicSubkeyPacket.cs
index 6e1aeda98..0e1065b72 100644
--- a/crypto/src/bcpg/PublicSubkeyPacket.cs
+++ b/crypto/src/bcpg/PublicSubkeyPacket.cs
@@ -1,5 +1,6 @@
using System;
using System.IO;
+
namespace Org.BouncyCastle.Bcpg
{
/// <remarks>Basic packet for a PGP public subkey</remarks>
@@ -21,10 +22,9 @@ namespace Org.BouncyCastle.Bcpg
{
}
- public override void Encode(
- BcpgOutputStream bcpgOut)
+ public override void Encode(BcpgOutputStream bcpgOut)
{
- bcpgOut.WritePacket(PacketTag.PublicSubkey, GetEncodedContents(), true);
+ bcpgOut.WritePacket(PacketTag.PublicSubkey, GetEncodedContents());
}
}
}
diff --git a/crypto/src/bcpg/SecretKeyPacket.cs b/crypto/src/bcpg/SecretKeyPacket.cs
index d9ceab4f1..1bc684a69 100644
--- a/crypto/src/bcpg/SecretKeyPacket.cs
+++ b/crypto/src/bcpg/SecretKeyPacket.cs
@@ -161,10 +161,9 @@ namespace Org.BouncyCastle.Bcpg
return bOut.ToArray();
}
- public override void Encode(
- BcpgOutputStream bcpgOut)
+ public override void Encode(BcpgOutputStream bcpgOut)
{
- bcpgOut.WritePacket(PacketTag.SecretKey, GetEncodedContents(), true);
+ bcpgOut.WritePacket(PacketTag.SecretKey, GetEncodedContents());
}
}
}
diff --git a/crypto/src/bcpg/SecretSubkeyPacket.cs b/crypto/src/bcpg/SecretSubkeyPacket.cs
index 8f1746942..2d405aec2 100644
--- a/crypto/src/bcpg/SecretSubkeyPacket.cs
+++ b/crypto/src/bcpg/SecretSubkeyPacket.cs
@@ -34,10 +34,9 @@ namespace Org.BouncyCastle.Bcpg
{
}
- public override void Encode(
- BcpgOutputStream bcpgOut)
+ public override void Encode(BcpgOutputStream bcpgOut)
{
- bcpgOut.WritePacket(PacketTag.SecretSubkey, GetEncodedContents(), true);
+ bcpgOut.WritePacket(PacketTag.SecretSubkey, GetEncodedContents());
}
}
}
diff --git a/crypto/src/bcpg/SignaturePacket.cs b/crypto/src/bcpg/SignaturePacket.cs
index 09e6b059d..99ca7df40 100644
--- a/crypto/src/bcpg/SignaturePacket.cs
+++ b/crypto/src/bcpg/SignaturePacket.cs
@@ -392,7 +392,7 @@ namespace Org.BouncyCastle.Bcpg
}
}
- bcpgOut.WritePacket(PacketTag.Signature, bOut.ToArray(), true);
+ bcpgOut.WritePacket(PacketTag.Signature, bOut.ToArray());
}
private static void EncodeLengthAndData(
diff --git a/crypto/src/bcpg/SignatureSubpacket.cs b/crypto/src/bcpg/SignatureSubpacket.cs
index c2186f373..f7f82b2a6 100644
--- a/crypto/src/bcpg/SignatureSubpacket.cs
+++ b/crypto/src/bcpg/SignatureSubpacket.cs
@@ -103,8 +103,7 @@ namespace Org.BouncyCastle.Bcpg
if (obj == this)
return true;
- SignatureSubpacket other = obj as SignatureSubpacket;
- if (null == other)
+ if (!(obj is SignatureSubpacket other))
return false;
return this.type == other.type
diff --git a/crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs b/crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs
index 85bcc2874..901088e33 100644
--- a/crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs
+++ b/crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs
@@ -82,7 +82,7 @@ namespace Org.BouncyCastle.Bcpg
}
}
- bcpgOut.WritePacket(PacketTag.SymmetricKeyEncryptedSessionKey, bOut.ToArray(), true);
+ bcpgOut.WritePacket(PacketTag.SymmetricKeyEncryptedSessionKey, bOut.ToArray());
}
}
}
diff --git a/crypto/src/bcpg/TrustPacket.cs b/crypto/src/bcpg/TrustPacket.cs
index 6f1969c2a..a2a30177f 100644
--- a/crypto/src/bcpg/TrustPacket.cs
+++ b/crypto/src/bcpg/TrustPacket.cs
@@ -9,8 +9,7 @@ namespace Org.BouncyCastle.Bcpg
{
private readonly byte[] levelAndTrustAmount;
- public TrustPacket(
- BcpgInputStream bcpgIn)
+ public TrustPacket(BcpgInputStream bcpgIn)
{
MemoryStream bOut = new MemoryStream();
@@ -23,10 +22,9 @@ namespace Org.BouncyCastle.Bcpg
levelAndTrustAmount = bOut.ToArray();
}
- public TrustPacket(
- int trustCode)
+ public TrustPacket(int trustCode)
{
- this.levelAndTrustAmount = new byte[]{ (byte) trustCode };
+ this.levelAndTrustAmount = new byte[]{ (byte)trustCode };
}
public byte[] GetLevelAndTrustAmount()
@@ -34,10 +32,9 @@ namespace Org.BouncyCastle.Bcpg
return (byte[]) levelAndTrustAmount.Clone();
}
- public override void Encode(
- BcpgOutputStream bcpgOut)
+ public override void Encode(BcpgOutputStream bcpgOut)
{
- bcpgOut.WritePacket(PacketTag.Trust, levelAndTrustAmount, true);
+ bcpgOut.WritePacket(PacketTag.Trust, levelAndTrustAmount);
}
}
}
diff --git a/crypto/src/bcpg/UserAttributePacket.cs b/crypto/src/bcpg/UserAttributePacket.cs
index 861f62f5d..0be24e006 100644
--- a/crypto/src/bcpg/UserAttributePacket.cs
+++ b/crypto/src/bcpg/UserAttributePacket.cs
@@ -37,8 +37,7 @@ namespace Org.BouncyCastle.Bcpg
return subpackets;
}
- public override void Encode(
- BcpgOutputStream bcpgOut)
+ public override void Encode(BcpgOutputStream bcpgOut)
{
MemoryStream bOut = new MemoryStream();
diff --git a/crypto/src/bcpg/UserIdPacket.cs b/crypto/src/bcpg/UserIdPacket.cs
index a175e74a6..7d3d3a846 100644
--- a/crypto/src/bcpg/UserIdPacket.cs
+++ b/crypto/src/bcpg/UserIdPacket.cs
@@ -1,37 +1,56 @@
using System;
using System.Text;
+using Org.BouncyCastle.Utilities;
+
namespace Org.BouncyCastle.Bcpg
{
/**
* Basic type for a user ID packet.
*/
public class UserIdPacket
- : ContainedPacket
+ : ContainedPacket, IUserDataPacket
{
private readonly byte[] idData;
- public UserIdPacket(
- BcpgInputStream bcpgIn)
+ public UserIdPacket(BcpgInputStream bcpgIn)
{
this.idData = bcpgIn.ReadAll();
}
- public UserIdPacket(
- string id)
+ public UserIdPacket(string id)
{
this.idData = Encoding.UTF8.GetBytes(id);
}
- public string GetId()
+ public UserIdPacket(byte[] rawId)
+ {
+ this.idData = Arrays.Clone(rawId);
+ }
+
+ public string GetId()
{
return Encoding.UTF8.GetString(idData, 0, idData.Length);
}
- public override void Encode(
- BcpgOutputStream bcpgOut)
+ public byte[] GetRawId() => Arrays.Clone(idData);
+
+ public override bool Equals(object obj)
+ {
+ if (!(obj is UserIdPacket other))
+ return false;
+
+ return Arrays.AreEqual(this.idData, other.idData);
+ }
+
+ public override int GetHashCode()
+ {
+ return Arrays.GetHashCode(this.idData);
+ }
+
+ public override void Encode(BcpgOutputStream bcpgOut)
{
- bcpgOut.WritePacket(PacketTag.UserId, idData, true);
+ bcpgOut.WritePacket(PacketTag.UserId, idData);
}
}
}
diff --git a/crypto/src/bcpg/sig/SignerUserId.cs b/crypto/src/bcpg/sig/SignerUserId.cs
index 6f812e210..bfa00d078 100644
--- a/crypto/src/bcpg/sig/SignerUserId.cs
+++ b/crypto/src/bcpg/sig/SignerUserId.cs
@@ -1,3 +1,5 @@
+using Org.BouncyCastle.Utilities;
+
namespace Org.BouncyCastle.Bcpg.Sig
{
/**
@@ -6,44 +8,18 @@ namespace Org.BouncyCastle.Bcpg.Sig
public class SignerUserId
: SignatureSubpacket
{
- private static byte[] UserIdToBytes(
- string id)
- {
- byte[] idData = new byte[id.Length];
-
- for (int i = 0; i != id.Length; i++)
- {
- idData[i] = (byte)id[i];
- }
-
- return idData;
- }
-
- public SignerUserId(
- bool critical,
- bool isLongLength,
- byte[] data)
+ public SignerUserId(bool critical, bool isLongLength, byte[] data)
: base(SignatureSubpacketTag.SignerUserId, critical, isLongLength, data)
{
}
- public SignerUserId(
- bool critical,
- string userId)
- : base(SignatureSubpacketTag.SignerUserId, critical, false, UserIdToBytes(userId))
+ public SignerUserId(bool critical, string userId)
+ : base(SignatureSubpacketTag.SignerUserId, critical, false, Strings.ToUtf8ByteArray(userId))
{
}
- public string GetId()
- {
- char[] chars = new char[data.Length];
-
- for (int i = 0; i != chars.Length; i++)
- {
- chars[i] = (char)(data[i] & 0xff);
- }
+ public string GetId() => Strings.FromUtf8ByteArray(data);
- return new string(chars);
- }
+ public byte[] GetRawId() => Arrays.Clone(data);
}
}
diff --git a/crypto/src/openpgp/PGPKeyRing.cs b/crypto/src/openpgp/PGPKeyRing.cs
index 52db80209..63069350e 100644
--- a/crypto/src/openpgp/PGPKeyRing.cs
+++ b/crypto/src/openpgp/PGPKeyRing.cs
@@ -42,20 +42,19 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
}
}
- internal static void ReadUserIDs(BcpgInputStream pIn, out IList<object> ids, out IList<TrustPacket> idTrusts,
- out IList<IList<PgpSignature>> idSigs)
+ internal static void ReadUserIDs(BcpgInputStream pIn, out IList<IUserDataPacket> ids,
+ out IList<TrustPacket> idTrusts, out IList<IList<PgpSignature>> idSigs)
{
- ids = new List<object>();
+ ids = new List<IUserDataPacket>();
idTrusts = new List<TrustPacket>();
idSigs = new List<IList<PgpSignature>>();
while (IsUserTag(pIn.SkipMarkerPackets()))
{
Packet obj = pIn.ReadPacket();
- if (obj is UserIdPacket)
+ if (obj is UserIdPacket id)
{
- UserIdPacket id = (UserIdPacket)obj;
- ids.Add(id.GetId());
+ ids.Add(id);
}
else
{
diff --git a/crypto/src/openpgp/PgpPublicKey.cs b/crypto/src/openpgp/PgpPublicKey.cs
index 6fa7cfae8..b4ae92838 100644
--- a/crypto/src/openpgp/PgpPublicKey.cs
+++ b/crypto/src/openpgp/PgpPublicKey.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Drawing;
using System.IO;
using Org.BouncyCastle.Asn1.Cryptlib;
@@ -86,17 +87,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
PgpSignature.DirectKey,
};
- private long keyId;
- private byte[] fingerprint;
- private int keyStrength;
-
internal PublicKeyPacket publicPk;
internal TrustPacket trustPk;
internal IList<PgpSignature> keySigs = new List<PgpSignature>();
- internal IList<object> ids = new List<object>();
+ internal IList<IUserDataPacket> ids = new List<IUserDataPacket>();
internal IList<TrustPacket> idTrusts = new List<TrustPacket>();
internal IList<IList<PgpSignature>> idSigs = new List<IList<PgpSignature>>();
- internal IList<PgpSignature> subSigs;
+
+ internal IList<PgpSignature> subSigs = null;
+
+ private long keyId;
+ private byte[] fingerprint;
+ private int keyStrength;
private void Init()
{
@@ -261,7 +263,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
}
this.publicPk = new PublicKeyPacket(algorithm, time, bcpgKey);
- this.ids = new List<object>();
+ this.ids = new List<IUserDataPacket>();
this.idSigs = new List<IList<PgpSignature>>();
try
@@ -275,7 +277,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
}
public PgpPublicKey(PublicKeyPacket publicPk)
- : this(publicPk, new List<object>(), new List<IList<PgpSignature>>())
+ : this(publicPk, new List<IUserDataPacket>(), new List<IList<PgpSignature>>())
{
}
@@ -311,7 +313,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
this.publicPk = pubKey.publicPk;
this.keySigs = new List<PgpSignature>(pubKey.keySigs);
- this.ids = new List<object>(pubKey.ids);
+ this.ids = new List<IUserDataPacket>(pubKey.ids);
this.idTrusts = new List<TrustPacket>(pubKey.idTrusts);
this.idSigs = new List<IList<PgpSignature>>(pubKey.idSigs.Count);
@@ -334,7 +336,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
PublicKeyPacket publicPk,
TrustPacket trustPk,
IList<PgpSignature> keySigs,
- IList<object> ids,
+ IList<IUserDataPacket> ids,
IList<TrustPacket> idTrusts,
IList<IList<PgpSignature>> idSigs)
{
@@ -350,7 +352,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
internal PgpPublicKey(
PublicKeyPacket publicPk,
- IList<object> ids,
+ IList<IUserDataPacket> ids,
IList<IList<PgpSignature>> idSigs)
{
this.publicPk = publicPk;
@@ -359,6 +361,26 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
Init();
}
+ internal PgpPublicKey(
+ PgpPublicKey original,
+ TrustPacket trustPk,
+ List<PgpSignature> keySigs,
+ List<IUserDataPacket> ids,
+ List<TrustPacket> idTrusts,
+ IList<IList<PgpSignature>> idSigs)
+ {
+ this.publicPk = original.publicPk;
+ this.fingerprint = original.fingerprint;
+ this.keyStrength = original.keyStrength;
+ this.keyId = original.keyId;
+
+ this.trustPk = trustPk;
+ this.keySigs = keySigs;
+ this.ids = ids;
+ this.idTrusts = idTrusts;
+ this.idSigs = idSigs;
+ }
+
/// <summary>The version of this key.</summary>
public int Version
{
@@ -456,13 +478,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
return expiryTime;
}
- /// <summary>The keyId associated with the public key.</summary>
+ /// <summary>The key ID associated with the public key.</summary>
public long KeyId
{
get { return keyId; }
}
- /// <summary>The fingerprint of the key</summary>
+ /// <summary>The fingerprint of the public key</summary>
public byte[] GetFingerprint()
{
return (byte[]) fingerprint.Clone();
@@ -648,11 +670,29 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
{
var result = new List<string>();
- foreach (object id in ids)
+ foreach (var id in ids)
{
- if (id is string s)
+ if (id is UserIdPacket userId)
{
- result.Add(s);
+ result.Add(userId.GetId());
+ }
+ }
+
+ return CollectionUtilities.Proxy(result);
+ }
+
+ /// <summary>Return any userIDs associated with the key in raw byte form.</summary>
+ /// <remarks>No attempt is made to convert the IDs into strings.</remarks>
+ /// <returns>An <c>IEnumerable</c> of <c>byte[]</c>.</returns>
+ public IEnumerable<byte[]> GetRawUserIds()
+ {
+ var result = new List<byte[]>();
+
+ foreach (var id in ids)
+ {
+ if (id is UserIdPacket userId)
+ {
+ result.Add(userId.GetRawId());
}
}
@@ -665,9 +705,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
{
var result = new List<PgpUserAttributeSubpacketVector>();
- foreach (object o in ids)
+ foreach (var id in ids)
{
- if (o is PgpUserAttributeSubpacketVector v)
+ if (id is PgpUserAttributeSubpacketVector v)
{
result.Add(v);
}
@@ -684,22 +724,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
if (id == null)
throw new ArgumentNullException(nameof(id));
- var result = new List<PgpSignature>();
- bool userIdFound = false;
+ return GetSignaturesForId(new UserIdPacket(id));
+ }
- for (int i = 0; i != ids.Count; i++)
- {
- if (id.Equals(ids[i]))
- {
- userIdFound = true;
- result.AddRange(idSigs[i]);
- }
- }
+ public IEnumerable<PgpSignature> GetSignaturesForId(byte[] rawId)
+ {
+ if (rawId == null)
+ throw new ArgumentNullException(nameof(rawId));
- return userIdFound ? CollectionUtilities.Proxy(result) : null;
+ return GetSignaturesForId(new UserIdPacket(rawId));
}
- private IEnumerable<PgpSignature> GetSignaturesForID(UserIdPacket id)
+ private IEnumerable<PgpSignature> GetSignaturesForId(UserIdPacket id)
{
var signatures = new List<PgpSignature>();
bool userIdFound = false;
@@ -819,13 +855,24 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
return bOut.ToArray();
}
- public void Encode(
- Stream outStr)
+ public void Encode(Stream outStr)
+ {
+ Encode(outStr, false);
+ }
+
+ /**
+ * Encode the key to outStream, with trust packets stripped out if forTransfer is true.
+ *
+ * @param outStream stream to write the key encoding to.
+ * @param forTransfer if the purpose of encoding is to send key to other users.
+ * @throws IOException in case of encoding error.
+ */
+ public void Encode(Stream outStr, bool forTransfer)
{
BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr);
bcpgOut.WritePacket(publicPk);
- if (trustPk != null)
+ if (!forTransfer && trustPk != null)
{
bcpgOut.WritePacket(trustPk);
}
@@ -839,11 +886,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
for (int i = 0; i != ids.Count; i++)
{
- if (ids[i] is string)
+ if (ids[i] is UserIdPacket id)
{
- string id = (string) ids[i];
-
- bcpgOut.WritePacket(new UserIdPacket(id));
+ bcpgOut.WritePacket(id);
}
else
{
@@ -851,14 +896,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
bcpgOut.WritePacket(new UserAttributePacket(v.ToSubpacketArray()));
}
- if (idTrusts[i] != null)
+ if (!forTransfer && idTrusts[i] != null)
{
- bcpgOut.WritePacket((ContainedPacket)idTrusts[i]);
+ bcpgOut.WritePacket((TrustPacket)idTrusts[i]);
}
foreach (PgpSignature sig in idSigs[i])
{
- sig.Encode(bcpgOut);
+ sig.Encode(bcpgOut, forTransfer);
}
}
}
@@ -910,7 +955,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
string id,
PgpSignature certification)
{
- return AddCert(key, id, certification);
+ return AddCert(key, new UserIdPacket(id), certification);
}
/// <summary>Add a certification for the given UserAttributeSubpackets to the given public key.</summary>
@@ -928,7 +973,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
private static PgpPublicKey AddCert(
PgpPublicKey key,
- object id,
+ IUserDataPacket id,
PgpSignature certification)
{
PgpPublicKey returnKey = new PgpPublicKey(key);
@@ -966,8 +1011,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
/// <returns>
/// The re-certified key, or null if the user attribute subpacket was not found on the key.
/// </returns>
- public static PgpPublicKey RemoveCertification(
- PgpPublicKey key,
+ public static PgpPublicKey RemoveCertification(PgpPublicKey key,
PgpUserAttributeSubpacketVector userAttributes)
{
return RemoveCert(key, userAttributes);
@@ -977,16 +1021,21 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
/// <param name="key">The key the certifications are to be removed from.</param>
/// <param name="id">The ID that is to be removed.</param>
/// <returns>The re-certified key, or null if the ID was not found on the key.</returns>
- public static PgpPublicKey RemoveCertification(
- PgpPublicKey key,
- string id)
+ public static PgpPublicKey RemoveCertification(PgpPublicKey key, string id)
{
- return RemoveCert(key, id);
+ return RemoveCert(key, new UserIdPacket(id));
}
- private static PgpPublicKey RemoveCert(
- PgpPublicKey key,
- object id)
+ /// <summary>Remove any certifications associated with a given ID on a key.</summary>
+ /// <param name="key">The key the certifications are to be removed from.</param>
+ /// <param name="rawId">The ID that is to be removed in raw byte form.</param>
+ /// <returns>The re-certified key, or null if the ID was not found on the key.</returns>
+ public static PgpPublicKey RemoveCertification(PgpPublicKey key, byte[] rawId)
+ {
+ return RemoveCert(key, new UserIdPacket(rawId));
+ }
+
+ private static PgpPublicKey RemoveCert(PgpPublicKey key, IUserDataPacket id)
{
PgpPublicKey returnKey = new PgpPublicKey(key);
bool found = false;
@@ -1007,15 +1056,22 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
/// <summary>Remove a certification associated with a given ID on a key.</summary>
/// <param name="key">The key the certifications are to be removed from.</param>
+ /// <param name="id">The ID that the certfication is to be removed from (in its raw byte form).</param>
+ /// <param name="certification">The certfication to be removed.</param>
+ /// <returns>The re-certified key, or null if the certification was not found.</returns>
+ public static PgpPublicKey RemoveCertification(PgpPublicKey key, byte[] id, PgpSignature certification)
+ {
+ return RemoveCert(key, new UserIdPacket(id), certification);
+ }
+
+ /// <summary>Remove a certification associated with a given ID on a key.</summary>
+ /// <param name="key">The key the certifications are to be removed from.</param>
/// <param name="id">The ID that the certfication is to be removed from.</param>
/// <param name="certification">The certfication to be removed.</param>
/// <returns>The re-certified key, or null if the certification was not found.</returns>
- public static PgpPublicKey RemoveCertification(
- PgpPublicKey key,
- string id,
- PgpSignature certification)
+ public static PgpPublicKey RemoveCertification(PgpPublicKey key, string id, PgpSignature certification)
{
- return RemoveCert(key, id, certification);
+ return RemoveCert(key, new UserIdPacket(id), certification);
}
/// <summary>Remove a certification associated with a given user attributes on a key.</summary>
@@ -1023,18 +1079,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
/// <param name="userAttributes">The user attributes that the certfication is to be removed from.</param>
/// <param name="certification">The certification to be removed.</param>
/// <returns>The re-certified key, or null if the certification was not found.</returns>
- public static PgpPublicKey RemoveCertification(
- PgpPublicKey key,
- PgpUserAttributeSubpacketVector userAttributes,
- PgpSignature certification)
+ public static PgpPublicKey RemoveCertification(PgpPublicKey key, PgpUserAttributeSubpacketVector userAttributes,
+ PgpSignature certification)
{
return RemoveCert(key, userAttributes, certification);
}
- private static PgpPublicKey RemoveCert(
- PgpPublicKey key,
- object id,
- PgpSignature certification)
+ private static PgpPublicKey RemoveCert(PgpPublicKey key, IUserDataPacket id, PgpSignature certification)
{
PgpPublicKey returnKey = new PgpPublicKey(key);
bool found = false;
@@ -1043,13 +1094,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
{
if (id.Equals(returnKey.ids[i]))
{
- var certs = returnKey.idSigs[i];
- found = certs.Contains(certification);
-
- if (found)
- {
- certs.Remove(certification);
- }
+ found |= returnKey.idSigs[i].Remove(certification);
}
}
@@ -1060,35 +1105,23 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
/// <param name="key">The key the revocation is to be added to.</param>
/// <param name="certification">The key signature to be added.</param>
/// <returns>The new changed public key object.</returns>
- public static PgpPublicKey AddCertification(
- PgpPublicKey key,
- PgpSignature certification)
+ public static PgpPublicKey AddCertification(PgpPublicKey key, PgpSignature certification)
{
if (key.IsMasterKey)
{
if (certification.SignatureType == PgpSignature.SubkeyRevocation)
- {
throw new ArgumentException("signature type incorrect for master key revocation.");
- }
}
else
{
if (certification.SignatureType == PgpSignature.KeyRevocation)
- {
throw new ArgumentException("signature type incorrect for sub-key revocation.");
- }
}
PgpPublicKey returnKey = new PgpPublicKey(key);
+ var sigs = returnKey.subSigs ?? returnKey.keySigs;
- if (returnKey.subSigs != null)
- {
- returnKey.subSigs.Add(certification);
- }
- else
- {
- returnKey.keySigs.Add(certification);
- }
+ sigs.Add(certification);
return returnKey;
}
@@ -1102,33 +1135,171 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
var returnKey = new PgpPublicKey(key);
var sigs = returnKey.subSigs ?? returnKey.keySigs;
- if (sigs.Remove(certification))
- return returnKey;
+ bool found = sigs.Remove(certification);
- // TODO Java uses getRawUserIDs
- foreach (string id in key.GetUserIds())
+ foreach (var idSigs in returnKey.idSigs)
{
- if (ContainsSignature(key.GetSignaturesForId(id), certification))
- return RemoveCertification(returnKey, id, certification);
+ found |= idSigs.Remove(certification);
}
- foreach (PgpUserAttributeSubpacketVector id in key.GetUserAttributes())
+ return found ? returnKey : null;
+ }
+
+ /**
+ * Merge this the given local public key with another, potentially fresher copy.
+ * The resulting {@link PGPPublicKey} contains the sum of both keys user-ids and signatures.
+ * <p>
+ * If joinTrustPackets is set to true and the copy carries a trust packet,
+ * the joined key will copy the trust-packet from the copy.
+ * Otherwise, it will carry the trust packet of the local key.
+ *
+ * @param key local public key
+ * @param copy copy of the public key (e.g. from a key server)
+ * @param joinTrustPackets if true, trust packets from the copy are copied over into the resulting key
+ * @param allowSubkeySigsOnNonSubkey if true, subkey signatures on the copy will be present in the merged key, even if key was not a subkey before.
+ * @return joined key
+ * @throws PGPException
+ */
+ public static PgpPublicKey Join(PgpPublicKey key, PgpPublicKey copy, bool joinTrustPackets,
+ bool allowSubkeySigsOnNonSubkey)
+ {
+ if (key.KeyId != copy.keyId)
+ throw new ArgumentException("Key-ID mismatch.");
+
+ TrustPacket trustPk = key.trustPk;
+ List<PgpSignature> keySigs = new List<PgpSignature>(key.keySigs);
+ List<IUserDataPacket> ids = new List<IUserDataPacket>(key.ids);
+ List<TrustPacket> idTrusts = new List<TrustPacket>(key.idTrusts);
+ List<IList<PgpSignature>> idSigs = new List<IList<PgpSignature>>(key.idSigs);
+ List<PgpSignature> subSigs = key.subSigs == null ? null : new List<PgpSignature>(key.subSigs);
+
+ if (joinTrustPackets)
{
- if (ContainsSignature(key.GetSignaturesForUserAttribute(id), certification))
- return RemoveCertification(returnKey, id, certification);
+ if (copy.trustPk != null)
+ {
+ trustPk = copy.trustPk;
+ }
}
- return returnKey;
- }
+ // key signatures
+ foreach (PgpSignature keySig in copy.keySigs)
+ {
+ bool found = false;
+ for (int i = 0; i < keySigs.Count; i++)
+ {
+ PgpSignature existingKeySig = keySigs[i];
+ if (PgpSignature.IsSignatureEncodingEqual(existingKeySig, keySig))
+ {
+ found = true;
+ // join existing sig with copy to apply modifications in unhashed subpackets
+ existingKeySig = PgpSignature.Join(existingKeySig, keySig);
+ keySigs[i] = existingKeySig;
+ break;
+ }
+ }
+ if (found)
+ break;
- private static bool ContainsSignature(IEnumerable<PgpSignature> signatures, PgpSignature signature)
- {
- foreach (PgpSignature candidate in signatures)
+ keySigs.Add(keySig);
+ }
+
+ // user-ids and id sigs
+ for (int idIdx = 0; idIdx < copy.ids.Count; idIdx++)
+ {
+ IUserDataPacket copyId = copy.ids[idIdx];
+ List<PgpSignature> copyIdSigs = new List<PgpSignature>(copy.idSigs[idIdx]);
+ TrustPacket copyTrust = copy.idTrusts[idIdx];
+
+ int existingIdIndex = -1;
+ for (int i = 0; i < ids.Count; i++)
+ {
+ IUserDataPacket existingId = ids[i];
+ if (existingId.Equals(copyId))
+ {
+ existingIdIndex = i;
+ break;
+ }
+ }
+
+ // new user-id
+ if (existingIdIndex == -1)
+ {
+ ids.Add(copyId);
+ idSigs.Add(copyIdSigs);
+ idTrusts.Add(joinTrustPackets ? copyTrust : null);
+ continue;
+ }
+
+ // existing user-id
+ if (joinTrustPackets && copyTrust != null)
+ {
+ TrustPacket existingTrust = idTrusts[existingIdIndex];
+ if (existingTrust == null ||
+ Arrays.AreEqual(copyTrust.GetLevelAndTrustAmount(), existingTrust.GetLevelAndTrustAmount()))
+ {
+ idTrusts[existingIdIndex] = copyTrust;
+ }
+ }
+
+ var existingIdSigs = idSigs[existingIdIndex];
+ foreach (PgpSignature newSig in copyIdSigs)
+ {
+ bool found = false;
+ for (int i = 0; i < existingIdSigs.Count; i++)
+ {
+ PgpSignature existingSig = existingIdSigs[i];
+ if (PgpSignature.IsSignatureEncodingEqual(newSig, existingSig))
+ {
+ found = true;
+ // join existing sig with copy to apply modifications in unhashed subpackets
+ existingSig = PgpSignature.Join(existingSig, newSig);
+ existingIdSigs[i] = existingSig;
+ break;
+ }
+ }
+ if (!found)
+ {
+ existingIdSigs.Add(newSig);
+ }
+ }
+ }
+
+ // subSigs
+ if (copy.subSigs != null)
{
- if (signature == candidate)
- return true;
+ if (subSigs == null && allowSubkeySigsOnNonSubkey)
+ {
+ subSigs = new List<PgpSignature>(copy.subSigs);
+ }
+ else
+ {
+ foreach (PgpSignature copySubSig in copy.subSigs)
+ {
+ bool found = false;
+ for (int i = 0; subSigs != null && i < subSigs.Count; i++)
+ {
+ PgpSignature existingSubSig = subSigs[i];
+ if (PgpSignature.IsSignatureEncodingEqual(existingSubSig, copySubSig))
+ {
+ found = true;
+ // join existing sig with copy to apply modifications in unhashed subpackets
+ existingSubSig = PgpSignature.Join(existingSubSig, copySubSig);
+ subSigs[i] = existingSubSig;
+ break;
+ }
+ }
+ if (!found && subSigs != null)
+ {
+ subSigs.Add(copySubSig);
+ }
+ }
+ }
}
- return false;
+
+ PgpPublicKey merged = new PgpPublicKey(key, trustPk, keySigs, ids, idTrusts, idSigs);
+ merged.subSigs = subSigs;
+
+ return merged;
}
}
}
diff --git a/crypto/src/openpgp/PgpPublicKeyRing.cs b/crypto/src/openpgp/PgpPublicKeyRing.cs
index ebbb95634..e1a2a83f2 100644
--- a/crypto/src/openpgp/PgpPublicKeyRing.cs
+++ b/crypto/src/openpgp/PgpPublicKeyRing.cs
@@ -50,10 +50,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
// direct signatures and revocations
var keySigs = ReadSignaturesAndTrust(bcpgInput);
- IList<object> ids;
- IList<TrustPacket> idTrusts;
- IList<IList<PgpSignature>> idSigs;
- ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs);
+ ReadUserIDs(bcpgInput, out var ids, out var idTrusts, out var idSigs);
keys.Add(new PgpPublicKey(pubPk, trustPk, keySigs, ids, idTrusts, idSigs));
@@ -204,5 +201,74 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
return new PgpPublicKey(pk, kTrust, sigList);
}
+
+ /**
+ * Join two copies of the same certificate.
+ * The certificates must have the same primary key, but may carry different subkeys, user-ids and signatures.
+ * The resulting certificate will carry the sum of both certificates subkeys, user-ids and signatures.
+ * <p>
+ * This method will ignore trust packets on the second copy of the certificate and instead
+ * copy the local certificate's trust packets to the joined certificate.
+ *
+ * @param first local copy of the certificate
+ * @param second remote copy of the certificate (e.g. from a key server)
+ * @return joined key ring
+ * @throws PGPException
+ */
+ public static PgpPublicKeyRing Join(PgpPublicKeyRing first, PgpPublicKeyRing second)
+ {
+ return Join(first, second, false, false);
+ }
+
+ /**
+ * Join two copies of the same certificate.
+ * The certificates must have the same primary key, but may carry different subkeys, user-ids and signatures.
+ * The resulting certificate will carry the sum of both certificates subkeys, user-ids and signatures.
+ * <p>
+ * For each subkey holds: If joinTrustPackets is set to true and the second key is carrying a trust packet,
+ * the trust packet will be copied to the joined key.
+ * Otherwise, the joined key will carry the trust packet of the local copy.
+ *
+ * @param first local copy of the certificate
+ * @param second remote copy of the certificate (e.g. from a key server)
+ * @param joinTrustPackets if true, trust packets from the second certificate copy will be carried over into the joined certificate
+ * @param allowSubkeySigsOnNonSubkey if true, the resulting joined certificate may carry subkey signatures on its primary key
+ * @return joined certificate
+ * @throws PGPException
+ */
+ public static PgpPublicKeyRing Join(PgpPublicKeyRing first, PgpPublicKeyRing second, bool joinTrustPackets,
+ bool allowSubkeySigsOnNonSubkey)
+ {
+ if (!Arrays.AreEqual(first.GetPublicKey().GetFingerprint(), second.GetPublicKey().GetFingerprint()))
+ throw new ArgumentException("Cannot merge certificates with differing primary keys.");
+
+ var secondKeys = new HashSet<long>();
+ foreach (var key in second.GetPublicKeys())
+ {
+ secondKeys.Add(key.KeyId);
+ }
+
+ var merged = new List<PgpPublicKey>();
+ foreach (var key in first.GetPublicKeys())
+ {
+ var copy = second.GetPublicKey(key.KeyId);
+ if (copy != null)
+ {
+ merged.Add(PgpPublicKey.Join(key, copy, joinTrustPackets, allowSubkeySigsOnNonSubkey));
+ secondKeys.Remove(key.KeyId);
+ }
+ else
+ {
+ merged.Add(key);
+ }
+ }
+
+ foreach (var additionalKeyId in secondKeys)
+ {
+ merged.Add(second.GetPublicKey(additionalKeyId));
+ }
+
+ return new PgpPublicKeyRing(merged);
+ }
}
}
diff --git a/crypto/src/openpgp/PgpSecretKey.cs b/crypto/src/openpgp/PgpSecretKey.cs
index 0103cc187..a120f97d8 100644
--- a/crypto/src/openpgp/PgpSecretKey.cs
+++ b/crypto/src/openpgp/PgpSecretKey.cs
@@ -479,6 +479,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
get { return pub.KeyId; }
}
+ /// <summary>The fingerprint of the public key associated with this key.</summary>
+ public byte[] GetFingerprint()
+ {
+ return pub.GetFingerprint();
+ }
+
/// <summary>Return the S2K usage associated with this key.</summary>
public int S2kUsage
{
@@ -832,10 +838,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
for (int i = 0; i != pub.ids.Count; i++)
{
- object pubID = pub.ids[i];
- if (pubID is string id)
+ var pubID = pub.ids[i];
+ if (pubID is UserIdPacket id)
{
- bcpgOut.WritePacket(new UserIdPacket(id));
+ bcpgOut.WritePacket(id);
}
else if (pubID is PgpUserAttributeSubpacketVector v)
{
diff --git a/crypto/src/openpgp/PgpSignature.cs b/crypto/src/openpgp/PgpSignature.cs
index 9b596f279..d1146183a 100644
--- a/crypto/src/openpgp/PgpSignature.cs
+++ b/crypto/src/openpgp/PgpSignature.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
using Org.BouncyCastle.Asn1;
@@ -38,6 +39,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
public const int SubkeyRevocation = 0x28;
public const int CertificationRevocation = 0x30;
public const int Timestamp = 0x40;
+ public const int ThirdPartyConfirmation = 0x50;
private readonly SignaturePacket sigPck;
private readonly int signatureType;
@@ -52,8 +54,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
{
}
- internal PgpSignature(
- SignaturePacket sigPacket)
+ internal PgpSignature(SignaturePacket sigPacket)
: this(sigPacket, null)
{
}
@@ -83,6 +84,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
get { return sigPck.HashAlgorithm; }
}
+ /// <summary>Return the digest prefix of the signature.</summary>
+ public byte[] GetDigestPrefix()
+ {
+ return sigPck.GetFingerprint();
+ }
+
/// <summary>Return true if this signature represents a certification.</summary>
public bool IsCertification()
{
@@ -194,14 +201,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
public bool Verify()
{
byte[] trailer = GetSignatureTrailer();
+
sig.BlockUpdate(trailer, 0, trailer.Length);
return sig.VerifySignature(GetSignature());
}
- private void UpdateWithIdData(
- int header,
- byte[] idBytes)
+ private void UpdateWithIdData(int header, byte[] idBytes)
{
this.Update(
(byte) header,
@@ -212,8 +218,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
this.Update(idBytes);
}
- private void UpdateWithPublicKey(
- PgpPublicKey key)
+ private void UpdateWithPublicKey(PgpPublicKey key)
{
byte[] keyBytes = GetEncodedPublicKey(key);
@@ -231,9 +236,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
/// <param name="userAttributes">User attributes the key was stored under.</param>
/// <param name="key">The key to be verified.</param>
/// <returns>True, if the signature matches, false otherwise.</returns>
- public bool VerifyCertification(
- PgpUserAttributeSubpacketVector userAttributes,
- PgpPublicKey key)
+ public bool VerifyCertification(PgpUserAttributeSubpacketVector userAttributes, PgpPublicKey key)
{
UpdateWithPublicKey(key);
@@ -254,9 +257,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
throw new PgpException("cannot encode subpacket array", e);
}
- this.Update(sigPck.GetSignatureTrailer());
-
- return sig.VerifySignature(GetSignature());
+ return Verify();
}
/// <summary>
@@ -266,9 +267,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
/// <param name="id">ID the key was stored under.</param>
/// <param name="key">The key to be verified.</param>
/// <returns>True, if the signature matches, false otherwise.</returns>
- public bool VerifyCertification(
- string id,
- PgpPublicKey key)
+ public bool VerifyCertification(string id, PgpPublicKey key)
{
UpdateWithPublicKey(key);
@@ -277,9 +276,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
//
UpdateWithIdData(0xb4, Strings.ToUtf8ByteArray(id));
- Update(sigPck.GetSignatureTrailer());
-
- return sig.VerifySignature(GetSignature());
+ return Verify();
}
/// <summary>Verify a certification for the passed in key against the passed in master key.</summary>
@@ -293,9 +290,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
UpdateWithPublicKey(masterKey);
UpdateWithPublicKey(pubKey);
- Update(sigPck.GetSignatureTrailer());
-
- return sig.VerifySignature(GetSignature());
+ return Verify();
}
/// <summary>Verify a key certification, such as revocation, for the passed in key.</summary>
@@ -312,9 +307,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
UpdateWithPublicKey(pubKey);
- Update(sigPck.GetSignatureTrailer());
-
- return sig.VerifySignature(GetSignature());
+ return Verify();
}
public int SignatureType
@@ -438,13 +431,28 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
return bOut.ToArray();
}
- public void Encode(Stream outStream)
+ public void Encode(Stream outStream)
{
- var bcpgOut = BcpgOutputStream.Wrap(outStream);
+ Encode(outStream, false);
+ }
- bcpgOut.WritePacket(sigPck);
+ /**
+ * Encode the signature to outStream, with trust packets stripped out if forTransfer is true.
+ *
+ * @param outStream stream to write the key encoding to.
+ * @param forTransfer if the purpose of encoding is to send key to other users.
+ * @throws IOException in case of encoding error.
+ */
+ public void Encode(Stream outStream, bool forTransfer)
+ {
+ // Exportable signatures MUST NOT be exported if forTransfer==true
+ if (forTransfer && (!GetHashedSubPackets().IsExportable() || !GetUnhashedSubPackets().IsExportable()))
+ return;
- if (trustPck != null)
+ var bcpgOut = BcpgOutputStream.Wrap(outStream);
+
+ bcpgOut.WritePacket(sigPck);
+ if (!forTransfer && trustPck != null)
{
bcpgOut.WritePacket(trustPck);
}
@@ -480,5 +488,44 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
return false;
}
}
+
+ public static bool IsSignatureEncodingEqual(PgpSignature sig1, PgpSignature sig2)
+ {
+ return Arrays.AreEqual(sig1.sigPck.GetSignatureBytes(), sig2.sigPck.GetSignatureBytes());
+ }
+
+ public static PgpSignature Join(PgpSignature sig1, PgpSignature sig2)
+ {
+ if (!IsSignatureEncodingEqual(sig1, sig2))
+ throw new ArgumentException("These are different signatures.");
+
+ // merge unhashed subpackets
+ SignatureSubpacket[] sig1Unhashed = sig1.GetUnhashedSubPackets().ToSubpacketArray();
+ SignatureSubpacket[] sig2Unhashed = sig2.GetUnhashedSubPackets().ToSubpacketArray();
+
+ var merged = new List<SignatureSubpacket>(sig1Unhashed);
+
+ foreach (var subpacket in sig2Unhashed)
+ {
+ if (!merged.Contains(subpacket))
+ {
+ merged.Add(subpacket);
+ }
+ }
+
+ SignatureSubpacket[] unhashed = merged.ToArray();
+ return new PgpSignature(
+ new SignaturePacket(
+ sig1.SignatureType,
+ sig1.KeyId,
+ sig1.KeyAlgorithm,
+ sig1.HashAlgorithm,
+ sig1.GetHashedSubPackets().ToSubpacketArray(),
+ unhashed,
+ sig1.GetDigestPrefix(),
+ sig1.sigPck.GetSignature()
+ )
+ );
+ }
}
}
diff --git a/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs b/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs
index 07b9fee17..010e7e052 100644
--- a/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs
+++ b/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs
@@ -5,7 +5,7 @@ using Org.BouncyCastle.Bcpg.Sig;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
- /// <remarks>Generator for signature subpackets.</remarks>
+ /// <remarks>Generator for signature subpackets.</remarks>
public class PgpSignatureSubpacketGenerator
{
private readonly List<SignatureSubpacket> list = new List<SignatureSubpacket>();
@@ -35,23 +35,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
}
}
- public void SetRevocable(
- bool isCritical,
- bool isRevocable)
+ public void SetRevocable(bool isCritical, bool isRevocable)
{
list.Add(new Revocable(isCritical, isRevocable));
}
- public void SetExportable(
- bool isCritical,
- bool isExportable)
+ public void SetExportable(bool isCritical, bool isExportable)
{
list.Add(new Exportable(isCritical, isExportable));
}
- public void SetFeature(
- bool isCritical,
- byte feature)
+ public void SetFeature(bool isCritical, byte feature)
{
list.Add(new Features(isCritical, feature));
}
@@ -63,10 +57,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
/// <param name="isCritical">true if the packet is critical.</param>
/// <param name="depth">depth level.</param>
/// <param name="trustAmount">trust amount.</param>
- public void SetTrust(
- bool isCritical,
- int depth,
- int trustAmount)
+ public void SetTrust(bool isCritical, int depth, int trustAmount)
{
list.Add(new TrustSignature(isCritical, depth, trustAmount));
}
@@ -77,9 +68,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
/// </summary>
/// <param name="isCritical">True, if should be treated as critical, false otherwise.</param>
/// <param name="seconds">The number of seconds the key is valid, or zero if no expiry.</param>
- public void SetKeyExpirationTime(
- bool isCritical,
- long seconds)
+ public void SetKeyExpirationTime(bool isCritical, long seconds)
{
list.Add(new KeyExpirationTime(isCritical, seconds));
}
@@ -90,9 +79,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
/// </summary>
/// <param name="isCritical">True, if should be treated as critical, false otherwise.</param>
/// <param name="seconds">The number of seconds the signature is valid, or zero if no expiry.</param>
- public void SetSignatureExpirationTime(
- bool isCritical,
- long seconds)
+ public void SetSignatureExpirationTime(bool isCritical, long seconds)
{
list.Add(new SignatureExpirationTime(isCritical, seconds));
}
@@ -103,54 +90,56 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
/// Note: this overrides the generation of a creation time when the signature
/// is generated.</p>
/// </summary>
- public void SetSignatureCreationTime(
- bool isCritical,
- DateTime date)
+ public void SetSignatureCreationTime(bool isCritical, DateTime date)
{
list.Add(new SignatureCreationTime(isCritical, date));
}
- public void SetPreferredHashAlgorithms(
- bool isCritical,
- int[] algorithms)
+ public void SetPreferredHashAlgorithms(bool isCritical, int[] algorithms)
{
list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredHashAlgorithms, isCritical, algorithms));
}
- public void SetPreferredSymmetricAlgorithms(
- bool isCritical,
- int[] algorithms)
+ public void SetPreferredSymmetricAlgorithms(bool isCritical, int[] algorithms)
{
list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredSymmetricAlgorithms, isCritical, algorithms));
}
- public void SetPreferredCompressionAlgorithms(
- bool isCritical,
- int[] algorithms)
+ public void SetPreferredCompressionAlgorithms(bool isCritical, int[] algorithms)
{
list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredCompressionAlgorithms, isCritical, algorithms));
}
- public void SetKeyFlags(
- bool isCritical,
- int flags)
+ public void SetPreferredAeadAlgorithms(bool isCritical, int[] algorithms)
+ {
+ list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredAeadAlgorithms, isCritical, algorithms));
+ }
+
+ public void AddPolicyUrl(bool isCritical, string policyUrl)
+ {
+ list.Add(new PolicyUrl(isCritical, policyUrl));
+ }
+
+ public void SetKeyFlags(bool isCritical, int flags)
{
list.Add(new KeyFlags(isCritical, flags));
}
- public void SetSignerUserId(
- bool isCritical,
- string userId)
+ [Obsolete("Use 'AddSignerUserId' instead")]
+ public void SetSignerUserId(bool isCritical, string userId)
+ {
+ AddSignerUserId(isCritical, userId);
+ }
+
+ public void AddSignerUserId(bool isCritical, string userId)
{
if (userId == null)
throw new ArgumentNullException("userId");
- list.Add(new SignerUserId(isCritical, userId));
+ list.Add(new SignerUserId(isCritical, userId));
}
- public void SetSignerUserId(
- bool isCritical,
- byte[] rawUserId)
+ public void SetSignerUserId(bool isCritical, byte[] rawUserId)
{
if (rawUserId == null)
throw new ArgumentNullException("rawUserId");
@@ -158,70 +147,116 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
list.Add(new SignerUserId(isCritical, false, rawUserId));
}
- public void SetEmbeddedSignature(
- bool isCritical,
- PgpSignature pgpSignature)
+ [Obsolete("Use 'AddEmbeddedSignature' instead")]
+ public void SetEmbeddedSignature(bool isCritical, PgpSignature pgpSignature)
{
- byte[] sig = pgpSignature.GetEncoded();
- byte[] data;
-
- // TODO Should be >= ?
- if (sig.Length - 1 > 256)
- {
- data = new byte[sig.Length - 3];
- }
- else
- {
- data = new byte[sig.Length - 2];
- }
-
- Array.Copy(sig, sig.Length - data.Length, data, 0, data.Length);
-
- list.Add(new EmbeddedSignature(isCritical, false, data));
+ AddEmbeddedSignature(isCritical, pgpSignature);
}
- public void SetPrimaryUserId(
- bool isCritical,
- bool isPrimaryUserId)
+ public void AddEmbeddedSignature(bool isCritical, PgpSignature pgpSignature)
+ {
+ byte[] sig = pgpSignature.GetEncoded();
+ byte[] data;
+
+ // TODO Should be >= ?
+ if (sig.Length - 1 > 256)
+ {
+ data = new byte[sig.Length - 3];
+ }
+ else
+ {
+ data = new byte[sig.Length - 2];
+ }
+
+ Array.Copy(sig, sig.Length - data.Length, data, 0, data.Length);
+
+ list.Add(new EmbeddedSignature(isCritical, false, data));
+ }
+
+ public void SetPrimaryUserId(bool isCritical, bool isPrimaryUserId)
{
list.Add(new PrimaryUserId(isCritical, isPrimaryUserId));
}
- public void SetNotationData(
- bool isCritical,
- bool isHumanReadable,
- string notationName,
- string notationValue)
+ [Obsolete("Use 'AddNotationData' instead")]
+ public void SetNotationData(bool isCritical, bool isHumanReadable, string notationName, string notationValue)
{
- list.Add(new NotationData(isCritical, isHumanReadable, notationName, notationValue));
+ AddNotationData(isCritical, isHumanReadable, notationName, notationValue);
}
- /// <summary>
- /// Sets revocation reason sub packet
- /// </summary>
- public void SetRevocationReason(bool isCritical, RevocationReasonTag reason,
- string description)
+ public void AddNotationData(bool isCritical, bool isHumanReadable, string notationName, string notationValue)
+ {
+ list.Add(new NotationData(isCritical, isHumanReadable, notationName, notationValue));
+ }
+
+ /// <summary>
+ /// Sets revocation reason sub packet
+ /// </summary>
+ public void SetRevocationReason(bool isCritical, RevocationReasonTag reason, string description)
{
list.Add(new RevocationReason(isCritical, reason, description));
}
- /// <summary>
- /// Sets revocation key sub packet
- /// </summary>
+ [Obsolete("Use 'AddRevocationKey' instead")]
public void SetRevocationKey(bool isCritical, PublicKeyAlgorithmTag keyAlgorithm, byte[] fingerprint)
{
- list.Add(new RevocationKey(isCritical, RevocationKeyTag.ClassDefault, keyAlgorithm, fingerprint));
+ AddRevocationKey(isCritical, keyAlgorithm, fingerprint);
}
- /// <summary>
- /// Sets issuer key sub packet
- /// </summary>
- public void SetIssuerKeyID(bool isCritical, long keyID)
+ public void AddRevocationKey(bool isCritical, PublicKeyAlgorithmTag keyAlgorithm, byte[] fingerprint)
+ {
+ list.Add(new RevocationKey(isCritical, RevocationKeyTag.ClassDefault, keyAlgorithm, fingerprint));
+ }
+
+ /// <summary>
+ /// Sets issuer key sub packet
+ /// </summary>
+ public void SetIssuerKeyID(bool isCritical, long keyID)
{
list.Add(new IssuerKeyId(isCritical, keyID));
- }
+ }
+
+ public void SetSignatureTarget(bool isCritical, int publicKeyAlgorithm, int hashAlgorithm, byte[] hashData)
+ {
+ list.Add(new SignatureTarget(isCritical, publicKeyAlgorithm, hashAlgorithm, hashData));
+ }
+
+ public void SetIssuerFingerprint(bool isCritical, PgpSecretKey secretKey)
+ {
+ SetIssuerFingerprint(isCritical, secretKey.PublicKey);
+ }
+
+ public void SetIssuerFingerprint(bool isCritical, PgpPublicKey publicKey)
+ {
+ list.Add(new IssuerFingerprint(isCritical, publicKey.Version, publicKey.GetFingerprint()));
+ }
+
+ public void AddIntendedRecipientFingerprint(bool isCritical, PgpPublicKey publicKey)
+ {
+ list.Add(new IntendedRecipientFingerprint(isCritical, publicKey.Version, publicKey.GetFingerprint()));
+ }
+
+ public void AddCustomSubpacket(SignatureSubpacket subpacket)
+ {
+ list.Add(subpacket);
+ }
+
+ public bool RemovePacket(SignatureSubpacket packet)
+ {
+ return list.Remove(packet);
+ }
+
+ public bool HasSubpacket(SignatureSubpacketTag type)
+ {
+ return null != list.Find(subpacket => subpacket.SubpacketType == type);
+ }
+
+ public SignatureSubpacket[] GetSubpackets(SignatureSubpacketTag type)
+ {
+ return list.FindAll(subpacket => subpacket.SubpacketType == type).ToArray();
+ }
- public PgpSignatureSubpacketVector Generate()
+ public PgpSignatureSubpacketVector Generate()
{
return new PgpSignatureSubpacketVector(list.ToArray());
}
diff --git a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
index 9417b14fa..ae387198c 100644
--- a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
+++ b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
@@ -1,39 +1,32 @@
using System;
+using System.Collections.Generic;
using System.IO;
using Org.BouncyCastle.Bcpg.Sig;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
- /// <remarks>Container for a list of signature subpackets.</remarks>
+ /// <remarks>Container for a list of signature subpackets.</remarks>
public class PgpSignatureSubpacketVector
{
public static PgpSignatureSubpacketVector FromSubpackets(SignatureSubpacket[] packets)
{
- if (packets == null)
- {
- packets = new SignatureSubpacket[0];
- }
- return new PgpSignatureSubpacketVector(packets);
+ return new PgpSignatureSubpacketVector(packets ?? new SignatureSubpacket[0]);
}
private readonly SignatureSubpacket[] packets;
- internal PgpSignatureSubpacketVector(
- SignatureSubpacket[] packets)
+ internal PgpSignatureSubpacketVector(SignatureSubpacket[] packets)
{
this.packets = packets;
}
- public SignatureSubpacket GetSubpacket(
- SignatureSubpacketTag type)
+ public SignatureSubpacket GetSubpacket(SignatureSubpacketTag type)
{
for (int i = 0; i != packets.Length; i++)
{
if (packets[i].SubpacketType == type)
- {
return packets[i];
- }
}
return null;
@@ -45,8 +38,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
* @param type type to look for.
* @return true if present, false otherwise.
*/
- public bool HasSubpacket(
- SignatureSubpacketTag type)
+ public bool HasSubpacket(SignatureSubpacketTag type)
{
return GetSubpacket(type) != null;
}
@@ -56,8 +48,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
* @param type subpacket type code
* @return an array of zero or more matching subpackets.
*/
- public SignatureSubpacket[] GetSubpackets(
- SignatureSubpacketTag type)
+ public SignatureSubpacket[] GetSubpackets(SignatureSubpacketTag type)
{
int count = 0;
for (int i = 0; i < packets.Length; ++i)
@@ -82,6 +73,27 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
return result;
}
+ /// <exception cref="PgpException"/>
+ public PgpSignatureList GetEmbeddedSignatures()
+ {
+ SignatureSubpacket[] sigs = GetSubpackets(SignatureSubpacketTag.EmbeddedSignature);
+ PgpSignature[] l = new PgpSignature[sigs.Length];
+
+ for (int i = 0; i < sigs.Length; i++)
+ {
+ try
+ {
+ l[i] = new PgpSignature(SignaturePacket.FromByteArray(sigs[i].GetData()));
+ }
+ catch (IOException e)
+ {
+ throw new PgpException("Unable to parse signature packet: " + e.Message, e);
+ }
+ }
+
+ return new PgpSignatureList(l);
+ }
+
public NotationData[] GetNotationDataOccurrences()
{
SignatureSubpacket[] notations = GetSubpackets(SignatureSubpacketTag.NotationData);
@@ -95,11 +107,26 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
return vals;
}
- public long GetIssuerKeyId()
+ public NotationData[] GetNotationDataOccurrences(string notationName)
+ {
+ NotationData[] notations = GetNotationDataOccurrences();
+ var notationsWithName = new List<NotationData>();
+ for (int i = 0; i != notations.Length; i++)
+ {
+ NotationData notation = notations[i];
+ if (notation.GetNotationName().Equals(notationName))
+ {
+ notationsWithName.Add(notation);
+ }
+ }
+ return notationsWithName.ToArray();
+ }
+
+ public long GetIssuerKeyId()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.IssuerKeyId);
- return p == null ? 0 : ((IssuerKeyId) p).KeyId;
+ return p == null ? 0 : ((IssuerKeyId)p).KeyId;
}
public bool HasSignatureCreationTime()
@@ -109,26 +136,27 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
public DateTime GetSignatureCreationTime()
{
- SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.CreationTime);
-
- if (p == null)
- {
- throw new PgpException("SignatureCreationTime not available");
- }
+ SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.CreationTime)
+ ?? throw new PgpException("SignatureCreationTime not available");
return ((SignatureCreationTime)p).GetTime();
}
- /// <summary>
- /// Return the number of seconds a signature is valid for after its creation date.
- /// A value of zero means the signature never expires.
- /// </summary>
- /// <returns>Seconds a signature is valid for.</returns>
+ public bool HasSignatureExpirationTime()
+ {
+ return GetSubpacket(SignatureSubpacketTag.ExpireTime) != null;
+ }
+
+ /// <summary>
+ /// Return the number of seconds a signature is valid for after its creation date.
+ /// A value of zero means the signature never expires.
+ /// </summary>
+ /// <returns>Seconds a signature is valid for.</returns>
public long GetSignatureExpirationTime()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.ExpireTime);
- return p == null ? 0 : ((SignatureExpirationTime) p).Time;
+ return p == null ? 0 : ((SignatureExpirationTime)p).Time;
}
/// <summary>
@@ -140,77 +168,58 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.KeyExpireTime);
- return p == null ? 0 : ((KeyExpirationTime) p).Time;
+ return p == null ? 0 : ((KeyExpirationTime)p).Time;
}
public int[] GetPreferredHashAlgorithms()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredHashAlgorithms);
- return p == null ? null : ((PreferredAlgorithms) p).GetPreferences();
+ return p == null ? null : ((PreferredAlgorithms)p).GetPreferences();
}
public int[] GetPreferredSymmetricAlgorithms()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredSymmetricAlgorithms);
- return p == null ? null : ((PreferredAlgorithms) p).GetPreferences();
+ return p == null ? null : ((PreferredAlgorithms)p).GetPreferences();
}
public int[] GetPreferredCompressionAlgorithms()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredCompressionAlgorithms);
- return p == null ? null : ((PreferredAlgorithms) p).GetPreferences();
+ return p == null ? null : ((PreferredAlgorithms)p).GetPreferences();
+ }
+
+ public int[] GetPreferredAeadAlgorithms()
+ {
+ SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredAeadAlgorithms);
+
+ return p == null ? null : ((PreferredAlgorithms)p).GetPreferences();
}
- public int GetKeyFlags()
+ public int GetKeyFlags()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.KeyFlags);
- return p == null ? 0 : ((KeyFlags) p).Flags;
+ return p == null ? 0 : ((KeyFlags)p).Flags;
}
public string GetSignerUserId()
{
SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.SignerUserId);
- return p == null ? null : ((SignerUserId) p).GetId();
+ return p == null ? null : ((SignerUserId)p).GetId();
}
public bool IsPrimaryUserId()
{
- PrimaryUserId primaryId = (PrimaryUserId)
- this.GetSubpacket(SignatureSubpacketTag.PrimaryUserId);
-
- if (primaryId != null)
- {
- return primaryId.IsPrimaryUserId();
- }
+ PrimaryUserId primaryId = (PrimaryUserId)GetSubpacket(SignatureSubpacketTag.PrimaryUserId);
- return false;
+ return primaryId != null && primaryId.IsPrimaryUserId();
}
- public PgpSignatureList GetEmbeddedSignatures()
- {
- SignatureSubpacket [] sigs = GetSubpackets(SignatureSubpacketTag.EmbeddedSignature);
- PgpSignature[] l = new PgpSignature[sigs.Length];
-
- for (int i = 0; i < sigs.Length; i++)
- {
- try
- {
- l[i] = new PgpSignature(SignaturePacket.FromByteArray(sigs[i].GetData()));
- }
- catch (IOException e)
- {
- throw new PgpException("Unable to parse signature packet: " + e.Message, e);
- }
- }
-
- return new PgpSignatureList(l);
- }
-
public SignatureSubpacketTag[] GetCriticalTags()
{
int count = 0;
@@ -237,25 +246,152 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
return list;
}
+ public SignatureTarget GetSignatureTarget()
+ {
+ SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.SignatureTarget);
+
+ return p == null ? null : new SignatureTarget(p.IsCritical(), p.IsLongLength(), p.GetData());
+ }
+
public Features GetFeatures()
{
- SignatureSubpacket p = this.GetSubpacket(SignatureSubpacketTag.Features);
+ SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.Features);
- if (p == null)
- return null;
+ return p == null ? null : new Features(p.IsCritical(), p.IsLongLength(), p.GetData());
+ }
- return new Features(p.IsCritical(), p.IsLongLength(), p.GetData());
+ public IssuerFingerprint GetIssuerFingerprint()
+ {
+ SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.IssuerFingerprint);
+
+ return p == null ? null : new IssuerFingerprint(p.IsCritical(), p.IsLongLength(), p.GetData());
+ }
+
+ public IntendedRecipientFingerprint GetIntendedRecipientFingerprint()
+ {
+ SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.IntendedRecipientFingerprint);
+
+ return p == null ? null : new IntendedRecipientFingerprint(p.IsCritical(), p.IsLongLength(), p.GetData());
+ }
+
+ public IntendedRecipientFingerprint[] GetIntendedRecipientFingerprints()
+ {
+ SignatureSubpacket[] subpackets = GetSubpackets(SignatureSubpacketTag.IntendedRecipientFingerprint);
+ IntendedRecipientFingerprint[] recipients = new IntendedRecipientFingerprint[subpackets.Length];
+ for (int i = 0; i < recipients.Length; i++)
+ {
+ SignatureSubpacket p = subpackets[i];
+ recipients[i] = new IntendedRecipientFingerprint(p.IsCritical(), p.IsLongLength(), p.GetData());
+ }
+ return recipients;
+ }
+
+ public Exportable GetExportable()
+ {
+ SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.Exportable);
+
+ return p == null ? null : new Exportable(p.IsCritical(), p.IsLongLength(), p.GetData());
+ }
+
+ public bool IsExportable()
+ {
+ Exportable exportable = GetExportable();
+ return exportable == null || exportable.IsExportable();
+ }
+
+ public PolicyUrl GetPolicyUrl()
+ {
+ SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PolicyUrl);
+
+ return p == null ? null : new PolicyUrl(p.IsCritical(), p.IsLongLength(), p.GetData());
+ }
+
+ public PolicyUrl[] GetPolicyUrls()
+ {
+ SignatureSubpacket[] subpackets = GetSubpackets(SignatureSubpacketTag.PolicyUrl);
+ PolicyUrl[] policyUrls = new PolicyUrl[subpackets.Length];
+ for (int i = 0; i < subpackets.Length; i++)
+ {
+ SignatureSubpacket p = subpackets[i];
+ policyUrls[i] = new PolicyUrl(p.IsCritical(), p.IsLongLength(), p.GetData());
+ }
+ return policyUrls;
+ }
+
+ public RegularExpression GetRegularExpression()
+ {
+ SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.RegExp);
+
+ return p == null ? null : new RegularExpression(p.IsCritical(), p.IsLongLength(), p.GetData());
+ }
+
+ public RegularExpression[] GetRegularExpressions()
+ {
+ SignatureSubpacket[] subpackets = GetSubpackets(SignatureSubpacketTag.RegExp);
+ RegularExpression[] regexes = new RegularExpression[subpackets.Length];
+ for (int i = 0; i < regexes.Length; i++)
+ {
+ SignatureSubpacket p = subpackets[i];
+ regexes[i] = new RegularExpression(p.IsCritical(), p.IsLongLength(), p.GetData());
+ }
+ return regexes;
+ }
+
+ public Revocable GetRevocable()
+ {
+ SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.Revocable);
+
+ return p == null ? null : new Revocable(p.IsCritical(), p.IsLongLength(), p.GetData());
+ }
+
+ public bool IsRevocable()
+ {
+ Revocable revocable = GetRevocable();
+ return revocable == null || revocable.IsRevocable();
+ }
+
+ public RevocationKey[] GetRevocationKeys()
+ {
+ SignatureSubpacket[] subpackets = GetSubpackets(SignatureSubpacketTag.RevocationKey);
+ RevocationKey[] revocationKeys = new RevocationKey[subpackets.Length];
+ for (int i = 0; i < revocationKeys.Length; i++)
+ {
+ SignatureSubpacket p = subpackets[i];
+ revocationKeys[i] = new RevocationKey(p.IsCritical(), p.IsLongLength(), p.GetData());
+ }
+ return revocationKeys;
+ }
+
+ public RevocationReason GetRevocationReason()
+ {
+ SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.RevocationReason);
+
+ return p == null ? null : new RevocationReason(p.IsCritical(), p.IsLongLength(), p.GetData());
+ }
+
+ public TrustSignature GetTrust()
+ {
+ SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.TrustSig);
+
+ return p == null ? null : new TrustSignature(p.IsCritical(), p.IsLongLength(), p.GetData());
}
/// <summary>Return the number of packets this vector contains.</summary>
- public int Count
- {
- get { return packets.Length; }
- }
+ public int Count => packets.Length;
internal SignatureSubpacket[] ToSubpacketArray()
{
return packets;
}
+
+ /**
+ * Return a copy of the subpackets in this vector.
+ *
+ * @return an array containing the vector subpackets in order.
+ */
+ public SignatureSubpacket[] ToArray()
+ {
+ return (SignatureSubpacket[])packets.Clone();
+ }
}
}
diff --git a/crypto/src/openpgp/PgpUserAttributeSubpacketVector.cs b/crypto/src/openpgp/PgpUserAttributeSubpacketVector.cs
index df5081390..e9a42e4b7 100644
--- a/crypto/src/openpgp/PgpUserAttributeSubpacketVector.cs
+++ b/crypto/src/openpgp/PgpUserAttributeSubpacketVector.cs
@@ -4,6 +4,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// <remarks>Container for a list of user attribute subpackets.</remarks>
public class PgpUserAttributeSubpacketVector
+ : IUserDataPacket
{
public static PgpUserAttributeSubpacketVector FromSubpackets(UserAttributeSubpacket[] packets)
{
@@ -22,15 +23,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
this.packets = packets;
}
- public UserAttributeSubpacket GetSubpacket(
- UserAttributeSubpacketTag type)
+ public UserAttributeSubpacket GetSubpacket(UserAttributeSubpacketTag type)
{
for (int i = 0; i != packets.Length; i++)
{
if (packets[i].SubpacketType == type)
- {
return packets[i];
- }
}
return null;
@@ -48,28 +46,21 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
return packets;
}
- public override bool Equals(
- object obj)
+ public override bool Equals(object obj)
{
if (obj == this)
return true;
- PgpUserAttributeSubpacketVector other = obj as PgpUserAttributeSubpacketVector;
-
- if (other == null)
+ if (!(obj is PgpUserAttributeSubpacketVector other))
return false;
if (other.packets.Length != packets.Length)
- {
return false;
- }
for (int i = 0; i != packets.Length; i++)
{
if (!other.packets[i].Equals(packets[i]))
- {
return false;
- }
}
return true;
diff --git a/crypto/test/src/openpgp/test/PolicyUrlTest.cs b/crypto/test/src/openpgp/test/PolicyUrlTest.cs
new file mode 100644
index 000000000..d98397f39
--- /dev/null
+++ b/crypto/test/src/openpgp/test/PolicyUrlTest.cs
@@ -0,0 +1,58 @@
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Bcpg.Sig;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp.Tests
+{
+ [TestFixture]
+ public class PolicyUrlTest
+ {
+ [Test]
+ public void TestGetUrl()
+ {
+ PolicyUrl policyUrl = new PolicyUrl(true, "https://bouncycastle.org/policy/alice.txt");
+ Assert.IsTrue(policyUrl.IsCritical());
+ Assert.AreEqual("https://bouncycastle.org/policy/alice.txt", policyUrl.Url);
+
+ policyUrl = new PolicyUrl(false, "https://bouncycastle.org/policy/bob.txt");
+ Assert.IsFalse(policyUrl.IsCritical());
+ Assert.AreEqual("https://bouncycastle.org/policy/bob.txt", policyUrl.Url);
+ }
+
+ [Test]
+ public void TestParsingFromSignature()
+ {
+ string signatureWithPolicyUrl = "-----BEGIN PGP SIGNATURE-----\n" +
+ "\n" +
+ "iKQEHxYKAFYFAmIRIAgJEDXXpSQjWzWvFiEEVSc3S9X9kRTsyfjqNdelJCNbNa8u\n" +
+ "Gmh0dHBzOi8vZXhhbXBsZS5vcmcvfmFsaWNlL3NpZ25pbmctcG9saWN5LnR4dAAA\n" +
+ "NnwBAImA2KdiS/7kLWoQpwc+A6N2PtAvLxG0gkZmGzYgRWvGAP9g4GLAA/GQ0plr\n" +
+ "Xn7uLnOG49S1fFA9P+R1Dd8Qoa4+Dg==\n" +
+ "=OPUu\n" +
+ "-----END PGP SIGNATURE-----\n";
+
+ MemoryStream byteIn = new MemoryStream(Strings.ToByteArray(signatureWithPolicyUrl), false);
+ ArmoredInputStream armorIn = new ArmoredInputStream(byteIn);
+ PgpObjectFactory objectFactory = new PgpObjectFactory(armorIn);
+
+ PgpSignatureList signatures = (PgpSignatureList)objectFactory.NextPgpObject();
+ PgpSignature signature = signatures[0];
+
+ PolicyUrl policyUrl = signature.GetHashedSubPackets().GetPolicyUrl();
+ Assert.AreEqual("https://example.org/~alice/signing-policy.txt", policyUrl.Url);
+
+ PolicyUrl other = new PolicyUrl(false, "https://example.org/~alice/signing-policy.txt");
+
+ MemoryStream first = new MemoryStream();
+ policyUrl.Encode(first);
+
+ MemoryStream second = new MemoryStream();
+ other.Encode(second);
+
+ Assert.IsTrue(Arrays.AreEqual(first.ToArray(), second.ToArray()));
+ }
+ }
+}
|