Skip to content

Commit 17466b6

Browse files
committed
Fix VFS signature verification, add v3 cert derivation
1 parent 8dbe60c commit 17466b6

File tree

2 files changed

+47
-32
lines changed

2 files changed

+47
-32
lines changed

LibConeshell/ConeshellV2.cs

+19-28
Original file line numberDiff line numberDiff line change
@@ -279,18 +279,18 @@ private byte[] DecryptMessageInternal(byte[] encryptedBody, byte[] key, byte[] i
279279
0x1C3AF78Eu
280280
};
281281

282-
public virtual byte[] DecryptVfs(byte[] dbData, bool skipVerification = true)
282+
public virtual byte[] DecryptVfs(byte[] dbData, bool skipVerification = false)
283283
{
284284
var inputStream = new MemoryStream(dbData);
285285
var inputReader = new BinaryReader(inputStream);
286286

287287
if (inputReader.ReadUInt32() != VfsHeaderMagic)
288288
throw new InvalidDataException("Invalid database header.");
289289

290-
return DecryptVfsInternal(dbData, inputReader, skipVerification);
290+
return DecryptVfsInternal(dbData, inputReader, skipVerification, !skipVerification ? DeriveVfsPublicKey(VfsCertConstants) : "");
291291
}
292292

293-
protected byte[] DecryptVfsInternal(byte[] dbData, BinaryReader inputReader, bool skipVerification, int headerOffset = 0)
293+
protected static byte[] DecryptVfsInternal(byte[] dbData, BinaryReader inputReader, bool skipVerification, string publicKey = "", int headerOffset = 0)
294294
{
295295
const int fullHeaderSize = 0x4 + 0x4 + 0x10 + 0x10 + 0x4 + 0x10 + 0x100;
296296
var headerSize = fullHeaderSize - headerOffset;
@@ -303,27 +303,10 @@ protected byte[] DecryptVfsInternal(byte[] dbData, BinaryReader inputReader, boo
303303
var gcmIv = inputReader.ReadBytes(0x10);
304304
var gcmAdd2 = inputReader.ReadUInt32();
305305
var gcmTag = inputReader.ReadBytes(0x10);
306-
var verifySig = inputReader.ReadBytes(0x100);
306+
var signature = inputReader.ReadBytes(0x100);
307307

308308
var gcmAdd = BitConverter.GetBytes(gcmAdd1).Concat(BitConverter.GetBytes(gcmAdd2)).ToArray();
309309

310-
if (!skipVerification)
311-
{
312-
var rsaEncPublicKey = Encoding.UTF8.GetString(VfsDerivePublicKey()[..^1]);
313-
using var rsaPublicKeyStringReader = new StringReader(rsaEncPublicKey);
314-
var rsaPublicKeyPemReader = new PemReader(rsaPublicKeyStringReader);
315-
if (rsaPublicKeyPemReader.ReadObject() is not RsaKeyParameters rsaPublicKey)
316-
throw new InvalidDataException("Failed to parse derived VFS public key.");
317-
318-
var signer = new RsaDigestSigner(new Sha1Digest());
319-
signer.Init(false, rsaPublicKey);
320-
signer.BlockUpdate(gcmTag, 0, gcmTag.Length);
321-
var sigResult = signer.VerifySignature(verifySig);
322-
323-
if (!sigResult)
324-
throw new InvalidDataException("Failed to verify VFS signature.");
325-
}
326-
327310
var encryptedLength = dbData.Length - headerSize;
328311
var encryptedData = new byte[encryptedLength + gcmTag.Length];
329312
if (inputReader.Read(encryptedData, 0, encryptedLength) != encryptedLength)
@@ -333,6 +316,17 @@ protected byte[] DecryptVfsInternal(byte[] dbData, BinaryReader inputReader, boo
333316

334317
inputReader.Dispose();
335318

319+
if (!skipVerification)
320+
{
321+
var signedData = gcmTag.Concat(BitConverter.GetBytes(encryptedLength - 4)).ToArray();
322+
323+
var rsa = RSA.Create();
324+
rsa.ImportFromPem(publicKey);
325+
var sigResult = rsa.VerifyHash(signedData, signature, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
326+
if (!sigResult)
327+
throw new InvalidDataException("Failed to verify VFS signature.");
328+
}
329+
336330
var gcm = new GcmBlockCipher(new AesEngine());
337331
gcm.Init(false, new AeadParameters(new KeyParameter(gcmKey), gcmTag.Length * 8, gcmIv, gcmAdd));
338332

@@ -356,12 +350,9 @@ protected byte[] DecryptVfsInternal(byte[] dbData, BinaryReader inputReader, boo
356350
: bodyData;
357351
}
358352

359-
private byte[] VfsDerivePublicKey()
353+
protected static string DeriveVfsPublicKey(uint[] encCert, ulong seed = 0x8BE53A46A921AF07, ulong add = 0x31D7038E3C2AB8B)
360354
{
361-
const ulong roundConstant1 = 0x5851F42D4C957F2D;
362-
const ulong roundConstant2 = 0x31D7038E3C2AB8B;
363-
364-
var round = 0x8BE53A46A921AF07;
355+
var round = seed;
365356
var result = new byte[0x1c4];
366357
using var outputStream = new MemoryStream(result);
367358
using var outputWriter = new BinaryWriter(outputStream);
@@ -370,15 +361,15 @@ private byte[] VfsDerivePublicKey()
370361
{
371362
for (int i = 0; i < 0x1c4 / 4; i++)
372363
{
373-
var rk = VfsCertConstants[i];
364+
var rk = encCert[i];
374365
var rg = (round ^ (round >> 18)) >> 27;
375366
var rv = BitOperations.RotateRight((uint)rg, (int)(round >> 59)) ^ rk;
376367
outputWriter.Write(rv);
377368
round = TransformConstant * round + add;
378369
}
379370
}
380371

381-
return result;
372+
return Encoding.UTF8.GetString(result[..^1]);
382373
}
383374

384375
#endregion

LibConeshell/ConeshellV3.cs

+28-4
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,14 @@ protected override byte[] DeriveDeviceSecret(byte[] sharedSecret)
9595

9696
protected override uint VfsHeaderMagic => 0x03007ADA;
9797

98+
private static ulong ReadBigEndianULong(BinaryReader reader)
99+
=> BinaryPrimitives.ReadUInt64BigEndian(reader.ReadBytes(8));
100+
98101
public override byte[] DecryptVfs(byte[] dbData, bool skipVerification = false /* TODO: Not implemented */)
99102
{
103+
if (!skipVerification && _vfsCert == null)
104+
throw new ArgumentException("You need to supply a VFS certificate for signature verification.", nameof(skipVerification));
105+
100106
var inputStream = new MemoryStream(dbData);
101107
var inputReader = new BinaryReader(inputStream);
102108

@@ -110,16 +116,34 @@ public override byte[] DecryptVfs(byte[] dbData, bool skipVerification = false /
110116
inputStream = new MemoryStream(processedData);
111117
inputReader = new BinaryReader(inputStream);
112118

113-
return DecryptVfsInternal(processedData, inputReader, true, 4);
119+
var publicKey = "";
120+
if (!skipVerification)
121+
{
122+
using var encCertStream = new MemoryStream(_vfsCert!);
123+
using var encCertReader = new BinaryReader(encCertStream);
124+
encCertReader.BaseStream.Position += 4;
125+
126+
var transformConstant1 = ReadBigEndianULong(encCertReader);
127+
var transformConstant2 = ReadBigEndianULong(encCertReader);
128+
129+
var transformMixed1 = (2 * transformConstant2) | 1;
130+
var transformMixed2 = transformMixed1 + TransformConstant * (transformMixed1 + transformConstant1);
131+
132+
var encCert = new uint[0x1c4 / 4];
133+
for (int i = 0; i < 0x1c4 / 4; i++)
134+
encCert[i] = encCertReader.ReadUInt32();
135+
136+
publicKey = DeriveVfsPublicKey(encCert, transformMixed2, transformMixed1);
137+
138+
}
139+
140+
return DecryptVfsInternal(processedData, inputReader, skipVerification, publicKey, 4);
114141
}
115142

116143
private static byte[] PreprocessVfs(BinaryReader dbReader, int remainingLength)
117144
{
118145
const int transformLength = 16;
119146

120-
ulong ReadBigEndianULong(BinaryReader reader)
121-
=> BinaryPrimitives.ReadUInt64BigEndian(reader.ReadBytes(8));
122-
123147
unchecked
124148
{
125149
var transformInputConstant1 = ReadBigEndianULong(dbReader);

0 commit comments

Comments
 (0)