diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index cb00f9912..0a0e2c1da 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -2324,6 +2324,26 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\bcpg\ECDHPublicBCPGKey.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\bcpg\ECDsaPublicBCPGKey.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\bcpg\ECPublicBCPGKey.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\bcpg\ECSecretBCPGKey.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\bcpg\ElGamalPublicBCPGKey.cs"
SubType = "Code"
BuildAction = "Compile"
diff --git a/crypto/src/bcpg/ECDHPublicBCPGKey.cs b/crypto/src/bcpg/ECDHPublicBCPGKey.cs
new file mode 100644
index 000000000..b85379586
--- /dev/null
+++ b/crypto/src/bcpg/ECDHPublicBCPGKey.cs
@@ -0,0 +1,102 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Math.EC;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Base class for an ECDH Public Key.</remarks>
+ public class ECDHPublicBcpgKey
+ : ECPublicBcpgKey
+ {
+ private byte reserved;
+ private byte hashFunctionId;
+ private byte symAlgorithmId;
+
+ /// <param name="bcpgIn">The stream to read the packet from.</param>
+ public ECDHPublicBcpgKey(
+ BcpgInputStream bcpgIn)
+ : base(bcpgIn)
+ {
+ int length = bcpgIn.ReadByte();
+ byte[] kdfParameters = new byte[length];
+ if (kdfParameters.Length != 3)
+ throw new InvalidOperationException("kdf parameters size of 3 expected.");
+
+ bcpgIn.ReadFully(kdfParameters);
+
+ reserved = kdfParameters[0];
+ hashFunctionId = kdfParameters[1];
+ symAlgorithmId = kdfParameters[2];
+
+ VerifyHashAlgorithm();
+ VerifySymmetricKeyAlgorithm();
+ }
+
+ public ECDHPublicBcpgKey(
+ DerObjectIdentifier oid,
+ ECPoint point,
+ int hashAlgorithm,
+ int symmetricKeyAlgorithm)
+ : base(oid, point)
+ {
+ reserved = 1;
+ hashFunctionId = (byte)hashAlgorithm;
+ symAlgorithmId = (byte)symmetricKeyAlgorithm;
+
+ VerifyHashAlgorithm();
+ VerifySymmetricKeyAlgorithm();
+ }
+
+ public virtual byte Reserved
+ {
+ get { return reserved; }
+ }
+
+ public virtual byte HashAlgorithm
+ {
+ get { return hashFunctionId; }
+ }
+
+ public virtual byte SymmetricKeyAlgorithm
+ {
+ get { return symAlgorithmId; }
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ base.Encode(bcpgOut);
+ bcpgOut.WriteByte(0x3);
+ bcpgOut.WriteByte(reserved);
+ bcpgOut.WriteByte(hashFunctionId);
+ bcpgOut.WriteByte(symAlgorithmId);
+ }
+
+ private void VerifyHashAlgorithm()
+ {
+ switch ((HashAlgorithmTag)hashFunctionId)
+ {
+ case HashAlgorithmTag.Sha256:
+ case HashAlgorithmTag.Sha384:
+ case HashAlgorithmTag.Sha512:
+ break;
+ default:
+ throw new InvalidOperationException("Hash algorithm must be SHA-256 or stronger.");
+ }
+ }
+
+ private void VerifySymmetricKeyAlgorithm()
+ {
+ switch ((SymmetricKeyAlgorithmTag)symAlgorithmId)
+ {
+ case SymmetricKeyAlgorithmTag.Aes128:
+ case SymmetricKeyAlgorithmTag.Aes192:
+ case SymmetricKeyAlgorithmTag.Aes256:
+ break;
+ default:
+ throw new InvalidOperationException("Symmetric key algorithm must be AES-128 or stronger.");
+ }
+ }
+ }
+}
diff --git a/crypto/src/bcpg/ECDsaPublicBCPGKey.cs b/crypto/src/bcpg/ECDsaPublicBCPGKey.cs
new file mode 100644
index 000000000..5f0c8ac55
--- /dev/null
+++ b/crypto/src/bcpg/ECDsaPublicBCPGKey.cs
@@ -0,0 +1,34 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Base class for an ECDSA Public Key.</remarks>
+ public class ECDsaPublicBcpgKey
+ : ECPublicBcpgKey
+ {
+ /// <param name="bcpgIn">The stream to read the packet from.</param>
+ protected internal ECDsaPublicBcpgKey(
+ BcpgInputStream bcpgIn)
+ : base(bcpgIn)
+ {
+ }
+
+ public ECDsaPublicBcpgKey(
+ DerObjectIdentifier oid,
+ ECPoint point)
+ : base(oid, point)
+ {
+ }
+
+ public ECDsaPublicBcpgKey(
+ DerObjectIdentifier oid,
+ BigInteger encodedPoint)
+ : base(oid, encodedPoint)
+ {
+ }
+ }
+}
diff --git a/crypto/src/bcpg/ECPublicBCPGKey.cs b/crypto/src/bcpg/ECPublicBCPGKey.cs
new file mode 100644
index 000000000..f328f9dc3
--- /dev/null
+++ b/crypto/src/bcpg/ECPublicBCPGKey.cs
@@ -0,0 +1,97 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Base class for an EC Public Key.</remarks>
+ public abstract class ECPublicBcpgKey
+ : BcpgObject, IBcpgKey
+ {
+ internal DerObjectIdentifier oid;
+ internal BigInteger point;
+
+ /// <param name="bcpgIn">The stream to read the packet from.</param>
+ protected ECPublicBcpgKey(
+ BcpgInputStream bcpgIn)
+ {
+ this.oid = DerObjectIdentifier.GetInstance(Asn1Object.FromByteArray(ReadBytesOfEncodedLength(bcpgIn)));
+ this.point = new MPInteger(bcpgIn).Value;
+ }
+
+ protected ECPublicBcpgKey(
+ DerObjectIdentifier oid,
+ ECPoint point)
+ {
+ this.point = new BigInteger(1, point.GetEncoded());
+ this.oid = oid;
+ }
+
+ protected ECPublicBcpgKey(
+ DerObjectIdentifier oid,
+ BigInteger encodedPoint)
+ {
+ this.point = encodedPoint;
+ this.oid = oid;
+ }
+
+ /// <summary>The format, as a string, always "PGP".</summary>
+ public string Format
+ {
+ get { return "PGP"; }
+ }
+
+ /// <summary>Return the standard PGP encoding of the key.</summary>
+ public override byte[] GetEncoded()
+ {
+ try
+ {
+ return base.GetEncoded();
+ }
+ catch (IOException)
+ {
+ return null;
+ }
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ byte[] oid = this.oid.GetEncoded();
+ bcpgOut.Write(oid, 1, oid.Length - 1);
+
+ MPInteger point = new MPInteger(this.point);
+ bcpgOut.WriteObject(point);
+ }
+
+ public virtual BigInteger EncodedPoint
+ {
+ get { return point; }
+ }
+
+ public virtual DerObjectIdentifier CurveOid
+ {
+ get { return oid; }
+ }
+
+ protected static byte[] ReadBytesOfEncodedLength(
+ BcpgInputStream bcpgIn)
+ {
+ int length = bcpgIn.ReadByte();
+ if (length == 0 || length == 0xFF)
+ {
+ throw new IOException("future extensions not yet implemented.");
+ }
+
+ byte[] buffer = new byte[length + 2];
+ bcpgIn.ReadFully(buffer, 2, buffer.Length - 2);
+ buffer[0] = (byte)0x06;
+ buffer[1] = (byte)length;
+
+ return buffer;
+ }
+ }
+}
diff --git a/crypto/src/bcpg/ECSecretBCPGKey.cs b/crypto/src/bcpg/ECSecretBCPGKey.cs
new file mode 100644
index 000000000..22e0a3473
--- /dev/null
+++ b/crypto/src/bcpg/ECSecretBCPGKey.cs
@@ -0,0 +1,56 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Bcpg
+{
+ /// <remarks>Base class for an EC Secret Key.</remarks>
+ public class ECSecretBcpgKey
+ : BcpgObject, IBcpgKey
+ {
+ internal MPInteger x;
+
+ public ECSecretBcpgKey(
+ BcpgInputStream bcpgIn)
+ {
+ this.x = new MPInteger(bcpgIn);
+ }
+
+ public ECSecretBcpgKey(
+ BigInteger x)
+ {
+ this.x = new MPInteger(x);
+ }
+
+ /// <summary>The format, as a string, always "PGP".</summary>
+ public string Format
+ {
+ get { return "PGP"; }
+ }
+
+ /// <summary>Return the standard PGP encoding of the key.</summary>
+ public override byte[] GetEncoded()
+ {
+ try
+ {
+ return base.GetEncoded();
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+ }
+
+ public override void Encode(
+ BcpgOutputStream bcpgOut)
+ {
+ bcpgOut.WriteObject(x);
+ }
+
+ public virtual BigInteger X
+ {
+ get { return x.Value; }
+ }
+ }
+}
diff --git a/crypto/src/bcpg/PublicKeyPacket.cs b/crypto/src/bcpg/PublicKeyPacket.cs
index a45aeb469..cea5c8ed2 100644
--- a/crypto/src/bcpg/PublicKeyPacket.cs
+++ b/crypto/src/bcpg/PublicKeyPacket.cs
@@ -44,6 +44,12 @@ namespace Org.BouncyCastle.Bcpg
case PublicKeyAlgorithmTag.ElGamalGeneral:
key = new ElGamalPublicBcpgKey(bcpgIn);
break;
+ case PublicKeyAlgorithmTag.EC:
+ key = new ECDHPublicBcpgKey(bcpgIn);
+ break;
+ case PublicKeyAlgorithmTag.ECDsa:
+ key = new ECDsaPublicBcpgKey(bcpgIn);
+ break;
default:
throw new IOException("unknown PGP public key algorithm encountered");
}
diff --git a/crypto/src/bcpg/SignaturePacket.cs b/crypto/src/bcpg/SignaturePacket.cs
index 605ce84c4..5b91c15a3 100644
--- a/crypto/src/bcpg/SignaturePacket.cs
+++ b/crypto/src/bcpg/SignaturePacket.cs
@@ -146,6 +146,11 @@ namespace Org.BouncyCastle.Bcpg
MPInteger y = new MPInteger(bcpgIn);
signature = new MPInteger[]{ p, g, y };
break;
+ case PublicKeyAlgorithmTag.ECDsa:
+ MPInteger ecR = new MPInteger(bcpgIn);
+ MPInteger ecS = new MPInteger(bcpgIn);
+ signature = new MPInteger[]{ ecR, ecS };
+ break;
default:
if (keyAlgorithm >= PublicKeyAlgorithmTag.Experimental_1 && keyAlgorithm <= PublicKeyAlgorithmTag.Experimental_11)
{
diff --git a/crypto/src/crypto/modes/CcmBlockCipher.cs b/crypto/src/crypto/modes/CcmBlockCipher.cs
index e0b1e6b54..19e273d7c 100644
--- a/crypto/src/crypto/modes/CcmBlockCipher.cs
+++ b/crypto/src/crypto/modes/CcmBlockCipher.cs
@@ -268,9 +268,10 @@ namespace Org.BouncyCastle.Crypto.Modes
outputLen = inLen + macSize;
Check.OutputLength(output, outOff, outputLen, "Output buffer too short.");
- calculateMac(input, inOff, inLen, macBlock);
+ CalculateMac(input, inOff, inLen, macBlock);
- ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0); // S0
+ byte[] encMac = new byte[BlockSize];
+ ctrCipher.ProcessBlock(macBlock, 0, encMac, 0); // S0
while (inIndex < (inOff + inLen - BlockSize)) // S1...
{
@@ -287,7 +288,7 @@ namespace Org.BouncyCastle.Crypto.Modes
Array.Copy(block, 0, output, outIndex, inLen + inOff - inIndex);
- Array.Copy(macBlock, 0, output, outOff + inLen, macSize);
+ Array.Copy(encMac, 0, output, outOff + inLen, macSize);
}
else
{
@@ -323,7 +324,7 @@ namespace Org.BouncyCastle.Crypto.Modes
byte[] calculatedMacBlock = new byte[BlockSize];
- calculateMac(output, outOff, outputLen, calculatedMacBlock);
+ CalculateMac(output, outOff, outputLen, calculatedMacBlock);
if (!Arrays.ConstantTimeAreEqual(macBlock, calculatedMacBlock))
throw new InvalidCipherTextException("mac check in CCM failed");
@@ -332,7 +333,7 @@ namespace Org.BouncyCastle.Crypto.Modes
return outputLen;
}
- private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
+ private int CalculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
{
IMac cMac = new CbcBlockCipherMac(cipher, macSize * 8);
diff --git a/crypto/src/openpgp/PgpPublicKey.cs b/crypto/src/openpgp/PgpPublicKey.cs
index 249b94ea6..5bde2c8fe 100644
--- a/crypto/src/openpgp/PgpPublicKey.cs
+++ b/crypto/src/openpgp/PgpPublicKey.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections;
using System.IO;
+using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
@@ -104,6 +105,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
{
this.keyStrength = ((ElGamalPublicBcpgKey)key).P.BitLength;
}
+ else if (key is ECPublicBcpgKey)
+ {
+ this.keyStrength = ECNamedCurveTable.GetByOid(((ECPublicBcpgKey)key).CurveOid).Curve.FieldSize;
+ }
}
}
|