Skip to content

Commit

Permalink
Migrate to new S Expression code from bcgit/bc-csharp#282
Browse files Browse the repository at this point in the history
  • Loading branch information
filipnavara committed Dec 16, 2020
1 parent 51cc8a6 commit 6441531
Show file tree
Hide file tree
Showing 4 changed files with 473 additions and 194 deletions.
239 changes: 147 additions & 92 deletions src/Org/BouncyCastle/Bcpg/OpenPgp/PgpSecretKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1113,58 +1113,6 @@ public static PgpSecretKey ParseSecretKeyFromSExprRaw(Stream inputStream, byte[]
return DoParseSecretKeyFromSExpr(inputStream, rawPassPhrase, false, pubKey);
}

internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[] rawPassPhrase, bool clearPassPhrase, 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, rawPassPhrase, clearPassPhrase, curveName);
// TODO: check SHA-1 hash.

return new PgpSecretKey(new SecretKeyPacket(pubKey.PublicKeyPacket, SymmetricKeyAlgorithmTag.Null, null, null,
new ECSecretBcpgKey(new MPInteger(dValue)).GetEncoded()), pubKey);
}

throw new PgpException("unknown key type found");
}

/// <summary>
/// Parse a secret key from one of the GPG S expression keys.
/// </summary>
Expand All @@ -1174,7 +1122,7 @@ internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[
/// </remarks>
public static PgpSecretKey ParseSecretKeyFromSExpr(Stream inputStream, char[] passPhrase)
{
return DoParseSecretKeyFromSExpr(inputStream, PgpUtilities.EncodePassPhrase(passPhrase, false), true);
return DoParseSecretKeyFromSExpr(inputStream, PgpUtilities.EncodePassPhrase(passPhrase, false), true, null);
}

/// <summary>
Expand All @@ -1185,7 +1133,7 @@ public static PgpSecretKey ParseSecretKeyFromSExpr(Stream inputStream, char[] pa
/// </remarks>
public static PgpSecretKey ParseSecretKeyFromSExprUtf8(Stream inputStream, char[] passPhrase)
{
return DoParseSecretKeyFromSExpr(inputStream, PgpUtilities.EncodePassPhrase(passPhrase, true), true);
return DoParseSecretKeyFromSExpr(inputStream, PgpUtilities.EncodePassPhrase(passPhrase, true), true, null);
}

/// <summary>
Expand All @@ -1196,31 +1144,33 @@ public static PgpSecretKey ParseSecretKeyFromSExprUtf8(Stream inputStream, char[
/// </remarks>
public static PgpSecretKey ParseSecretKeyFromSExprRaw(Stream inputStream, byte[] rawPassPhrase)
{
return DoParseSecretKeyFromSExpr(inputStream, rawPassPhrase, false);
return DoParseSecretKeyFromSExpr(inputStream, rawPassPhrase, false, null);
}

/// <summary>
/// Parse a secret key from one of the GPG S expression keys.
/// </summary>
internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[] rawPassPhrase, bool clearPassPhrase)
internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[] rawPassPhrase, bool clearPassPhrase, PgpPublicKey pubKey)
{
SXprUtilities.SkipOpenParenthesis(inputStream);
SXprReader reader = new SXprReader(inputStream);

string type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
reader.SkipOpenParenthesis();

string type = reader.ReadString();
if (type.Equals("protected-private-key"))
{
SXprUtilities.SkipOpenParenthesis(inputStream);
reader.SkipOpenParenthesis();

string curveName;
Oid curveOid;

string keyType = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
string keyType = reader.ReadString();
if (keyType.Equals("ecc"))
{
SXprUtilities.SkipOpenParenthesis(inputStream);
reader.SkipOpenParenthesis();

string curveID = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
curveName = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
string curveID = reader.ReadString();
curveName = reader.ReadString();

switch (curveName)
{
Expand All @@ -1230,92 +1180,197 @@ internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[
case "brainpoolP256r1": curveOid = new Oid("1.3.36.3.3.2.8.1.1.7"); break;
case "brainpoolP384r1": curveOid = new Oid("1.3.36.3.3.2.8.1.1.11"); break;
case "brainpoolP512r1": curveOid = new Oid("1.3.36.3.3.2.8.1.1.13"); break;
// FIXME: curve25519
case "Curve25519": curveOid = new Oid("1.3.6.1.4.1.3029.1.5.1"); break;
case "Ed25519": curveOid = new Oid("1.3.6.1.4.1.11591.15.1"); break;
default:
throw new PgpException("unknown curve algorithm");
}

SXprUtilities.SkipCloseParenthesis(inputStream);
reader.SkipCloseParenthesis();
}
else
{
throw new PgpException("no curve details found");
}

byte[] qVal;
string flags = null;

SXprUtilities.SkipOpenParenthesis(inputStream);
reader.SkipOpenParenthesis();

type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
type = reader.ReadString();
if (type == "flags")
{
// Skip over flags
flags = reader.ReadString();
reader.SkipCloseParenthesis();
reader.SkipOpenParenthesis();
type = reader.ReadString();
}
if (type.Equals("q"))
{
qVal = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
qVal = reader.ReadBytes();
}
else
{
throw new PgpException("no q value found");
}

PublicKeyPacket pubPacket = new PublicKeyPacket(PublicKeyAlgorithmTag.ECDsa, DateTime.UtcNow,
new ECDsaPublicBcpgKey(curveOid, new MPInteger(qVal)));
if (pubKey == null)
{
PublicKeyPacket pubPacket = new PublicKeyPacket(
flags == "eddsa" ? PublicKeyAlgorithmTag.EdDsa : PublicKeyAlgorithmTag.ECDsa, DateTime.UtcNow,
new ECDsaPublicBcpgKey(curveOid, new MPInteger(qVal)));
pubKey = new PgpPublicKey(pubPacket);
}

SXprUtilities.SkipCloseParenthesis(inputStream);
reader.SkipCloseParenthesis();

byte[] dValue = GetDValue(inputStream, rawPassPhrase, clearPassPhrase, curveName);
// TODO: check SHA-1 hash.
byte[] dValue = GetDValue(reader, pubKey.PublicKeyPacket, rawPassPhrase, clearPassPhrase, curveName);

return new PgpSecretKey(new SecretKeyPacket(pubPacket, SymmetricKeyAlgorithmTag.Null, null, null,
new ECSecretBcpgKey(new MPInteger(dValue)).GetEncoded()), new PgpPublicKey(pubPacket));
return new PgpSecretKey(new SecretKeyPacket(pubKey.PublicKeyPacket, SymmetricKeyAlgorithmTag.Null, null, null,
new ECSecretBcpgKey(new MPInteger(dValue)).GetEncoded()), pubKey);
}

throw new PgpException("unknown key type found");
}

private static byte[] GetDValue(Stream inputStream, byte[] rawPassPhrase, bool clearPassPhrase, string curveName)
private static void WriteSExprPublicKey(SXprWriter writer, PublicKeyPacket pubPacket, string curveName, string protectedAt)
{
writer.StartList();
switch (pubPacket.Algorithm)
{
case PublicKeyAlgorithmTag.ECDsa:
case PublicKeyAlgorithmTag.EdDsa:
writer.WriteString("ecc");
writer.StartList();
writer.WriteString("curve");
writer.WriteString(curveName);
writer.EndList();
if (pubPacket.Algorithm == PublicKeyAlgorithmTag.EdDsa)
{
writer.StartList();
writer.WriteString("flags");
writer.WriteString("eddsa");
writer.EndList();
}
writer.StartList();
writer.WriteString("q");
writer.WriteBytes(((ECDsaPublicBcpgKey)pubPacket.Key).EncodedPoint.Value);
writer.EndList();
break;

case PublicKeyAlgorithmTag.RsaEncrypt:
case PublicKeyAlgorithmTag.RsaSign:
case PublicKeyAlgorithmTag.RsaGeneral:
RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey)pubPacket.Key;
writer.WriteString("rsa");
writer.StartList();
writer.WriteString("n");
writer.WriteBytes(rsaK.Modulus.Value);
writer.EndList();
writer.StartList();
writer.WriteString("e");
writer.WriteBytes(rsaK.PublicExponent.Value);
writer.EndList();
break;

// TODO: DSA, etc.
default:
throw new PgpException("unsupported algorithm in S expression");
}

if (protectedAt != null)
{
writer.StartList();
writer.WriteString("protected-at");
writer.WriteString(protectedAt);
writer.EndList();
}
writer.EndList();
}

private static byte[] GetDValue(SXprReader reader, PublicKeyPacket publicKey, byte[] rawPassPhrase, bool clearPassPhrase, string curveName)
{
string type;
SXprUtilities.SkipOpenParenthesis(inputStream);
reader.SkipOpenParenthesis();

string protection;
string protectedAt = null;
S2k s2k;
byte[] iv;
byte[] secKeyData;

type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
type = reader.ReadString();
if (type.Equals("protected"))
{
protection = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
protection = reader.ReadString();

reader.SkipOpenParenthesis();

s2k = reader.ParseS2k();

iv = reader.ReadBytes();

SXprUtilities.SkipOpenParenthesis(inputStream);
reader.SkipCloseParenthesis();

s2k = SXprUtilities.ParseS2k(inputStream);
secKeyData = reader.ReadBytes();

iv = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
reader.SkipCloseParenthesis();

SXprUtilities.SkipCloseParenthesis(inputStream);
reader.SkipOpenParenthesis();

secKeyData = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
if (reader.ReadString().Equals("protected-at"))
{
protectedAt = reader.ReadString();
}
}
else
{
throw new PgpException("protected block not found");
}

// TODO: recognise other algorithms
byte[] key = PgpUtilities.DoMakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag.Aes128, s2k, rawPassPhrase, clearPassPhrase);
byte[] data;
byte[] key;

switch (protection)
{
case "openpgp-s2k3-sha1-aes256-cbc":
case "openpgp-s2k3-sha1-aes-cbc":
SymmetricKeyAlgorithmTag symmAlg =
protection.Equals("openpgp-s2k3-sha1-aes256-cbc") ? SymmetricKeyAlgorithmTag.Aes256 : SymmetricKeyAlgorithmTag.Aes128;
key = PgpUtilities.DoMakeKeyFromPassPhrase(symmAlg, s2k, rawPassPhrase, clearPassPhrase);
data = RecoverKeyData(symmAlg, CipherMode.CBC, key, iv, secKeyData, 0, secKeyData.Length);
// TODO: check SHA-1 hash.
break;

byte[] data = RecoverKeyData(SymmetricKeyAlgorithmTag.Aes128, CipherMode.CBC, key, iv, secKeyData, 0, secKeyData.Length);
case "openpgp-s2k3-ocb-aes":
MemoryStream aad = new MemoryStream();
WriteSExprPublicKey(new SXprWriter(aad), publicKey, curveName, protectedAt);
key = PgpUtilities.DoMakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag.Aes128, s2k, rawPassPhrase, clearPassPhrase);
/*IBufferedCipher c = CipherUtilities.GetCipher("AES/OCB");
c.Init(false, new AeadParameters(key, 128, iv, aad.ToArray()));
data = c.DoFinal(secKeyData, 0, secKeyData.Length);*/
// TODO: AES/OCB support
throw new NotImplementedException();
break;

case "openpgp-native":
default:
throw new PgpException(protection + " key format is not supported yet");
}

//
// 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());
reader = new SXprReader(keyIn);
reader.SkipOpenParenthesis();
reader.SkipOpenParenthesis();
reader.SkipOpenParenthesis();
String name = reader.ReadString();
return reader.ReadBytes();
}
}
}
Loading

0 comments on commit 6441531

Please sign in to comment.