Skip to content

Commit 6790031

Browse files
committed
Fix PKESKv6 / SKESKv6 generation
1 parent f310da4 commit 6790031

9 files changed

+471
-142
lines changed

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,20 @@ public void encode(
337337
pOut.write(secKeyData);
338338
}
339339
}
340-
else if (version == VERSION_5 || version == VERSION_6)
340+
else if (version == VERSION_5)
341+
{
342+
pOut.write(encAlgorithm);
343+
pOut.write(aeadAlgorithm);
344+
pOut.writeObject(s2k);
345+
pOut.write(iv);
346+
347+
if (secKeyData != null && secKeyData.length > 0)
348+
{
349+
pOut.write(secKeyData);
350+
}
351+
pOut.write(authTag);
352+
}
353+
else if (version == VERSION_6)
341354
{
342355
int s2kLen = s2k.getEncoded().length;
343356
int count = 1 + 1 + 1 + s2kLen + iv.length;

pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedDataGenerator.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.bouncycastle.openpgp.operator.PGPDataEncryptorBuilder;
2222
import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
2323
import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator;
24+
import org.bouncycastle.openpgp.operator.PublicKeyKeyEncryptionMethodGenerator;
2425
import org.bouncycastle.util.io.TeeOutputStream;
2526

2627
/**
@@ -228,6 +229,7 @@ private OutputStream open(
228229
if (dataEncryptorBuilder.getAeadAlgorithm() != -1 && !isV5StyleAEAD)
229230
{
230231
sessionKey = PGPUtil.makeRandomKey(defAlgorithm, rand);
232+
sessionInfo = createSessionInfo(defAlgorithm, sessionKey);
231233
// In OpenPGP v6, we need an additional step to derive a message key and IV from the session info.
232234
// Since we cannot inject the IV into the data encryptor, we append it to the message key.
233235
byte[] info = SymmetricEncIntegrityPacket.createAAData(
@@ -271,7 +273,7 @@ else if (directS2K)
271273
{
272274
//https://www.rfc-editor.org/rfc/rfc9580.html#section-3.7.2.1 Table 2
273275
//AEAD(HKDF(S2K(passphrase), info), secrets, packetprefix)
274-
writeOpenPGPv6ESKPacket(method, aeadDataEncryptor.getAEADAlgorithm(), sessionKey);
276+
writeOpenPGPv6ESKPacket(method, aeadDataEncryptor.getAEADAlgorithm(), sessionInfo);
275277
}
276278
}
277279
// OpenPGP v4
@@ -386,12 +388,13 @@ private void writeOpenPGPv4ESKPacket(PGPKeyEncryptionMethodGenerator m, byte[] s
386388
if (m instanceof PBEKeyEncryptionMethodGenerator)
387389
{
388390
PBEKeyEncryptionMethodGenerator mGen = (PBEKeyEncryptionMethodGenerator)m;
389-
ContainedPacket esk = m.generate(mGen.getSessionKeyWrapperAlgorithm(defAlgorithm), sessionInfo);
391+
ContainedPacket esk = mGen.generateV4(mGen.getSessionKeyWrapperAlgorithm(defAlgorithm), sessionInfo);
390392
pOut.writePacket(esk);
391393
}
392-
else
394+
else if (m instanceof PublicKeyKeyEncryptionMethodGenerator)
393395
{
394-
pOut.writePacket(m.generate(defAlgorithm, sessionInfo));
396+
PublicKeyKeyEncryptionMethodGenerator mGen = (PublicKeyKeyEncryptionMethodGenerator) m;
397+
pOut.writePacket(mGen.generateV3(defAlgorithm, sessionInfo));
395398
}
396399
}
397400

@@ -411,15 +414,16 @@ private void writeOpenPGPv5ESKPacket(PGPKeyEncryptionMethodGenerator m, byte[] s
411414
if (m instanceof PBEKeyEncryptionMethodGenerator)
412415
{
413416
PBEKeyEncryptionMethodGenerator mGen = (PBEKeyEncryptionMethodGenerator)m;
414-
ContainedPacket esk = m.generateV5(
417+
ContainedPacket esk = mGen.generateV5(
415418
mGen.getSessionKeyWrapperAlgorithm(defAlgorithm),
416419
dataEncryptorBuilder.getAeadAlgorithm(),
417420
sessionInfo);
418421
pOut.writePacket(esk);
419422
}
420-
else
423+
else if (m instanceof PublicKeyKeyEncryptionMethodGenerator)
421424
{
422-
pOut.writePacket(m.generate(defAlgorithm, sessionInfo));
425+
PublicKeyKeyEncryptionMethodGenerator mGen = (PublicKeyKeyEncryptionMethodGenerator) m;
426+
pOut.writePacket(mGen.generateV3(defAlgorithm, sessionInfo));
423427
}
424428
}
425429

@@ -440,15 +444,16 @@ private void writeOpenPGPv6ESKPacket(PGPKeyEncryptionMethodGenerator m, int aead
440444
if (m instanceof PBEKeyEncryptionMethodGenerator)
441445
{
442446
PBEKeyEncryptionMethodGenerator mGen = (PBEKeyEncryptionMethodGenerator)m;
443-
ContainedPacket esk = m.generateV6(
447+
ContainedPacket esk = mGen.generateV6(
444448
mGen.getSessionKeyWrapperAlgorithm(defAlgorithm),
445449
aeadAlgorithm,
446450
sessionInfo);
447451
pOut.writePacket(esk);
448452
}
449-
else
453+
else if (m instanceof PublicKeyKeyEncryptionMethodGenerator)
450454
{
451-
pOut.writePacket(m.generate(defAlgorithm, sessionInfo));
455+
PublicKeyKeyEncryptionMethodGenerator mGen = (PublicKeyKeyEncryptionMethodGenerator) m;
456+
pOut.writePacket(mGen.generateV6(sessionInfo));
452457
}
453458
}
454459

pg/src/main/java/org/bouncycastle/openpgp/operator/PBEKeyEncryptionMethodGenerator.java

Lines changed: 43 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -160,33 +160,48 @@ public byte[] getKey(int encAlgorithm)
160160
return PGPUtil.makeKeyFromPassPhrase(s2kDigestCalculator, encAlgorithm, s2k, passPhrase);
161161
}
162162

163-
@Override
164-
public ContainedPacket generateV5(int kekAlgorithm, int aeadAlgorithm, byte[] sessionInfo)
165-
throws PGPException
163+
/**
164+
* Generates a version 4 Public-Key-Encrypted-Session-Key (PKESK) packet, encoding the encrypted
165+
* session-key for this method.
166+
* PKESKv4 packets are used by Symmetrically-Encrypted-Integrity-Protected-Data (SEIPD) packets
167+
* of version 1, or by (deprecated) Symmetrically-Encrypted-Data (SED) packets.
168+
* You can use PKESKv4 packets with OpenPGP v4 keys, but MUST NOT use them when producing
169+
* SEIPDv2 packets (use {@link #generateV6(int, int, byte[])} instead in that case).
170+
*
171+
* @param encAlgorithm the {@link SymmetricKeyAlgorithmTags encryption algorithm} being used
172+
* @param sessionInfo session data generated by the encrypted data generator.
173+
* @return a packet encoding the provided information and the configuration of this instance.
174+
*
175+
* @throws PGPException if an error occurs constructing the packet.
176+
*/
177+
public ContainedPacket generateV4(int encAlgorithm, byte[] sessionInfo)
178+
throws PGPException
166179
{
167-
return generate(kekAlgorithm, sessionInfo);
168-
// TODO: Implement v5 SKESK creation properly.
169-
// return generateV5ESK(kekAlgorithm, aeadAlgorithm, sessionInfo);
170-
}
180+
if (sessionInfo == null)
181+
{
182+
return SymmetricKeyEncSessionPacket.createV4Packet(encAlgorithm, s2k, null);
183+
}
171184

172-
@Override
173-
public ContainedPacket generateV6(int kekAlgorithm, int aeadAlgorithm, byte[] sessionInfo)
174-
throws PGPException
175-
{
176-
return generateV6ESK(kekAlgorithm, aeadAlgorithm, sessionInfo);
185+
byte[] key = getKey(encAlgorithm);
186+
//
187+
// the passed in session info has the an RSA/ElGamal checksum added to it, for PBE this is not included.
188+
//
189+
byte[] nSessionInfo = new byte[sessionInfo.length - 2];
190+
191+
System.arraycopy(sessionInfo, 0, nSessionInfo, 0, nSessionInfo.length);
192+
193+
return SymmetricKeyEncSessionPacket.createV4Packet(encAlgorithm, s2k, encryptSessionInfo(encAlgorithm, key, nSessionInfo));
177194
}
178195

179-
// If we use this method, roundtripping v5 AEAD is broken.
180-
// TODO: Investigate
181-
private ContainedPacket generateV5ESK(int kekAlgorithm, int aeadAlgorithm, byte[] sessionInfo)
182-
throws PGPException
196+
public ContainedPacket generateV5(int kekAlgorithm, int aeadAlgorithm, byte[] sessionInfo)
197+
throws PGPException
183198
{
184199
byte[] ikm = getKey(kekAlgorithm);
185200
byte[] info = new byte[]{
186-
(byte)0xC3,
187-
(byte)SymmetricKeyEncSessionPacket.VERSION_5,
188-
(byte)kekAlgorithm,
189-
(byte)aeadAlgorithm
201+
(byte)0xC3,
202+
(byte)SymmetricKeyEncSessionPacket.VERSION_5,
203+
(byte)kekAlgorithm,
204+
(byte)aeadAlgorithm
190205
};
191206

192207
byte[] iv = new byte[AEADUtils.getIVLength(aeadAlgorithm)];
@@ -201,56 +216,31 @@ private ContainedPacket generateV5ESK(int kekAlgorithm, int aeadAlgorithm, byte[
201216
return SymmetricKeyEncSessionPacket.createV5Packet(kekAlgorithm, aeadAlgorithm, iv, s2k, esk, tag);
202217
}
203218

204-
private ContainedPacket generateV6ESK(int kekAlgorithm, int aeadAlgorithm, byte[] sessionKey)
219+
public ContainedPacket generateV6(int kekAlgorithm, int aeadAlgorithm, byte[] sessionInfo)
205220
throws PGPException
206221
{
207222
byte[] ikm = getKey(kekAlgorithm);
208223
byte[] info = new byte[]{
209-
(byte)0xC3,
210-
(byte)SymmetricKeyEncSessionPacket.VERSION_6,
211-
(byte)kekAlgorithm,
212-
(byte)aeadAlgorithm
224+
(byte)0xC3,
225+
(byte)SymmetricKeyEncSessionPacket.VERSION_6,
226+
(byte)kekAlgorithm,
227+
(byte)aeadAlgorithm
213228
};
214229
byte[] kek = generateV6KEK(kekAlgorithm, ikm, info);
215230

216231
byte[] iv = new byte[AEADUtils.getIVLength(aeadAlgorithm)];
217232
random.nextBytes(iv);
218233

234+
byte[] sk = new byte[sessionInfo.length - 3];
235+
System.arraycopy(sessionInfo, 1, sk, 0, sk.length);
219236
int tagLen = AEADUtils.getAuthTagLength(aeadAlgorithm);
220-
byte[] eskAndTag = getEskAndTag(kekAlgorithm, aeadAlgorithm, sessionKey, kek, iv, info);
237+
byte[] eskAndTag = getEskAndTag(kekAlgorithm, aeadAlgorithm, sk, kek, iv, info);
221238
byte[] esk = Arrays.copyOfRange(eskAndTag, 0, eskAndTag.length - tagLen);
222239
byte[] tag = Arrays.copyOfRange(eskAndTag, esk.length, eskAndTag.length);
223240

224241
return SymmetricKeyEncSessionPacket.createV6Packet(kekAlgorithm, aeadAlgorithm, iv, s2k, esk, tag);
225242
}
226243

227-
/**
228-
* Generate a V4 SKESK packet.
229-
*
230-
* @param encAlgorithm the {@link SymmetricKeyAlgorithmTags encryption algorithm} being used
231-
* @param sessionInfo session data generated by the encrypted data generator.
232-
* @return v4 SKESK packet
233-
* @throws PGPException
234-
*/
235-
public ContainedPacket generate(int encAlgorithm, byte[] sessionInfo)
236-
throws PGPException
237-
{
238-
if (sessionInfo == null)
239-
{
240-
return SymmetricKeyEncSessionPacket.createV4Packet(encAlgorithm, s2k, null);
241-
}
242-
243-
byte[] key = getKey(encAlgorithm);
244-
//
245-
// the passed in session info has the an RSA/ElGamal checksum added to it, for PBE this is not included.
246-
//
247-
byte[] nSessionInfo = new byte[sessionInfo.length - 2];
248-
249-
System.arraycopy(sessionInfo, 0, nSessionInfo, 0, nSessionInfo.length);
250-
251-
return SymmetricKeyEncSessionPacket.createV4Packet(encAlgorithm, s2k, encryptSessionInfo(encAlgorithm, key, nSessionInfo));
252-
}
253-
254244
protected byte[] getSessionKey(byte[] sessionInfo)
255245
{
256246
byte[] sessionKey = new byte[sessionInfo.length - 3];
Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,11 @@
11
package org.bouncycastle.openpgp.operator;
22

3-
import org.bouncycastle.bcpg.ContainedPacket;
4-
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
53
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
6-
import org.bouncycastle.openpgp.PGPException;
74

85
/**
96
* An encryption method that can be applied to encrypt data in a {@link PGPEncryptedDataGenerator}.
107
*/
118
public abstract class PGPKeyEncryptionMethodGenerator
129
{
13-
/**
14-
* Generates a packet encoding the details of this encryption method.
15-
*
16-
* @param encAlgorithm the {@link SymmetricKeyAlgorithmTags encryption algorithm} being used
17-
* @param sessionInfo session data generated by the encrypted data generator.
18-
* @return a packet encoding the provided information and the configuration of this instance.
19-
* @throws PGPException if an error occurs constructing the packet.
20-
*/
21-
public abstract ContainedPacket generate(int encAlgorithm, byte[] sessionInfo)
22-
throws PGPException;
2310

24-
public abstract ContainedPacket generateV5(int encAlgorithm, int aeadAlgorithm, byte[] sessionInfo)
25-
throws PGPException;
26-
27-
public abstract ContainedPacket generateV6(int encAlgorithm, int aeadAlgorithm, byte[] sessionInfo)
28-
throws PGPException;
2911
}

pg/src/main/java/org/bouncycastle/openpgp/operator/PublicKeyKeyEncryptionMethodGenerator.java

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ private static boolean getSessionKeyObfuscationDefault()
2424
return !Properties.isOverrideSetTo(SESSION_KEY_OBFUSCATION_PROPERTY, false);
2525
}
2626

27-
private PGPPublicKey pubKey;
27+
private final PGPPublicKey pubKey;
2828

2929
protected boolean sessionKeyObfuscation;
3030
protected boolean useWildcardRecipient;
@@ -102,7 +102,7 @@ public PublicKeyKeyEncryptionMethodGenerator setUseWildcardRecipient(boolean ena
102102
return this;
103103
}
104104

105-
public byte[][] processSessionInfo(
105+
public byte[][] encodeEncryptedSessionInfo(
106106
byte[] encryptedSessionInfo)
107107
throws PGPException
108108
{
@@ -155,8 +155,8 @@ private byte[] convertToEncodedMPI(byte[] encryptedSessionInfo)
155155
}
156156
}
157157

158-
public ContainedPacket generate(int encAlgorithm, byte[] sessionInfo)
159-
throws PGPException
158+
public ContainedPacket generateV3(int encAlgorithm, byte[] sessionInfo)
159+
throws PGPException
160160
{
161161
long keyId;
162162
if (useWildcardRecipient)
@@ -167,47 +167,72 @@ public ContainedPacket generate(int encAlgorithm, byte[] sessionInfo)
167167
{
168168
keyId = pubKey.getKeyID();
169169
}
170-
return PublicKeyEncSessionPacket.createV3PKESKPacket(keyId, pubKey.getAlgorithm(), processSessionInfo(encryptSessionInfo(pubKey, sessionInfo)));
170+
byte[] encryptedSessionInfo = encryptSessionInfoV3(pubKey, sessionInfo);
171+
byte[][] encodedEncSessionInfo = encodeEncryptedSessionInfo(encryptedSessionInfo);
172+
return PublicKeyEncSessionPacket.createV3PKESKPacket(keyId, pubKey.getAlgorithm(), encodedEncSessionInfo);
171173
}
172174

173-
@Override
174-
public ContainedPacket generateV5(int encAlgorithm, int aeadAlgorithm, byte[] sessionInfo)
175+
public ContainedPacket generateV6(byte[] sessionInfo)
175176
throws PGPException
176177
{
177-
// TODO: Implement
178-
return null;
178+
byte[] keyFingerprint;
179+
int keyVersion;
180+
if (useWildcardRecipient)
181+
{
182+
keyFingerprint = WILDCARD_FINGERPRINT;
183+
keyVersion = 0;
184+
}
185+
else
186+
{
187+
keyFingerprint = pubKey.getFingerprint();
188+
keyVersion = pubKey.getVersion();
189+
}
190+
byte[] encryptedSessionInfo = encryptSessionInfoV6(pubKey, sessionInfo);
191+
byte[][] encodedEncSessionInfo = encodeEncryptedSessionInfo(encryptedSessionInfo);
192+
return PublicKeyEncSessionPacket.createV6PKESKPacket(keyVersion, keyFingerprint, pubKey.getAlgorithm(), encodedEncSessionInfo);
179193
}
180194

181-
@Override
182-
public ContainedPacket generateV6(int encAlgorithm, int aeadAlgorithm, byte[] sessionInfo)
183-
throws PGPException
184-
{
185-
// TODO: Implement
186-
return null;
187-
}
195+
abstract protected byte[] encryptSessionInfoV3(PGPPublicKey pubKey, byte[] sessionInfo)
196+
throws PGPException;
188197

189-
abstract protected byte[] encryptSessionInfo(PGPPublicKey pubKey, byte[] sessionInfo)
198+
abstract protected byte[] encryptSessionInfoV6(PGPPublicKey pubKey, byte[] sessionInfo)
190199
throws PGPException;
191200

192-
protected static byte[] getSessionInfo(byte[] ephPubEncoding, byte[] c)
201+
protected static byte[] concatECDHEphKeyWithWrappedSessionKey(byte[] ephPubEncoding, byte[] wrappedSessionKey)
193202
throws IOException
194203
{
195-
byte[] VB = new MPInteger(new BigInteger(1, ephPubEncoding)).getEncoded();
204+
// https://www.rfc-editor.org/rfc/rfc9580.html#section-11.5-16
205+
206+
byte[] mpiEncodedEphemeralKey = new MPInteger(new BigInteger(1, ephPubEncoding))
207+
.getEncoded();
208+
byte[] out = new byte[mpiEncodedEphemeralKey.length + 1 + wrappedSessionKey.length];
209+
// eph key
210+
System.arraycopy(mpiEncodedEphemeralKey, 0, out, 0, mpiEncodedEphemeralKey.length);
211+
// enc session-key len
212+
out[mpiEncodedEphemeralKey.length] = (byte) wrappedSessionKey.length;
213+
// enc session-key
214+
System.arraycopy(wrappedSessionKey, 0, out, mpiEncodedEphemeralKey.length + 1, wrappedSessionKey.length);
215+
216+
return out;
217+
}
196218

197-
byte[] rv = new byte[VB.length + 1 + c.length];
198-
System.arraycopy(VB, 0, rv, 0, VB.length);
199-
rv[VB.length] = (byte)c.length;
200-
System.arraycopy(c, 0, rv, VB.length + 1, c.length);
201-
return rv;
219+
private static byte[] getSessionInfo(byte[] ephPubEncoding, int symmetricKeyAlgorithm, byte[] c)
220+
{
221+
return getSessionInfo(ephPubEncoding, new byte[]{(byte) symmetricKeyAlgorithm}, c);
202222
}
203223

204-
protected static byte[] getSessionInfo(byte[] VB, int sysmmetricKeyAlgorithm, byte[] c)
224+
protected static byte[] getSessionInfo(byte[] ephPubEncoding, byte[] optSymKeyAlgorithm, byte[] wrappedSessionKey)
205225
{
206-
byte[] rv = new byte[VB.length + 2 + c.length];
207-
System.arraycopy(VB, 0, rv, 0, VB.length);
208-
rv[VB.length] = (byte)(c.length + 1);
209-
rv[VB.length + 1] = (byte)sysmmetricKeyAlgorithm;
210-
System.arraycopy(c, 0, rv, VB.length + 2, c.length);
211-
return rv;
226+
int len = ephPubEncoding.length + 1 + optSymKeyAlgorithm.length + wrappedSessionKey.length;
227+
byte[] out = new byte[len];
228+
// ephemeral pub key
229+
System.arraycopy(ephPubEncoding, 0, out, 0, ephPubEncoding.length);
230+
// len of two/one next fields
231+
out[ephPubEncoding.length] = (byte) (wrappedSessionKey.length + optSymKeyAlgorithm.length);
232+
// (optional) sym key alg
233+
System.arraycopy(optSymKeyAlgorithm, 0, out, ephPubEncoding.length + 1, optSymKeyAlgorithm.length);
234+
// wrapped session key
235+
System.arraycopy(wrappedSessionKey, 0, out, ephPubEncoding.length + 1 + optSymKeyAlgorithm.length, wrappedSessionKey.length);
236+
return out;
212237
}
213238
}

0 commit comments

Comments
 (0)