diff --git a/crypto/src/asn1/x9/ECNamedCurveTable.cs b/crypto/src/asn1/x9/ECNamedCurveTable.cs
index d8315c16f..92d4393a8 100644
--- a/crypto/src/asn1/x9/ECNamedCurveTable.cs
+++ b/crypto/src/asn1/x9/ECNamedCurveTable.cs
@@ -49,6 +49,28 @@ namespace Org.BouncyCastle.Asn1.X9
return ecP;
}
+ public static string GetName(DerObjectIdentifier oid)
+ {
+ string name = X962NamedCurves.GetName(oid);
+ if (name == null)
+ {
+ name = SecNamedCurves.GetName(oid);
+ }
+ if (name == null)
+ {
+ name = NistNamedCurves.GetName(oid);
+ }
+ if (name == null)
+ {
+ name = TeleTrusTNamedCurves.GetName(oid);
+ }
+ if (name == null)
+ {
+ name = AnssiNamedCurves.GetName(oid);
+ }
+ return name;
+ }
+
/**
* return the object identifier signified by the passed in name. Null
* if there is no object identifier associated with name.
diff --git a/crypto/src/bcpg/ECDHPublicBCPGKey.cs b/crypto/src/bcpg/ECDHPublicBCPGKey.cs
index b85379586..dc225e31e 100644
--- a/crypto/src/bcpg/ECDHPublicBCPGKey.cs
+++ b/crypto/src/bcpg/ECDHPublicBCPGKey.cs
@@ -10,8 +10,8 @@ namespace Org.BouncyCastle.Bcpg
: ECPublicBcpgKey
{
private byte reserved;
- private byte hashFunctionId;
- private byte symAlgorithmId;
+ private HashAlgorithmTag hashFunctionId;
+ private SymmetricKeyAlgorithmTag symAlgorithmId;
/// <param name="bcpgIn">The stream to read the packet from.</param>
public ECDHPublicBcpgKey(
@@ -26,8 +26,8 @@ namespace Org.BouncyCastle.Bcpg
bcpgIn.ReadFully(kdfParameters);
reserved = kdfParameters[0];
- hashFunctionId = kdfParameters[1];
- symAlgorithmId = kdfParameters[2];
+ hashFunctionId = (HashAlgorithmTag)kdfParameters[1];
+ symAlgorithmId = (SymmetricKeyAlgorithmTag)kdfParameters[2];
VerifyHashAlgorithm();
VerifySymmetricKeyAlgorithm();
@@ -36,13 +36,13 @@ namespace Org.BouncyCastle.Bcpg
public ECDHPublicBcpgKey(
DerObjectIdentifier oid,
ECPoint point,
- int hashAlgorithm,
- int symmetricKeyAlgorithm)
+ HashAlgorithmTag hashAlgorithm,
+ SymmetricKeyAlgorithmTag symmetricKeyAlgorithm)
: base(oid, point)
{
reserved = 1;
- hashFunctionId = (byte)hashAlgorithm;
- symAlgorithmId = (byte)symmetricKeyAlgorithm;
+ hashFunctionId = hashAlgorithm;
+ symAlgorithmId = symmetricKeyAlgorithm;
VerifyHashAlgorithm();
VerifySymmetricKeyAlgorithm();
@@ -53,12 +53,12 @@ namespace Org.BouncyCastle.Bcpg
get { return reserved; }
}
- public virtual byte HashAlgorithm
+ public virtual HashAlgorithmTag HashAlgorithm
{
get { return hashFunctionId; }
}
- public virtual byte SymmetricKeyAlgorithm
+ public virtual SymmetricKeyAlgorithmTag SymmetricKeyAlgorithm
{
get { return symAlgorithmId; }
}
@@ -69,8 +69,8 @@ namespace Org.BouncyCastle.Bcpg
base.Encode(bcpgOut);
bcpgOut.WriteByte(0x3);
bcpgOut.WriteByte(reserved);
- bcpgOut.WriteByte(hashFunctionId);
- bcpgOut.WriteByte(symAlgorithmId);
+ bcpgOut.WriteByte((byte)hashFunctionId);
+ bcpgOut.WriteByte((byte)symAlgorithmId);
}
private void VerifyHashAlgorithm()
diff --git a/crypto/src/bcpg/PublicKeyAlgorithmTags.cs b/crypto/src/bcpg/PublicKeyAlgorithmTags.cs
index 4a6704c14..9e30b54f7 100644
--- a/crypto/src/bcpg/PublicKeyAlgorithmTags.cs
+++ b/crypto/src/bcpg/PublicKeyAlgorithmTags.cs
@@ -1,3 +1,5 @@
+using System;
+
namespace Org.BouncyCastle.Bcpg
{
/// <remarks>Public Key Algorithm tag numbers.</remarks>
@@ -8,6 +10,7 @@ namespace Org.BouncyCastle.Bcpg
RsaSign = 3, // RSA Sign-Only
ElGamalEncrypt = 16, // Elgamal (Encrypt-Only), see [ELGAMAL]
Dsa = 17, // DSA (Digital Signature Standard)
+ [Obsolete("Use 'ECDH' instead")]
EC = 18, // Reserved for Elliptic Curve
ECDH = 18, // Reserved for Elliptic Curve (actual algorithm name)
ECDsa = 19, // Reserved for ECDSA
diff --git a/crypto/src/bcpg/PublicKeyEncSessionPacket.cs b/crypto/src/bcpg/PublicKeyEncSessionPacket.cs
index d10605f1d..74d04f7aa 100644
--- a/crypto/src/bcpg/PublicKeyEncSessionPacket.cs
+++ b/crypto/src/bcpg/PublicKeyEncSessionPacket.cs
@@ -2,6 +2,8 @@ using System;
using System.IO;
using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Bcpg
{
@@ -12,7 +14,7 @@ namespace Org.BouncyCastle.Bcpg
private int version;
private long keyId;
private PublicKeyAlgorithmTag algorithm;
- private BigInteger[] data;
+ private byte[][] data;
internal PublicKeyEncSessionPacket(
BcpgInputStream bcpgIn)
@@ -34,33 +36,41 @@ namespace Org.BouncyCastle.Bcpg
{
case PublicKeyAlgorithmTag.RsaEncrypt:
case PublicKeyAlgorithmTag.RsaGeneral:
- data = new BigInteger[]{ new MPInteger(bcpgIn).Value };
+ data = new byte[][]{ new MPInteger(bcpgIn).GetEncoded() };
break;
case PublicKeyAlgorithmTag.ElGamalEncrypt:
case PublicKeyAlgorithmTag.ElGamalGeneral:
- data = new BigInteger[]
- {
- new MPInteger(bcpgIn).Value,
- new MPInteger(bcpgIn).Value
- };
+ MPInteger p = new MPInteger(bcpgIn);
+ MPInteger g = new MPInteger(bcpgIn);
+ data = new byte[][]{
+ p.GetEncoded(),
+ g.GetEncoded(),
+ };
break;
+ case PublicKeyAlgorithmTag.ECDH:
+ data = new byte[][]{ Streams.ReadAll(bcpgIn) };
+ break;
default:
throw new IOException("unknown PGP public key algorithm encountered");
}
}
- public PublicKeyEncSessionPacket(
- long keyId,
- PublicKeyAlgorithmTag algorithm,
- BigInteger[] data)
+ public PublicKeyEncSessionPacket(
+ long keyId,
+ PublicKeyAlgorithmTag algorithm,
+ byte[][] data)
{
this.version = 3;
this.keyId = keyId;
this.algorithm = algorithm;
- this.data = (BigInteger[]) data.Clone();
+ this.data = new byte[data.Length][];
+ for (int i = 0; i < data.Length; ++i)
+ {
+ this.data[i] = Arrays.Clone(data[i]);
+ }
}
- public int Version
+ public int Version
{
get { return version; }
}
@@ -75,12 +85,12 @@ namespace Org.BouncyCastle.Bcpg
get { return algorithm; }
}
- public BigInteger[] GetEncSessionKey()
+ public byte[][] GetEncSessionKey()
{
- return (BigInteger[]) data.Clone();
+ return data;
}
- public override void Encode(
+ public override void Encode(
BcpgOutputStream bcpgOut)
{
MemoryStream bOut = new MemoryStream();
@@ -92,12 +102,14 @@ namespace Org.BouncyCastle.Bcpg
pOut.WriteByte((byte)algorithm);
- for (int i = 0; i != data.Length; i++)
- {
- MPInteger.Encode(pOut, data[i]);
- }
+ for (int i = 0; i < data.Length; ++i)
+ {
+ pOut.Write(data[i]);
+ }
+
+ pOut.Close();
- bcpgOut.WritePacket(PacketTag.PublicKeyEncryptedSession , bOut.ToArray(), true);
+ bcpgOut.WritePacket(PacketTag.PublicKeyEncryptedSession , bOut.ToArray(), true);
}
}
}
diff --git a/crypto/src/bcpg/PublicKeyPacket.cs b/crypto/src/bcpg/PublicKeyPacket.cs
index cea5c8ed2..bbed941dc 100644
--- a/crypto/src/bcpg/PublicKeyPacket.cs
+++ b/crypto/src/bcpg/PublicKeyPacket.cs
@@ -44,7 +44,7 @@ namespace Org.BouncyCastle.Bcpg
case PublicKeyAlgorithmTag.ElGamalGeneral:
key = new ElGamalPublicBcpgKey(bcpgIn);
break;
- case PublicKeyAlgorithmTag.EC:
+ case PublicKeyAlgorithmTag.ECDH:
key = new ECDHPublicBcpgKey(bcpgIn);
break;
case PublicKeyAlgorithmTag.ECDsa:
diff --git a/crypto/src/bcpg/S2k.cs b/crypto/src/bcpg/S2k.cs
index f6d306890..33fd792fe 100644
--- a/crypto/src/bcpg/S2k.cs
+++ b/crypto/src/bcpg/S2k.cs
@@ -84,19 +84,19 @@ namespace Org.BouncyCastle.Bcpg
this.itCount = itCount;
}
- public int Type
+ public virtual int Type
{
get { return type; }
}
/// <summary>The hash algorithm.</summary>
- public HashAlgorithmTag HashAlgorithm
+ public virtual HashAlgorithmTag HashAlgorithm
{
get { return algorithm; }
}
/// <summary>The IV for the key generation algorithm.</summary>
- public byte[] GetIV()
+ public virtual byte[] GetIV()
{
return Arrays.Clone(iv);
}
@@ -108,13 +108,13 @@ namespace Org.BouncyCastle.Bcpg
}
/// <summary>The iteration count</summary>
- public long IterationCount
+ public virtual long IterationCount
{
get { return (16 + (itCount & 15)) << ((itCount >> 4) + ExpBias); }
}
/// <summary>The protection mode - only if GnuDummyS2K</summary>
- public int ProtectionMode
+ public virtual int ProtectionMode
{
get { return protectionMode; }
}
diff --git a/crypto/src/bcpg/SignatureSubpacket.cs b/crypto/src/bcpg/SignatureSubpacket.cs
index ac26f8a3c..d99315599 100644
--- a/crypto/src/bcpg/SignatureSubpacket.cs
+++ b/crypto/src/bcpg/SignatureSubpacket.cs
@@ -7,20 +7,22 @@ namespace Org.BouncyCastle.Bcpg
{
private readonly SignatureSubpacketTag type;
private readonly bool critical;
-
- internal readonly byte[] data;
+ private readonly bool isLongLength;
+ internal byte[] data;
protected internal SignatureSubpacket(
SignatureSubpacketTag type,
bool critical,
+ bool isLongLength,
byte[] data)
{
this.type = type;
this.critical = critical;
+ this.isLongLength = isLongLength;
this.data = data;
}
- public SignatureSubpacketTag SubpacketType
+ public SignatureSubpacketTag SubpacketType
{
get { return type; }
}
@@ -30,7 +32,12 @@ namespace Org.BouncyCastle.Bcpg
return critical;
}
- /// <summary>Return the generic data making up the packet.</summary>
+ public bool IsLongLength()
+ {
+ return isLongLength;
+ }
+
+ /// <summary>Return the generic data making up the packet.</summary>
public byte[] GetData()
{
return (byte[]) data.Clone();
@@ -41,18 +48,7 @@ namespace Org.BouncyCastle.Bcpg
{
int bodyLen = data.Length + 1;
- if (bodyLen < 192)
- {
- os.WriteByte((byte)bodyLen);
- }
- else if (bodyLen <= 8383)
- {
- bodyLen -= 192;
-
- os.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192));
- os.WriteByte((byte)bodyLen);
- }
- else
+ if (isLongLength)
{
os.WriteByte(0xff);
os.WriteByte((byte)(bodyLen >> 24));
@@ -60,6 +56,28 @@ namespace Org.BouncyCastle.Bcpg
os.WriteByte((byte)(bodyLen >> 8));
os.WriteByte((byte)bodyLen);
}
+ else
+ {
+ if (bodyLen < 192)
+ {
+ os.WriteByte((byte)bodyLen);
+ }
+ else if (bodyLen <= 8383)
+ {
+ bodyLen -= 192;
+
+ os.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192));
+ os.WriteByte((byte)bodyLen);
+ }
+ else
+ {
+ os.WriteByte(0xff);
+ os.WriteByte((byte)(bodyLen >> 24));
+ os.WriteByte((byte)(bodyLen >> 16));
+ os.WriteByte((byte)(bodyLen >> 8));
+ os.WriteByte((byte)bodyLen);
+ }
+ }
if (critical)
{
diff --git a/crypto/src/bcpg/SignatureSubpacketsReader.cs b/crypto/src/bcpg/SignatureSubpacketsReader.cs
index 8dd1af332..80bedb07c 100644
--- a/crypto/src/bcpg/SignatureSubpacketsReader.cs
+++ b/crypto/src/bcpg/SignatureSubpacketsReader.cs
@@ -1,6 +1,8 @@
using System;
using System.IO;
+
using Org.BouncyCastle.Bcpg.Sig;
+using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Bcpg
@@ -25,7 +27,9 @@ namespace Org.BouncyCastle.Bcpg
return null;
int bodyLen = 0;
- if (l < 192)
+ bool isLongLength = false;
+
+ if (l < 192)
{
bodyLen = l;
}
@@ -35,54 +39,90 @@ namespace Org.BouncyCastle.Bcpg
}
else if (l == 255)
{
+ isLongLength = true;
bodyLen = (input.ReadByte() << 24) | (input.ReadByte() << 16)
| (input.ReadByte() << 8) | input.ReadByte();
}
else
{
- // TODO Error?
+ throw new IOException("unexpected length header");
}
- int tag = input.ReadByte();
+ int tag = input.ReadByte();
if (tag < 0)
throw new EndOfStreamException("unexpected EOF reading signature sub packet");
- byte[] data = new byte[bodyLen - 1];
- if (Streams.ReadFully(input, data) < data.Length)
- throw new EndOfStreamException();
+ byte[] data = new byte[bodyLen - 1];
+
+ //
+ // this may seem a bit strange but it turns out some applications miscode the length
+ // in fixed length fields, so we check the length we do get, only throwing an exception if
+ // we really cannot continue
+ //
+ int bytesRead = Streams.ReadFully(input, data);
+
+ bool isCritical = ((tag & 0x80) != 0);
+ SignatureSubpacketTag type = (SignatureSubpacketTag)(tag & 0x7f);
- bool isCritical = ((tag & 0x80) != 0);
- SignatureSubpacketTag type = (SignatureSubpacketTag)(tag & 0x7f);
- switch (type)
+ if (bytesRead != data.Length)
+ {
+ switch (type)
+ {
+ case SignatureSubpacketTag.CreationTime:
+ data = CheckData(data, 4, bytesRead, "Signature Creation Time");
+ break;
+ case SignatureSubpacketTag.IssuerKeyId:
+ data = CheckData(data, 8, bytesRead, "Issuer");
+ break;
+ case SignatureSubpacketTag.KeyExpireTime:
+ data = CheckData(data, 4, bytesRead, "Signature Key Expiration Time");
+ break;
+ case SignatureSubpacketTag.ExpireTime:
+ data = CheckData(data, 4, bytesRead, "Signature Expiration Time");
+ break;
+ default:
+ throw new EndOfStreamException("truncated subpacket data.");
+ }
+ }
+
+ switch (type)
{
case SignatureSubpacketTag.CreationTime:
- return new SignatureCreationTime(isCritical, data);
+ return new SignatureCreationTime(isCritical, isLongLength, data);
case SignatureSubpacketTag.KeyExpireTime:
- return new KeyExpirationTime(isCritical, data);
+ return new KeyExpirationTime(isCritical, isLongLength, data);
case SignatureSubpacketTag.ExpireTime:
- return new SignatureExpirationTime(isCritical, data);
+ return new SignatureExpirationTime(isCritical, isLongLength, data);
case SignatureSubpacketTag.Revocable:
- return new Revocable(isCritical, data);
+ return new Revocable(isCritical, isLongLength, data);
case SignatureSubpacketTag.Exportable:
- return new Exportable(isCritical, data);
+ return new Exportable(isCritical, isLongLength, data);
case SignatureSubpacketTag.IssuerKeyId:
- return new IssuerKeyId(isCritical, data);
+ return new IssuerKeyId(isCritical, isLongLength, data);
case SignatureSubpacketTag.TrustSig:
- return new TrustSignature(isCritical, data);
+ return new TrustSignature(isCritical, isLongLength, data);
case SignatureSubpacketTag.PreferredCompressionAlgorithms:
case SignatureSubpacketTag.PreferredHashAlgorithms:
case SignatureSubpacketTag.PreferredSymmetricAlgorithms:
- return new PreferredAlgorithms(type, isCritical, data);
+ return new PreferredAlgorithms(type, isCritical, isLongLength, data);
case SignatureSubpacketTag.KeyFlags:
- return new KeyFlags(isCritical, data);
+ return new KeyFlags(isCritical, isLongLength, data);
case SignatureSubpacketTag.PrimaryUserId:
- return new PrimaryUserId(isCritical, data);
+ return new PrimaryUserId(isCritical, isLongLength, data);
case SignatureSubpacketTag.SignerUserId:
- return new SignerUserId(isCritical, data);
+ return new SignerUserId(isCritical, isLongLength, data);
case SignatureSubpacketTag.NotationData:
- return new NotationData(isCritical, data);
+ return new NotationData(isCritical, isLongLength, data);
}
- return new SignatureSubpacket(type, isCritical, data);
+ return new SignatureSubpacket(type, isCritical, isLongLength, data);
}
+
+ private byte[] CheckData(byte[] data, int expected, int bytesRead, string name)
+ {
+ if (bytesRead != expected)
+ throw new EndOfStreamException("truncated " + name + " subpacket data.");
+
+ return Arrays.CopyOfRange(data, 0, expected);
+ }
}
}
diff --git a/crypto/src/bcpg/sig/EmbeddedSignature.cs b/crypto/src/bcpg/sig/EmbeddedSignature.cs
index e47604ac8..fffdaef73 100644
--- a/crypto/src/bcpg/sig/EmbeddedSignature.cs
+++ b/crypto/src/bcpg/sig/EmbeddedSignature.cs
@@ -10,8 +10,9 @@ namespace Org.BouncyCastle.Bcpg.Sig
{
public EmbeddedSignature(
bool critical,
+ bool isLongLength,
byte[] data)
- : base(SignatureSubpacketTag.EmbeddedSignature, critical, data)
+ : base(SignatureSubpacketTag.EmbeddedSignature, critical, isLongLength, data)
{
}
}
diff --git a/crypto/src/bcpg/sig/Exportable.cs b/crypto/src/bcpg/sig/Exportable.cs
index 4455c3814..4d030346f 100644
--- a/crypto/src/bcpg/sig/Exportable.cs
+++ b/crypto/src/bcpg/sig/Exportable.cs
@@ -1,7 +1,5 @@
using System;
-
-
namespace Org.BouncyCastle.Bcpg.Sig
{
/**
@@ -27,15 +25,16 @@ namespace Org.BouncyCastle.Bcpg.Sig
public Exportable(
bool critical,
- byte[] data)
- : base(SignatureSubpacketTag.Exportable, critical, data)
+ bool isLongLength,
+ byte[] data)
+ : base(SignatureSubpacketTag.Exportable, critical, isLongLength, data)
{
}
public Exportable(
bool critical,
bool isExportable)
- : base(SignatureSubpacketTag.Exportable, critical, BooleanToByteArray(isExportable))
+ : base(SignatureSubpacketTag.Exportable, critical, false, BooleanToByteArray(isExportable))
{
}
diff --git a/crypto/src/bcpg/sig/Features.cs b/crypto/src/bcpg/sig/Features.cs
new file mode 100644
index 000000000..29584239a
--- /dev/null
+++ b/crypto/src/bcpg/sig/Features.cs
@@ -0,0 +1,75 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg.Sig
+{
+ /**
+ * packet giving signature expiration time.
+ */
+ public class Features
+ : SignatureSubpacket
+ {
+ /** Identifier for the modification detection feature */
+ public static readonly byte FEATURE_MODIFICATION_DETECTION = 1;
+
+ private static byte[] FeatureToByteArray(byte feature)
+ {
+ return new byte[]{ feature };
+ }
+
+ public Features(
+ bool critical,
+ bool isLongLength,
+ byte[] data)
+ : base(SignatureSubpacketTag.Features, critical, isLongLength, data)
+ {
+ }
+
+ public Features(bool critical, byte feature)
+ : base(SignatureSubpacketTag.Features, critical, false, FeatureToByteArray(feature))
+ {
+ }
+
+ /**
+ * Returns if modification detection is supported.
+ */
+ public bool SupportsModificationDetection
+ {
+ get { return SupportsFeature(FEATURE_MODIFICATION_DETECTION); }
+ }
+
+ /**
+ * Returns if a particular feature is supported.
+ */
+ public bool SupportsFeature(byte feature)
+ {
+ return Array.IndexOf(data, feature) >= 0;
+ }
+
+ /**
+ * Sets support for a particular feature.
+ */
+ private void SetSupportsFeature(byte feature, bool support)
+ {
+ if (feature == 0)
+ throw new ArgumentException("cannot be 0", "feature");
+
+ int i = Array.IndexOf(data, feature);
+ if ((i >= 0) == support)
+ return;
+
+ if (support)
+ {
+ data = Arrays.Append(data, feature);
+ }
+ else
+ {
+ byte[] temp = new byte[data.Length - 1];
+ Array.Copy(data, 0, temp, 0, i);
+ Array.Copy(data, i + 1, temp, i, temp.Length - i);
+ data = temp;
+ }
+ }
+ }
+}
diff --git a/crypto/src/bcpg/sig/IssuerKeyId.cs b/crypto/src/bcpg/sig/IssuerKeyId.cs
index 91490d33b..627ea3ecf 100644
--- a/crypto/src/bcpg/sig/IssuerKeyId.cs
+++ b/crypto/src/bcpg/sig/IssuerKeyId.cs
@@ -29,15 +29,16 @@ namespace Org.BouncyCastle.Bcpg.Sig
public IssuerKeyId(
bool critical,
- byte[] data)
- : base(SignatureSubpacketTag.IssuerKeyId, critical, data)
+ bool isLongLength,
+ byte[] data)
+ : base(SignatureSubpacketTag.IssuerKeyId, critical, isLongLength, data)
{
}
public IssuerKeyId(
bool critical,
- long keyId)
- : base(SignatureSubpacketTag.IssuerKeyId, critical, KeyIdToBytes(keyId))
+ long keyId)
+ : base(SignatureSubpacketTag.IssuerKeyId, critical, false, KeyIdToBytes(keyId))
{
}
diff --git a/crypto/src/bcpg/sig/KeyExpirationTime.cs b/crypto/src/bcpg/sig/KeyExpirationTime.cs
index 23b4cac29..dfd3e76fd 100644
--- a/crypto/src/bcpg/sig/KeyExpirationTime.cs
+++ b/crypto/src/bcpg/sig/KeyExpirationTime.cs
@@ -1,7 +1,5 @@
using System;
-
-
namespace Org.BouncyCastle.Bcpg.Sig
{
/**
@@ -25,15 +23,16 @@ namespace Org.BouncyCastle.Bcpg.Sig
public KeyExpirationTime(
bool critical,
- byte[] data)
- : base(SignatureSubpacketTag.KeyExpireTime, critical, data)
+ bool isLongLength,
+ byte[] data)
+ : base(SignatureSubpacketTag.KeyExpireTime, critical, isLongLength, data)
{
}
public KeyExpirationTime(
bool critical,
- long seconds)
- : base(SignatureSubpacketTag.KeyExpireTime, critical, TimeToBytes(seconds))
+ long seconds)
+ : base(SignatureSubpacketTag.KeyExpireTime, critical, false, TimeToBytes(seconds))
{
}
diff --git a/crypto/src/bcpg/sig/KeyFlags.cs b/crypto/src/bcpg/sig/KeyFlags.cs
index 0592301b3..5b5d85a72 100644
--- a/crypto/src/bcpg/sig/KeyFlags.cs
+++ b/crypto/src/bcpg/sig/KeyFlags.cs
@@ -40,15 +40,16 @@ namespace Org.BouncyCastle.Bcpg.Sig
public KeyFlags(
bool critical,
- byte[] data)
- : base(SignatureSubpacketTag.KeyFlags, critical, data)
+ bool isLongLength,
+ byte[] data)
+ : base(SignatureSubpacketTag.KeyFlags, critical, isLongLength, data)
{
}
public KeyFlags(
- bool critical,
- int flags)
- : base(SignatureSubpacketTag.KeyFlags, critical, IntToByteArray(flags))
+ bool critical,
+ int flags)
+ : base(SignatureSubpacketTag.KeyFlags, critical, false, IntToByteArray(flags))
{
}
diff --git a/crypto/src/bcpg/sig/NotationData.cs b/crypto/src/bcpg/sig/NotationData.cs
index ccc9aa745..9ac6f89cf 100644
--- a/crypto/src/bcpg/sig/NotationData.cs
+++ b/crypto/src/bcpg/sig/NotationData.cs
@@ -17,8 +17,9 @@ namespace Org.BouncyCastle.Bcpg.Sig
public NotationData(
bool critical,
- byte[] data)
- : base(SignatureSubpacketTag.NotationData, critical, data)
+ bool isLongLength,
+ byte[] data)
+ : base(SignatureSubpacketTag.NotationData, critical, isLongLength, data)
{
}
@@ -27,12 +28,12 @@ namespace Org.BouncyCastle.Bcpg.Sig
bool humanReadable,
string notationName,
string notationValue)
- : base(SignatureSubpacketTag.NotationData, critical,
- createData(humanReadable, notationName, notationValue))
+ : base(SignatureSubpacketTag.NotationData, critical, false,
+ CreateData(humanReadable, notationName, notationValue))
{
}
- private static byte[] createData(
+ private static byte[] CreateData(
bool humanReadable,
string notationName,
string notationValue)
diff --git a/crypto/src/bcpg/sig/PreferredAlgorithms.cs b/crypto/src/bcpg/sig/PreferredAlgorithms.cs
index 0f282a38c..9514bed2b 100644
--- a/crypto/src/bcpg/sig/PreferredAlgorithms.cs
+++ b/crypto/src/bcpg/sig/PreferredAlgorithms.cs
@@ -1,7 +1,5 @@
using System;
-
-
namespace Org.BouncyCastle.Bcpg.Sig
{
/**
@@ -24,24 +22,25 @@ namespace Org.BouncyCastle.Bcpg.Sig
}
public PreferredAlgorithms(
- SignatureSubpacketTag type,
- bool critical,
- byte[] data)
- : base(type, critical, data)
+ SignatureSubpacketTag type,
+ bool critical,
+ bool isLongLength,
+ byte[] data)
+ : base(type, critical, isLongLength, data)
{
}
public PreferredAlgorithms(
- SignatureSubpacketTag type,
- bool critical,
- int[] preferences)
- : base(type, critical, IntToByteArray(preferences))
+ SignatureSubpacketTag type,
+ bool critical,
+ int[] preferences)
+ : base(type, critical, false, IntToByteArray(preferences))
{
}
public int[] GetPreferences()
{
- int[] v = new int[data.Length];
+ int[] v = new int[data.Length];
for (int i = 0; i != v.Length; i++)
{
diff --git a/crypto/src/bcpg/sig/PrimaryUserId.cs b/crypto/src/bcpg/sig/PrimaryUserId.cs
index fc0353afd..1f16f40eb 100644
--- a/crypto/src/bcpg/sig/PrimaryUserId.cs
+++ b/crypto/src/bcpg/sig/PrimaryUserId.cs
@@ -1,7 +1,5 @@
using System;
-
-
namespace Org.BouncyCastle.Bcpg.Sig
{
/**
@@ -28,15 +26,16 @@ namespace Org.BouncyCastle.Bcpg.Sig
public PrimaryUserId(
bool critical,
- byte[] data)
- : base(SignatureSubpacketTag.PrimaryUserId, critical, data)
+ bool isLongLength,
+ byte[] data)
+ : base(SignatureSubpacketTag.PrimaryUserId, critical, isLongLength, data)
{
}
public PrimaryUserId(
bool critical,
bool isPrimaryUserId)
- : base(SignatureSubpacketTag.PrimaryUserId, critical, BooleanToByteArray(isPrimaryUserId))
+ : base(SignatureSubpacketTag.PrimaryUserId, critical, false, BooleanToByteArray(isPrimaryUserId))
{
}
diff --git a/crypto/src/bcpg/sig/Revocable.cs b/crypto/src/bcpg/sig/Revocable.cs
index b5e94feec..7aa91391f 100644
--- a/crypto/src/bcpg/sig/Revocable.cs
+++ b/crypto/src/bcpg/sig/Revocable.cs
@@ -1,7 +1,5 @@
using System;
-
-
namespace Org.BouncyCastle.Bcpg.Sig
{
/**
@@ -28,16 +26,17 @@ namespace Org.BouncyCastle.Bcpg.Sig
public Revocable(
bool critical,
- byte[] data)
- : base(SignatureSubpacketTag.Revocable, critical, data)
- {
+ bool isLongLength,
+ byte[] data)
+ : base(SignatureSubpacketTag.Revocable, critical, isLongLength, data)
+ {
}
public Revocable(
bool critical,
bool isRevocable)
- : base(SignatureSubpacketTag.Revocable, critical, BooleanToByteArray(isRevocable))
- {
+ : base(SignatureSubpacketTag.Revocable, critical, false, BooleanToByteArray(isRevocable))
+ {
}
public bool IsRevocable()
diff --git a/crypto/src/bcpg/sig/RevocationKey.cs b/crypto/src/bcpg/sig/RevocationKey.cs
index 66982cb5a..11467d2af 100644
--- a/crypto/src/bcpg/sig/RevocationKey.cs
+++ b/crypto/src/bcpg/sig/RevocationKey.cs
@@ -14,17 +14,18 @@ namespace Org.BouncyCastle.Bcpg
// 20 octets of fingerprint
public RevocationKey(
bool isCritical,
- byte[] data)
- : base(SignatureSubpacketTag.RevocationKey, isCritical, data)
+ bool isLongLength,
+ byte[] data)
+ : base(SignatureSubpacketTag.RevocationKey, isCritical, isLongLength, data)
{
}
- public RevocationKey(
+ public RevocationKey(
bool isCritical,
RevocationKeyTag signatureClass,
PublicKeyAlgorithmTag keyAlgorithm,
byte[] fingerprint)
- : base(SignatureSubpacketTag.RevocationKey, isCritical,
+ : base(SignatureSubpacketTag.RevocationKey, isCritical, false,
CreateData(signatureClass, keyAlgorithm, fingerprint))
{
}
diff --git a/crypto/src/bcpg/sig/RevocationReason.cs b/crypto/src/bcpg/sig/RevocationReason.cs
index 98e9b0a3d..42afd5f5b 100644
--- a/crypto/src/bcpg/sig/RevocationReason.cs
+++ b/crypto/src/bcpg/sig/RevocationReason.cs
@@ -11,16 +11,16 @@ namespace Org.BouncyCastle.Bcpg
public class RevocationReason
: SignatureSubpacket
{
- public RevocationReason(bool isCritical, byte[] data)
- : base(SignatureSubpacketTag.RevocationReason, isCritical, data)
+ public RevocationReason(bool isCritical, bool isLongLength, byte[] data)
+ : base(SignatureSubpacketTag.RevocationReason, isCritical, isLongLength, data)
{
}
- public RevocationReason(
- bool isCritical,
- RevocationReasonTag reason,
- string description)
- : base(SignatureSubpacketTag.RevocationReason, isCritical, CreateData(reason, description))
+ public RevocationReason(
+ bool isCritical,
+ RevocationReasonTag reason,
+ string description)
+ : base(SignatureSubpacketTag.RevocationReason, isCritical, false, CreateData(reason, description))
{
}
diff --git a/crypto/src/bcpg/sig/SignatureCreationTime.cs b/crypto/src/bcpg/sig/SignatureCreationTime.cs
index e6f241f11..d172e5d52 100644
--- a/crypto/src/bcpg/sig/SignatureCreationTime.cs
+++ b/crypto/src/bcpg/sig/SignatureCreationTime.cs
@@ -21,18 +21,22 @@ namespace Org.BouncyCastle.Bcpg.Sig
data[3] = (byte)t;
return data;
}
+
public SignatureCreationTime(
- bool critical,
- byte[] data)
- : base(SignatureSubpacketTag.CreationTime, critical, data)
+ bool critical,
+ bool isLongLength,
+ byte[] data)
+ : base(SignatureSubpacketTag.CreationTime, critical, isLongLength, data)
{
}
+
public SignatureCreationTime(
- bool critical,
- DateTime date)
- : base(SignatureSubpacketTag.CreationTime, critical, TimeToBytes(date))
+ bool critical,
+ DateTime date)
+ : base(SignatureSubpacketTag.CreationTime, critical, false, TimeToBytes(date))
{
}
+
public DateTime GetTime()
{
long time = (long)(
diff --git a/crypto/src/bcpg/sig/SignatureExpirationTime.cs b/crypto/src/bcpg/sig/SignatureExpirationTime.cs
index 7fddf5743..24f0a9f8a 100644
--- a/crypto/src/bcpg/sig/SignatureExpirationTime.cs
+++ b/crypto/src/bcpg/sig/SignatureExpirationTime.cs
@@ -1,7 +1,5 @@
using System;
-
-
namespace Org.BouncyCastle.Bcpg.Sig
{
/**
@@ -11,29 +9,28 @@ namespace Org.BouncyCastle.Bcpg.Sig
: SignatureSubpacket
{
protected static byte[] TimeToBytes(
- long t)
+ long t)
{
- byte[] data = new byte[4];
-
+ byte[] data = new byte[4];
data[0] = (byte)(t >> 24);
data[1] = (byte)(t >> 16);
data[2] = (byte)(t >> 8);
data[3] = (byte)t;
-
return data;
}
public SignatureExpirationTime(
bool critical,
- byte[] data)
- : base(SignatureSubpacketTag.ExpireTime, critical, data)
- {
+ bool isLongLength,
+ byte[] data)
+ : base(SignatureSubpacketTag.ExpireTime, critical, isLongLength, data)
+ {
}
public SignatureExpirationTime(
bool critical,
- long seconds)
- : base(SignatureSubpacketTag.ExpireTime, critical, TimeToBytes(seconds))
+ long seconds)
+ : base(SignatureSubpacketTag.ExpireTime, critical, false, TimeToBytes(seconds))
{
}
diff --git a/crypto/src/bcpg/sig/SignerUserId.cs b/crypto/src/bcpg/sig/SignerUserId.cs
index 98cc808e7..8ab62ed2e 100644
--- a/crypto/src/bcpg/sig/SignerUserId.cs
+++ b/crypto/src/bcpg/sig/SignerUserId.cs
@@ -24,20 +24,21 @@ namespace Org.BouncyCastle.Bcpg.Sig
}
public SignerUserId(
- bool critical,
- byte[] data)
- : base(SignatureSubpacketTag.SignerUserId, critical, data)
+ bool critical,
+ bool isLongLength,
+ byte[] data)
+ : base(SignatureSubpacketTag.SignerUserId, critical, isLongLength, data)
{
}
- public SignerUserId(
- bool critical,
- string userId)
- : base(SignatureSubpacketTag.SignerUserId, critical, UserIdToBytes(userId))
+ public SignerUserId(
+ bool critical,
+ string userId)
+ : base(SignatureSubpacketTag.SignerUserId, critical, false, UserIdToBytes(userId))
{
}
- public string GetId()
+ public string GetId()
{
char[] chars = new char[data.Length];
diff --git a/crypto/src/bcpg/sig/TrustSignature.cs b/crypto/src/bcpg/sig/TrustSignature.cs
index bbadd3067..91458826d 100644
--- a/crypto/src/bcpg/sig/TrustSignature.cs
+++ b/crypto/src/bcpg/sig/TrustSignature.cs
@@ -16,17 +16,18 @@ namespace Org.BouncyCastle.Bcpg.Sig
}
public TrustSignature(
- bool critical,
- byte[] data)
- : base(SignatureSubpacketTag.TrustSig, critical, data)
+ bool critical,
+ bool isLongLength,
+ byte[] data)
+ : base(SignatureSubpacketTag.TrustSig, critical, isLongLength, data)
{
}
public TrustSignature(
- bool critical,
- int depth,
- int trustAmount)
- : base(SignatureSubpacketTag.TrustSig, critical, IntToByteArray(depth, trustAmount))
+ bool critical,
+ int depth,
+ int trustAmount)
+ : base(SignatureSubpacketTag.TrustSig, critical, false, IntToByteArray(depth, trustAmount))
{
}
diff --git a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs
index f46f99d37..2a2e63961 100644
--- a/crypto/src/openpgp/PgpEncryptedDataGenerator.cs
+++ b/crypto/src/openpgp/PgpEncryptedDataGenerator.cs
@@ -3,10 +3,13 @@ using System.Collections;
using System.Diagnostics;
using System.IO;
+using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
@@ -79,70 +82,133 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
: EncMethod
{
internal PgpPublicKey pubKey;
- internal BigInteger[] data;
+ internal byte[][] data;
- internal PubMethod(
- PgpPublicKey pubKey)
+ internal PubMethod(PgpPublicKey pubKey)
{
this.pubKey = pubKey;
}
- public override void AddSessionInfo(
- byte[] si,
+ public override void AddSessionInfo(
+ byte[] sessionInfo,
SecureRandom random)
{
- IBufferedCipher c;
+ byte[] encryptedSessionInfo = EncryptSessionInfo(sessionInfo, random);
+
+ this.data = ProcessSessionInfo(encryptedSessionInfo);
+ }
- switch (pubKey.Algorithm)
+ private byte[] EncryptSessionInfo(byte[] sessionInfo, SecureRandom random)
+ {
+ if (pubKey.Algorithm != PublicKeyAlgorithmTag.ECDH)
{
- case PublicKeyAlgorithmTag.RsaEncrypt:
- case PublicKeyAlgorithmTag.RsaGeneral:
- c = CipherUtilities.GetCipher("RSA//PKCS1Padding");
- break;
- case PublicKeyAlgorithmTag.ElGamalEncrypt:
- case PublicKeyAlgorithmTag.ElGamalGeneral:
- c = CipherUtilities.GetCipher("ElGamal/ECB/PKCS1Padding");
- break;
- case PublicKeyAlgorithmTag.Dsa:
- throw new PgpException("Can't use DSA for encryption.");
- case PublicKeyAlgorithmTag.ECDsa:
- throw new PgpException("Can't use ECDSA for encryption.");
- default:
- throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm);
+ IBufferedCipher c;
+ switch (pubKey.Algorithm)
+ {
+ case PublicKeyAlgorithmTag.RsaEncrypt:
+ case PublicKeyAlgorithmTag.RsaGeneral:
+ c = CipherUtilities.GetCipher("RSA//PKCS1Padding");
+ break;
+ case PublicKeyAlgorithmTag.ElGamalEncrypt:
+ case PublicKeyAlgorithmTag.ElGamalGeneral:
+ c = CipherUtilities.GetCipher("ElGamal/ECB/PKCS1Padding");
+ break;
+ case PublicKeyAlgorithmTag.Dsa:
+ throw new PgpException("Can't use DSA for encryption.");
+ case PublicKeyAlgorithmTag.ECDsa:
+ throw new PgpException("Can't use ECDSA for encryption.");
+ default:
+ throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm);
+ }
+
+ AsymmetricKeyParameter akp = pubKey.GetKey();
+ c.Init(true, new ParametersWithRandom(akp, random));
+ return c.DoFinal(sessionInfo);
}
- AsymmetricKeyParameter akp = pubKey.GetKey();
+ ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKey.PublicKeyPacket.Key;
+
+ // Generate the ephemeral key pair
+ IAsymmetricCipherKeyPairGenerator gen = GeneratorUtilities.GetKeyPairGenerator("ECDH");
+ gen.Init(new ECKeyGenerationParameters(ecKey.CurveOid, random));
+
+ AsymmetricCipherKeyPair ephKp = gen.GenerateKeyPair();
+ ECPrivateKeyParameters ephPriv = (ECPrivateKeyParameters)ephKp.Private;
+ ECPublicKeyParameters ephPub = (ECPublicKeyParameters)ephKp.Public;
+
+ ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey.GetKey();
+ ECPoint S = pub.Q.Multiply(ephPriv.D).Normalize();
+
+ KeyParameter key = new KeyParameter(Rfc6637Utilities.CreateKey(pubKey.PublicKeyPacket, S));
+
+ IWrapper w = PgpUtilities.CreateWrapper(ecKey.SymmetricKeyAlgorithm);
+ w.Init(true, new ParametersWithRandom(key, random));
+
+ byte[] paddedSessionData = PgpPad.PadSessionData(sessionInfo);
+
+ byte[] C = w.Wrap(paddedSessionData, 0, paddedSessionData.Length);
+ byte[] VB = new MPInteger(new BigInteger(1, ephPub.Q.GetEncoded(false))).GetEncoded();
- c.Init(true, new ParametersWithRandom(akp, random));
+ byte[] rv = new byte[VB.Length + 1 + C.Length];
- byte[] encKey = c.DoFinal(si);
+ Array.Copy(VB, 0, rv, 0, VB.Length);
+ rv[VB.Length] = (byte)C.Length;
+ Array.Copy(C, 0, rv, VB.Length + 1, C.Length);
- switch (pubKey.Algorithm)
+ return rv;
+ }
+
+ private byte[][] ProcessSessionInfo(byte[] encryptedSessionInfo)
+ {
+ byte[][] data;
+
+ switch (pubKey.Algorithm)
{
- case PublicKeyAlgorithmTag.RsaEncrypt:
- case PublicKeyAlgorithmTag.RsaGeneral:
- data = new BigInteger[]{ new BigInteger(1, encKey) };
- break;
- case PublicKeyAlgorithmTag.ElGamalEncrypt:
- case PublicKeyAlgorithmTag.ElGamalGeneral:
- int halfLength = encKey.Length / 2;
- data = new BigInteger[]
- {
- new BigInteger(1, encKey, 0, halfLength),
- new BigInteger(1, encKey, halfLength, halfLength)
- };
- break;
- default:
- throw new PgpException("unknown asymmetric algorithm: " + encAlgorithm);
+ case PublicKeyAlgorithmTag.RsaEncrypt:
+ case PublicKeyAlgorithmTag.RsaGeneral:
+ data = new byte[][] { ConvertToEncodedMpi(encryptedSessionInfo) };
+ break;
+ case PublicKeyAlgorithmTag.ElGamalEncrypt:
+ case PublicKeyAlgorithmTag.ElGamalGeneral:
+ int halfLength = encryptedSessionInfo.Length / 2;
+ byte[] b1 = new byte[halfLength];
+ byte[] b2 = new byte[halfLength];
+
+ Array.Copy(encryptedSessionInfo, 0, b1, 0, halfLength);
+ Array.Copy(encryptedSessionInfo, halfLength, b2, 0, halfLength);
+
+ data = new byte[][] {
+ ConvertToEncodedMpi(b1),
+ ConvertToEncodedMpi(b2),
+ };
+ break;
+ case PublicKeyAlgorithmTag.ECDH:
+ data = new byte[][]{ encryptedSessionInfo };
+ break;
+ default:
+ throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm);
}
+
+ return data;
}
- public override void Encode(BcpgOutputStream pOut)
+ private byte[] ConvertToEncodedMpi(byte[] encryptedSessionInfo)
+ {
+ try
+ {
+ return new MPInteger(new BigInteger(1, encryptedSessionInfo)).GetEncoded();
+ }
+ catch (IOException e)
+ {
+ throw new PgpException("Invalid MPI encoding: " + e.Message, e);
+ }
+ }
+
+ public override void Encode(BcpgOutputStream pOut)
{
- PublicKeyEncSessionPacket pk = new PublicKeyEncSessionPacket(
- pubKey.KeyId, pubKey.Algorithm, data);
+ PublicKeyEncSessionPacket pk = new PublicKeyEncSessionPacket(pubKey.KeyId, pubKey.Algorithm, data);
- pOut.WritePacket(pk);
+ pOut.WritePacket(pk);
}
}
diff --git a/crypto/src/openpgp/PgpKeyPair.cs b/crypto/src/openpgp/PgpKeyPair.cs
index 6efb03a42..9cf78fa6f 100644
--- a/crypto/src/openpgp/PgpKeyPair.cs
+++ b/crypto/src/openpgp/PgpKeyPair.cs
@@ -34,7 +34,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
DateTime time)
{
this.pub = new PgpPublicKey(algorithm, pubKey, time);
- this.priv = new PgpPrivateKey(privKey, pub.KeyId);
+ this.priv = new PgpPrivateKey(pub.KeyId, pub.PublicKeyPacket, privKey);
}
/// <summary>Create a key pair from a PgpPrivateKey and a PgpPublicKey.</summary>
diff --git a/crypto/src/openpgp/PgpPad.cs b/crypto/src/openpgp/PgpPad.cs
new file mode 100644
index 000000000..48f7f2f44
--- /dev/null
+++ b/crypto/src/openpgp/PgpPad.cs
@@ -0,0 +1,45 @@
+using System;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+ /// <remarks>Padding functions.</remarks>
+ public sealed class PgpPad
+ {
+ private PgpPad()
+ {
+ }
+
+ public static byte[] PadSessionData(byte[] sessionInfo)
+ {
+ byte[] result = new byte[40];
+
+ Array.Copy(sessionInfo, 0, result, 0, sessionInfo.Length);
+
+ byte padValue = (byte)(result.Length - sessionInfo.Length);
+
+ for (int i = sessionInfo.Length; i != result.Length; i++)
+ {
+ result[i] = padValue;
+ }
+
+ return result;
+ }
+
+ public static byte[] UnpadSessionData(byte[] encoded)
+ {
+ byte padValue = encoded[encoded.Length - 1];
+
+ for (int i = encoded.Length - padValue; i != encoded.Length; i++)
+ {
+ if (encoded[i] != padValue)
+ throw new PgpException("bad padding found in session data");
+ }
+
+ byte[] taggedKey = new byte[encoded.Length - padValue];
+
+ Array.Copy(encoded, 0, taggedKey, 0, taggedKey.Length);
+
+ return taggedKey;
+ }
+ }
+}
diff --git a/crypto/src/openpgp/PgpPrivateKey.cs b/crypto/src/openpgp/PgpPrivateKey.cs
index 154c87cd7..61487a5b2 100644
--- a/crypto/src/openpgp/PgpPrivateKey.cs
+++ b/crypto/src/openpgp/PgpPrivateKey.cs
@@ -7,33 +7,42 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
/// <remarks>General class to contain a private key for use with other OpenPGP objects.</remarks>
public class PgpPrivateKey
{
- private readonly long keyId;
+ private readonly long keyID;
+ private readonly PublicKeyPacket publicKeyPacket;
private readonly AsymmetricKeyParameter privateKey;
- /// <summary>
- /// Create a PgpPrivateKey from a regular private key and the ID of its
- /// associated public key.
+ /// <summary>
+ /// Create a PgpPrivateKey from a keyID, the associated public data packet, and a regular private key.
/// </summary>
- /// <param name="privateKey">Private key to use.</param>
- /// <param name="keyId">ID of the corresponding public key.</param>
- public PgpPrivateKey(
- AsymmetricKeyParameter privateKey,
- long keyId)
+ /// <param name="keyID">ID of the corresponding public key.</param>
+ /// <param name="publicKeyPacket">the public key data packet to be associated with this private key.</param>
+ /// <param name="privateKey">the private key data packet to be associated with this private key.</param>
+ public PgpPrivateKey(
+ long keyID,
+ PublicKeyPacket publicKeyPacket,
+ AsymmetricKeyParameter privateKey)
{
if (!privateKey.IsPrivate)
throw new ArgumentException("Expected a private key", "privateKey");
- this.privateKey = privateKey;
- this.keyId = keyId;
+ this.keyID = keyID;
+ this.publicKeyPacket = publicKeyPacket;
+ this.privateKey = privateKey;
}
- /// <summary>The keyId associated with the contained private key.</summary>
+ /// <summary>The keyId associated with the contained private key.</summary>
public long KeyId
{
- get { return keyId; }
+ get { return keyID; }
}
- /// <summary>The contained private key.</summary>
+ /// <summary>The public key packet associated with this private key, if available.</summary>
+ public PublicKeyPacket PublicKeyPacket
+ {
+ get { return publicKeyPacket; }
+ }
+
+ /// <summary>The contained private key.</summary>
public AsymmetricKeyParameter Key
{
get { return privateKey; }
diff --git a/crypto/src/openpgp/PgpPublicKey.cs b/crypto/src/openpgp/PgpPublicKey.cs
index 5bde2c8fe..904e29913 100644
--- a/crypto/src/openpgp/PgpPublicKey.cs
+++ b/crypto/src/openpgp/PgpPublicKey.cs
@@ -2,10 +2,14 @@ using System;
using System.Collections;
using System.IO;
+using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Collections;
@@ -15,6 +19,54 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
/// <remarks>General class to handle a PGP public key object.</remarks>
public class PgpPublicKey
{
+ public static byte[] CalculateFingerprint(PublicKeyPacket publicPk)
+ {
+ IBcpgKey key = publicPk.Key;
+ IDigest digest;
+
+ if (publicPk.Version <= 3)
+ {
+ RsaPublicBcpgKey rK = (RsaPublicBcpgKey)key;
+
+ try
+ {
+ digest = DigestUtilities.GetDigest("MD5");
+ UpdateDigest(digest, rK.Modulus);
+ UpdateDigest(digest, rK.PublicExponent);
+ }
+ catch (Exception e)
+ {
+ throw new PgpException("can't encode key components: " + e.Message, e);
+ }
+ }
+ else
+ {
+ try
+ {
+ byte[] kBytes = publicPk.GetEncodedContents();
+
+ digest = DigestUtilities.GetDigest("SHA1");
+
+ digest.Update(0x99);
+ digest.Update((byte)(kBytes.Length >> 8));
+ digest.Update((byte)kBytes.Length);
+ digest.BlockUpdate(kBytes, 0, kBytes.Length);
+ }
+ catch (Exception e)
+ {
+ throw new PgpException("can't encode key components: " + e.Message, e);
+ }
+ }
+
+ return DigestUtilities.DoFinal(digest);
+ }
+
+ private static void UpdateDigest(IDigest d, BigInteger b)
+ {
+ byte[] bytes = b.ToByteArrayUnsigned();
+ d.BlockUpdate(bytes, 0, bytes.Length);
+ }
+
private static readonly int[] MasterKeyCertificationTypes = new int[]
{
PgpSignature.PositiveCertification,
@@ -39,51 +91,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
{
IBcpgKey key = publicPk.Key;
+ this.fingerprint = CalculateFingerprint(publicPk);
+
if (publicPk.Version <= 3)
{
RsaPublicBcpgKey rK = (RsaPublicBcpgKey) key;
this.keyId = rK.Modulus.LongValue;
-
- try
- {
- IDigest digest = DigestUtilities.GetDigest("MD5");
-
- byte[] bytes = rK.Modulus.ToByteArrayUnsigned();
- digest.BlockUpdate(bytes, 0, bytes.Length);
-
- bytes = rK.PublicExponent.ToByteArrayUnsigned();
- digest.BlockUpdate(bytes, 0, bytes.Length);
-
- this.fingerprint = DigestUtilities.DoFinal(digest);
- }
- //catch (NoSuchAlgorithmException)
- catch (Exception e)
- {
- throw new IOException("can't find MD5", e);
- }
-
this.keyStrength = rK.Modulus.BitLength;
}
else
{
- byte[] kBytes = publicPk.GetEncodedContents();
-
- try
- {
- IDigest digest = DigestUtilities.GetDigest("SHA1");
-
- digest.Update(0x99);
- digest.Update((byte)(kBytes.Length >> 8));
- digest.Update((byte)kBytes.Length);
- digest.BlockUpdate(kBytes, 0, kBytes.Length);
- this.fingerprint = DigestUtilities.DoFinal(digest);
- }
- catch (Exception e)
- {
- throw new IOException("can't find SHA1", e);
- }
-
this.keyId = (long)(((ulong)fingerprint[fingerprint.Length - 8] << 56)
| ((ulong)fingerprint[fingerprint.Length - 7] << 48)
| ((ulong)fingerprint[fingerprint.Length - 6] << 40)
@@ -107,7 +125,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
}
else if (key is ECPublicBcpgKey)
{
- this.keyStrength = ECNamedCurveTable.GetByOid(((ECPublicBcpgKey)key).CurveOid).Curve.FieldSize;
+ this.keyStrength = ECKeyPairGenerator.FindECCurveByOid(((ECPublicBcpgKey)key).CurveOid).Curve.FieldSize;
}
}
}
@@ -146,6 +164,23 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
bcpgKey = new DsaPublicBcpgKey(dP.P, dP.Q, dP.G, dK.Y);
}
+ else if (pubKey is ECPublicKeyParameters)
+ {
+ ECPublicKeyParameters ecK = (ECPublicKeyParameters)pubKey;
+
+ if (algorithm == PublicKeyAlgorithmTag.ECDH)
+ {
+ bcpgKey = new ECDHPublicBcpgKey(ecK.PublicKeyParamSet, ecK.Q, HashAlgorithmTag.Sha256, SymmetricKeyAlgorithmTag.Aes128);
+ }
+ else if (algorithm == PublicKeyAlgorithmTag.ECDsa)
+ {
+ bcpgKey = new ECDsaPublicBcpgKey(ecK.PublicKeyParamSet, ecK.Q);
+ }
+ else
+ {
+ throw new PgpException("unknown EC algorithm");
+ }
+ }
else if (pubKey is ElGamalPublicKeyParameters)
{
ElGamalPublicKeyParameters eK = (ElGamalPublicKeyParameters) pubKey;
@@ -172,6 +207,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
}
}
+ public PgpPublicKey(PublicKeyPacket publicPk)
+ : this(publicPk, Platform.CreateArrayList(), Platform.CreateArrayList())
+ {
+ }
+
/// <summary>Constructor for a sub-key.</summary>
internal PgpPublicKey(
PublicKeyPacket publicPk,
@@ -426,14 +466,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
case PublicKeyAlgorithmTag.RsaEncrypt:
case PublicKeyAlgorithmTag.RsaGeneral:
case PublicKeyAlgorithmTag.RsaSign:
- RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey) publicPk.Key;
+ RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey)publicPk.Key;
return new RsaKeyParameters(false, rsaK.Modulus, rsaK.PublicExponent);
case PublicKeyAlgorithmTag.Dsa:
- DsaPublicBcpgKey dsaK = (DsaPublicBcpgKey) publicPk.Key;
+ DsaPublicBcpgKey dsaK = (DsaPublicBcpgKey)publicPk.Key;
return new DsaPublicKeyParameters(dsaK.Y, new DsaParameters(dsaK.P, dsaK.Q, dsaK.G));
+ case PublicKeyAlgorithmTag.ECDsa:
+ return GetECKey("ECDSA");
+ case PublicKeyAlgorithmTag.ECDH:
+ return GetECKey("ECDH");
case PublicKeyAlgorithmTag.ElGamalEncrypt:
case PublicKeyAlgorithmTag.ElGamalGeneral:
- ElGamalPublicBcpgKey elK = (ElGamalPublicBcpgKey) publicPk.Key;
+ ElGamalPublicBcpgKey elK = (ElGamalPublicBcpgKey)publicPk.Key;
return new ElGamalPublicKeyParameters(elK.Y, new ElGamalParameters(elK.P, elK.G));
default:
throw new PgpException("unknown public key algorithm encountered");
@@ -449,6 +493,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
}
}
+ private ECPublicKeyParameters GetECKey(string algorithm)
+ {
+ ECPublicBcpgKey ecK = (ECPublicBcpgKey)publicPk.Key;
+ X9ECParameters x9 = ECKeyPairGenerator.FindECCurveByOid(ecK.CurveOid);
+ ECPoint q = x9.Curve.DecodePoint(BigIntegers.AsUnsignedByteArray(ecK.EncodedPoint));
+ return new ECPublicKeyParameters(algorithm, q, ecK.CurveOid);
+ }
+
/// <summary>Allows enumeration of any user IDs associated with the key.</summary>
/// <returns>An <c>IEnumerable</c> of <c>string</c> objects.</returns>
public IEnumerable GetUserIds()
diff --git a/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs b/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
index b6504cbcd..c2a351182 100644
--- a/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
+++ b/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
@@ -1,10 +1,13 @@
using System;
using System.IO;
+using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO;
@@ -77,22 +80,29 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
public SymmetricKeyAlgorithmTag GetSymmetricAlgorithm(
PgpPrivateKey privKey)
{
- byte[] plain = fetchSymmetricKeyData(privKey);
+ byte[] sessionData = RecoverSessionData(privKey);
- return (SymmetricKeyAlgorithmTag) plain[0];
+ return (SymmetricKeyAlgorithmTag)sessionData[0];
}
- /// <summary>Return the decrypted data stream for the packet.</summary>
+ /// <summary>Return the decrypted data stream for the packet.</summary>
public Stream GetDataStream(
PgpPrivateKey privKey)
{
- byte[] plain = fetchSymmetricKeyData(privKey);
+ byte[] sessionData = RecoverSessionData(privKey);
- IBufferedCipher c2;
- string cipherName = PgpUtilities.GetSymmetricCipherName((SymmetricKeyAlgorithmTag) plain[0]);
+ if (!ConfirmCheckSum(sessionData))
+ throw new PgpKeyValidationException("key checksum failed");
+
+ SymmetricKeyAlgorithmTag symmAlg = (SymmetricKeyAlgorithmTag)sessionData[0];
+ if (symmAlg == SymmetricKeyAlgorithmTag.Null)
+ return encData.GetInputStream();
+
+ IBufferedCipher cipher;
+ string cipherName = PgpUtilities.GetSymmetricCipherName(symmAlg);
string cName = cipherName;
- try
+ try
{
if (encData is SymmetricEncIntegrityPacket)
{
@@ -103,7 +113,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
cName += "/OpenPGPCFB/NoPadding";
}
- c2 = CipherUtilities.GetCipher(cName);
+ cipher = CipherUtilities.GetCipher(cName);
}
catch (PgpException e)
{
@@ -114,19 +124,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
throw new PgpException("exception creating cipher", e);
}
- if (c2 == null)
- return encData.GetInputStream();
-
- try
+ try
{
KeyParameter key = ParameterUtilities.CreateKeyParameter(
- cipherName, plain, 1, plain.Length - 3);
+ cipherName, sessionData, 1, sessionData.Length - 3);
- byte[] iv = new byte[c2.GetBlockSize()];
+ byte[] iv = new byte[cipher.GetBlockSize()];
- c2.Init(false, new ParametersWithIV(key, iv));
+ cipher.Init(false, new ParametersWithIV(key, iv));
- encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), c2, null));
+ encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), cipher, null));
if (encData is SymmetricEncIntegrityPacket)
{
@@ -178,75 +185,88 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
}
}
- private byte[] fetchSymmetricKeyData(
- PgpPrivateKey privKey)
+ private byte[] RecoverSessionData(PgpPrivateKey privKey)
{
- IBufferedCipher c1 = GetKeyCipher(keyData.Algorithm);
+ byte[][] secKeyData = keyData.GetEncSessionKey();
+
+ if (keyData.Algorithm == PublicKeyAlgorithmTag.ECDH)
+ {
+ ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)privKey.PublicKeyPacket.Key;
+ X9ECParameters x9Params = ECKeyPairGenerator.FindECCurveByOid(ecKey.CurveOid);
+
+ byte[] enc = secKeyData[0];
+
+ int pLen = ((((enc[0] & 0xff) << 8) + (enc[1] & 0xff)) + 7) / 8;
+ byte[] pEnc = new byte[pLen];
+
+ Array.Copy(enc, 2, pEnc, 0, pLen);
+
+ byte[] keyEnc = new byte[enc[pLen + 2]];
+
+ Array.Copy(enc, 2 + pLen + 1, keyEnc, 0, keyEnc.Length);
+
+ ECPoint publicPoint = x9Params.Curve.DecodePoint(pEnc);
+
+ ECPrivateKeyParameters privKeyParams = (ECPrivateKeyParameters)privKey.Key;
+ ECPoint S = publicPoint.Multiply(privKeyParams.D).Normalize();
+
+ KeyParameter key = new KeyParameter(Rfc6637Utilities.CreateKey(privKey.PublicKeyPacket, S));
+
+ IWrapper w = PgpUtilities.CreateWrapper(ecKey.SymmetricKeyAlgorithm);
+ w.Init(false, key);
- try
+ return PgpPad.UnpadSessionData(w.Unwrap(keyEnc, 0, keyEnc.Length));
+ }
+
+ IBufferedCipher cipher = GetKeyCipher(keyData.Algorithm);
+
+ try
{
- c1.Init(false, privKey.Key);
+ cipher.Init(false, privKey.Key);
}
catch (InvalidKeyException e)
{
throw new PgpException("error setting asymmetric cipher", e);
}
- BigInteger[] keyD = keyData.GetEncSessionKey();
-
- if (keyData.Algorithm == PublicKeyAlgorithmTag.RsaEncrypt
+ if (keyData.Algorithm == PublicKeyAlgorithmTag.RsaEncrypt
|| keyData.Algorithm == PublicKeyAlgorithmTag.RsaGeneral)
{
- c1.ProcessBytes(keyD[0].ToByteArrayUnsigned());
+ byte[] bi = secKeyData[0];
+
+ cipher.ProcessBytes(bi, 2, bi.Length - 2);
}
else
{
ElGamalPrivateKeyParameters k = (ElGamalPrivateKeyParameters)privKey.Key;
int size = (k.Parameters.P.BitLength + 7) / 8;
- byte[] bi = keyD[0].ToByteArray();
-
- int diff = bi.Length - size;
- if (diff >= 0)
- {
- c1.ProcessBytes(bi, diff, size);
- }
- else
- {
- byte[] zeros = new byte[-diff];
- c1.ProcessBytes(zeros);
- c1.ProcessBytes(bi);
- }
-
- bi = keyD[1].ToByteArray();
-
- diff = bi.Length - size;
- if (diff >= 0)
- {
- c1.ProcessBytes(bi, diff, size);
- }
- else
- {
- byte[] zeros = new byte[-diff];
- c1.ProcessBytes(zeros);
- c1.ProcessBytes(bi);
- }
+ ProcessEncodedMpi(cipher, size, secKeyData[0]);
+ ProcessEncodedMpi(cipher, size, secKeyData[1]);
}
- byte[] plain;
- try
+ try
{
- plain = c1.DoFinal();
+ return cipher.DoFinal();
}
catch (Exception e)
{
throw new PgpException("exception decrypting secret key", e);
}
-
- if (!ConfirmCheckSum(plain))
- throw new PgpKeyValidationException("key checksum failed");
-
- return plain;
}
+
+ private static void ProcessEncodedMpi(IBufferedCipher cipher, int size, byte[] mpiEnc)
+ {
+ if (mpiEnc.Length - 2 > size) // leading Zero? Shouldn't happen but...
+ {
+ cipher.ProcessBytes(mpiEnc, 3, mpiEnc.Length - 3);
+ }
+ else
+ {
+ byte[] tmp = new byte[size];
+ Array.Copy(mpiEnc, 2, tmp, tmp.Length - (mpiEnc.Length - 2), mpiEnc.Length - 2);
+ cipher.ProcessBytes(tmp, 0, tmp.Length);
+ }
+ }
}
}
diff --git a/crypto/src/openpgp/PgpSecretKey.cs b/crypto/src/openpgp/PgpSecretKey.cs
index 872316dd7..980f9222b 100644
--- a/crypto/src/openpgp/PgpSecretKey.cs
+++ b/crypto/src/openpgp/PgpSecretKey.cs
@@ -2,8 +2,11 @@ using System;
using System.Collections;
using System.IO;
+using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
@@ -59,6 +62,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
DsaPrivateKeyParameters dsK = (DsaPrivateKeyParameters) privKey.Key;
secKey = new DsaSecretBcpgKey(dsK.X);
break;
+ case PublicKeyAlgorithmTag.ECDH:
+ case PublicKeyAlgorithmTag.ECDsa:
+ ECPrivateKeyParameters ecK = (ECPrivateKeyParameters)privKey.Key;
+ secKey = new ECSecretBcpgKey(ecK.D);
+ break;
case PublicKeyAlgorithmTag.ElGamalEncrypt:
case PublicKeyAlgorithmTag.ElGamalGeneral:
ElGamalPrivateKeyParameters esK = (ElGamalPrivateKeyParameters) privKey.Key;
@@ -309,24 +317,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
private byte[] ExtractKeyData(
char[] passPhrase)
{
- SymmetricKeyAlgorithmTag alg = secret.EncAlgorithm;
+ SymmetricKeyAlgorithmTag encAlgorithm = secret.EncAlgorithm;
byte[] encData = secret.GetSecretKeyData();
- if (alg == SymmetricKeyAlgorithmTag.Null)
+ if (encAlgorithm == SymmetricKeyAlgorithmTag.Null)
// TODO Check checksum here?
return encData;
- IBufferedCipher c = null;
- try
- {
- string cName = PgpUtilities.GetSymmetricCipherName(alg);
- c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding");
- }
- catch (Exception e)
- {
- throw new PgpException("Exception creating cipher", e);
- }
-
// TODO Factor this block out as 'decryptData'
try
{
@@ -336,9 +333,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
if (secret.PublicKeyPacket.Version >= 4)
{
- c.Init(false, new ParametersWithIV(key, iv));
-
- data = c.DoFinal(encData);
+ data = RecoverKeyData(encAlgorithm, "/CFB/NoPadding", key, iv, encData, 0, encData.Length);
bool useSha1 = secret.S2kUsage == SecretKeyPacket.UsageSha1;
byte[] check = Checksum(useSha1, data, (useSha1) ? data.Length - 20 : data.Length - 2);
@@ -364,15 +359,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
for (int i = 0; i != 4; i++)
{
- c.Init(false, new ParametersWithIV(key, iv));
-
int encLen = (((encData[pos] << 8) | (encData[pos + 1] & 0xff)) + 7) / 8;
data[pos] = encData[pos];
data[pos + 1] = encData[pos + 1];
pos += 2;
- c.DoFinal(encData, pos, encLen, data, pos);
+ byte[] tmp = RecoverKeyData(encAlgorithm, "/CFB/NoPadding", key, iv, encData, pos, encLen);
+ Array.Copy(tmp, 0, data, pos, encLen);
pos += encLen;
if (i != 3)
@@ -416,6 +410,25 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
}
}
+ private static byte[] RecoverKeyData(SymmetricKeyAlgorithmTag encAlgorithm, string modeAndPadding,
+ KeyParameter key, byte[] iv, byte[] keyData, int keyOff, int keyLen)
+ {
+ IBufferedCipher c;
+ try
+ {
+ string cName = PgpUtilities.GetSymmetricCipherName(encAlgorithm);
+ c = CipherUtilities.GetCipher(cName + modeAndPadding);
+ }
+ catch (Exception e)
+ {
+ throw new PgpException("Exception creating cipher", e);
+ }
+
+ c.Init(false, new ParametersWithIV(key, iv));
+
+ return c.DoFinal(keyData, keyOff, keyLen);
+ }
+
/// <summary>Extract a <c>PgpPrivateKey</c> from this secret key's encrypted contents.</summary>
public PgpPrivateKey ExtractPrivateKey(
char[] passPhrase)
@@ -453,6 +466,12 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
DsaParameters dsaParams = new DsaParameters(dsaPub.P, dsaPub.Q, dsaPub.G);
privateKey = new DsaPrivateKeyParameters(dsaPriv.X, dsaParams);
break;
+ case PublicKeyAlgorithmTag.ECDH:
+ privateKey = GetECKey("ECDH", bcpgIn);
+ break;
+ case PublicKeyAlgorithmTag.ECDsa:
+ privateKey = GetECKey("ECDSA", bcpgIn);
+ break;
case PublicKeyAlgorithmTag.ElGamalEncrypt:
case PublicKeyAlgorithmTag.ElGamalGeneral:
ElGamalPublicBcpgKey elPub = (ElGamalPublicBcpgKey)pubPk.Key;
@@ -464,7 +483,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
throw new PgpException("unknown public key algorithm encountered");
}
- return new PgpPrivateKey(privateKey, KeyId);
+ return new PgpPrivateKey(KeyId, pubPk, privateKey);
}
catch (PgpException e)
{
@@ -476,6 +495,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
}
}
+ private ECPrivateKeyParameters GetECKey(string algorithm, BcpgInputStream bcpgIn)
+ {
+ ECPublicBcpgKey ecdsaPub = (ECPublicBcpgKey)secret.PublicKeyPacket.Key;
+ ECSecretBcpgKey ecdsaPriv = new ECSecretBcpgKey(bcpgIn);
+ return new ECPrivateKeyParameters(algorithm, ecdsaPriv.X, ecdsaPub.CurveOid);
+ }
+
private static byte[] Checksum(
bool useSha1,
byte[] bytes,
@@ -698,5 +724,174 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
return c.DoFinal(rawKeyData);
}
+
+ /**
+ * Parse a secret key from one of the GPG S expression keys associating it with the passed in public key.
+ *
+ * @return a secret key object.
+ */
+ public static PgpSecretKey ParseSecretKeyFromSExpr(Stream inputStream, char[] passPhrase, PgpPublicKey pubKey)
+ {
+ SXprUtilities.SkipOpenParenthesis(inputStream);
+
+ string type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+ if (type.Equals("protected-private-key"))
+ {
+ SXprUtilities.SkipOpenParenthesis(inputStream);
+
+ string curveName;
+
+ string keyType = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+ if (keyType.Equals("ecc"))
+ {
+ SXprUtilities.SkipOpenParenthesis(inputStream);
+
+ string curveID = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+ curveName = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+
+ SXprUtilities.SkipCloseParenthesis(inputStream);
+ }
+ else
+ {
+ throw new PgpException("no curve details found");
+ }
+
+ byte[] qVal;
+
+ SXprUtilities.SkipOpenParenthesis(inputStream);
+
+ type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+ if (type.Equals("q"))
+ {
+ qVal = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
+ }
+ else
+ {
+ throw new PgpException("no q value found");
+ }
+
+ SXprUtilities.SkipCloseParenthesis(inputStream);
+
+ byte[] dValue = GetDValue(inputStream, passPhrase, curveName);
+ // TODO: check SHA-1 hash.
+
+ return new PgpSecretKey(new SecretKeyPacket(pubKey.PublicKeyPacket, SymmetricKeyAlgorithmTag.Null, null, null,
+ new ECSecretBcpgKey(new BigInteger(1, dValue)).GetEncoded()), pubKey);
+ }
+
+ throw new PgpException("unknown key type found");
+ }
+
+ /**
+ * Parse a secret key from one of the GPG S expression keys.
+ *
+ * @return a secret key object.
+ */
+ public static PgpSecretKey ParseSecretKeyFromSExpr(Stream inputStream, char[] passPhrase)
+ {
+ SXprUtilities.SkipOpenParenthesis(inputStream);
+
+ string type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+ if (type.Equals("protected-private-key"))
+ {
+ SXprUtilities.SkipOpenParenthesis(inputStream);
+
+ string curveName;
+
+ string keyType = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+ if (keyType.Equals("ecc"))
+ {
+ SXprUtilities.SkipOpenParenthesis(inputStream);
+
+ string curveID = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+ curveName = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+
+ if (curveName.StartsWith("NIST "))
+ {
+ curveName = curveName.Substring("NIST ".Length);
+ }
+
+ SXprUtilities.SkipCloseParenthesis(inputStream);
+ }
+ else
+ {
+ throw new PgpException("no curve details found");
+ }
+
+ byte[] qVal;
+
+ SXprUtilities.SkipOpenParenthesis(inputStream);
+
+ type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+ if (type.Equals("q"))
+ {
+ qVal = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
+ }
+ else
+ {
+ throw new PgpException("no q value found");
+ }
+
+ PublicKeyPacket pubPacket = new PublicKeyPacket(PublicKeyAlgorithmTag.ECDsa, DateTime.UtcNow,
+ new ECDsaPublicBcpgKey(ECNamedCurveTable.GetOid(curveName), new BigInteger(1, qVal)));
+
+ SXprUtilities.SkipCloseParenthesis(inputStream);
+
+ byte[] dValue = GetDValue(inputStream, passPhrase, curveName);
+ // TODO: check SHA-1 hash.
+
+ return new PgpSecretKey(new SecretKeyPacket(pubPacket, SymmetricKeyAlgorithmTag.Null, null, null,
+ new ECSecretBcpgKey(new BigInteger(1, dValue)).GetEncoded()), new PgpPublicKey(pubPacket));
+ }
+
+ throw new PgpException("unknown key type found");
+ }
+
+ private static byte[] GetDValue(Stream inputStream, char[] passPhrase, string curveName)
+ {
+ string type;
+ SXprUtilities.SkipOpenParenthesis(inputStream);
+
+ string protection;
+ S2k s2k;
+ byte[] iv;
+ byte[] secKeyData;
+
+ type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+ if (type.Equals("protected"))
+ {
+ protection = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
+
+ SXprUtilities.SkipOpenParenthesis(inputStream);
+
+ s2k = SXprUtilities.ParseS2k(inputStream);
+
+ iv = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
+
+ SXprUtilities.SkipCloseParenthesis(inputStream);
+
+ secKeyData = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
+ }
+ else
+ {
+ throw new PgpException("protected block not found");
+ }
+
+ // TODO: recognise other algorithms
+ KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag.Aes128, s2k, passPhrase);
+
+ byte[] data = RecoverKeyData(SymmetricKeyAlgorithmTag.Aes128, "/CBC/NoPadding", key, iv, secKeyData, 0, secKeyData.Length);
+
+ //
+ // parse the secret key S-expr
+ //
+ Stream keyIn = new MemoryStream(data, false);
+
+ SXprUtilities.SkipOpenParenthesis(keyIn);
+ SXprUtilities.SkipOpenParenthesis(keyIn);
+ SXprUtilities.SkipOpenParenthesis(keyIn);
+ String name = SXprUtilities.ReadString(keyIn, keyIn.ReadByte());
+ return SXprUtilities.ReadBytes(keyIn, keyIn.ReadByte());
+ }
}
}
diff --git a/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs b/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs
index 4adf64012..d2177d09c 100644
--- a/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs
+++ b/crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs
@@ -25,7 +25,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
list.Add(new Exportable(isCritical, isExportable));
}
- /// <summary>
+ public void SetFeature(
+ bool isCritical,
+ byte feature)
+ {
+ list.Add(new Features(isCritical, feature));
+ }
+
+ /// <summary>
/// Add a TrustSignature packet to the signature. The values for depth and trust are largely
/// installation dependent but there are some guidelines in RFC 4880 - 5.2.3.13.
/// </summary>
@@ -117,7 +124,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
list.Add(new SignerUserId(isCritical, userId));
}
- public void SetEmbeddedSignature(
+ public void SetSignerUserId(
+ bool isCritical,
+ byte[] rawUserId)
+ {
+ if (rawUserId == null)
+ throw new ArgumentNullException("rawUserId");
+
+ list.Add(new SignerUserId(isCritical, false, rawUserId));
+ }
+
+ public void SetEmbeddedSignature(
bool isCritical,
PgpSignature pgpSignature)
{
@@ -136,7 +153,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
Array.Copy(sig, sig.Length - data.Length, data, 0, data.Length);
- list.Add(new EmbeddedSignature(isCritical, data));
+ list.Add(new EmbeddedSignature(isCritical, false, data));
}
public void SetPrimaryUserId(
diff --git a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
index 68fe4b594..156243f4e 100644
--- a/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
+++ b/crypto/src/openpgp/PgpSignatureSubpacketVector.cs
@@ -209,7 +209,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
return list;
}
- [Obsolete("Use 'Count' property instead")]
+ public Features GetFeatures()
+ {
+ SignatureSubpacket p = this.GetSubpacket(SignatureSubpacketTag.Features);
+
+ if (p == null)
+ return null;
+
+ return new Features(p.IsCritical(), p.IsLongLength(), p.GetData());
+ }
+
+ [Obsolete("Use 'Count' property instead")]
public int Size
{
get { return packets.Length; }
diff --git a/crypto/src/openpgp/PgpUtilities.cs b/crypto/src/openpgp/PgpUtilities.cs
index 32e37b819..e4551db07 100644
--- a/crypto/src/openpgp/PgpUtilities.cs
+++ b/crypto/src/openpgp/PgpUtilities.cs
@@ -86,7 +86,13 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
case PublicKeyAlgorithmTag.Dsa:
encAlg = "DSA";
break;
- case PublicKeyAlgorithmTag.ElGamalEncrypt: // in some malformed cases.
+ case PublicKeyAlgorithmTag.ECDH:
+ encAlg = "ECDH";
+ break;
+ case PublicKeyAlgorithmTag.ECDsa:
+ encAlg = "ECDSA";
+ break;
+ case PublicKeyAlgorithmTag.ElGamalEncrypt: // in some malformed cases.
case PublicKeyAlgorithmTag.ElGamalGeneral:
encAlg = "ElGamal";
break;
@@ -135,7 +141,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
}
}
- public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm)
+ public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm)
{
int keySize;
switch (algorithm)
@@ -193,7 +199,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
char[] passPhrase)
{
int keySize = GetKeySize(algorithm);
- byte[] pBytes = Strings.ToByteArray(new string(passPhrase));
+ byte[] pBytes = Encoding.UTF8.GetBytes(passPhrase);
byte[] keyBytes = new byte[(keySize + 7) / 8];
int generatedBytes = 0;
@@ -431,5 +437,22 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
return new ArmoredInputStream(inputStream, hasHeaders);
}
}
+
+ internal static IWrapper CreateWrapper(SymmetricKeyAlgorithmTag encAlgorithm)
+ {
+ switch (encAlgorithm)
+ {
+ case SymmetricKeyAlgorithmTag.Aes128:
+ case SymmetricKeyAlgorithmTag.Aes192:
+ case SymmetricKeyAlgorithmTag.Aes256:
+ return WrapperUtilities.GetWrapper("AESWRAP");
+ case SymmetricKeyAlgorithmTag.Camellia128:
+ case SymmetricKeyAlgorithmTag.Camellia192:
+ case SymmetricKeyAlgorithmTag.Camellia256:
+ return WrapperUtilities.GetWrapper("CAMELLIAWRAP");
+ default:
+ throw new PgpException("unknown wrap algorithm: " + encAlgorithm);
+ }
+ }
}
}
diff --git a/crypto/src/openpgp/Rfc6637Utilities.cs b/crypto/src/openpgp/Rfc6637Utilities.cs
new file mode 100644
index 000000000..5d992ec51
--- /dev/null
+++ b/crypto/src/openpgp/Rfc6637Utilities.cs
@@ -0,0 +1,138 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+ public sealed class Rfc6637Utilities
+ {
+ private Rfc6637Utilities()
+ {
+ }
+
+ // "Anonymous Sender ", which is the octet sequence
+ private static readonly byte[] ANONYMOUS_SENDER = Hex.Decode("416E6F6E796D6F75732053656E64657220202020");
+
+ public static string GetAgreementAlgorithm(PublicKeyPacket pubKeyData)
+ {
+ ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key;
+
+ switch (ecKey.HashAlgorithm)
+ {
+ case HashAlgorithmTag.Sha256:
+ return "ECCDHwithSHA256CKDF";
+ case HashAlgorithmTag.Sha384:
+ return "ECCDHwithSHA384CKDF";
+ case HashAlgorithmTag.Sha512:
+ return "ECCDHwithSHA512CKDF";
+ default:
+ throw new ArgumentException("Unknown hash algorithm specified: " + ecKey.HashAlgorithm);
+ }
+ }
+
+ public static DerObjectIdentifier GetKeyEncryptionOID(SymmetricKeyAlgorithmTag algID)
+ {
+ switch (algID)
+ {
+ case SymmetricKeyAlgorithmTag.Aes128:
+ return NistObjectIdentifiers.IdAes128Wrap;
+ case SymmetricKeyAlgorithmTag.Aes192:
+ return NistObjectIdentifiers.IdAes192Wrap;
+ case SymmetricKeyAlgorithmTag.Aes256:
+ return NistObjectIdentifiers.IdAes256Wrap;
+ default:
+ throw new PgpException("unknown symmetric algorithm ID: " + algID);
+ }
+ }
+
+ public static int GetKeyLength(SymmetricKeyAlgorithmTag algID)
+ {
+ switch (algID)
+ {
+ case SymmetricKeyAlgorithmTag.Aes128:
+ return 16;
+ case SymmetricKeyAlgorithmTag.Aes192:
+ return 24;
+ case SymmetricKeyAlgorithmTag.Aes256:
+ return 32;
+ default:
+ throw new PgpException("unknown symmetric algorithm ID: " + algID);
+ }
+ }
+
+ public static byte[] CreateKey(PublicKeyPacket pubKeyData, ECPoint s)
+ {
+ byte[] userKeyingMaterial = CreateUserKeyingMaterial(pubKeyData);
+
+ ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key;
+
+ return Kdf(ecKey.HashAlgorithm, s, GetKeyLength(ecKey.SymmetricKeyAlgorithm), userKeyingMaterial);
+ }
+
+ // RFC 6637 - Section 8
+ // curve_OID_len = (byte)len(curve_OID);
+ // Param = curve_OID_len || curve_OID || public_key_alg_ID || 03
+ // || 01 || KDF_hash_ID || KEK_alg_ID for AESKeyWrap || "Anonymous
+ // Sender " || recipient_fingerprint;
+ // Z_len = the key size for the KEK_alg_ID used with AESKeyWrap
+ // Compute Z = KDF( S, Z_len, Param );
+ public static byte[] CreateUserKeyingMaterial(PublicKeyPacket pubKeyData)
+ {
+ MemoryStream pOut = new MemoryStream();
+ ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)pubKeyData.Key;
+ byte[] encOid = ecKey.CurveOid.GetEncoded();
+
+ pOut.Write(encOid, 1, encOid.Length - 1);
+ pOut.WriteByte((byte)pubKeyData.Algorithm);
+ pOut.WriteByte(0x03);
+ pOut.WriteByte(0x01);
+ pOut.WriteByte((byte)ecKey.HashAlgorithm);
+ pOut.WriteByte((byte)ecKey.SymmetricKeyAlgorithm);
+ pOut.Write(ANONYMOUS_SENDER, 0, ANONYMOUS_SENDER.Length);
+
+ byte[] fingerprint = PgpPublicKey.CalculateFingerprint(pubKeyData);
+ pOut.Write(fingerprint, 0, fingerprint.Length);
+
+ return pOut.ToArray();
+ }
+
+ // RFC 6637 - Section 7
+ // Implements KDF( X, oBits, Param );
+ // Input: point X = (x,y)
+ // oBits - the desired size of output
+ // hBits - the size of output of hash function Hash
+ // Param - octets representing the parameters
+ // Assumes that oBits <= hBits
+ // Convert the point X to the octet string, see section 6:
+ // ZB' = 04 || x || y
+ // and extract the x portion from ZB'
+ // ZB = x;
+ // MB = Hash ( 00 || 00 || 00 || 01 || ZB || Param );
+ // return oBits leftmost bits of MB.
+ private static byte[] Kdf(HashAlgorithmTag digestAlg, ECPoint s, int keyLen, byte[] parameters)
+ {
+ byte[] ZB = s.XCoord.GetEncoded();
+
+ string digestName = PgpUtilities.GetDigestName(digestAlg);
+ IDigest digest = DigestUtilities.GetDigest(digestName);
+
+ digest.Update(0x00);
+ digest.Update(0x00);
+ digest.Update(0x00);
+ digest.Update(0x01);
+ digest.BlockUpdate(ZB, 0, ZB.Length);
+ digest.BlockUpdate(parameters, 0, parameters.Length);
+
+ byte[] hash = DigestUtilities.DoFinal(digest);
+
+ return Arrays.CopyOfRange(hash, 0, keyLen);
+ }
+ }
+}
diff --git a/crypto/src/openpgp/SXprUtilities.cs b/crypto/src/openpgp/SXprUtilities.cs
new file mode 100644
index 000000000..68ff373a8
--- /dev/null
+++ b/crypto/src/openpgp/SXprUtilities.cs
@@ -0,0 +1,102 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+ /**
+ * Utility functions for looking a S-expression keys. This class will move when it finds a better home!
+ * <p>
+ * Format documented here:
+ * http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=agent/keyformat.txt;h=42c4b1f06faf1bbe71ffadc2fee0fad6bec91a97;hb=refs/heads/master
+ * </p>
+ */
+ public sealed class SXprUtilities
+ {
+ private SXprUtilities()
+ {
+ }
+
+ private static int ReadLength(Stream input, int ch)
+ {
+ int len = ch - '0';
+
+ while ((ch = input.ReadByte()) >= 0 && ch != ':')
+ {
+ len = len * 10 + ch - '0';
+ }
+
+ return len;
+ }
+
+ internal static string ReadString(Stream input, int ch)
+ {
+ int len = ReadLength(input, ch);
+
+ char[] chars = new char[len];
+
+ for (int i = 0; i != chars.Length; i++)
+ {
+ chars[i] = (char)input.ReadByte();
+ }
+
+ return new string(chars);
+ }
+
+ internal static byte[] ReadBytes(Stream input, int ch)
+ {
+ int len = ReadLength(input, ch);
+
+ byte[] data = new byte[len];
+
+ Streams.ReadFully(input, data);
+
+ return data;
+ }
+
+ internal static S2k ParseS2k(Stream input)
+ {
+ SkipOpenParenthesis(input);
+
+ string alg = ReadString(input, input.ReadByte());
+ byte[] iv = ReadBytes(input, input.ReadByte());
+ long iterationCount = Int64.Parse(ReadString(input, input.ReadByte()));
+
+ SkipCloseParenthesis(input);
+
+ // we have to return the actual iteration count provided.
+ return new MyS2k(HashAlgorithmTag.Sha1, iv, iterationCount);
+ }
+
+ internal static void SkipOpenParenthesis(Stream input)
+ {
+ int ch = input.ReadByte();
+ if (ch != '(')
+ throw new IOException("unknown character encountered");
+ }
+
+ internal static void SkipCloseParenthesis(Stream input)
+ {
+ int ch = input.ReadByte();
+ if (ch != ')')
+ throw new IOException("unknown character encountered");
+ }
+
+ private class MyS2k : S2k
+ {
+ private readonly long mIterationCount64;
+
+ internal MyS2k(HashAlgorithmTag algorithm, byte[] iv, long iterationCount64)
+ : base(algorithm, iv, (int)iterationCount64)
+ {
+ this.mIterationCount64 = iterationCount64;
+ }
+
+ public override long IterationCount
+ {
+ get { return mIterationCount64; }
+ }
+ }
+ }
+}
|