Skip to content

Commit 87c7c97

Browse files
committed
Implement v6 packet parsing for OPS and SignaturePackets
1 parent ee6da85 commit 87c7c97

File tree

2 files changed

+451
-156
lines changed

2 files changed

+451
-156
lines changed

pg/src/main/java/org/bouncycastle/bcpg/OnePassSignaturePacket.java

Lines changed: 174 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,48 @@
11
package org.bouncycastle.bcpg;
22

3+
import org.bouncycastle.util.io.Streams;
4+
35
import java.io.ByteArrayOutputStream;
46
import java.io.IOException;
57

68
/**
7-
* generic signature object
9+
* One-Pass-Signature packet.
10+
* OPS packets are used to enable verification of signed messages in one-pass by providing necessary metadata
11+
* about the signed data up front, so the consumer can start processing the signed data without needing
12+
* to process the signature packet at the end of the data stream first.
13+
* <b>
14+
* There are two versions of this packet currently defined.
15+
* Version 3 OPS packets are used with {@link SignaturePacket SignaturePackets} of version 3 and 4.
16+
* Version 6 OPS packets are used with {@link SignaturePacket SignaturePackets} of version 6.
17+
* It is not clear to me, which version of the OPS packet is intended to be used with version 5 signatures.
18+
*
19+
* @see <a href="https://www.rfc-editor.org/rfc/rfc4880.html#section-5.4">
20+
* Definition of version 3 OPS packets in RFC4880</a>
21+
* @see <a href="https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-13.html#name-one-pass-signature-packet-t">
22+
* Definition of version 3 and 6 OPS packets in crypto-refresh</a>
23+
* @see <a href="https://www.ietf.org/archive/id/draft-koch-librepgp-00.html#section-5.4">
24+
* Definition of version 3 and 6 OPS packets in librepgp</a>
825
*/
926
public class OnePassSignaturePacket
1027
extends ContainedPacket
1128
{
12-
private int version;
13-
private int sigType;
14-
private int hashAlgorithm;
15-
private int keyAlgorithm;
16-
private long keyID;
17-
private int isContaining;
18-
29+
public static final int VERSION_3 = 3;
30+
public static final int VERSION_6 = 6;
31+
32+
private final int version;
33+
private final int sigType;
34+
private final int hashAlgorithm;
35+
private final int keyAlgorithm;
36+
private final long keyID;
37+
private final byte[] fingerprint;
38+
private final byte[] salt;
39+
private final int isContaining;
40+
41+
/**
42+
* Parse a {@link OnePassSignaturePacket} from an OpenPGP packet input stream.
43+
* @param in OpenPGP packet input stream
44+
* @throws IOException when the end of stream is prematurely reached, or when the packet is malformed
45+
*/
1946
OnePassSignaturePacket(
2047
BCPGInputStream in)
2148
throws IOException
@@ -26,19 +53,58 @@ public class OnePassSignaturePacket
2653
sigType = in.read();
2754
hashAlgorithm = in.read();
2855
keyAlgorithm = in.read();
29-
30-
keyID |= (long)in.read() << 56;
31-
keyID |= (long)in.read() << 48;
32-
keyID |= (long)in.read() << 40;
33-
keyID |= (long)in.read() << 32;
34-
keyID |= (long)in.read() << 24;
35-
keyID |= (long)in.read() << 16;
36-
keyID |= (long)in.read() << 8;
37-
keyID |= in.read();
38-
56+
57+
if (version == VERSION_3)
58+
{
59+
keyID = StreamUtil.readKeyID(in);
60+
fingerprint = null;
61+
salt = null;
62+
}
63+
else if (version == VERSION_6)
64+
{
65+
int saltLen = in.read();
66+
if (saltLen < 0) {
67+
throw new IOException("Version 6 OPS packet has invalid salt length.");
68+
}
69+
salt = new byte[saltLen];
70+
in.readFully(salt);
71+
72+
fingerprint = new byte[32];
73+
in.readFully(fingerprint);
74+
75+
// TODO: Replace with FingerprintUtil
76+
keyID = ((fingerprint[0] & 0xffL) << 56) |
77+
((fingerprint[1] & 0xffL) << 48) |
78+
((fingerprint[2] & 0xffL) << 40) |
79+
((fingerprint[3] & 0xffL) << 32) |
80+
((fingerprint[4] & 0xffL) << 24) |
81+
((fingerprint[5] & 0xffL) << 16) |
82+
((fingerprint[6] & 0xffL) << 8) |
83+
((fingerprint[7] & 0xffL));
84+
}
85+
else
86+
{
87+
Streams.drain(in);
88+
throw new UnsupportedPacketVersionException("Unsupported OnePassSignature packet version encountered: " + version);
89+
}
90+
3991
isContaining = in.read();
4092
}
41-
93+
94+
/**
95+
* Create a version 3 {@link OnePassSignaturePacket}.
96+
* Version 3 OPS packets are used with version 3 and version 4 {@link SignaturePacket SignaturePackets}.
97+
* <b>
98+
* To create an OPS packet for use with a version 6 {@link SignaturePacket},
99+
* see {@link OnePassSignaturePacket#OnePassSignaturePacket(int, int, int, byte[], byte[], boolean)}.
100+
*
101+
* @param sigType signature type
102+
* @param hashAlgorithm hash algorithm tag
103+
* @param keyAlgorithm public key algorithm tag
104+
* @param keyID id of the signing key
105+
* @param isNested if false, there is another OPS packet after this one, which applies to the same data.
106+
* it true, the corresponding signature is calculated also over succeeding additional OPS packets.
107+
*/
42108
public OnePassSignaturePacket(
43109
int sigType,
44110
int hashAlgorithm,
@@ -48,14 +114,63 @@ public OnePassSignaturePacket(
48114
{
49115
super(ONE_PASS_SIGNATURE);
50116

51-
this.version = 3;
117+
this.version = VERSION_3;
52118
this.sigType = sigType;
53119
this.hashAlgorithm = hashAlgorithm;
54120
this.keyAlgorithm = keyAlgorithm;
55121
this.keyID = keyID;
122+
this.fingerprint = null;
123+
this.salt = null;
56124
this.isContaining = (isNested) ? 0 : 1;
57125
}
58-
126+
127+
/**
128+
* Create a version 6 {@link OnePassSignaturePacket}.
129+
*
130+
* @param sigType signature type
131+
* @param hashAlgorithm hash algorithm tag
132+
* @param keyAlgorithm public key algorithm tag
133+
* @param salt random salt. The length of this array depends on the hash algorithm in use.
134+
* @param fingerprint 32 octet fingerprint of the (v6) signing key
135+
* @param isNested if false, there is another OPS packet after this one, which applies to the same data.
136+
* it true, the corresponding signature is calculated also over succeeding additional OPS packets.
137+
*/
138+
public OnePassSignaturePacket(
139+
int sigType,
140+
int hashAlgorithm,
141+
int keyAlgorithm,
142+
byte[] salt,
143+
byte[] fingerprint,
144+
boolean isNested)
145+
{
146+
super(ONE_PASS_SIGNATURE);
147+
148+
this.version = VERSION_6;
149+
this.sigType = sigType;
150+
this.hashAlgorithm = hashAlgorithm;
151+
this.keyAlgorithm = keyAlgorithm;
152+
this.salt = salt;
153+
this.fingerprint = fingerprint;
154+
this.isContaining = (isNested) ? 0 : 1;
155+
// TODO: Replace with FingerprintUtil
156+
keyID = ((fingerprint[0] & 0xffL) << 56) |
157+
((fingerprint[1] & 0xffL) << 48) |
158+
((fingerprint[2] & 0xffL) << 40) |
159+
((fingerprint[3] & 0xffL) << 32) |
160+
((fingerprint[4] & 0xffL) << 24) |
161+
((fingerprint[5] & 0xffL) << 16) |
162+
((fingerprint[6] & 0xffL) << 8) |
163+
((fingerprint[7] & 0xffL));
164+
}
165+
166+
/**
167+
* Return the packet version.
168+
* @return version
169+
*/
170+
public int getVersion() {
171+
return version;
172+
}
173+
59174
/**
60175
* Return the signature type.
61176
* @return the signature type
@@ -66,32 +181,53 @@ public int getSignatureType()
66181
}
67182

68183
/**
69-
* return the encryption algorithm tag
184+
* Return the ID of the public key encryption algorithm.
185+
* @return public key algorithm tag
70186
*/
71187
public int getKeyAlgorithm()
72188
{
73189
return keyAlgorithm;
74190
}
75191

76192
/**
77-
* return the hashAlgorithm tag
193+
* Return the algorithm ID of the hash algorithm.
194+
* @return hash algorithm tag
78195
*/
79196
public int getHashAlgorithm()
80197
{
81198
return hashAlgorithm;
82199
}
83200

84201
/**
85-
* @return long
202+
* Return the key-id of the signing key.
203+
* @return key id
86204
*/
87205
public long getKeyID()
88206
{
89207
return keyID;
90208
}
91209

210+
/**
211+
* Return the version 6 fingerprint of the issuer.
212+
* Only for version 6 packets.
213+
* @return 32 bytes issuer fingerprint
214+
*/
215+
public byte[] getFingerprint() {
216+
return fingerprint;
217+
}
218+
219+
/**
220+
* Return the salt used in the signature.
221+
* Only for version 6 packets.
222+
* @return salt
223+
*/
224+
public byte[] getSalt() {
225+
return salt;
226+
}
227+
92228
/**
93229
* Return true, if the signature contains any signatures that follow.
94-
* An bracketing OPS is followed by additional OPS packets and is calculated over all the data between itself
230+
* A bracketing OPS is followed by additional OPS packets and is calculated over all the data between itself
95231
* and its corresponding signature (it is an attestation for encapsulated signatures).
96232
*
97233
* @return true if encapsulating, false otherwise
@@ -102,7 +238,9 @@ public boolean isContaining()
102238
}
103239

104240
/**
105-
*
241+
* Encode the contents of this packet into the given packet output stream.
242+
*
243+
* @param out OpenPGP packet output stream
106244
*/
107245
public void encode(
108246
BCPGOutputStream out)
@@ -116,7 +254,16 @@ public void encode(
116254
pOut.write(hashAlgorithm);
117255
pOut.write(keyAlgorithm);
118256

119-
StreamUtil.writeKeyID(pOut, keyID);
257+
if (version == VERSION_3)
258+
{
259+
StreamUtil.writeKeyID(pOut, keyID);
260+
}
261+
else if (version == VERSION_6)
262+
{
263+
pOut.write(salt.length);
264+
pOut.write(salt);
265+
pOut.write(fingerprint);
266+
}
120267

121268
pOut.write(isContaining);
122269

0 commit comments

Comments
 (0)