From 5bc032381d9e8a067230a247b2eb7f56940b80b2 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 13 Dec 2021 15:43:32 +0100 Subject: [PATCH] StreamDumper: Use PrintWriter as base --- .../subpackets/KeyServerPreferences.java | 5 +- .../org/pgpainless/util/StreamDumper.java | 349 +++++++++++------- .../org/pgpainless/util/StreamDumpTest.java | 119 +++++- 3 files changed, 331 insertions(+), 142 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/KeyServerPreferences.java b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/KeyServerPreferences.java index e02ac93b..7253d6a7 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/KeyServerPreferences.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/KeyServerPreferences.java @@ -1,6 +1,9 @@ +// SPDX-FileCopyrightText: 2021 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + package org.pgpainless.signature.subpackets; -import java.sql.Array; import java.util.ArrayList; import java.util.HashMap; import java.util.List; diff --git a/pgpainless-core/src/main/java/org/pgpainless/util/StreamDumper.java b/pgpainless-core/src/main/java/org/pgpainless/util/StreamDumper.java index d278bbab..dbbedc92 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/util/StreamDumper.java +++ b/pgpainless-core/src/main/java/org/pgpainless/util/StreamDumper.java @@ -1,11 +1,19 @@ +// SPDX-FileCopyrightText: 2021 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + package org.pgpainless.util; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; import java.util.Arrays; import java.util.Date; import java.util.Iterator; +import java.util.List; +import org.bouncycastle.bcpg.Packet; import org.bouncycastle.bcpg.SignatureSubpacket; import org.bouncycastle.bcpg.sig.EmbeddedSignature; import org.bouncycastle.bcpg.sig.Exportable; @@ -63,18 +71,30 @@ import org.pgpainless.signature.subpackets.KeyServerPreferences; public class StreamDumper { - public static void dump(InputStream inputStream, PGPSessionKey sessionKey) throws IOException, PGPException { + public static void main(String[] args) throws PGPException, IOException { + if (args.length == 0) { + dump(System.in, null, System.out); + } else if (args.length == 1) { + PGPSessionKey sessionKey = PGPSessionKey.fromAsciiRepresentation(args[0]); + dump(System.in, sessionKey, System.out); + } else { + // CHECKSTYLE:OFF + System.err.println("Usage: StreamDumper [session-key]"); + // CHECKSTYLE:ON + } - StringBuilder stringBuilder = new StringBuilder(); - StringBuilderWrapper sbw = new StringBuilderWrapper(stringBuilder); - - PGPObjectFactory objectFactory = new BcPGPObjectFactory(inputStream); - walkObjects(sbw, objectFactory, sessionKey); - - System.out.println(sbw); } - private static void walkObjects(StringBuilderWrapper sbw, PGPObjectFactory objectFactory, PGPSessionKey sessionKey) throws IOException, PGPException { + public static void dump(InputStream inputStream, PGPSessionKey sessionKey, OutputStream outputStream) throws IOException, PGPException { + PrintWriter printWriter = new PrintWriter(outputStream); + PrintWriterWrapper pww = new PrintWriterWrapper(printWriter); + + PGPObjectFactory objectFactory = new BcPGPObjectFactory(inputStream); + walkObjects(pww, objectFactory, sessionKey); + printWriter.flush(); + } + + private static void walkObjects(PrintWriterWrapper pww, PGPObjectFactory objectFactory, PGPSessionKey sessionKey) throws IOException, PGPException { Object next; while ((next = objectFactory.nextObject()) != null) { @@ -84,7 +104,7 @@ public class StreamDumper { Iterator iterator = onePassSignatures.iterator(); while (iterator.hasNext()) { PGPOnePassSignature pgpOnePassSignature = iterator.next(); - sbw.appendLine("One-Pass Signature Packet").iind() + pww.appendLine("One-Pass Signature Packet").iind() .appendLine("Type: " + SignatureType.valueOf(pgpOnePassSignature.getSignatureType())) .appendLine("Public Key Algorithm: " + PublicKeyAlgorithm.fromId(pgpOnePassSignature.getKeyAlgorithm())) .appendLine("Hash Algorithm: " + HashAlgorithm.fromId(pgpOnePassSignature.getHashAlgorithm())) @@ -97,8 +117,8 @@ public class StreamDumper { else if (next instanceof PGPSignatureList) { PGPSignatureList signatures = (PGPSignatureList) next; for (PGPSignature signature : signatures) { - appendSignature(sbw, signature); - sbw.emptyLine(); + appendSignature(pww, signature); + pww.emptyLine(); } } @@ -109,39 +129,79 @@ public class StreamDumper { sessionKeyDataDecryptorFactory = new BcSessionKeyDataDecryptorFactory(sessionKey); } for (PGPEncryptedData encryptedData : encryptedDataList) { + + boolean decrypted = false; if (encryptedData instanceof PGPPublicKeyEncryptedData) { PGPPublicKeyEncryptedData pkesk = (PGPPublicKeyEncryptedData) encryptedData; - sbw.appendLine("Public-Key Encrypted Session Key Packet").iind() + pww.appendLine("Public-Key Encrypted Session Key Packet").iind() .appendLine("Recipient: " + Long.toHexString(pkesk.getKeyID())); if (sessionKeyDataDecryptorFactory != null) { try { InputStream inputStream = pkesk.getDataStream(sessionKeyDataDecryptorFactory); - sbw.appendLine("Session Key: " + Hex.toHexString(sessionKey.getKey())); - sbw.appendLine("Symmetric Algorithm: " + SymmetricKeyAlgorithm.fromId(sessionKey.getAlgorithm())); - sbw.appendLine("Decryption Successful"); - sbw.emptyLine(); + pww.appendLine("Session Key: " + Hex.toHexString(sessionKey.getKey())); + pww.appendLine("Symmetric Algorithm: " + SymmetricKeyAlgorithm.fromId(sessionKey.getAlgorithm())); + pww.appendLine("Decryption Successful"); + pww.emptyLine(); PGPObjectFactory decryptedFactory = new BcPGPObjectFactory(inputStream); - walkObjects(sbw, decryptedFactory, sessionKey); - } catch (PGPException e) { - sbw.appendLine("Decryption Failed"); + walkObjects(pww, decryptedFactory, sessionKey); + decrypted = true; + } catch (PGPException | IOException e) { + pww.appendLine("Decryption Failed") + .emptyLine(); } + } else { + pww.appendLine("No Decryption Method") + .emptyLine(); } if (pkesk.isIntegrityProtected()) { - sbw.appendLine("Modification Detection Code Packet").iind() - .appendLine("Valid: " + pkesk.verify()) - .dind(); + pww.appendLine("Modification Detection Code Packet").iind(); + if (decrypted) { + pww.appendLine("Valid: " + pkesk.verify()); + } } - } else if (encryptedData instanceof PGPPBEEncryptedData) { + pww.dind(); + } + + else if (encryptedData instanceof PGPPBEEncryptedData) { PGPPBEEncryptedData skesk = (PGPPBEEncryptedData) encryptedData; - sbw.appendLine("Symmetric-Key Encrypted Session Key Packet").iind() + pww.appendLine("Symmetric-Key Encrypted Session Key Packet").iind() .appendLine("Integrity Protected: " + skesk.isIntegrityProtected()) .dind().emptyLine(); + + if (sessionKeyDataDecryptorFactory != null) { + try { + InputStream inputStream = skesk.getDataStream(sessionKeyDataDecryptorFactory); + pww.appendLine("Session Key: " + Hex.toHexString(sessionKey.getKey())); + pww.appendLine("Symmetric Algorithm: " + SymmetricKeyAlgorithm.fromId(sessionKey.getAlgorithm())); + pww.appendLine("Decryption Successful"); + pww.emptyLine(); + + PGPObjectFactory decryptedFactory = new BcPGPObjectFactory(inputStream); + walkObjects(pww, decryptedFactory, sessionKey); + decrypted = true; + } catch (PGPException | IOException e) { + pww.appendLine("Decryption Failed"); + } + } else { + pww.appendLine("No Decryption Method") + .emptyLine(); + } + + if (skesk.isIntegrityProtected()) { + pww.appendLine("Modification Detection Code Packet").iind(); + if (decrypted) { + pww.appendLine("Valid: " + skesk.verify()); + } + } + pww.dind(); } + + pww.dind().emptyLine(); } - sbw.emptyLine(); + pww.emptyLine(); } else if (next instanceof PGPLiteralData) { @@ -150,13 +210,13 @@ public class StreamDumper { String fileName = literalData.getFileName(); Date modificationDate = literalData.getModificationTime(); - sbw.appendLine("Literal Data Packet").iind() + pww.appendLine("Literal Data Packet").iind() .appendLine("Format: " + encoding); if (fileName != null && !fileName.isEmpty()) { - sbw.appendLine("File Name: " + fileName); + pww.appendLine("File Name: " + fileName); } if (modificationDate != null && modificationDate.getTime() != 0) { - sbw.appendLine("Modification Date: " + DateUtil.formatUTCDate(modificationDate)); + pww.appendLine("Modification Date: " + DateUtil.formatUTCDate(modificationDate)); } byte[] peek = new byte[512]; @@ -169,24 +229,24 @@ public class StreamDumper { if (read != -1) { content = new String(peek, 0, read).replace("\r", "\\r").replace("\n", "\\n"); } - sbw.appendLine("Content: \"" + content + "\"") + pww.appendLine("Content: \"" + content + "\"") .dind() .emptyLine(); } else if (next instanceof PGPCompressedData) { PGPCompressedData compressedData = (PGPCompressedData) next; - sbw.appendLine("Compressed Data Packet").iind() + pww.appendLine("Compressed Data Packet").iind() .appendLine("Algorithm: " + CompressionAlgorithm.fromId(compressedData.getAlgorithm())) .emptyLine(); PGPObjectFactory compressedFactory = new BcPGPObjectFactory(compressedData.getDataStream()); - walkObjects(sbw, compressedFactory, sessionKey); - sbw.dind(); + walkObjects(pww, compressedFactory, sessionKey); + pww.dind(); } else if (next instanceof PGPMarker) { - sbw.appendLine("Marker Packet") + pww.appendLine("Marker Packet") .emptyLine(); } @@ -194,55 +254,29 @@ public class StreamDumper { PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) next; for (PGPSecretKey secretKey : secretKeys) { - PGPPublicKey publicKey = secretKey.getPublicKey(); - sbw.appendLine(publicKey.isMasterKey() ? "Secret-Key Packet" : "Secret-Subkey Packet").iind() - .appendLine("Version: " + publicKey.getVersion()) - .appendLine("Creation Time: " + DateUtil.formatUTCDate(publicKey.getCreationTime())) - .appendLine("Public Key Algorithm: " + PublicKeyAlgorithm.fromId(publicKey.getAlgorithm())) - .appendLine("Public Key Size: " + publicKey.getBitStrength()) - .appendLine("Fingerprint: " + Hex.toHexString(publicKey.getFingerprint())) - .appendLine("Key-ID: " + Long.toHexString(publicKey.getKeyID())) - .dind().emptyLine(); - - for (Iterator iter = publicKey.getKeySignatures(); iter.hasNext(); ) { - PGPSignature signature = iter.next(); - appendSignature(sbw, signature); - } - - for (Iterator it = publicKey.getUserIDs(); it.hasNext(); ) { - String userId = it.next(); - sbw.appendLine("User-ID Packet").iind() - .appendLine("Value: " + userId) - .dind().emptyLine(); - - for (Iterator iter = publicKey.getSignaturesForID(userId); iter.hasNext(); ) { - PGPSignature signature = iter.next(); - appendSignature(sbw, signature); - sbw.emptyLine(); - } - } - + appendSecretKey(pww, secretKey); } for (Iterator it = secretKeys.getExtraPublicKeys(); it.hasNext(); ) { PGPPublicKey publicKey = it.next(); - sbw.appendLine("Public-Key Packet").iind() - .appendLine("Version: " + publicKey.getVersion()) - .appendLine("Creation Time: " + DateUtil.formatUTCDate(publicKey.getCreationTime())) - .appendLine("Public Key Algorithm: " + PublicKeyAlgorithm.fromId(publicKey.getAlgorithm())) - .appendLine("Public Key Size: " + publicKey.getBitStrength()) - .appendLine("Fingerprint: " + Hex.toHexString(publicKey.getFingerprint())) - .appendLine("Key-ID: " + Long.toHexString(publicKey.getKeyID())) - .dind().emptyLine(); + appendPublicKey(pww, publicKey); } } else if (next instanceof PGPPublicKeyRing) { - + PGPPublicKeyRing publicKeys = (PGPPublicKeyRing) next; + for (PGPPublicKey publicKey : publicKeys) { + appendPublicKey(pww, publicKey); + } } else if (next instanceof PGPPublicKey) { + appendPublicKey(pww, (PGPPublicKey) next); + } + else if (next instanceof Packet) { + Packet packet = (Packet) next; + pww.appendLine("Experimental Packet: " + packet.toString()); } /* @@ -270,32 +304,89 @@ public class StreamDumper { } } - private static void appendSignature(StringBuilderWrapper sbw, PGPSignature signature) throws PGPException { - sbw.appendLine("Signature Packet").iind() + private static void appendSecretKey(PrintWriterWrapper pww, PGPSecretKey secretKey) throws PGPException { + PGPPublicKey publicKey = secretKey.getPublicKey(); + pww.appendLine(publicKey.isMasterKey() ? "Secret-Key Packet" : "Secret-Subkey Packet").iind() + .appendLine("Version: " + publicKey.getVersion()) + .appendLine("Creation Time: " + DateUtil.formatUTCDate(publicKey.getCreationTime())) + .appendLine("Public Key Algorithm: " + PublicKeyAlgorithm.fromId(publicKey.getAlgorithm())) + .appendLine("Public Key Size: " + publicKey.getBitStrength()) + .appendLine("Fingerprint: " + Hex.toHexString(publicKey.getFingerprint())) + .appendLine("Key-ID: " + Long.toHexString(publicKey.getKeyID())) + .dind().emptyLine(); + + appendPublicKeyAppendix(pww, publicKey); + } + + private static void appendPublicKey(PrintWriterWrapper pww, PGPPublicKey publicKey) throws PGPException { + pww.appendLine(publicKey.isMasterKey() ? "Public-Key Packet" : "Public-Subkey Packet").iind() + .appendLine("Version: " + publicKey.getVersion()) + .appendLine("Creation Time: " + DateUtil.formatUTCDate(publicKey.getCreationTime())) + .appendLine("Public Key Algorithm: " + PublicKeyAlgorithm.fromId(publicKey.getAlgorithm())) + .appendLine("Public Key Size: " + publicKey.getBitStrength()) + .appendLine("Fingerprint: " + Hex.toHexString(publicKey.getFingerprint())) + .appendLine("Key-ID: " + Long.toHexString(publicKey.getKeyID())) + .dind().emptyLine(); + + appendPublicKeyAppendix(pww, publicKey); + } + + private static void appendPublicKeyAppendix(PrintWriterWrapper pww, PGPPublicKey publicKey) throws PGPException { + List allSignatures = CollectionUtils.iteratorToList(publicKey.getSignatures()); + List directKeySignatures = CollectionUtils.iteratorToList(publicKey.getSignaturesOfType(SignatureType.DIRECT_KEY.getCode())); + + for (PGPSignature signature : directKeySignatures) { + allSignatures.remove(signature); + appendSignature(pww, signature); + pww.emptyLine(); + } + + for (Iterator it = publicKey.getUserIDs(); it.hasNext(); ) { + String userId = it.next(); + pww.appendLine("User-ID Packet").iind() + .appendLine("Value: " + userId) + .dind().emptyLine(); + + List userIdSigs = CollectionUtils.iteratorToList(publicKey.getSignaturesForID(userId)); + for (PGPSignature signature : userIdSigs) { + appendSignature(pww, signature); + allSignatures.remove(signature); + pww.emptyLine(); + } + } + + for (PGPSignature signature : allSignatures) { + appendSignature(pww, signature); + pww.emptyLine(); + } + } + + private static void appendSignature(PrintWriterWrapper pww, PGPSignature signature) throws PGPException { + pww.appendLine("Signature Packet").iind() .appendLine("Version: " + signature.getVersion()) .appendLine("Type: " + SignatureType.valueOf(signature.getSignatureType())) .appendLine("Public Key Algorithm: " + PublicKeyAlgorithm.fromId(signature.getKeyAlgorithm())) .appendLine("Hash Algorithm: " + HashAlgorithm.fromId(signature.getHashAlgorithm())); if (signature.getHashedSubPackets().toArray().length != 0) { - sbw.appendLine("Hashed Area:").iind(); - appendSubpacketVector(sbw, signature.getHashedSubPackets()); - sbw.dind(); + pww.appendLine("Hashed Area:").iind(); + appendSubpacketVector(pww, signature.getHashedSubPackets()); + pww.dind(); } if (signature.getUnhashedSubPackets().toArray().length != 0) { - sbw.appendLine("Unhashed Area:").iind(); - appendSubpacketVector(sbw, signature.getUnhashedSubPackets()); - sbw.dind(); + pww.appendLine("Unhashed Area:").iind(); + appendSubpacketVector(pww, signature.getUnhashedSubPackets()); + pww.dind(); } - sbw.appendLine("Digest Prefix: " + Hex.toHexString(signature.getDigestPrefix())) + pww.appendLine("Digest Prefix: " + Hex.toHexString(signature.getDigestPrefix())) .appendLine("Signature: ").iind() .appendLine(Hex.toHexString(signature.getSignature())).dind() .dind(); } - private static void appendSubpacketVector(StringBuilderWrapper sbw, PGPSignatureSubpacketVector vector) throws PGPException { + private static void appendSubpacketVector(PrintWriterWrapper pww, PGPSignatureSubpacketVector vector) throws PGPException { PGPSignatureList embeddedSignatures = vector.getEmbeddedSignatures(); int embeddedSigCount = 0; @@ -303,36 +394,36 @@ public class StreamDumper { switch (org.pgpainless.algorithm.SignatureSubpacket.fromCode(subpacket.getType())) { case signatureCreationTime: SignatureCreationTime signatureCreationTime = (SignatureCreationTime) subpacket; - sbw.appendLine("Signature Creation Time: " + DateUtil.formatUTCDate(signatureCreationTime.getTime()) + (signatureCreationTime.isCritical() ? " (critical)" : "")); + pww.appendLine("Signature Creation Time: " + DateUtil.formatUTCDate(signatureCreationTime.getTime()) + (signatureCreationTime.isCritical() ? " (critical)" : "")); break; case signatureExpirationTime: SignatureExpirationTime signatureExpirationTime = (SignatureExpirationTime) subpacket; - sbw.appendLine("Signature Expiration Time: " + signatureExpirationTime.getTime() + (signatureExpirationTime.isCritical() ? " (critical)" : "")); + pww.appendLine("Signature Expiration Time: " + signatureExpirationTime.getTime() + (signatureExpirationTime.isCritical() ? " (critical)" : "")); break; case exportableCertification: Exportable exportable = (Exportable) subpacket; - sbw.appendLine("Exportable: " + exportable.isExportable() + (exportable.isCritical() ? " (critical)" : "")); + pww.appendLine("Exportable: " + exportable.isExportable() + (exportable.isCritical() ? " (critical)" : "")); break; case trustSignature: TrustSignature trustSignature = (TrustSignature) subpacket; - sbw.appendLine("Trust Signature" + (trustSignature.isCritical() ? " (critical)" : "") + ":").iind() + pww.appendLine("Trust Signature" + (trustSignature.isCritical() ? " (critical)" : "") + ":").iind() .appendLine("Depth: " + trustSignature.getDepth()) .appendLine("Amount: " + trustSignature.getTrustAmount()) .dind(); break; case regularExpression: - sbw.appendLine("Regular Expression: " + new String(subpacket.getData())); + pww.appendLine("Regular Expression: " + new String(subpacket.getData())); break; case revocable: Revocable revocable = (Revocable) subpacket; - sbw.appendLine("Revocable: " + revocable.isRevocable() + (revocable.isCritical() ? " (critical)" : "")); + pww.appendLine("Revocable: " + revocable.isRevocable() + (revocable.isCritical() ? " (critical)" : "")); break; case keyExpirationTime: KeyExpirationTime keyExpirationTime = (KeyExpirationTime) subpacket; - sbw.appendLine("Key Expiration Time: " + keyExpirationTime.getTime() + (keyExpirationTime.isCritical() ? " (critical)" : "")); + pww.appendLine("Key Expiration Time: " + keyExpirationTime.getTime() + (keyExpirationTime.isCritical() ? " (critical)" : "")); break; case placeholder: - sbw.appendLine("Placeholder: " + new String(subpacket.getData())); + pww.appendLine("Placeholder: " + new String(subpacket.getData())); break; case preferredSymmetricAlgorithms: PreferredAlgorithms preferredSymmetricAlgorithms = (PreferredAlgorithms) subpacket; @@ -341,11 +432,11 @@ public class StreamDumper { for (int i = 0; i < symAlgs.length; i++) { symAlgs[i] = SymmetricKeyAlgorithm.fromId(symAlgIds[i]); } - sbw.appendLine("Preferred Symmetric Algorithms: " + Arrays.toString(symAlgs) + (preferredSymmetricAlgorithms.isCritical() ? " (critical)" : "")); + pww.appendLine("Preferred Symmetric Algorithms: " + Arrays.toString(symAlgs) + (preferredSymmetricAlgorithms.isCritical() ? " (critical)" : "")); break; case revocationKey: RevocationKey revocationKey = (RevocationKey) subpacket; - sbw.appendLine("Revocation Key" + (revocationKey.isCritical() ? " (critical)" : "") + ":").iind() + pww.appendLine("Revocation Key" + (revocationKey.isCritical() ? " (critical)" : "") + ":").iind() .appendLine("Key Algorithm: " + PublicKeyAlgorithm.fromId(revocationKey.getAlgorithm())) .appendLine("Signature Class: " + revocationKey.getSignatureClass()) .appendLine("Fingerprint: " + new String(revocationKey.getFingerprint())) @@ -353,11 +444,11 @@ public class StreamDumper { break; case issuerKeyId: IssuerKeyID issuerKeyID = (IssuerKeyID) subpacket; - sbw.appendLine("Issuer Key ID: " + Long.toHexString(issuerKeyID.getKeyID()) + (issuerKeyID.isCritical() ? " (critical)" : "")); + pww.appendLine("Issuer Key ID: " + Long.toHexString(issuerKeyID.getKeyID()) + (issuerKeyID.isCritical() ? " (critical)" : "")); break; case notationData: NotationData notationData = (NotationData) subpacket; - sbw.appendLine("Notation Data" + (notationData.isCritical() ? " (critical)" : "") + ":").iind() + pww.appendLine("Notation Data" + (notationData.isCritical() ? " (critical)" : "") + ":").iind() .appendLine("Notation Name: " + notationData.getNotationName()) .appendLine("Notation Value: " + notationData.getNotationValue()) .dind(); @@ -369,7 +460,7 @@ public class StreamDumper { for (int i = 0; i < hashAlgs.length; i++) { hashAlgs[i] = HashAlgorithm.fromId(hashAlgIds[i]); } - sbw.appendLine("Preferred Hash Algorithms: " + Arrays.toString(hashAlgs) + (preferredHashAlgorithms.isCritical() ? " (critical)" : "")); + pww.appendLine("Preferred Hash Algorithms: " + Arrays.toString(hashAlgs) + (preferredHashAlgorithms.isCritical() ? " (critical)" : "")); break; case preferredCompressionAlgorithms: PreferredAlgorithms preferredCompressionAlgorithms = (PreferredAlgorithms) subpacket; @@ -378,44 +469,44 @@ public class StreamDumper { for (int i = 0; i < compAlgs.length; i++) { compAlgs[i] = CompressionAlgorithm.fromId(compAlgIds[i]); } - sbw.appendLine("Preferred Compression Algorithms: " + Arrays.toString(compAlgs) + (preferredCompressionAlgorithms.isCritical() ? " (critical)" : "")); + pww.appendLine("Preferred Compression Algorithms: " + Arrays.toString(compAlgs) + (preferredCompressionAlgorithms.isCritical() ? " (critical)" : "")); break; case keyServerPreferences: KeyServerPreferences preferences = new KeyServerPreferences(subpacket); - sbw.appendLine("Key Server Preferences: " + Arrays.toString(preferences.getPreferences().toArray(new KeyServerPreferences.Pref[0]))); + pww.appendLine("Key Server Preferences: " + Arrays.toString(preferences.getPreferences().toArray(new KeyServerPreferences.Pref[0]))); break; case preferredKeyServers: - sbw.appendLine("Preferred Key Servers: " + new String(subpacket.getData())); + pww.appendLine("Preferred Key Servers: " + new String(subpacket.getData())); break; case primaryUserId: PrimaryUserID primaryUserID = (PrimaryUserID) subpacket; - sbw.appendLine("Primary User-ID: " + primaryUserID.isPrimaryUserID() + (primaryUserID.isCritical() ? " (critical)" : "")); + pww.appendLine("Primary User-ID: " + primaryUserID.isPrimaryUserID() + (primaryUserID.isCritical() ? " (critical)" : "")); break; case policyUrl: - sbw.appendLine("Policy-URL: " + new String(subpacket.getData())); + pww.appendLine("Policy-URL: " + new String(subpacket.getData())); break; case keyFlags: KeyFlags keyFlags = (KeyFlags) subpacket; KeyFlag[] flags = KeyFlag.fromBitmask(keyFlags.getFlags()).toArray(new KeyFlag[0]); - sbw.appendLine("Key Flags: " + Arrays.toString(flags) + (keyFlags.isCritical() ? " (critical)" : "")); + pww.appendLine("Key Flags: " + Arrays.toString(flags) + (keyFlags.isCritical() ? " (critical)" : "")); break; case signerUserId: SignerUserID signerUserID = (SignerUserID) subpacket; - sbw.appendLine("Signer User-ID: " + signerUserID.getID() + (signerUserID.isCritical() ? " (critical)" : "")); + pww.appendLine("Signer User-ID: " + signerUserID.getID() + (signerUserID.isCritical() ? " (critical)" : "")); break; case revocationReason: RevocationReason revocationReason = (RevocationReason) subpacket; - sbw.appendLine("Revocation Reason: " + RevocationAttributes.Reason.fromCode(revocationReason.getRevocationReason()) + (revocationReason.isCritical() ? " (critical)" : "")).iind() + pww.appendLine("Revocation Reason: " + RevocationAttributes.Reason.fromCode(revocationReason.getRevocationReason()) + (revocationReason.isCritical() ? " (critical)" : "")).iind() .appendLine("Description: " + revocationReason.getRevocationDescription()).dind(); break; case features: Features features = (Features) subpacket; Feature[] featurez = Feature.fromBitmask(features.getFeatures()).toArray(new Feature[0]); - sbw.appendLine("Features: " + Arrays.toString(featurez) + (features.isCritical() ? " (critical)" : "")); + pww.appendLine("Features: " + Arrays.toString(featurez) + (features.isCritical() ? " (critical)" : "")); break; case signatureTarget: SignatureTarget signatureTarget = (SignatureTarget) subpacket; - sbw.appendLine("Signature Target" + (signatureTarget.isCritical() ? " (critical)" : "" + ":")).iind() + pww.appendLine("Signature Target" + (signatureTarget.isCritical() ? " (critical)" : "" + ":")).iind() .appendLine("Public Key Algorithm: " + PublicKeyAlgorithm.fromId(signatureTarget.getPublicKeyAlgorithm())) .appendLine("Hash Algorithm: " + HashAlgorithm.fromId(signatureTarget.getHashAlgorithm())) .appendLine("Hash Data: " + Hex.toHexString(signatureTarget.getHashData())) @@ -423,74 +514,76 @@ public class StreamDumper { break; case embeddedSignature: EmbeddedSignature embeddedSignature = (EmbeddedSignature) subpacket; - sbw.appendLine("Embedded Signature" + (embeddedSignature.isCritical() ? " (critical)" : "") + ":").iind(); - appendSignature(sbw, embeddedSignatures.get(embeddedSigCount++)); + pww.appendLine("Embedded Signature" + (embeddedSignature.isCritical() ? " (critical)" : "") + ":").iind(); + appendSignature(pww, embeddedSignatures.get(embeddedSigCount++)); break; case issuerFingerprint: IssuerFingerprint issuerFingerprint = (IssuerFingerprint) subpacket; - sbw.appendLine("Issuer Fingerprint: " + Hex.toHexString(issuerFingerprint.getFingerprint()) + (issuerFingerprint.isCritical() ? " (critical)" : "")); + pww.appendLine("Issuer Fingerprint: " + Hex.toHexString(issuerFingerprint.getFingerprint()) + (issuerFingerprint.isCritical() ? " (critical)" : "")); break; case preferredAEADAlgorithms: PreferredAlgorithms preferredAEADAlgorithms = (PreferredAlgorithms) subpacket; int[] aeadAlgIds = preferredAEADAlgorithms.getPreferences(); - sbw.appendLine("Preferred AEAD Algorithms: " + Arrays.toString(aeadAlgIds) + (preferredAEADAlgorithms.isCritical() ? " (critical)" : "")); + pww.appendLine("Preferred AEAD Algorithms: " + Arrays.toString(aeadAlgIds) + (preferredAEADAlgorithms.isCritical() ? " (critical)" : "")); break; case intendedRecipientFingerprint: IntendedRecipientFingerprint intendedRecipientFingerprint = (IntendedRecipientFingerprint) subpacket; - sbw.appendLine("Intended Recipient Fingerprint" + (intendedRecipientFingerprint.isCritical() ? " critical" : "") + ":").iind() + pww.appendLine("Intended Recipient Fingerprint" + (intendedRecipientFingerprint.isCritical() ? " critical" : "") + ":").iind() .appendLine("Key Version: " + intendedRecipientFingerprint.getKeyVersion()) .appendLine("Fingerprint: " + Hex.toHexString(intendedRecipientFingerprint.getFingerprint())) .dind(); break; case attestedCertification: - sbw.appendLine("Attested Certification: " + new String(subpacket.getData())); + pww.appendLine("Attested Certification: " + new String(subpacket.getData())); break; default: - sbw.appendLine("Experimental Subpacket (Tag " + subpacket.getType() + ")" + (subpacket.isCritical() ? " (critical)" : "") + ":" + new String(subpacket.getData())); + int type = subpacket.getType(); + if (type >= 60 && type <= 63) { + pww.appendLine("Experimental Subpacket (Tag " + type + ")" + (subpacket.isCritical() ? " (critical)" : "") + ":" + new String(subpacket.getData())); + } else { + pww.appendLine("Unknown Subpacket (Tag " + type + ")" + (subpacket.isCritical() ? " (critical)" : "") + ":" + new String(subpacket.getData())); + } } } } - public static class StringBuilderWrapper { + public static class PrintWriterWrapper { private final int spacesPerLevel = 2; - private final StringBuilder sb; + private final PrintWriter pw; private int indentationLevel = 0; - public StringBuilderWrapper(StringBuilder sb) { - this.sb = sb; + public PrintWriterWrapper(PrintWriter pw) { + this.pw = pw; } - public StringBuilderWrapper appendLine(String line) { + public PrintWriterWrapper appendLine(String line) { spaces(); - sb.append(line).append('\n'); + pw.write(line); + pw.write('\n'); return this; } - public StringBuilderWrapper iind() { + public PrintWriterWrapper iind() { indentationLevel++; return this; } - public StringBuilderWrapper dind() { + public PrintWriterWrapper dind() { indentationLevel--; return this; } - public StringBuilderWrapper emptyLine() { - sb.append('\n'); + public PrintWriterWrapper emptyLine() { + pw.write('\n'); return this; } - private StringBuilderWrapper spaces() { + private PrintWriterWrapper spaces() { for (int i = 0; i < indentationLevel * spacesPerLevel; i++) { - sb.append(' '); + pw.write(' '); } return this; } - - public String toString() { - return sb.toString(); - } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/test/java/org/pgpainless/util/StreamDumpTest.java b/pgpainless-core/src/test/java/org/pgpainless/util/StreamDumpTest.java index f8324826..63d0eb03 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/util/StreamDumpTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/util/StreamDumpTest.java @@ -1,22 +1,36 @@ +// SPDX-FileCopyrightText: 2021 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + package org.pgpainless.util; import java.io.ByteArrayInputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; +import java.io.InputStream; +import java.nio.charset.Charset; import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPSessionKey; -import org.bouncycastle.util.encoders.Hex; import org.junit.jupiter.api.Test; -import org.pgpainless.algorithm.SymmetricKeyAlgorithm; public class StreamDumpTest { - @Test - public void test() throws IOException, PGPException { + private static InputStream stringToStream(String string) throws IOException { + return new ArmoredInputStream(new ByteArrayInputStream(string.getBytes(Charset.forName("UTF8")))); + } - String secretKey = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + + private static void printDataName(String name) { + // CHECKSTYLE:OFF + System.out.println("##############################################"); + System.out.println(name); + System.out.println("##############################################"); + // CHECKSTYLE:ON + } + + @Test + public void dumpSecretKey() throws IOException, PGPException { + String data = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + "\n" + "lFgEWx6DORYJKwYBBAHaRw8BAQdABJa6xH6/nQoBQtVuqaenNLrKvkJ5gniGtBH3\n" + "tsK+ckkAAP9uxXBqYoH/Kh+rjNMKRO6pgdkoYTYvMh5TVcQHR6LzoA+ttCxFbW1l\n" + @@ -34,7 +48,14 @@ public class StreamDumpTest { "=HYsE\n" + "-----END PGP PRIVATE KEY BLOCK-----\n"; - String encryptedMessage = "-----BEGIN PGP MESSAGE-----\n" + + printDataName("Secret Key"); + InputStream inputStream = stringToStream(data); + StreamDumper.dump(inputStream, null, System.out); + } + + @Test + public void dumpEncryptedMessage() throws IOException, PGPException { + String data = "-----BEGIN PGP MESSAGE-----\n" + "\n" + "wV4Di9iOlMDSAzMSAQdAu/2VmD0uZASFHqAD0IVNq7C8rdsJ+ZQd2nQsuBilygUw\n" + "9bK+bOzU6ksTZgKgdAjO8zpvM+N0B3L0TtiwLr5rj0rPkCyVLdACnBpWOCZCMpsK\n" + @@ -42,9 +63,74 @@ public class StreamDumpTest { "r66Eaq8vCTmJpcsy0BYTiQ==\n" + "=FY/Q\n" + "-----END PGP MESSAGE-----\n"; - PGPSessionKey sessionKey = new PGPSessionKey(SymmetricKeyAlgorithm.AES_256.getAlgorithmId(), Hex.decode("920B1779565C8DF4DD9DB46966CDF2B51BC882C241DE8EF437CADEC711E7EB04")); - String signedMessage = "-----BEGIN PGP MESSAGE-----\n" + + printDataName("Encrypted Message"); + InputStream inputStream = stringToStream(data); + StreamDumper.dump(inputStream, null, System.out); + } + + @Test + public void dumpEncryptedMessage_withSessionKey() throws PGPException, IOException { + String data = "-----BEGIN PGP MESSAGE-----\n" + + "\n" + + "wV4Di9iOlMDSAzMSAQdAu/2VmD0uZASFHqAD0IVNq7C8rdsJ+ZQd2nQsuBilygUw\n" + + "9bK+bOzU6ksTZgKgdAjO8zpvM+N0B3L0TtiwLr5rj0rPkCyVLdACnBpWOCZCMpsK\n" + + "0j4B4um24+oCDHtxRu1e1IvsboBtGN9ElxidGAiUdPJ3L0QrNVgzdmVTwuywtIHW\n" + + "r66Eaq8vCTmJpcsy0BYTiQ==\n" + + "=FY/Q\n" + + "-----END PGP MESSAGE-----\n"; + PGPSessionKey sessionKey = PGPSessionKey + .fromAsciiRepresentation("9:920B1779565C8DF4DD9DB46966CDF2B51BC882C241DE8EF437CADEC711E7EB04"); + + printDataName("Encrypted Message (with Session Key)"); + InputStream inputStream = stringToStream(data); + StreamDumper.dump(inputStream, sessionKey, System.out); + } + + @Test + public void dumpEncryptedMessage_withWrongSessionKey() throws PGPException, IOException { + String data = "-----BEGIN PGP MESSAGE-----\n" + + "\n" + + "wV4Di9iOlMDSAzMSAQdAu/2VmD0uZASFHqAD0IVNq7C8rdsJ+ZQd2nQsuBilygUw\n" + + "9bK+bOzU6ksTZgKgdAjO8zpvM+N0B3L0TtiwLr5rj0rPkCyVLdACnBpWOCZCMpsK\n" + + "0j4B4um24+oCDHtxRu1e1IvsboBtGN9ElxidGAiUdPJ3L0QrNVgzdmVTwuywtIHW\n" + + "r66Eaq8vCTmJpcsy0BYTiQ==\n" + + "=FY/Q\n" + + "-----END PGP MESSAGE-----\n"; + PGPSessionKey sessionKey = PGPSessionKey + .fromAsciiRepresentation("9:920B1779565C8DF4DD9DB46966CDF2B51BC882C241DE8EF437CADEC711E7EBFF"); + + printDataName("Encrypted Message (with Wrong Session Key)"); + InputStream inputStream = stringToStream(data); + StreamDumper.dump(inputStream, sessionKey, System.out); + } + + @Test + public void dumpEncryptedMessageForTwoRecipients() throws PGPException, IOException { + String data = "-----BEGIN PGP MESSAGE-----\n" + + "\n" + + "wV4Di9iOlMDSAzMSAQdAALX6636VfMV+kljW5l6AfcRtnp9RpCO7GG/UrMhpKCgw\n" + + "sx1mBvZsfw6y0MUJgNoOHjAlFhxl7SgjH+hVxCs1EjV+BiFm5XcH0Sz3x0AmT4Ev\n" + + "wcBMA0niEYFmySYyAQf/a3QjVA5qxQhD/22JGkvt0EsnOebIEKoA4IqUm0bHOfzH\n" + + "yiolVp2e8TDGTGDfuPGZe7kWACN5xMshoinSOw5vlGl0OYQt5kqO9ihk8SUtFz/n\n" + + "zpjP4R8g5XfHQ6A8aTm7XzjcYiImYJcanvqsPz1oVbXUFjgjWP+IdbGEH2+oahrO\n" + + "TYTkaIIFSPENjhwhELA5J6whdYIGPAd4Flqa885Bwq7/mJtizGSUqfMm/vy9Ynyv\n" + + "PM9gO44U6f5WNJjVORAAvGRs+yNoDemOR+Tk5lhq69gUysRbTDFKvalAnIg+jDCs\n" + + "gov/OsSifJfMJpfkp6B/eq1g3VUxXfXMH9JlpUI1r9I+Ac9cg2nmvMJEYndoeinS\n" + + "pXaFOPETcoKe502lXn5nb03sGMBD8jRXd1RiFvvB3emqIczBe1r4a7ntVfDp5Hs=\n" + + "=QCSD\n" + + "-----END PGP MESSAGE-----\n"; + PGPSessionKey sessionKey = PGPSessionKey + .fromAsciiRepresentation("9:092F816748B2C6FCA49130E931F9DDCF46E4106CE3C4A8437AB660E0C6FED0A1"); + + printDataName("Encrypted Message for Two Recipients"); + InputStream inputStream = stringToStream(data); + StreamDumper.dump(inputStream, sessionKey, System.out); + } + + @Test + public void dumpSignedMessage() throws PGPException, IOException { + String data = "-----BEGIN PGP MESSAGE-----\n" + "\n" + "xA0DAAgB0D9vhlIm/osBxA0DAAoWCTXiD9yZ2YYByxRiAAAAAABIZWxsbywgd29y\n" + "bGQhCsJ1BAAWCgAnBYJcdpUyFiEEjeZMrRdY/BlFYM9ZCTXiD9yZ2YYJEAk14g/c\n" + @@ -60,7 +146,14 @@ public class StreamDumpTest { "=BDwS\n" + "-----END PGP MESSAGE-----\n"; - String compressedSignedMessage = "-----BEGIN PGP MESSAGE-----\n" + + printDataName("Signed Message"); + InputStream inputStream = stringToStream(data); + StreamDumper.dump(inputStream, null, System.out); + } + + @Test + public void dumpSignedCompressedMessage() throws PGPException, IOException { + String data = "-----BEGIN PGP MESSAGE-----\n" + "\n" + "owGbwMvMwCH2sPOSUOzzWymMp0WSGGK6InU9UnNy8nUUyvOLclIUuTpKWRjEOBhk\n" + "xRRZ8jZHv1c9Zfp0SurkSJguViaQFgYuTgGYSJ09w/8M9xn6J9em8Bvs3rHgYd+G\n" + @@ -69,8 +162,8 @@ public class StreamDumpTest { "=56Gw\n" + "-----END PGP MESSAGE-----"; - ArmoredInputStream inputStream = new ArmoredInputStream(new ByteArrayInputStream(secretKey.getBytes(StandardCharsets.UTF_8))); - - StreamDumper.dump(inputStream, sessionKey); + printDataName("Signed Compressed Data"); + InputStream inputStream = stringToStream(data); + StreamDumper.dump(inputStream, null, System.out); } }