diff options
Diffstat (limited to 'crypto/src/openpgp/PgpUtilities.cs')
-rw-r--r-- | crypto/src/openpgp/PgpUtilities.cs | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/crypto/src/openpgp/PgpUtilities.cs b/crypto/src/openpgp/PgpUtilities.cs new file mode 100644 index 000000000..7cd3dfc4f --- /dev/null +++ b/crypto/src/openpgp/PgpUtilities.cs @@ -0,0 +1,426 @@ +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; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Basic utility class.</remarks> + public sealed class PgpUtilities + { + private PgpUtilities() + { + } + + public static MPInteger[] DsaSigToMpi( + byte[] encoding) + { + DerInteger i1, i2; + + try + { + Asn1Sequence s = (Asn1Sequence) Asn1Object.FromByteArray(encoding); + + i1 = (DerInteger) s[0]; + i2 = (DerInteger) s[1]; + } + catch (IOException e) + { + throw new PgpException("exception encoding signature", e); + } + + return new MPInteger[]{ new MPInteger(i1.Value), new MPInteger(i2.Value) }; + } + + public static MPInteger[] RsaSigToMpi( + byte[] encoding) + { + return new MPInteger[]{ new MPInteger(new BigInteger(1, encoding)) }; + } + + public static string GetDigestName( + HashAlgorithmTag hashAlgorithm) + { + switch (hashAlgorithm) + { + case HashAlgorithmTag.Sha1: + return "SHA1"; + case HashAlgorithmTag.MD2: + return "MD2"; + case HashAlgorithmTag.MD5: + return "MD5"; + case HashAlgorithmTag.RipeMD160: + return "RIPEMD160"; + case HashAlgorithmTag.Sha224: + return "SHA224"; + case HashAlgorithmTag.Sha256: + return "SHA256"; + case HashAlgorithmTag.Sha384: + return "SHA384"; + case HashAlgorithmTag.Sha512: + return "SHA512"; + default: + throw new PgpException("unknown hash algorithm tag in GetDigestName: " + hashAlgorithm); + } + } + + public static string GetSignatureName( + PublicKeyAlgorithmTag keyAlgorithm, + HashAlgorithmTag hashAlgorithm) + { + string encAlg; + switch (keyAlgorithm) + { + case PublicKeyAlgorithmTag.RsaGeneral: + case PublicKeyAlgorithmTag.RsaSign: + encAlg = "RSA"; + break; + case PublicKeyAlgorithmTag.Dsa: + encAlg = "DSA"; + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: // in some malformed cases. + case PublicKeyAlgorithmTag.ElGamalGeneral: + encAlg = "ElGamal"; + break; + default: + throw new PgpException("unknown algorithm tag in signature:" + keyAlgorithm); + } + + return GetDigestName(hashAlgorithm) + "with" + encAlg; + } + + public static string GetSymmetricCipherName( + SymmetricKeyAlgorithmTag algorithm) + { + switch (algorithm) + { + case SymmetricKeyAlgorithmTag.Null: + return null; + case SymmetricKeyAlgorithmTag.TripleDes: + return "DESEDE"; + case SymmetricKeyAlgorithmTag.Idea: + return "IDEA"; + case SymmetricKeyAlgorithmTag.Cast5: + return "CAST5"; + case SymmetricKeyAlgorithmTag.Blowfish: + return "Blowfish"; + case SymmetricKeyAlgorithmTag.Safer: + return "SAFER"; + case SymmetricKeyAlgorithmTag.Des: + return "DES"; + case SymmetricKeyAlgorithmTag.Aes128: + return "AES"; + case SymmetricKeyAlgorithmTag.Aes192: + return "AES"; + case SymmetricKeyAlgorithmTag.Aes256: + return "AES"; + case SymmetricKeyAlgorithmTag.Twofish: + return "Twofish"; + default: + throw new PgpException("unknown symmetric algorithm: " + algorithm); + } + } + + public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm) + { + int keySize; + switch (algorithm) + { + case SymmetricKeyAlgorithmTag.Des: + keySize = 64; + break; + case SymmetricKeyAlgorithmTag.Idea: + case SymmetricKeyAlgorithmTag.Cast5: + case SymmetricKeyAlgorithmTag.Blowfish: + case SymmetricKeyAlgorithmTag.Safer: + case SymmetricKeyAlgorithmTag.Aes128: + keySize = 128; + break; + case SymmetricKeyAlgorithmTag.TripleDes: + case SymmetricKeyAlgorithmTag.Aes192: + keySize = 192; + break; + case SymmetricKeyAlgorithmTag.Aes256: + case SymmetricKeyAlgorithmTag.Twofish: + keySize = 256; + break; + default: + throw new PgpException("unknown symmetric algorithm: " + algorithm); + } + + return keySize; + } + + public static KeyParameter MakeKey( + SymmetricKeyAlgorithmTag algorithm, + byte[] keyBytes) + { + string algName = GetSymmetricCipherName(algorithm); + + return ParameterUtilities.CreateKeyParameter(algName, keyBytes); + } + + public static KeyParameter MakeRandomKey( + SymmetricKeyAlgorithmTag algorithm, + SecureRandom random) + { + int keySize = GetKeySize(algorithm); + byte[] keyBytes = new byte[(keySize + 7) / 8]; + random.NextBytes(keyBytes); + return MakeKey(algorithm, keyBytes); + } + + public static KeyParameter MakeKeyFromPassPhrase( + SymmetricKeyAlgorithmTag algorithm, + S2k s2k, + char[] passPhrase) + { + int keySize = GetKeySize(algorithm); + byte[] pBytes = Strings.ToByteArray(new string(passPhrase)); + byte[] keyBytes = new byte[(keySize + 7) / 8]; + + int generatedBytes = 0; + int loopCount = 0; + + while (generatedBytes < keyBytes.Length) + { + IDigest digest; + if (s2k != null) + { + string digestName = GetDigestName(s2k.HashAlgorithm); + + try + { + digest = DigestUtilities.GetDigest(digestName); + } + catch (Exception e) + { + throw new PgpException("can't find S2k digest", e); + } + + for (int i = 0; i != loopCount; i++) + { + digest.Update(0); + } + + byte[] iv = s2k.GetIV(); + + switch (s2k.Type) + { + case S2k.Simple: + digest.BlockUpdate(pBytes, 0, pBytes.Length); + break; + case S2k.Salted: + digest.BlockUpdate(iv, 0, iv.Length); + digest.BlockUpdate(pBytes, 0, pBytes.Length); + break; + case S2k.SaltedAndIterated: + long count = s2k.IterationCount; + digest.BlockUpdate(iv, 0, iv.Length); + digest.BlockUpdate(pBytes, 0, pBytes.Length); + + count -= iv.Length + pBytes.Length; + + while (count > 0) + { + if (count < iv.Length) + { + digest.BlockUpdate(iv, 0, (int)count); + break; + } + else + { + digest.BlockUpdate(iv, 0, iv.Length); + count -= iv.Length; + } + + if (count < pBytes.Length) + { + digest.BlockUpdate(pBytes, 0, (int)count); + count = 0; + } + else + { + digest.BlockUpdate(pBytes, 0, pBytes.Length); + count -= pBytes.Length; + } + } + break; + default: + throw new PgpException("unknown S2k type: " + s2k.Type); + } + } + else + { + try + { + digest = DigestUtilities.GetDigest("MD5"); + + for (int i = 0; i != loopCount; i++) + { + digest.Update(0); + } + + digest.BlockUpdate(pBytes, 0, pBytes.Length); + } + catch (Exception e) + { + throw new PgpException("can't find MD5 digest", e); + } + } + + byte[] dig = DigestUtilities.DoFinal(digest); + + if (dig.Length > (keyBytes.Length - generatedBytes)) + { + Array.Copy(dig, 0, keyBytes, generatedBytes, keyBytes.Length - generatedBytes); + } + else + { + Array.Copy(dig, 0, keyBytes, generatedBytes, dig.Length); + } + + generatedBytes += dig.Length; + + loopCount++; + } + + Array.Clear(pBytes, 0, pBytes.Length); + + return MakeKey(algorithm, keyBytes); + } + + /// <summary>Write out the passed in file as a literal data packet.</summary> + public static void WriteFileToLiteralData( + Stream output, + char fileType, + FileInfo file) + { + PgpLiteralDataGenerator lData = new PgpLiteralDataGenerator(); + Stream pOut = lData.Open(output, fileType, file.Name, file.Length, file.LastWriteTime); + PipeFileContents(file, pOut, 4096); + } + + /// <summary>Write out the passed in file as a literal data packet in partial packet format.</summary> + public static void WriteFileToLiteralData( + Stream output, + char fileType, + FileInfo file, + byte[] buffer) + { + PgpLiteralDataGenerator lData = new PgpLiteralDataGenerator(); + Stream pOut = lData.Open(output, fileType, file.Name, file.LastWriteTime, buffer); + PipeFileContents(file, pOut, buffer.Length); + } + + private static void PipeFileContents(FileInfo file, Stream pOut, int bufSize) + { + FileStream inputStream = file.OpenRead(); + byte[] buf = new byte[bufSize]; + + int len; + while ((len = inputStream.Read(buf, 0, buf.Length)) > 0) + { + pOut.Write(buf, 0, len); + } + + pOut.Close(); + inputStream.Close(); + } + + private const int ReadAhead = 60; + + private static bool IsPossiblyBase64( + int ch) + { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') + || (ch >= '0' && ch <= '9') || (ch == '+') || (ch == '/') + || (ch == '\r') || (ch == '\n'); + } + + /// <summary> + /// Return either an ArmoredInputStream or a BcpgInputStream based on whether + /// the initial characters of the stream are binary PGP encodings or not. + /// </summary> + public static Stream GetDecoderStream( + Stream inputStream) + { + // TODO Remove this restriction? + if (!inputStream.CanSeek) + throw new ArgumentException("inputStream must be seek-able", "inputStream"); + + long markedPos = inputStream.Position; + + int ch = inputStream.ReadByte(); + if ((ch & 0x80) != 0) + { + inputStream.Position = markedPos; + + return inputStream; + } + else + { + if (!IsPossiblyBase64(ch)) + { + inputStream.Position = markedPos; + + return new ArmoredInputStream(inputStream); + } + + byte[] buf = new byte[ReadAhead]; + int count = 1; + int index = 1; + + buf[0] = (byte)ch; + while (count != ReadAhead && (ch = inputStream.ReadByte()) >= 0) + { + if (!IsPossiblyBase64(ch)) + { + inputStream.Position = markedPos; + + return new ArmoredInputStream(inputStream); + } + + if (ch != '\n' && ch != '\r') + { + buf[index++] = (byte)ch; + } + + count++; + } + + inputStream.Position = markedPos; + + // + // nothing but new lines, little else, assume regular armoring + // + if (count < 4) + { + return new ArmoredInputStream(inputStream); + } + + // + // test our non-blank data + // + byte[] firstBlock = new byte[8]; + Array.Copy(buf, 0, firstBlock, 0, firstBlock.Length); + byte[] decoded = Base64.Decode(firstBlock); + + // + // it's a base64 PGP block. + // + bool hasHeaders = (decoded[0] & 0x80) == 0; + + return new ArmoredInputStream(inputStream, hasHeaders); + } + } + } +} |