diff --git a/pg/src/main/java/org/bouncycastle/bcpg/OnePassSignaturePacket.java b/pg/src/main/java/org/bouncycastle/bcpg/OnePassSignaturePacket.java index d923019dbe..b00f45fcc4 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/OnePassSignaturePacket.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/OnePassSignaturePacket.java @@ -1,5 +1,8 @@ package org.bouncycastle.bcpg; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.io.Streams; + import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -9,12 +12,17 @@ public class OnePassSignaturePacket extends ContainedPacket { - private int version; - private int sigType; - private int hashAlgorithm; - private int keyAlgorithm; - private long keyID; - private int isContaining; + public static final int VERSION_3 = 3; + public static final int VERSION_6 = 6; + + private final int version; // v3, v6 + private final int sigType; // v3, v6 + private final int hashAlgorithm; // v3, v6 + private final int keyAlgorithm; // v3, v6 + private final byte[] salt; // v6 + private final long keyID; // v3 + private final byte[] keyFingerprint; // v6 + private final int isContaining; // v3, v6 OnePassSignaturePacket( BCPGInputStream in) @@ -24,19 +32,80 @@ public class OnePassSignaturePacket sigType = in.read(); hashAlgorithm = in.read(); keyAlgorithm = in.read(); - - keyID |= (long)in.read() << 56; - keyID |= (long)in.read() << 48; - keyID |= (long)in.read() << 40; - keyID |= (long)in.read() << 32; - keyID |= (long)in.read() << 24; - keyID |= (long)in.read() << 16; - keyID |= (long)in.read() << 8; - keyID |= in.read(); - - isContaining = in.read(); + + if (version == VERSION_3) + { + long keyID = 0; + keyID |= (long) in.read() << 56; + keyID |= (long) in.read() << 48; + keyID |= (long) in.read() << 40; + keyID |= (long) in.read() << 32; + keyID |= (long) in.read() << 24; + keyID |= (long) in.read() << 16; + keyID |= (long) in.read() << 8; + keyID |= in.read(); + this.keyID = keyID; + + isContaining = in.read(); + + this.salt = null; + this.keyFingerprint = null; + } + else if (version == VERSION_6) + { + keyID = 0; + int expectedSaltSize = SignaturePacket.getSaltSize(hashAlgorithm); + int saltSize = in.read(); + if (saltSize != expectedSaltSize) + { + Streams.drain(in); + throw new UnsupportedPacketVersionException("Unexpected salt size " + expectedSaltSize + ", got " + saltSize); + } + + salt = new byte[saltSize]; + in.readFully(salt); + + keyFingerprint = new byte[32]; + in.readFully(keyFingerprint); + + isContaining = in.read(); + } + else + { + Streams.drain(in); + throw new UnsupportedPacketVersionException("Unsupported One-Pass-Signature packet version encountered: " + version); + } } - + + public static OnePassSignaturePacket createVersion3Packet( + int sigType, + int hashAlgorithm, + int keyAlgorithm, + long keyID, + boolean isNested) + { + return new OnePassSignaturePacket(sigType, hashAlgorithm, keyAlgorithm, keyID, isNested); + } + + public static OnePassSignaturePacket createVersion6Packet( + int sigType, + int hashAlgorithm, + int keyAlgorithm, + byte[] salt, + byte[] keyFingerprint, + boolean isNested) + { + return new OnePassSignaturePacket(sigType, hashAlgorithm, keyAlgorithm, salt, keyFingerprint, isNested); + } + + /** + * Create an OPS packet of version {@link #VERSION_3}. + * @param sigType signature type + * @param hashAlgorithm hash algorithm identifier + * @param keyAlgorithm public key algorithm identifier + * @param keyID key id + * @param isNested is nested flag + */ public OnePassSignaturePacket( int sigType, int hashAlgorithm, @@ -44,14 +113,55 @@ public OnePassSignaturePacket( long keyID, boolean isNested) { - this.version = 3; - this.sigType = sigType; + this(VERSION_3, sigType, hashAlgorithm, keyAlgorithm, null, keyID, null, isNested); + } + + /** + * Create an OPS packet of version {@link #VERSION_6}. + * + * @param sigType signature type + * @param hashAlgorithm hash algorithm identifier + * @param keyAlgorithm public key algorithm identifier + * @param salt salt + * @param keyFingerprint key fingerprint + * @param isNested is nested flag + */ + public OnePassSignaturePacket( + int sigType, + int hashAlgorithm, + int keyAlgorithm, + byte[] salt, + byte[] keyFingerprint, + boolean isNested) + { + this(VERSION_6, sigType, hashAlgorithm, keyAlgorithm, salt, 0, keyFingerprint, isNested); + } + + public OnePassSignaturePacket( + int version, + int signatureType, + int hashAlgorithm, + int keyAlgorithm, + byte[] salt, + long keyID, + byte[] keyFingerprint, + boolean isNested) + { + this.version = version; + this.sigType = signatureType; this.hashAlgorithm = hashAlgorithm; this.keyAlgorithm = keyAlgorithm; this.keyID = keyID; - this.isContaining = (isNested) ? 0 : 1; + this.keyFingerprint = keyFingerprint; + this.salt = salt; + this.isContaining = isNested ? 0 : 1; } - + + public int getVersion() + { + return version; + } + /** * Return the signature type. * @return the signature type @@ -76,8 +186,24 @@ public int getHashAlgorithm() { return hashAlgorithm; } + + /** + * Return the salt of the signature. + * Only for {@link #VERSION_6}, returns
nullotherwise. + * + * @return salt + */ + public byte[] getSalt() { + if (salt != null) { + return Arrays.clone(salt); + } + return null; + } /** + * Return the key-id of the issuing key. + * Only for {@link #VERSION_3}. Signatures of version {@link #VERSION_6} use {@link #getKeyFingerprint()} instead. + * * @return long */ public long getKeyID() @@ -85,6 +211,19 @@ public long getKeyID() return keyID; } + /** + * Return the v6 fingerprint of the issuing key. + * Only for {@link #VERSION_6}. Signatures of version {@link #VERSION_3} use {@link #getKeyID()} instead. + * + * @return 32 byte array + */ + public byte[] getKeyFingerprint() { + if (keyFingerprint != null) { + return Arrays.clone(keyFingerprint); + } + return null; + } + /** * Return true, if the signature contains any signatures that follow. * An bracketing OPS is followed by additional OPS packets and is calculated over all the data between itself @@ -112,14 +251,28 @@ public void encode( pOut.write(hashAlgorithm); pOut.write(keyAlgorithm); - pOut.write((byte)(keyID >> 56)); - pOut.write((byte)(keyID >> 48)); - pOut.write((byte)(keyID >> 40)); - pOut.write((byte)(keyID >> 32)); - pOut.write((byte)(keyID >> 24)); - pOut.write((byte)(keyID >> 16)); - pOut.write((byte)(keyID >> 8)); - pOut.write((byte)(keyID)); + if (version == VERSION_3) + { + pOut.write((byte) (keyID >> 56)); + pOut.write((byte) (keyID >> 48)); + pOut.write((byte) (keyID >> 40)); + pOut.write((byte) (keyID >> 32)); + pOut.write((byte) (keyID >> 24)); + pOut.write((byte) (keyID >> 16)); + pOut.write((byte) (keyID >> 8)); + pOut.write((byte) (keyID)); + } + else if (version == VERSION_6) + { + pOut.write(salt.length); + pOut.write(salt); + + pOut.write(keyFingerprint); + } + else + { + throw new UnsupportedPacketVersionException("Unsupported One-Pass-Signature version encountered: " + version); + } pOut.write(isContaining); diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java index 5e3eac35b0..251f880c24 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java @@ -13,9 +13,14 @@ /** * generic signature packet */ -public class SignaturePacket +public class SignaturePacket extends ContainedPacket implements PublicKeyAlgorithmTags { + public static final int VERSION_3 = 3; + public static final int VERSION_4 = 4; // https://datatracker.ietf.org/doc/rfc4880/ + public static final int VERSION_5 = 5; // https://datatracker.ietf.org/doc/draft-koch-openpgp-2015-rfc4880bis/ + public static final int VERSION_6 = 6; // https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/ + private int version; private int signatureType; private long creationTime; @@ -27,216 +32,177 @@ public class SignaturePacket private SignatureSubpacket[] hashedData; private SignatureSubpacket[] unhashedData; private byte[] signatureEncoding; - - SignaturePacket( - BCPGInputStream in) - throws IOException + private byte[] salt = null; // v6 only + + public static SignaturePacket createVersion3Packet( + int signatureType, + long keyID, + int keyAlgorithm, + int hashAlgorithm, + long creationTime, + byte[] fingerPrint, + MPInteger[] signature) { - version = in.read(); - - if (version == 3 || version == 2) - { - int l = in.read(); - - signatureType = in.read(); - creationTime = (((long)in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read()) * 1000; - keyID |= (long)in.read() << 56; - keyID |= (long)in.read() << 48; - keyID |= (long)in.read() << 40; - keyID |= (long)in.read() << 32; - keyID |= (long)in.read() << 24; - keyID |= (long)in.read() << 16; - keyID |= (long)in.read() << 8; - keyID |= in.read(); - keyAlgorithm = in.read(); - hashAlgorithm = in.read(); - } - else if (version == 4) - { - signatureType = in.read(); - keyAlgorithm = in.read(); - hashAlgorithm = in.read(); - - int hashedLength = (in.read() << 8) | in.read(); - byte[] hashed = new byte[hashedLength]; - - in.readFully(hashed); + SignaturePacket signaturePacket = new SignaturePacket( + VERSION_3, + signatureType, + keyID, + keyAlgorithm, + hashAlgorithm, + null, + null, + fingerPrint, + null, + signature); - // - // read the signature sub packet data. - // - SignatureSubpacket sub; - SignatureSubpacketInputStream sIn = new SignatureSubpacketInputStream( - new ByteArrayInputStream(hashed)); + signaturePacket.creationTime = creationTime; - Vector v = new Vector(); - while ((sub = sIn.readPacket()) != null) - { - v.addElement(sub); - } - - hashedData = new SignatureSubpacket[v.size()]; - - for (int i = 0; i != hashedData.length; i++) - { - SignatureSubpacket p = (SignatureSubpacket)v.elementAt(i); - if (p instanceof IssuerKeyID) - { - keyID = ((IssuerKeyID)p).getKeyID(); - } - else if (p instanceof SignatureCreationTime) - { - creationTime = ((SignatureCreationTime)p).getTime().getTime(); - } - - hashedData[i] = p; - } - - int unhashedLength = (in.read() << 8) | in.read(); - byte[] unhashed = new byte[unhashedLength]; - - in.readFully(unhashed); - - sIn = new SignatureSubpacketInputStream( - new ByteArrayInputStream(unhashed)); - - v.removeAllElements(); - while ((sub = sIn.readPacket()) != null) - { - v.addElement(sub); - } - - unhashedData = new SignatureSubpacket[v.size()]; - - for (int i = 0; i != unhashedData.length; i++) - { - SignatureSubpacket p = (SignatureSubpacket)v.elementAt(i); - if (p instanceof IssuerKeyID) - { - keyID = ((IssuerKeyID)p).getKeyID(); - } - - unhashedData[i] = p; - } - } - else - { - Streams.drain(in); + return signaturePacket; + } - throw new UnsupportedPacketVersionException("unsupported version: " + version); - } - - fingerPrint = new byte[2]; - in.readFully(fingerPrint); - - switch (keyAlgorithm) - { - case RSA_GENERAL: - case RSA_SIGN: - MPInteger v = new MPInteger(in); - - signature = new MPInteger[1]; - signature[0] = v; - break; - case DSA: - MPInteger r = new MPInteger(in); - MPInteger s = new MPInteger(in); - - signature = new MPInteger[2]; - signature[0] = r; - signature[1] = s; - break; - case ELGAMAL_ENCRYPT: // yep, this really does happen sometimes. - case ELGAMAL_GENERAL: - MPInteger p = new MPInteger(in); - MPInteger g = new MPInteger(in); - MPInteger y = new MPInteger(in); - - signature = new MPInteger[3]; - signature[0] = p; - signature[1] = g; - signature[2] = y; - break; - case ECDSA: - case EDDSA_LEGACY: - MPInteger ecR = new MPInteger(in); - MPInteger ecS = new MPInteger(in); - - signature = new MPInteger[2]; - signature[0] = ecR; - signature[1] = ecS; - break; - default: - if (keyAlgorithm >= PublicKeyAlgorithmTags.EXPERIMENTAL_1 && keyAlgorithm <= PublicKeyAlgorithmTags.EXPERIMENTAL_11) - { - signature = null; - signatureEncoding = Streams.readAll(in); - } - else - { - throw new IOException("unknown signature key algorithm: " + keyAlgorithm); - } - } + public static SignaturePacket createVersion4Packet( + int signatureType, + long keyID, + int keyAlgorithm, + int hashAlgorithm, + SignatureSubpacket[] hashedData, + SignatureSubpacket[] unhashedData, + byte[] fingerPrint, + MPInteger[] signature) + { + return new SignaturePacket( + VERSION_4, + signatureType, + keyID, + keyAlgorithm, + hashAlgorithm, + hashedData, + unhashedData, + fingerPrint, + null, + signature); } - + + public static SignaturePacket createVersion5Packet( + int signatureType, + long keyID, + int keyAlgorithm, + int hashAlgorithm, + SignatureSubpacket[] hashedData, + SignatureSubpacket[] unhashedData, + byte[] fingerPrint, + MPInteger[] signature) + { + return new SignaturePacket( + VERSION_5, + signatureType, + keyID, + keyAlgorithm, + hashAlgorithm, + hashedData, + unhashedData, + fingerPrint, + null, + signature); + } + + public static SignaturePacket createVersion6Packet( + int signatureType, + long keyID, + int keyAlgorithm, + int hashAlgorithm, + SignatureSubpacket[] hashedData, + SignatureSubpacket[] unhashedData, + byte[] fingerPrint, + byte[] salt, + MPInteger[] signature) + { + return new SignaturePacket( + VERSION_6, + signatureType, + keyID, + keyAlgorithm, + hashAlgorithm, + hashedData, + unhashedData, + fingerPrint, + salt, + signature); + } + /** * Generate a version 4 signature packet. - * - * @param signatureType - * @param keyAlgorithm - * @param hashAlgorithm - * @param hashedData - * @param unhashedData - * @param fingerPrint - * @param signature + * + * @param signatureType signature type + * @param keyAlgorithm signature algorithm + * @param hashAlgorithm hash algorithm + * @param hashedData hashed signature subpackets + * @param unhashedData unhashed signature subpackets + * @param fingerPrint signature fingerprint + * @param signature signature */ public SignaturePacket( - int signatureType, - long keyID, - int keyAlgorithm, - int hashAlgorithm, - SignatureSubpacket[] hashedData, - SignatureSubpacket[] unhashedData, - byte[] fingerPrint, - MPInteger[] signature) + int signatureType, + long keyID, + int keyAlgorithm, + int hashAlgorithm, + SignatureSubpacket[] hashedData, + SignatureSubpacket[] unhashedData, + byte[] fingerPrint, + MPInteger[] signature) { - this(4, signatureType, keyID, keyAlgorithm, hashAlgorithm, hashedData, unhashedData, fingerPrint, signature); + this(VERSION_4, signatureType, keyID, keyAlgorithm, hashAlgorithm, hashedData, unhashedData, fingerPrint, null, signature); } - + /** * Generate a version 2/3 signature packet. - * - * @param signatureType - * @param keyAlgorithm - * @param hashAlgorithm - * @param fingerPrint - * @param signature + * + * @param version version + * @param signatureType signature type + * @param keyAlgorithm signature algorithm + * @param hashAlgorithm hash algorithm + * @param fingerPrint signature fingerprint + * @param signature signature */ public SignaturePacket( - int version, - int signatureType, - long keyID, - int keyAlgorithm, - int hashAlgorithm, - long creationTime, - byte[] fingerPrint, - MPInteger[] signature) + int version, + int signatureType, + long keyID, + int keyAlgorithm, + int hashAlgorithm, + long creationTime, + byte[] fingerPrint, + MPInteger[] signature) { - this(version, signatureType, keyID, keyAlgorithm, hashAlgorithm, null, null, fingerPrint, signature); - + this(version, signatureType, keyID, keyAlgorithm, hashAlgorithm, null, null, fingerPrint, null, signature); + this.creationTime = creationTime; } - + + /** + * Generate a signature packet. + * + * @param version signature version + * @param signatureType signature type + * @param keyID keyID of the signing key + * @param keyAlgorithm signature algorithm + * @param hashAlgorithm hash algorithm + * @param hashedData hashed signature subpackets + * @param unhashedData unhashed signature subpackets + * @param fingerPrint signature fingerprint + * @param signature signature + */ public SignaturePacket( - int version, - int signatureType, - long keyID, - int keyAlgorithm, - int hashAlgorithm, - SignatureSubpacket[] hashedData, - SignatureSubpacket[] unhashedData, - byte[] fingerPrint, - MPInteger[] signature) + int version, + int signatureType, + long keyID, + int keyAlgorithm, + int hashAlgorithm, + SignatureSubpacket[] hashedData, + SignatureSubpacket[] unhashedData, + byte[] fingerPrint, + byte[] salt, + MPInteger[] signature) { this.version = version; this.signatureType = signatureType; @@ -246,6 +212,7 @@ public SignaturePacket( this.hashedData = hashedData; this.unhashedData = unhashedData; this.fingerPrint = fingerPrint; + this.salt = salt; this.signature = signature; if (hashedData != null) @@ -253,7 +220,244 @@ public SignaturePacket( setCreationTime(); } } - + + SignaturePacket( + BCPGInputStream in) + throws IOException + { + version = in.read(); + + // TODO: Version 2 is not specified. Clarify? + if (version == VERSION_3 || version == 2) + { + parseVersion2or3Packet(in); + } + else if (version == VERSION_4 || version == VERSION_5) + { + parseVersion4or5Packet(in); + } + else if (version == VERSION_6) + { + parseVersion6Packet(in); + } + else + { + Streams.drain(in); + throw new UnsupportedPacketVersionException("unsupported version: " + version); + } + } + + private void parseVersion2or3Packet(BCPGInputStream in) throws IOException { + int l = in.read(); // MUST be 5. Enforce? + + signatureType = in.read(); + creationTime = (((long) in.read() << 24) | ((long) in.read() << 16) | ((long) in.read() << 8) | in.read()) * 1000; + keyID |= (long) in.read() << 56; + keyID |= (long) in.read() << 48; + keyID |= (long) in.read() << 40; + keyID |= (long) in.read() << 32; + keyID |= (long) in.read() << 24; + keyID |= (long) in.read() << 16; + keyID |= (long) in.read() << 8; + keyID |= in.read(); + keyAlgorithm = in.read(); + hashAlgorithm = in.read(); + + // 2 byte fp + fingerPrint = readFingerprint(in); + + readSignature(in); + } + + private void parseVersion4or5Packet(BCPGInputStream in) throws IOException { + signatureType = in.read(); + keyAlgorithm = in.read(); + hashAlgorithm = in.read(); + + hashedData = readSubpackets(in, true); + unhashedData = readSubpackets(in, false); + + // 2 byte fp + fingerPrint = readFingerprint(in); + readSignature(in); + } + + private void parseVersion6Packet(BCPGInputStream in) throws IOException { + signatureType = in.read(); + keyAlgorithm = in.read(); + hashAlgorithm = in.read(); + + hashedData = readSubpackets(in, true); + unhashedData = readSubpackets(in, false); + + // 2 byte fp + fingerPrint = readFingerprint(in); + + readSalt(in); + + readSignature(in); + } + + private SignatureSubpacket[] readSubpackets(BCPGInputStream in, boolean isHashed) + throws IOException + { + int length = readLength(in); + byte[] data = new byte[length]; + + in.readFully(data); + + // + // read the signature sub packet data. + // + SignatureSubpacket sub; + SignatureSubpacketInputStream sIn = new SignatureSubpacketInputStream( + new ByteArrayInputStream(data)); + + Vector