/// <summary> /// Recursive PGP Object handler. /// </summary> /// <param name="obj">Object to handle</param> /// <returns>Returns decrypted data if any</returns> byte[] DecryptHandlePgpObject(PgpObject obj) { logger.Trace("DecryptHandlePgpObject(" + obj.GetType().Name + ")"); byte[] ret = null; if (obj is PgpEncryptedDataList) { ret = HandlePgpEncryptedDataList(obj); } else if (obj is PgpCompressedData) { Context.IsCompressed = true; var compressedData = obj as PgpCompressedData; using (var compressedIn = compressedData.GetDataStream()) { var factory = new PgpObjectFactory(compressedIn); do { var nextObj = factory.NextPgpObject(); if (nextObj == null) break; var r = DecryptHandlePgpObject(nextObj); if (r != null) ret = r; } while (true); } } else if (obj is PgpOnePassSignatureList) { logger.Trace("DecryptHandlePgpObject: IsSigned"); Context.IsSigned = true; var signatureList = obj as PgpOnePassSignatureList; if (signatureList.Count > 1) { logger.Error("DecryptHandlePgpObject: Error, more than one signature present!"); throw new CryptoException("Error, more than one signature present!"); } Context.OnePassSignature = signatureList[0]; var publicKey = GetPublicKey(Context.OnePassSignature.KeyId); if (publicKey == null) { logger.Debug("DecryptHandlePgpObject: Failed to find public key: " + Context.OnePassSignature.KeyId); Context.OnePassSignature = null; } else Context.OnePassSignature.InitVerify(publicKey); } else if (obj is PgpSignatureList) { var signatureList = obj as PgpSignatureList; if (signatureList.Count > 1) { logger.Error("DecryptHandlePgpObject: Error, more than one signature present!"); throw new CryptoException("Error, more than one signature present!"); } Context.Signature = signatureList[0]; Context.IsSigned = true; if (Context.IsSigned && Context.OnePassSignature == null) { logger.Warn("DecryptHandlePgpObject: We don't have signature key for validation"); // We don't have signature key for validation Context.SignatureValidated = false; Context.SignedBy = null; } else if (Context.OnePassSignature == null) { } else { if (Context.OnePassSignature.Verify(Context.Signature)) { logger.Trace("DecryptHandlePgpObject: Context.OnePassSignature.Verify passed"); Context.SignatureValidated = true; Context.SignedBy = GetMasterPublicKey(Context.Signature.KeyId); } else { logger.Trace("DecryptHandlePgpObject: Context.OnePassSignature.Verify failed"); } } } else if (obj is PgpLiteralData) { var literalData = obj as PgpLiteralData; using (var dataOut = new MemoryStream()) { using (var dataIn = literalData.GetInputStream()) dataIn.CopyTo(dataOut); dataOut.Position = 0; ret = dataOut.ToArray(); } if (Context.OnePassSignature != null) Context.OnePassSignature.Update(ret, 0, ret.Length); else if (Context.Signature != null) { var publicKey = GetPublicKey(Context.Signature.KeyId); if (publicKey == null) { logger.Debug("DecryptHandlePgpObject: Failed to find public key: " + Context.OnePassSignature.KeyId); Context.Signature = null; } else { Context.Signature.InitVerify(publicKey); Context.Signature.Update(ret, 0, ret.Length); if (Context.Signature.Verify()) { logger.Trace("DecryptHandlePgpObject: Context.Signature.Verify passed"); Context.SignatureValidated = true; Context.SignedBy = GetMasterPublicKey(Context.Signature.KeyId); } else { logger.Trace("DecryptHandlePgpObject: Context.Signature.Verify failed"); } } } } else if (obj is PgpMarker) { // Skip, These packets are used by PGP 5.x to signal to earlier // versions of PGP (eg. 2.6.x) that the message requires newer // software to be read and understood. } else { logger.Debug("DecryptHandlePgpObject: Unknown pgp object: " + obj.GetType().ToString()); throw new CryptoException("Unknown Pgp Object: " + obj.GetType().ToString()); } logger.Trace("DecryptHandlePgpObject: Returning " + (ret == null ? "null" : ret.Length.ToString()) + " bytes"); return ret; }
/// <summary> /// Recursive PGP Object handler. /// </summary> /// <param name="obj">Object to handle</param> /// <returns>Returns decrypted data if any</returns> byte[] DecryptHandlePgpObject(PgpObject obj) { byte[] ret = null; if (obj is PgpEncryptedDataList) { Context.IsEncrypted = true; var dataList = obj as PgpEncryptedDataList; // Set once we have matched a keyid. bool secretKeyMatched = false; foreach (PgpPublicKeyEncryptedData encryptedData in dataList.GetEncryptedDataObjects()) { try { // NOTE: When content is encrypted to multiple recipients, only one of these blocks // will match a known KeyId. If a match is never made, then there is a problem :) Context.SecretKey = GetSecretKey(encryptedData.KeyId); if (Context.SecretKey == null) continue; secretKeyMatched = true; using (var cleartextIn = encryptedData.GetDataStream(Context.SecretKey.ExtractPrivateKey(Context.Password))) { var clearFactory = new PgpObjectFactory(cleartextIn); var nextObj = clearFactory.NextPgpObject(); var r = DecryptHandlePgpObject(nextObj); if (r != null) ret = r; } // This can fail due to integrity protection missing. // Legacy systems to not have this protection // Should make an option to ignore. try { if (!encryptedData.Verify()) throw new VerifyException("Verify of encrypted data failed!"); } catch (PgpException exx) { if (exx.Message == "data not integrity protected.") Context.FailedIntegrityCheck = true; else throw; } } catch (PgpException ex) { if (!(ex.InnerException is EndOfStreamException)) throw ex; } } if(!secretKeyMatched) throw new SecretKeyNotFoundException("Error, unable to locate decryption key."); } else if (obj is PgpCompressedData) { Context.IsCompressed = true; var compressedData = obj as PgpCompressedData; using (var compressedIn = compressedData.GetDataStream()) { var factory = new PgpObjectFactory(compressedIn); do { var nextObj = factory.NextPgpObject(); if (nextObj == null) break; var r = DecryptHandlePgpObject(nextObj); if (r != null) ret = r; } while (true); } } else if (obj is PgpOnePassSignatureList) { Context.IsSigned = true; var signatureList = obj as PgpOnePassSignatureList; if (signatureList.Count > 1) throw new CryptoException("Error, more than one signature present!"); Context.OnePassSignature = signatureList[0]; var publicKey = GetPublicKey(Context.OnePassSignature.KeyId); if (publicKey == null) { Context.OnePassSignature = null; } else Context.OnePassSignature.InitVerify(publicKey); } else if (obj is PgpSignatureList) { var signatureList = obj as PgpSignatureList; if (signatureList.Count > 1) throw new CryptoException("Error, more than one signature present!"); Context.Signature = signatureList[0]; if (Context.IsSigned && Context.OnePassSignature == null) { // We don't have signature key for validation Context.SignatureValidated = false; Context.SignedBy = null; } else if (Context.OnePassSignature == null) throw new CryptoException("Error, OnePassSignature was not found!"); else { if (Context.OnePassSignature.Verify(Context.Signature)) { Context.SignatureValidated = true; Context.SignedBy = GetPublicKey(Context.Signature.KeyId); } } } else if (obj is PgpLiteralData) { var literalData = obj as PgpLiteralData; using (var dataOut = new MemoryStream()) { using (var dataIn = literalData.GetInputStream()) dataIn.CopyTo(dataOut); dataOut.Position = 0; ret = dataOut.ToArray(); } if(Context.OnePassSignature != null) Context.OnePassSignature.Update(ret, 0, ret.Length); } else if (obj is PgpMarker) { // Skip, These packets are used by PGP 5.x to signal to earlier // versions of PGP (eg. 2.6.x) that the message requires newer // software to be read and understood. } else { throw new CryptoException("Unknown Pgp Object: " + obj.GetType().ToString()); } return ret; }
/// <summary> /// Recursive PGP Object handler. /// </summary> /// <param name="obj">Object to handle</param> /// <returns>Returns decrypted data if any</returns> byte[] DecryptHandlePgpObject(PgpObject obj) { logger.Trace("DecryptHandlePgpObject(" + obj.GetType().Name + ")"); byte[] ret = null; if (obj is PgpEncryptedDataList) { logger.Trace("DecryptHandlePgpObject: IsEncrypted"); Context.IsEncrypted = true; var dataList = obj as PgpEncryptedDataList; // Set once we have matched a keyid. bool secretKeyMatched = false; foreach (PgpPublicKeyEncryptedData encryptedData in dataList.GetEncryptedDataObjects()) { try { // If we have already found a key to use, skip others. It is possible // to have all the keys in our ring. if (Context.SecretKey != null) continue; // NOTE: When content is encrypted to multiple recipients, only one of these blocks // will match a known KeyId. If a match is never made, then there is a problem :) var masterSecretKey = GetMasterSecretKey(encryptedData.KeyId); var secretKey = GetSecretKey(encryptedData.KeyId); if (masterSecretKey == null || secretKey == null) continue; var passphrase = Context.PasswordCallback(masterSecretKey, secretKey); // Incorrect passphrase or cancel if (passphrase == null) continue; Context.SecretKey = masterSecretKey; logger.Trace("DecryptHandlePgpObject: Found key: " + encryptedData.KeyId); secretKeyMatched = true; using (var cleartextIn = encryptedData.GetDataStream( secretKey.ExtractPrivateKey(passphrase))) { var clearFactory = new PgpObjectFactory(cleartextIn); var nextObj = clearFactory.NextPgpObject(); if (nextObj == null) return null; var r = DecryptHandlePgpObject(nextObj); if (r != null) ret = r; } // This can fail due to integrity protection missing. // Legacy systems to not have this protection // Should make an option to ignore. try { if (!encryptedData.Verify()) { logger.Debug("DecryptHandlePgpObject: encryptedData.Verify failed"); throw new VerifyException("Verify of encrypted data failed!"); } } catch (PgpException exx) { logger.Debug("DecryptHandlePgpObject: " + exx.Message); // Legacy systems do not have this protection // Exposed as a flag to allow library consumer to // decide on correct coarse of action if (exx.Message == "data not integrity protected.") Context.FailedIntegrityCheck = true; else throw; } } catch (PgpException ex) { if (!(ex.InnerException is EndOfStreamException)) throw ex; } } if (!secretKeyMatched) { logger.Debug("DecryptHandlePgpObject: Decryption key not found"); throw new SecretKeyNotFoundException("Error, unable to locate decryption key."); } } else if (obj is PgpCompressedData) { Context.IsCompressed = true; var compressedData = obj as PgpCompressedData; using (var compressedIn = compressedData.GetDataStream()) { var factory = new PgpObjectFactory(compressedIn); do { var nextObj = factory.NextPgpObject(); if (nextObj == null) break; var r = DecryptHandlePgpObject(nextObj); if (r != null) ret = r; } while (true); } } else if (obj is PgpOnePassSignatureList) { logger.Trace("DecryptHandlePgpObject: IsSigned"); Context.IsSigned = true; var signatureList = obj as PgpOnePassSignatureList; if (signatureList.Count > 1) { logger.Error("DecryptHandlePgpObject: Error, more than one signature present!"); throw new CryptoException("Error, more than one signature present!"); } Context.OnePassSignature = signatureList[0]; var publicKey = GetPublicKey(Context.OnePassSignature.KeyId); if (publicKey == null) { logger.Debug("DecryptHandlePgpObject: Failed to find public key: " + Context.OnePassSignature.KeyId); Context.OnePassSignature = null; } else Context.OnePassSignature.InitVerify(publicKey); } else if (obj is PgpSignatureList) { var signatureList = obj as PgpSignatureList; if (signatureList.Count > 1) { logger.Error("DecryptHandlePgpObject: Error, more than one signature present!"); throw new CryptoException("Error, more than one signature present!"); } Context.Signature = signatureList[0]; Context.IsSigned = true; if (Context.IsSigned && Context.OnePassSignature == null) { logger.Warn("DecryptHandlePgpObject: We don't have signature key for validation"); // We don't have signature key for validation Context.SignatureValidated = false; Context.SignedBy = null; } else if (Context.OnePassSignature == null) { } else { if (Context.OnePassSignature.Verify(Context.Signature)) { logger.Trace("DecryptHandlePgpObject: Context.OnePassSignature.Verify passed"); Context.SignatureValidated = true; Context.SignedBy = GetMasterPublicKey(Context.Signature.KeyId); } else { logger.Trace("DecryptHandlePgpObject: Context.OnePassSignature.Verify failed"); } } } else if (obj is PgpLiteralData) { var literalData = obj as PgpLiteralData; using (var dataOut = new MemoryStream()) { using (var dataIn = literalData.GetInputStream()) dataIn.CopyTo(dataOut); dataOut.Position = 0; ret = dataOut.ToArray(); } if (Context.OnePassSignature != null) Context.OnePassSignature.Update(ret, 0, ret.Length); else if (Context.Signature != null) { var publicKey = GetPublicKey(Context.Signature.KeyId); if (publicKey == null) { logger.Debug("DecryptHandlePgpObject: Failed to find public key: " + Context.OnePassSignature.KeyId); Context.Signature = null; } else { Context.Signature.InitVerify(publicKey); Context.Signature.Update(ret, 0, ret.Length); if (Context.Signature.Verify()) { logger.Trace("DecryptHandlePgpObject: Context.Signature.Verify passed"); Context.SignatureValidated = true; Context.SignedBy = GetMasterPublicKey(Context.Signature.KeyId); } else { logger.Trace("DecryptHandlePgpObject: Context.Signature.Verify failed"); } } } } else if (obj is PgpMarker) { // Skip, These packets are used by PGP 5.x to signal to earlier // versions of PGP (eg. 2.6.x) that the message requires newer // software to be read and understood. } else { logger.Debug("DecryptHandlePgpObject: Unknown pgp object: " + obj.GetType().ToString()); throw new CryptoException("Unknown Pgp Object: " + obj.GetType().ToString()); } logger.Trace("DecryptHandlePgpObject: Returning " + (ret == null ? "null" : ret.Length.ToString()) + " bytes"); return ret; }