summary refs log tree commit diff
path: root/crypto/src/openpgp/PgpSecretKey.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/openpgp/PgpSecretKey.cs')
-rw-r--r--crypto/src/openpgp/PgpSecretKey.cs235
1 files changed, 215 insertions, 20 deletions
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());
+        }
     }
 }