From 5ceda408da88a1cf011427e602be112688ab15f2 Mon Sep 17 00:00:00 2001 From: Mbucari <37587114+Mbucari@users.noreply.github.com> Date: Fri, 15 Aug 2025 14:44:56 -0600 Subject: [PATCH 1/2] Use managed RSASSA-PSS with SHA-1 OpenSSL (the underlying RSA implementation on Linux) has deprecated SHA-1 signing. Used a managed implementation so that it does not error. --- Source/AudibleUtilities/Widevine/Device.cs | 90 ++++++++++++++++++++-- 1 file changed, 85 insertions(+), 5 deletions(-) diff --git a/Source/AudibleUtilities/Widevine/Device.cs b/Source/AudibleUtilities/Widevine/Device.cs index 28c91999..2dc9b9c4 100644 --- a/Source/AudibleUtilities/Widevine/Device.cs +++ b/Source/AudibleUtilities/Widevine/Device.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Numerics; using System.Security.Cryptography; #nullable enable @@ -56,18 +57,97 @@ internal class Device public byte[] SignMessage(byte[] message) { - using var sha1 = SHA1.Create(); - var digestion = sha1.ComputeHash(message); - return CdmKey.SignHash(digestion, HashAlgorithmName.SHA1, RSASignaturePadding.Pss); + var digestion = SHA1.HashData(message); + return PssSha1Signer.SignHash(CdmKey, digestion); } public bool VerifyMessage(byte[] message, byte[] signature) { - using var sha1 = SHA1.Create(); - var digestion = sha1.ComputeHash(message); + var digestion = SHA1.HashData(message); return CdmKey.VerifyHash(digestion, signature, HashAlgorithmName.SHA1, RSASignaturePadding.Pss); } public byte[] DecryptSessionKey(byte[] sessionKey) => CdmKey.Decrypt(sessionKey, RSAEncryptionPadding.OaepSHA1); + + /// + /// Completely managed implementation of RSASSA-PSS using SHA-1. + /// https://github.com/bcgit/bc-csharp/blob/master/crypto/src/crypto/signers/PssSigner.cs + /// + private static class PssSha1Signer + { + private const int Sha1DigestSize = 20; + private const int Trailer = 0xBC; + + public static byte[] SignHash(RSA rsa, ReadOnlySpan hash) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(hash.Length, Sha1DigestSize); + + var parameters = rsa.ExportParameters(true); + var Modulus = new BigInteger(parameters.Modulus, isUnsigned: true, isBigEndian: true); + var Exponent = new BigInteger(parameters.D, isUnsigned: true, isBigEndian: true); + var emBits = rsa.KeySize - 1; + var block = new byte[(emBits + 7) / 8]; + var firstByteMask = (byte)(0xFFU >> ((block.Length * 8) - emBits)); + + Span mDash = new byte[8 + 2 * Sha1DigestSize]; + + hash.CopyTo(mDash.Slice(8)); + var h = SHA1.HashData(mDash); + + block[^(2 * (Sha1DigestSize + 1))] = 1; + byte[] dbMask = MaskGeneratorFunction1(h, 0, h.Length, block.Length - Sha1DigestSize - 1); + for (int i = 0; i != dbMask.Length; i++) + block[i] ^= dbMask[i]; + + h.CopyTo(block, block.Length - Sha1DigestSize - 1); + + block[0] &= firstByteMask; + block[^1] = Trailer; + + var input = new BigInteger(block, isUnsigned: true, isBigEndian: true); + var result = BigInteger.ModPow(input, Exponent, Modulus); + return result.ToByteArray(isUnsigned: true, isBigEndian: true); + } + + private static byte[] MaskGeneratorFunction1(byte[] Z, int zOff, int zLen, int length) + { + byte[] mask = new byte[length]; + byte[] hashBuf = new byte[Sha1DigestSize]; + byte[] C = new byte[4]; + int counter = 0; + + using var sha = SHA1.Create(); + + for (; counter < (length / Sha1DigestSize); counter++) + { + ItoOSP(counter, C); + + sha.TransformBlock(Z, zOff, zLen, null, 0); + sha.TransformFinalBlock(C, 0, C.Length); + + sha.Hash!.CopyTo(mask, counter * Sha1DigestSize); + } + + if ((counter * Sha1DigestSize) < length) + { + ItoOSP(counter, C); + + sha.TransformBlock(Z, zOff, zLen, null, 0); + sha.TransformFinalBlock(C, 0, C.Length); + + Array.Copy(sha.Hash!, 0, mask, counter * Sha1DigestSize, mask.Length - (counter * Sha1DigestSize)); + } + + return mask; + } + + private static void ItoOSP(int i, byte[] sp) + { + sp[0] = (byte)((uint)i >> 24); + sp[1] = (byte)((uint)i >> 16); + sp[2] = (byte)((uint)i >> 8); + sp[3] = (byte)((uint)i >> 0); + } + } } From bfa0e4d338270fac713ee7bea3482789c416c298 Mon Sep 17 00:00:00 2001 From: MBucari Date: Sat, 16 Aug 2025 16:30:35 -0600 Subject: [PATCH 2/2] Parse floats with invariant culture --- Source/LibationFileManager/Templates/SeriesOrder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/LibationFileManager/Templates/SeriesOrder.cs b/Source/LibationFileManager/Templates/SeriesOrder.cs index ddf31a63..08c4b281 100644 --- a/Source/LibationFileManager/Templates/SeriesOrder.cs +++ b/Source/LibationFileManager/Templates/SeriesOrder.cs @@ -74,7 +74,7 @@ public class SeriesOrder : IFormattable continue; var substring = numString[s..e]; - if (float.TryParse(substring, out value)) + if (float.TryParse(substring, System.Globalization.CultureInfo.InvariantCulture, out value)) { range = new Range(s, e); return true;