using System; using System.IO; using System.Text; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Bcpg.OpenPgp; public sealed class PgpUtilities { private const int ReadAhead = 60; private PgpUtilities() { } public static MPInteger[] DsaSigToMpi(byte[] encoding) { DerInteger derInteger; DerInteger derInteger2; try { Asn1Sequence asn1Sequence = (Asn1Sequence)Asn1Object.FromByteArray(encoding); derInteger = (DerInteger)asn1Sequence[0]; derInteger2 = (DerInteger)asn1Sequence[1]; } catch (IOException exception) { throw new PgpException("exception encoding signature", exception); } return new MPInteger[2] { new MPInteger(derInteger.Value), new MPInteger(derInteger2.Value) }; } public static MPInteger[] RsaSigToMpi(byte[] encoding) { return new MPInteger[1] { new MPInteger(new BigInteger(1, encoding)) }; } public static string GetDigestName(HashAlgorithmTag hashAlgorithm) { return hashAlgorithm switch { HashAlgorithmTag.Sha1 => "SHA1", HashAlgorithmTag.MD2 => "MD2", HashAlgorithmTag.MD5 => "MD5", HashAlgorithmTag.RipeMD160 => "RIPEMD160", HashAlgorithmTag.Sha224 => "SHA224", HashAlgorithmTag.Sha256 => "SHA256", HashAlgorithmTag.Sha384 => "SHA384", HashAlgorithmTag.Sha512 => "SHA512", _ => throw new PgpException("unknown hash algorithm tag in GetDigestName: " + hashAlgorithm), }; } public static string GetSignatureName(PublicKeyAlgorithmTag keyAlgorithm, HashAlgorithmTag hashAlgorithm) { string text; switch (keyAlgorithm) { case PublicKeyAlgorithmTag.RsaGeneral: case PublicKeyAlgorithmTag.RsaSign: text = "RSA"; break; case PublicKeyAlgorithmTag.Dsa: text = "DSA"; break; case PublicKeyAlgorithmTag.EC: text = "ECDH"; break; case PublicKeyAlgorithmTag.ECDsa: text = "ECDSA"; break; case PublicKeyAlgorithmTag.ElGamalEncrypt: case PublicKeyAlgorithmTag.ElGamalGeneral: text = "ElGamal"; break; default: throw new PgpException("unknown algorithm tag in signature:" + keyAlgorithm); } return GetDigestName(hashAlgorithm) + "with" + text; } public static string GetSymmetricCipherName(SymmetricKeyAlgorithmTag algorithm) { return algorithm switch { SymmetricKeyAlgorithmTag.Null => null, SymmetricKeyAlgorithmTag.TripleDes => "DESEDE", SymmetricKeyAlgorithmTag.Idea => "IDEA", SymmetricKeyAlgorithmTag.Cast5 => "CAST5", SymmetricKeyAlgorithmTag.Blowfish => "Blowfish", SymmetricKeyAlgorithmTag.Safer => "SAFER", SymmetricKeyAlgorithmTag.Des => "DES", SymmetricKeyAlgorithmTag.Aes128 => "AES", SymmetricKeyAlgorithmTag.Aes192 => "AES", SymmetricKeyAlgorithmTag.Aes256 => "AES", SymmetricKeyAlgorithmTag.Twofish => "Twofish", SymmetricKeyAlgorithmTag.Camellia128 => "Camellia", SymmetricKeyAlgorithmTag.Camellia192 => "Camellia", SymmetricKeyAlgorithmTag.Camellia256 => "Camellia", _ => throw new PgpException("unknown symmetric algorithm: " + algorithm), }; } public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm) { switch (algorithm) { case SymmetricKeyAlgorithmTag.Des: return 64; case SymmetricKeyAlgorithmTag.Idea: case SymmetricKeyAlgorithmTag.Cast5: case SymmetricKeyAlgorithmTag.Blowfish: case SymmetricKeyAlgorithmTag.Safer: case SymmetricKeyAlgorithmTag.Aes128: case SymmetricKeyAlgorithmTag.Camellia128: return 128; case SymmetricKeyAlgorithmTag.TripleDes: case SymmetricKeyAlgorithmTag.Aes192: case SymmetricKeyAlgorithmTag.Camellia192: return 192; case SymmetricKeyAlgorithmTag.Aes256: case SymmetricKeyAlgorithmTag.Twofish: case SymmetricKeyAlgorithmTag.Camellia256: return 256; default: throw new PgpException("unknown symmetric algorithm: " + algorithm); } } public static KeyParameter MakeKey(SymmetricKeyAlgorithmTag algorithm, byte[] keyBytes) { string symmetricCipherName = GetSymmetricCipherName(algorithm); return ParameterUtilities.CreateKeyParameter(symmetricCipherName, keyBytes); } public static KeyParameter MakeRandomKey(SymmetricKeyAlgorithmTag algorithm, SecureRandom random) { int keySize = GetKeySize(algorithm); byte[] array = new byte[(keySize + 7) / 8]; random.NextBytes(array); return MakeKey(algorithm, array); } internal static byte[] EncodePassPhrase(char[] passPhrase, bool utf8) { if (passPhrase != null) { if (!utf8) { return Strings.ToByteArray(passPhrase); } return Encoding.UTF8.GetBytes(passPhrase); } return null; } public static KeyParameter MakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag algorithm, S2k s2k, char[] passPhrase) { return DoMakeKeyFromPassPhrase(algorithm, s2k, EncodePassPhrase(passPhrase, utf8: false), clearPassPhrase: true); } public static KeyParameter MakeKeyFromPassPhraseUtf8(SymmetricKeyAlgorithmTag algorithm, S2k s2k, char[] passPhrase) { return DoMakeKeyFromPassPhrase(algorithm, s2k, EncodePassPhrase(passPhrase, utf8: true), clearPassPhrase: true); } public static KeyParameter MakeKeyFromPassPhraseRaw(SymmetricKeyAlgorithmTag algorithm, S2k s2k, byte[] rawPassPhrase) { return DoMakeKeyFromPassPhrase(algorithm, s2k, rawPassPhrase, clearPassPhrase: false); } internal static KeyParameter DoMakeKeyFromPassPhrase(SymmetricKeyAlgorithmTag algorithm, S2k s2k, byte[] rawPassPhrase, bool clearPassPhrase) { int keySize = GetKeySize(algorithm); byte[] array = new byte[(keySize + 7) / 8]; int num = 0; int num2 = 0; while (num < array.Length) { IDigest digest; if (s2k != null) { string digestName = GetDigestName(s2k.HashAlgorithm); try { digest = DigestUtilities.GetDigest(digestName); } catch (Exception exception) { throw new PgpException("can't find S2k digest", exception); } for (int i = 0; i != num2; i++) { digest.Update(0); } byte[] iV = s2k.GetIV(); switch (s2k.Type) { case 0: digest.BlockUpdate(rawPassPhrase, 0, rawPassPhrase.Length); break; case 1: digest.BlockUpdate(iV, 0, iV.Length); digest.BlockUpdate(rawPassPhrase, 0, rawPassPhrase.Length); break; case 3: { long iterationCount = s2k.IterationCount; digest.BlockUpdate(iV, 0, iV.Length); digest.BlockUpdate(rawPassPhrase, 0, rawPassPhrase.Length); iterationCount -= iV.Length + rawPassPhrase.Length; while (iterationCount > 0) { if (iterationCount < iV.Length) { digest.BlockUpdate(iV, 0, (int)iterationCount); break; } digest.BlockUpdate(iV, 0, iV.Length); iterationCount -= iV.Length; if (iterationCount < rawPassPhrase.Length) { digest.BlockUpdate(rawPassPhrase, 0, (int)iterationCount); iterationCount = 0L; } else { digest.BlockUpdate(rawPassPhrase, 0, rawPassPhrase.Length); iterationCount -= rawPassPhrase.Length; } } break; } default: throw new PgpException("unknown S2k type: " + s2k.Type); } } else { try { digest = DigestUtilities.GetDigest("MD5"); for (int j = 0; j != num2; j++) { digest.Update(0); } digest.BlockUpdate(rawPassPhrase, 0, rawPassPhrase.Length); } catch (Exception exception2) { throw new PgpException("can't find MD5 digest", exception2); } } byte[] array2 = DigestUtilities.DoFinal(digest); if (array2.Length > array.Length - num) { Array.Copy(array2, 0, array, num, array.Length - num); } else { Array.Copy(array2, 0, array, num, array2.Length); } num += array2.Length; num2++; } if (clearPassPhrase && rawPassPhrase != null) { Array.Clear(rawPassPhrase, 0, rawPassPhrase.Length); } return MakeKey(algorithm, array); } public static void WriteFileToLiteralData(Stream output, char fileType, FileInfo file) { PgpLiteralDataGenerator pgpLiteralDataGenerator = new PgpLiteralDataGenerator(); Stream pOut = pgpLiteralDataGenerator.Open(output, fileType, file.Name, file.Length, file.LastWriteTime); PipeFileContents(file, pOut, 32768); } public static void WriteFileToLiteralData(Stream output, char fileType, FileInfo file, byte[] buffer) { PgpLiteralDataGenerator pgpLiteralDataGenerator = new PgpLiteralDataGenerator(); Stream pOut = pgpLiteralDataGenerator.Open(output, fileType, file.Name, file.LastWriteTime, buffer); PipeFileContents(file, pOut, buffer.Length); } private static void PipeFileContents(FileInfo file, Stream pOut, int bufSize) { FileStream fileStream = file.OpenRead(); byte[] array = new byte[bufSize]; try { int count; while ((count = fileStream.Read(array, 0, array.Length)) > 0) { pOut.Write(array, 0, count); } } finally { Array.Clear(array, 0, array.Length); Platform.Dispose(pOut); Platform.Dispose(fileStream); } } private static bool IsPossiblyBase64(int ch) { if ((ch < 65 || ch > 90) && (ch < 97 || ch > 122) && (ch < 48 || ch > 57) && ch != 43 && ch != 47 && ch != 13) { return ch == 10; } return true; } public static Stream GetDecoderStream(Stream inputStream) { if (!inputStream.CanSeek) { throw new ArgumentException("inputStream must be seek-able", "inputStream"); } long position = inputStream.Position; int num = inputStream.ReadByte(); if ((num & 0x80) != 0) { inputStream.Position = position; return inputStream; } if (!IsPossiblyBase64(num)) { inputStream.Position = position; return new ArmoredInputStream(inputStream); } byte[] array = new byte[60]; int i = 1; int num2 = 1; array[0] = (byte)num; for (; i != 60; i++) { if ((num = inputStream.ReadByte()) < 0) { break; } if (!IsPossiblyBase64(num)) { inputStream.Position = position; return new ArmoredInputStream(inputStream); } if (num != 10 && num != 13) { array[num2++] = (byte)num; } } inputStream.Position = position; if (i < 4) { return new ArmoredInputStream(inputStream); } byte[] array2 = new byte[8]; Array.Copy(array, 0, array2, 0, array2.Length); try { byte[] array3 = Base64.Decode(array2); bool hasHeaders = (array3[0] & 0x80) == 0; return new ArmoredInputStream(inputStream, hasHeaders); } catch (IOException ex) { throw ex; } catch (Exception ex2) { throw new IOException(ex2.Message); } } internal static IWrapper CreateWrapper(SymmetricKeyAlgorithmTag encAlgorithm) { switch (encAlgorithm) { case SymmetricKeyAlgorithmTag.Aes128: case SymmetricKeyAlgorithmTag.Aes192: case SymmetricKeyAlgorithmTag.Aes256: return WrapperUtilities.GetWrapper("AESWRAP"); case SymmetricKeyAlgorithmTag.Camellia128: case SymmetricKeyAlgorithmTag.Camellia192: case SymmetricKeyAlgorithmTag.Camellia256: return WrapperUtilities.GetWrapper("CAMELLIAWRAP"); default: throw new PgpException("unknown wrap algorithm: " + encAlgorithm); } } internal static byte[] GenerateIV(int length, SecureRandom random) { byte[] array = new byte[length]; random.NextBytes(array); return array; } internal static S2k GenerateS2k(HashAlgorithmTag hashAlgorithm, int s2kCount, SecureRandom random) { byte[] iv = GenerateIV(8, random); return new S2k(hashAlgorithm, iv, s2kCount); } }