From 6529112d56fb76cd823b314b2fa2c7f11ef37f36 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 24 Feb 2023 22:08:14 +0700 Subject: Bcpg: update signature subpackets --- crypto/src/bcpg/SignaturePacket.cs | 156 +++++++++------------ crypto/src/bcpg/SignatureSubpacket.cs | 2 +- crypto/src/bcpg/SignatureSubpacketTags.cs | 16 ++- crypto/src/bcpg/SignatureSubpacketsReader.cs | 15 ++ .../src/bcpg/sig/IntendedRecipientFingerprint.cs | 28 ++++ crypto/src/bcpg/sig/IssuerFingerprint.cs | 28 ++++ crypto/src/bcpg/sig/PolicyUrl.cs | 24 ++++ crypto/src/bcpg/sig/RegularExpression.cs | 40 ++++++ crypto/src/bcpg/sig/RevocationKey.cs | 23 +-- crypto/src/bcpg/sig/SignatureTarget.cs | 30 ++++ crypto/src/util/Strings.cs | 5 + 11 files changed, 255 insertions(+), 112 deletions(-) create mode 100644 crypto/src/bcpg/sig/IntendedRecipientFingerprint.cs create mode 100644 crypto/src/bcpg/sig/IssuerFingerprint.cs create mode 100644 crypto/src/bcpg/sig/PolicyUrl.cs create mode 100644 crypto/src/bcpg/sig/RegularExpression.cs create mode 100644 crypto/src/bcpg/sig/SignatureTarget.cs diff --git a/crypto/src/bcpg/SignaturePacket.cs b/crypto/src/bcpg/SignaturePacket.cs index a0e8588b3..09e6b059d 100644 --- a/crypto/src/bcpg/SignaturePacket.cs +++ b/crypto/src/bcpg/SignaturePacket.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using Org.BouncyCastle.Bcpg.Sig; +using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Date; using Org.BouncyCastle.Utilities.IO; @@ -24,8 +25,7 @@ namespace Org.BouncyCastle.Bcpg private SignatureSubpacket[] unhashedData; private byte[] signatureEncoding; - internal SignaturePacket( - BcpgInputStream bcpgIn) + internal SignaturePacket(BcpgInputStream bcpgIn) { version = bcpgIn.ReadByte(); @@ -125,42 +125,36 @@ namespace Org.BouncyCastle.Bcpg switch (keyAlgorithm) { - case PublicKeyAlgorithmTag.RsaGeneral: - case PublicKeyAlgorithmTag.RsaSign: - MPInteger v = new MPInteger(bcpgIn); - signature = new MPInteger[1]{ v }; - break; - case PublicKeyAlgorithmTag.Dsa: - MPInteger r = new MPInteger(bcpgIn); - MPInteger s = new MPInteger(bcpgIn); - signature = new MPInteger[2]{ r, s }; - break; - case PublicKeyAlgorithmTag.ElGamalEncrypt: // yep, this really does happen sometimes. - case PublicKeyAlgorithmTag.ElGamalGeneral: - MPInteger p = new MPInteger(bcpgIn); - MPInteger g = new MPInteger(bcpgIn); - MPInteger y = new MPInteger(bcpgIn); - signature = new MPInteger[3]{ p, g, y }; - break; - case PublicKeyAlgorithmTag.ECDsa: - case PublicKeyAlgorithmTag.EdDsa: - MPInteger ecR = new MPInteger(bcpgIn); - MPInteger ecS = new MPInteger(bcpgIn); - signature = new MPInteger[2]{ ecR, ecS }; - break; - default: - if (keyAlgorithm < PublicKeyAlgorithmTag.Experimental_1 || keyAlgorithm > PublicKeyAlgorithmTag.Experimental_11) - throw new IOException("unknown signature key algorithm: " + keyAlgorithm); - - signature = null; - MemoryStream bOut = new MemoryStream(); - int ch; - while ((ch = bcpgIn.ReadByte()) >= 0) - { - bOut.WriteByte((byte) ch); - } - signatureEncoding = bOut.ToArray(); - break; + case PublicKeyAlgorithmTag.RsaGeneral: + case PublicKeyAlgorithmTag.RsaSign: + MPInteger v = new MPInteger(bcpgIn); + signature = new MPInteger[1]{ v }; + break; + case PublicKeyAlgorithmTag.Dsa: + MPInteger r = new MPInteger(bcpgIn); + MPInteger s = new MPInteger(bcpgIn); + signature = new MPInteger[2]{ r, s }; + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: // yep, this really does happen sometimes. + case PublicKeyAlgorithmTag.ElGamalGeneral: + MPInteger p = new MPInteger(bcpgIn); + MPInteger g = new MPInteger(bcpgIn); + MPInteger y = new MPInteger(bcpgIn); + signature = new MPInteger[3]{ p, g, y }; + break; + case PublicKeyAlgorithmTag.ECDsa: + case PublicKeyAlgorithmTag.EdDsa: + MPInteger ecR = new MPInteger(bcpgIn); + MPInteger ecS = new MPInteger(bcpgIn); + signature = new MPInteger[2]{ ecR, ecS }; + break; + default: + if (keyAlgorithm < PublicKeyAlgorithmTag.Experimental_1 || keyAlgorithm > PublicKeyAlgorithmTag.Experimental_11) + throw new IOException("unknown signature key algorithm: " + keyAlgorithm); + + signature = null; + signatureEncoding = Streams.ReadAll(bcpgIn); + break; } } @@ -238,23 +232,23 @@ namespace Org.BouncyCastle.Bcpg } } - public int Version - { - get { return version; } - } + public int Version => version; - public int SignatureType - { - get { return signatureType; } - } + public int SignatureType => signatureType; - /** + /** * return the keyId * @return the keyId that created the signature. */ - public long KeyId + public long KeyId => keyId; + + /** + * Return the signatures fingerprint. + * @return fingerprint (digest prefix) of the signature + */ + public byte[] GetFingerprint() { - get { return keyId; } + return Arrays.Clone(fingerprint); } /** @@ -314,24 +308,15 @@ namespace Org.BouncyCastle.Bcpg return sOut.ToArray(); } - public PublicKeyAlgorithmTag KeyAlgorithm - { - get { return keyAlgorithm; } - } + public PublicKeyAlgorithmTag KeyAlgorithm => keyAlgorithm; - public HashAlgorithmTag HashAlgorithm - { - get { return hashAlgorithm; } - } + public HashAlgorithmTag HashAlgorithm => hashAlgorithm; - /** + /** * return the signature as a set of integers - note this is normalised to be the * ASN.1 encoding of what appears in the signature packet. */ - public MPInteger[] GetSignature() - { - return signature; - } + public MPInteger[] GetSignature() => signature; /** * Return the byte encoding of the signature section. @@ -340,43 +325,34 @@ namespace Org.BouncyCastle.Bcpg public byte[] GetSignatureBytes() { if (signatureEncoding != null) - { - return (byte[]) signatureEncoding.Clone(); - } + return (byte[])signatureEncoding.Clone(); MemoryStream bOut = new MemoryStream(); - BcpgOutputStream bcOut = new BcpgOutputStream(bOut); - foreach (MPInteger sigObj in signature) - { - try - { - bcOut.WriteObject(sigObj); - } - catch (IOException e) - { - throw new Exception("internal error: " + e); - } - } + using (var pOut = new BcpgOutputStream(bOut)) + { + foreach (MPInteger sigObj in signature) + { + try + { + pOut.WriteObject(sigObj); + } + catch (IOException e) + { + throw new Exception("internal error: " + e); + } + } + } - return bOut.ToArray(); + return bOut.ToArray(); } - public SignatureSubpacket[] GetHashedSubPackets() - { - return hashedData; - } + public SignatureSubpacket[] GetHashedSubPackets() => hashedData; - public SignatureSubpacket[] GetUnhashedSubPackets() - { - return unhashedData; - } + public SignatureSubpacket[] GetUnhashedSubPackets() => unhashedData; /// Return the creation time in milliseconds since 1 Jan., 1970 UTC. - public long CreationTime - { - get { return creationTime; } - } + public long CreationTime => creationTime; public override void Encode(BcpgOutputStream bcpgOut) { diff --git a/crypto/src/bcpg/SignatureSubpacket.cs b/crypto/src/bcpg/SignatureSubpacket.cs index 5accadc14..c2186f373 100644 --- a/crypto/src/bcpg/SignatureSubpacket.cs +++ b/crypto/src/bcpg/SignatureSubpacket.cs @@ -42,7 +42,7 @@ namespace Org.BouncyCastle.Bcpg /// Return the generic data making up the packet. public byte[] GetData() { - return (byte[]) data.Clone(); + return (byte[])data.Clone(); } public void Encode( diff --git a/crypto/src/bcpg/SignatureSubpacketTags.cs b/crypto/src/bcpg/SignatureSubpacketTags.cs index 1a8e254c0..0574c274b 100644 --- a/crypto/src/bcpg/SignatureSubpacketTags.cs +++ b/crypto/src/bcpg/SignatureSubpacketTags.cs @@ -25,9 +25,15 @@ namespace Org.BouncyCastle.Bcpg PolicyUrl = 26, // policy URL KeyFlags = 27, // key flags SignerUserId = 28, // signer's user id - RevocationReason = 29, // reason for revocation - Features = 30, // features - SignatureTarget = 31, // signature target - EmbeddedSignature = 32 // embedded signature - } + RevocationReason = 29, // reason for revocation + Features = 30, // features + SignatureTarget = 31, // signature target + EmbeddedSignature = 32, // embedded signature + IssuerFingerprint = 33, // issuer key fingerprint + //PreferredAeadAlgorithms = 34, // RESERVED since crypto-refresh-05 + IntendedRecipientFingerprint = 35, // intended recipient fingerprint + AttestedCertifications = 37, // attested certifications (RESERVED) + KeyBlock = 38, // Key Block (RESERVED) + PreferredAeadAlgorithms = 39, // preferred AEAD algorithms + } } diff --git a/crypto/src/bcpg/SignatureSubpacketsReader.cs b/crypto/src/bcpg/SignatureSubpacketsReader.cs index 86ad11207..35831fa9c 100644 --- a/crypto/src/bcpg/SignatureSubpacketsReader.cs +++ b/crypto/src/bcpg/SignatureSubpacketsReader.cs @@ -92,6 +92,8 @@ namespace Org.BouncyCastle.Bcpg { case SignatureSubpacketTag.CreationTime: return new SignatureCreationTime(isCritical, isLongLength, data); + case SignatureSubpacketTag.EmbeddedSignature: + return new EmbeddedSignature(isCritical, isLongLength, data); case SignatureSubpacketTag.KeyExpireTime: return new KeyExpirationTime(isCritical, isLongLength, data); case SignatureSubpacketTag.ExpireTime: @@ -100,6 +102,8 @@ namespace Org.BouncyCastle.Bcpg return new Revocable(isCritical, isLongLength, data); case SignatureSubpacketTag.Exportable: return new Exportable(isCritical, isLongLength, data); + case SignatureSubpacketTag.Features: + return new Features(isCritical, isLongLength, data); case SignatureSubpacketTag.IssuerKeyId: return new IssuerKeyId(isCritical, isLongLength, data); case SignatureSubpacketTag.TrustSig: @@ -107,19 +111,30 @@ namespace Org.BouncyCastle.Bcpg case SignatureSubpacketTag.PreferredCompressionAlgorithms: case SignatureSubpacketTag.PreferredHashAlgorithms: case SignatureSubpacketTag.PreferredSymmetricAlgorithms: + case SignatureSubpacketTag.PreferredAeadAlgorithms: return new PreferredAlgorithms(type, isCritical, isLongLength, data); case SignatureSubpacketTag.KeyFlags: return new KeyFlags(isCritical, isLongLength, data); + case SignatureSubpacketTag.PolicyUrl: + return new PolicyUrl(isCritical, isLongLength, data); case SignatureSubpacketTag.PrimaryUserId: return new PrimaryUserId(isCritical, isLongLength, data); case SignatureSubpacketTag.SignerUserId: return new SignerUserId(isCritical, isLongLength, data); case SignatureSubpacketTag.NotationData: return new NotationData(isCritical, isLongLength, data); + case SignatureSubpacketTag.RegExp: + return new RegularExpression(isCritical, isLongLength, data); case SignatureSubpacketTag.RevocationReason: return new RevocationReason(isCritical, isLongLength, data); case SignatureSubpacketTag.RevocationKey: return new RevocationKey(isCritical, isLongLength, data); + case SignatureSubpacketTag.SignatureTarget: + return new SignatureTarget(isCritical, isLongLength, data); + case SignatureSubpacketTag.IssuerFingerprint: + return new IssuerFingerprint(isCritical, isLongLength, data); + case SignatureSubpacketTag.IntendedRecipientFingerprint: + return new IntendedRecipientFingerprint(isCritical, isLongLength, data); } return new SignatureSubpacket(type, isCritical, isLongLength, data); } diff --git a/crypto/src/bcpg/sig/IntendedRecipientFingerprint.cs b/crypto/src/bcpg/sig/IntendedRecipientFingerprint.cs new file mode 100644 index 000000000..7077c2d40 --- /dev/null +++ b/crypto/src/bcpg/sig/IntendedRecipientFingerprint.cs @@ -0,0 +1,28 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * packet giving the intended recipient fingerprint. + */ + public class IntendedRecipientFingerprint + : SignatureSubpacket + { + public IntendedRecipientFingerprint(bool critical, bool isLongLength, byte[] data) + : base(SignatureSubpacketTag.IntendedRecipientFingerprint, critical, isLongLength, data) + { + } + + public IntendedRecipientFingerprint(bool critical, int keyVersion, byte[] fingerprint) + : base(SignatureSubpacketTag.IntendedRecipientFingerprint, critical, false, + Arrays.Prepend(fingerprint, (byte)keyVersion)) + { + } + + public int KeyVersion => data[0]; + + public byte[] GetFingerprint() => Arrays.CopyOfRange(data, 1, data.Length); + } +} diff --git a/crypto/src/bcpg/sig/IssuerFingerprint.cs b/crypto/src/bcpg/sig/IssuerFingerprint.cs new file mode 100644 index 000000000..469a8e0d8 --- /dev/null +++ b/crypto/src/bcpg/sig/IssuerFingerprint.cs @@ -0,0 +1,28 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * packet giving the issuer key fingerprint. + */ + public class IssuerFingerprint + : SignatureSubpacket + { + public IssuerFingerprint(bool critical, bool isLongLength, byte[] data) + : base(SignatureSubpacketTag.IssuerFingerprint, critical, isLongLength, data) + { + } + + public IssuerFingerprint(bool critical, int keyVersion, byte[] fingerprint) + : base(SignatureSubpacketTag.IssuerFingerprint, critical, false, + Arrays.Prepend(fingerprint, (byte)keyVersion)) + { + } + + public int KeyVersion => data[0]; + + public byte[] GetFingerprint() => Arrays.CopyOfRange(data, 1, data.Length); + } +} diff --git a/crypto/src/bcpg/sig/PolicyUrl.cs b/crypto/src/bcpg/sig/PolicyUrl.cs new file mode 100644 index 000000000..16f4d1a64 --- /dev/null +++ b/crypto/src/bcpg/sig/PolicyUrl.cs @@ -0,0 +1,24 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.Sig +{ + public class PolicyUrl + : SignatureSubpacket + { + public PolicyUrl(bool critical, string url) + : this(critical, false, Strings.ToUtf8ByteArray(url)) + { + } + + public PolicyUrl(bool critical, bool isLongLength, byte[] data) + : base(SignatureSubpacketTag.PolicyUrl, critical, isLongLength, data) + { + } + + public string Url => Strings.FromUtf8ByteArray(data); + + public byte[] GetRawUrl() => Arrays.Clone(data); + } +} diff --git a/crypto/src/bcpg/sig/RegularExpression.cs b/crypto/src/bcpg/sig/RegularExpression.cs new file mode 100644 index 000000000..c4b48ea2a --- /dev/null +++ b/crypto/src/bcpg/sig/RegularExpression.cs @@ -0,0 +1,40 @@ +using System; +using System.Text.RegularExpressions; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * Regexp Packet - RFC 4880 5.2.3.14. Note: the RFC says the byte encoding is to be null terminated. + */ + public class RegularExpression + : SignatureSubpacket + { + public RegularExpression(bool critical, bool isLongLength, byte[] data) + : base(SignatureSubpacketTag.RegExp, critical, isLongLength, data) + { + if (data[data.Length - 1] != 0) + throw new ArgumentException("data in regex missing null termination"); + } + + public RegularExpression(bool critical, string regex) + : base(SignatureSubpacketTag.RegExp, critical, false, ToNullTerminatedUtf8ByteArray(regex)) + { + } + + public string Regex + { + // last byte is null terminator + get { return Strings.FromUtf8ByteArray(data, 0, data.Length - 1); } + } + + public byte[] GetRawRegex() => Arrays.Clone(data); + + private static byte[] ToNullTerminatedUtf8ByteArray(string str) + { + byte[] utf8 = Strings.ToUtf8ByteArray(str); + return Arrays.Append(utf8, 0x00); + } + } +} diff --git a/crypto/src/bcpg/sig/RevocationKey.cs b/crypto/src/bcpg/sig/RevocationKey.cs index 11467d2af..e4ad4195b 100644 --- a/crypto/src/bcpg/sig/RevocationKey.cs +++ b/crypto/src/bcpg/sig/RevocationKey.cs @@ -12,28 +12,20 @@ namespace Org.BouncyCastle.Bcpg // 1 octet of class, // 1 octet of public-key algorithm ID, // 20 octets of fingerprint - public RevocationKey( - bool isCritical, - bool isLongLength, - byte[] data) + public RevocationKey(bool isCritical, bool isLongLength, byte[] data) : base(SignatureSubpacketTag.RevocationKey, isCritical, isLongLength, data) { } - public RevocationKey( - bool isCritical, - RevocationKeyTag signatureClass, - PublicKeyAlgorithmTag keyAlgorithm, - byte[] fingerprint) + public RevocationKey(bool isCritical, RevocationKeyTag signatureClass, PublicKeyAlgorithmTag keyAlgorithm, + byte[] fingerprint) : base(SignatureSubpacketTag.RevocationKey, isCritical, false, CreateData(signatureClass, keyAlgorithm, fingerprint)) { } - private static byte[] CreateData( - RevocationKeyTag signatureClass, - PublicKeyAlgorithmTag keyAlgorithm, - byte[] fingerprint) + private static byte[] CreateData(RevocationKeyTag signatureClass, PublicKeyAlgorithmTag keyAlgorithm, + byte[] fingerprint) { byte[] data = new byte[2 + fingerprint.Length]; data[0] = (byte)signatureClass; @@ -44,17 +36,16 @@ namespace Org.BouncyCastle.Bcpg public virtual RevocationKeyTag SignatureClass { - get { return (RevocationKeyTag)this.GetData()[0]; } + get { return (RevocationKeyTag)data[0]; } } public virtual PublicKeyAlgorithmTag Algorithm { - get { return (PublicKeyAlgorithmTag)this.GetData()[1]; } + get { return (PublicKeyAlgorithmTag)data[1]; } } public virtual byte[] GetFingerprint() { - byte[] data = this.GetData(); byte[] fingerprint = new byte[data.Length - 2]; Array.Copy(data, 2, fingerprint, 0, fingerprint.Length); return fingerprint; diff --git a/crypto/src/bcpg/sig/SignatureTarget.cs b/crypto/src/bcpg/sig/SignatureTarget.cs new file mode 100644 index 000000000..c6a8dc574 --- /dev/null +++ b/crypto/src/bcpg/sig/SignatureTarget.cs @@ -0,0 +1,30 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * RFC 4880, Section 5.2.3.25 - Signature Target subpacket. + */ + public class SignatureTarget + : SignatureSubpacket + { + public SignatureTarget(bool critical, bool isLongLength, byte[] data) + : base(SignatureSubpacketTag.SignatureTarget, critical, isLongLength, data) + { + } + + public SignatureTarget(bool critical, int publicKeyAlgorithm, int hashAlgorithm, byte[] hashData) + : base(SignatureSubpacketTag.SignatureTarget, critical, false, + Arrays.Concatenate(new byte[]{ (byte)publicKeyAlgorithm, (byte)hashAlgorithm }, hashData)) + { + } + + public int PublicKeyAlgorithm => data[0]; + + public int HashAlgorithm => data[1]; + + public byte[] GetHashData() => Arrays.CopyOfRange(data, 2, data.Length); + } +} diff --git a/crypto/src/util/Strings.cs b/crypto/src/util/Strings.cs index 29a95a07e..b1a63a3a1 100644 --- a/crypto/src/util/Strings.cs +++ b/crypto/src/util/Strings.cs @@ -78,6 +78,11 @@ namespace Org.BouncyCastle.Utilities return Encoding.UTF8.GetString(bytes); } + public static string FromUtf8ByteArray(byte[] bytes, int index, int count) + { + return Encoding.UTF8.GetString(bytes, index, count); + } + public static byte[] ToUtf8ByteArray(char[] cs) { return Encoding.UTF8.GetBytes(cs); -- cgit 1.4.1