Skip to content

Commit 1ddeba6

Browse files
committed
Add unit test for EdDSA, update S Expression parser to support more of the syntax used by GnuPG
1 parent 9f98858 commit 1ddeba6

File tree

4 files changed

+522
-77
lines changed

4 files changed

+522
-77
lines changed

crypto/src/openpgp/PgpPublicKey.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections;
33
using System.IO;
4+
using Org.BouncyCastle.Asn1.Gnu;
45
using Org.BouncyCastle.Asn1.Misc;
56
using Org.BouncyCastle.Asn1.Sec;
67
using Org.BouncyCastle.Asn1.X9;
@@ -10,6 +11,7 @@
1011
using Org.BouncyCastle.Crypto.Parameters;
1112
using Org.BouncyCastle.Math;
1213
using Org.BouncyCastle.Math.EC;
14+
using Org.BouncyCastle.Math.EC.Rfc8032;
1315
using Org.BouncyCastle.Security;
1416
using Org.BouncyCastle.Utilities;
1517
using Org.BouncyCastle.Utilities.Collections;
@@ -186,6 +188,14 @@ public PgpPublicKey(
186188
throw new PgpException("unknown EC algorithm");
187189
}
188190
}
191+
else if (pubKey is Ed25519PublicKeyParameters)
192+
{
193+
Ed25519PublicKeyParameters ecK = (Ed25519PublicKeyParameters)pubKey;
194+
byte[] encodedPoint = new byte[Ed25519.PublicKeySize + 1];
195+
encodedPoint[0] = 0x40;
196+
ecK.Encode(encodedPoint, 1);
197+
bcpgKey = new ECDsaPublicBcpgKey(GnuObjectIdentifiers.Ed25519, new BigInteger(1, encodedPoint));
198+
}
189199
else if (pubKey is ElGamalPublicKeyParameters)
190200
{
191201
ElGamalPublicKeyParameters eK = (ElGamalPublicKeyParameters) pubKey;

crypto/src/openpgp/PgpSecretKey.cs

Lines changed: 96 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
using System;
22
using System.Collections;
3+
using System.Diagnostics;
34
using System.IO;
5+
using Org.BouncyCastle.Asn1;
46
using Org.BouncyCastle.Asn1.Misc;
57
using Org.BouncyCastle.Asn1.X9;
68
using Org.BouncyCastle.Crypto;
9+
using Org.BouncyCastle.Crypto.EC;
710
using Org.BouncyCastle.Crypto.Generators;
811
using Org.BouncyCastle.Crypto.Parameters;
912
using Org.BouncyCastle.Math;
@@ -57,6 +60,10 @@ internal PgpSecretKey(
5760
ECPrivateKeyParameters ecK = (ECPrivateKeyParameters)privKey.Key;
5861
secKey = new ECSecretBcpgKey(ecK.D);
5962
break;
63+
case PublicKeyAlgorithmTag.EdDsa:
64+
Ed25519PrivateKeyParameters edK = (Ed25519PrivateKeyParameters)privKey.Key;
65+
secKey = new ECSecretBcpgKey(new BigInteger(1, edK.GetEncoded()));
66+
break;
6067
case PublicKeyAlgorithmTag.ElGamalEncrypt:
6168
case PublicKeyAlgorithmTag.ElGamalGeneral:
6269
ElGamalPrivateKeyParameters esK = (ElGamalPrivateKeyParameters) privKey.Key;
@@ -1111,24 +1118,26 @@ public static PgpSecretKey ParseSecretKeyFromSExprRaw(Stream inputStream, byte[]
11111118

11121119
internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[] rawPassPhrase, bool clearPassPhrase, PgpPublicKey pubKey)
11131120
{
1114-
SXprUtilities.SkipOpenParenthesis(inputStream);
1121+
SXprUtilities reader = new SXprUtilities(inputStream);
1122+
1123+
reader.SkipOpenParenthesis();
11151124

1116-
string type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
1125+
string type = reader.ReadString();
11171126
if (type.Equals("protected-private-key"))
11181127
{
1119-
SXprUtilities.SkipOpenParenthesis(inputStream);
1128+
reader.SkipOpenParenthesis();
11201129

11211130
string curveName;
11221131

1123-
string keyType = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
1132+
string keyType = reader.ReadString();
11241133
if (keyType.Equals("ecc"))
11251134
{
1126-
SXprUtilities.SkipOpenParenthesis(inputStream);
1135+
reader.SkipOpenParenthesis();
11271136

1128-
string curveID = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
1129-
curveName = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
1137+
string curveID = reader.ReadString();
1138+
curveName = reader.ReadString();
11301139

1131-
SXprUtilities.SkipCloseParenthesis(inputStream);
1140+
reader.SkipCloseParenthesis();
11321141
}
11331142
else
11341143
{
@@ -1137,21 +1146,21 @@ internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[
11371146

11381147
byte[] qVal;
11391148

1140-
SXprUtilities.SkipOpenParenthesis(inputStream);
1149+
reader.SkipOpenParenthesis();
11411150

1142-
type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
1151+
type = reader.ReadString();
11431152
if (type.Equals("q"))
11441153
{
1145-
qVal = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
1154+
qVal = reader.ReadBytes();
11461155
}
11471156
else
11481157
{
11491158
throw new PgpException("no q value found");
11501159
}
11511160

1152-
SXprUtilities.SkipCloseParenthesis(inputStream);
1161+
reader.SkipCloseParenthesis();
11531162

1154-
byte[] dValue = GetDValue(inputStream, rawPassPhrase, clearPassPhrase, curveName);
1163+
byte[] dValue = GetDValue(reader, rawPassPhrase, clearPassPhrase, curveName);
11551164
// TODO: check SHA-1 hash.
11561165

11571166
return new PgpSecretKey(new SecretKeyPacket(pubKey.PublicKeyPacket, SymmetricKeyAlgorithmTag.Null, null, null,
@@ -1170,7 +1179,7 @@ internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[
11701179
/// </remarks>
11711180
public static PgpSecretKey ParseSecretKeyFromSExpr(Stream inputStream, char[] passPhrase)
11721181
{
1173-
return DoParseSecretKeyFromSExpr(inputStream, PgpUtilities.EncodePassPhrase(passPhrase, false), true);
1182+
return DoParseSecretKeyFromSExpr(new SXprUtilities(inputStream), PgpUtilities.EncodePassPhrase(passPhrase, false), true);
11741183
}
11751184

11761185
/// <summary>
@@ -1181,7 +1190,7 @@ public static PgpSecretKey ParseSecretKeyFromSExpr(Stream inputStream, char[] pa
11811190
/// </remarks>
11821191
public static PgpSecretKey ParseSecretKeyFromSExprUtf8(Stream inputStream, char[] passPhrase)
11831192
{
1184-
return DoParseSecretKeyFromSExpr(inputStream, PgpUtilities.EncodePassPhrase(passPhrase, true), true);
1193+
return DoParseSecretKeyFromSExpr(new SXprUtilities(inputStream), PgpUtilities.EncodePassPhrase(passPhrase, true), true);
11851194
}
11861195

11871196
/// <summary>
@@ -1192,63 +1201,84 @@ public static PgpSecretKey ParseSecretKeyFromSExprUtf8(Stream inputStream, char[
11921201
/// </remarks>
11931202
public static PgpSecretKey ParseSecretKeyFromSExprRaw(Stream inputStream, byte[] rawPassPhrase)
11941203
{
1195-
return DoParseSecretKeyFromSExpr(inputStream, rawPassPhrase, false);
1204+
return DoParseSecretKeyFromSExpr(new SXprUtilities(inputStream), rawPassPhrase, false);
11961205
}
11971206

11981207
/// <summary>
11991208
/// Parse a secret key from one of the GPG S expression keys.
12001209
/// </summary>
1201-
internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[] rawPassPhrase, bool clearPassPhrase)
1210+
internal static PgpSecretKey DoParseSecretKeyFromSExpr(SXprUtilities reader, byte[] rawPassPhrase, bool clearPassPhrase)
12021211
{
1203-
SXprUtilities.SkipOpenParenthesis(inputStream);
1212+
reader.SkipOpenParenthesis();
12041213

1205-
string type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
1214+
string type = reader.ReadString();
12061215
if (type.Equals("protected-private-key"))
12071216
{
1208-
SXprUtilities.SkipOpenParenthesis(inputStream);
1217+
reader.SkipOpenParenthesis();
12091218

12101219
string curveName;
1220+
DerObjectIdentifier curveOid;
12111221

1212-
string keyType = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
1222+
string keyType = reader.ReadString();
12131223
if (keyType.Equals("ecc"))
12141224
{
1215-
SXprUtilities.SkipOpenParenthesis(inputStream);
1225+
reader.SkipOpenParenthesis();
12161226

1217-
string curveID = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
1218-
curveName = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
1227+
string curveID = reader.ReadString();
1228+
curveName = reader.ReadString();
12191229

12201230
if (Platform.StartsWith(curveName, "NIST "))
12211231
{
12221232
curveName = curveName.Substring("NIST ".Length);
12231233
}
12241234

1225-
SXprUtilities.SkipCloseParenthesis(inputStream);
1235+
curveOid = ECNamedCurveTable.GetOid(curveName);
1236+
if (curveOid == null)
1237+
{
1238+
curveOid = CustomNamedCurves.GetOid(curveName);
1239+
}
1240+
if (curveOid == null)
1241+
{
1242+
throw new PgpException("unknown curve");
1243+
}
1244+
1245+
reader.SkipCloseParenthesis();
12261246
}
12271247
else
12281248
{
12291249
throw new PgpException("no curve details found");
12301250
}
12311251

12321252
byte[] qVal;
1253+
string flags = null;
12331254

1234-
SXprUtilities.SkipOpenParenthesis(inputStream);
1255+
reader.SkipOpenParenthesis();
12351256

1236-
type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
1257+
type = reader.ReadString();
1258+
if (type == "flags")
1259+
{
1260+
// Skip over flags
1261+
flags = reader.ReadString();
1262+
reader.SkipCloseParenthesis();
1263+
reader.SkipOpenParenthesis();
1264+
type = reader.ReadString();
1265+
}
12371266
if (type.Equals("q"))
12381267
{
1239-
qVal = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
1268+
qVal = reader.ReadBytes();
12401269
}
12411270
else
12421271
{
12431272
throw new PgpException("no q value found");
12441273
}
12451274

1246-
PublicKeyPacket pubPacket = new PublicKeyPacket(PublicKeyAlgorithmTag.ECDsa, DateTime.UtcNow,
1247-
new ECDsaPublicBcpgKey(ECNamedCurveTable.GetOid(curveName), new BigInteger(1, qVal)));
1275+
PublicKeyPacket pubPacket = new PublicKeyPacket(
1276+
flags == "eddsa" ? PublicKeyAlgorithmTag.EdDsa : PublicKeyAlgorithmTag.ECDsa, DateTime.UtcNow,
1277+
new ECDsaPublicBcpgKey(curveOid, new BigInteger(1, qVal)));
12481278

1249-
SXprUtilities.SkipCloseParenthesis(inputStream);
1279+
reader.SkipCloseParenthesis();
12501280

1251-
byte[] dValue = GetDValue(inputStream, rawPassPhrase, clearPassPhrase, curveName);
1281+
byte[] dValue = GetDValue(reader, rawPassPhrase, clearPassPhrase, curveName);
12521282
// TODO: check SHA-1 hash.
12531283

12541284
return new PgpSecretKey(new SecretKeyPacket(pubPacket, SymmetricKeyAlgorithmTag.Null, null, null,
@@ -1258,51 +1288,68 @@ internal static PgpSecretKey DoParseSecretKeyFromSExpr(Stream inputStream, byte[
12581288
throw new PgpException("unknown key type found");
12591289
}
12601290

1261-
private static byte[] GetDValue(Stream inputStream, byte[] rawPassPhrase, bool clearPassPhrase, string curveName)
1291+
private static byte[] GetDValue(SXprUtilities reader, byte[] rawPassPhrase, bool clearPassPhrase, string curveName)
12621292
{
12631293
string type;
1264-
SXprUtilities.SkipOpenParenthesis(inputStream);
1294+
reader.SkipOpenParenthesis();
12651295

12661296
string protection;
12671297
S2k s2k;
12681298
byte[] iv;
12691299
byte[] secKeyData;
12701300

1271-
type = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
1301+
type = reader.ReadString();
12721302
if (type.Equals("protected"))
12731303
{
1274-
protection = SXprUtilities.ReadString(inputStream, inputStream.ReadByte());
1304+
protection = reader.ReadString();
12751305

1276-
SXprUtilities.SkipOpenParenthesis(inputStream);
1306+
reader.SkipOpenParenthesis();
12771307

1278-
s2k = SXprUtilities.ParseS2k(inputStream);
1308+
s2k = reader.ParseS2k();
12791309

1280-
iv = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
1310+
iv = reader.ReadBytes();
12811311

1282-
SXprUtilities.SkipCloseParenthesis(inputStream);
1312+
reader.SkipCloseParenthesis();
12831313

1284-
secKeyData = SXprUtilities.ReadBytes(inputStream, inputStream.ReadByte());
1314+
secKeyData = reader.ReadBytes();
12851315
}
12861316
else
12871317
{
12881318
throw new PgpException("protected block not found");
12891319
}
12901320

1291-
// TODO: recognise other algorithms
1292-
KeyParameter key = PgpUtilities.DoMakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag.Aes128, s2k, rawPassPhrase, clearPassPhrase);
1321+
// Valid values of protection: openpgp-s2k3-sha1-aes-cbc, openpgp-s2k3-ocb-aes, openpgp-native
1322+
byte[] data;
1323+
KeyParameter key;
1324+
1325+
switch (protection)
1326+
{
1327+
case "openpgp-s2k3-sha1-aes-cbc":
1328+
key = PgpUtilities.DoMakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag.Aes128, s2k, rawPassPhrase, clearPassPhrase);
1329+
data = RecoverKeyData(SymmetricKeyAlgorithmTag.Aes128, "/CBC/NoPadding", key, iv, secKeyData, 0, secKeyData.Length);
1330+
break;
1331+
1332+
case "openpgp-s2k3-ocb-aes":
1333+
key = PgpUtilities.DoMakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag.Aes128, s2k, rawPassPhrase, clearPassPhrase);
1334+
data = RecoverKeyData(SymmetricKeyAlgorithmTag.Aes128, "/OCB/NoPadding", key, iv, secKeyData, 0, secKeyData.Length);
1335+
break;
12931336

1294-
byte[] data = RecoverKeyData(SymmetricKeyAlgorithmTag.Aes128, "/CBC/NoPadding", key, iv, secKeyData, 0, secKeyData.Length);
1337+
case "openpgp-native":
1338+
default:
1339+
throw new PgpException(protection + " key format is not supported yet");
1340+
}
12951341

12961342
//
12971343
// parse the secret key S-expr
12981344
//
12991345
Stream keyIn = new MemoryStream(data, false);
13001346

1301-
SXprUtilities.SkipOpenParenthesis(keyIn);
1302-
SXprUtilities.SkipOpenParenthesis(keyIn);
1303-
SXprUtilities.SkipOpenParenthesis(keyIn);
1304-
String name = SXprUtilities.ReadString(keyIn, keyIn.ReadByte());
1305-
return SXprUtilities.ReadBytes(keyIn, keyIn.ReadByte());
1347+
reader = new SXprUtilities(keyIn);
1348+
reader.SkipOpenParenthesis();
1349+
reader.SkipOpenParenthesis();
1350+
reader.SkipOpenParenthesis();
1351+
String name = reader.ReadString();
1352+
return reader.ReadBytes();
13061353
}
13071354
}
13081355
}

0 commit comments

Comments
 (0)