From 06ba713c9b19102310675a6c58e07c68d8efb3c7 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 12 Oct 2015 15:49:54 +0700 Subject: Port of latest PGP tests and supporting code changes --- crypto/src/openpgp/PgpSecretKey.cs | 235 +++++++++++++++++++++++++++++++++---- 1 file changed, 215 insertions(+), 20 deletions(-) (limited to 'crypto/src/openpgp/PgpSecretKey.cs') 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); + } + /// Extract a PgpPrivateKey from this secret key's encrypted contents. 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()); + } } } -- cgit 1.4.1