Skip to content

Commit 6fe9905

Browse files
author
gefeili
committed
Centralize GnuPG keybox blob parsing
1 parent bce5865 commit 6fe9905

3 files changed

Lines changed: 142 additions & 297 deletions

File tree

‎pg/src/main/java/org/bouncycastle/gpg/keybox/CertificateBlob.java‎

Lines changed: 4 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package org.bouncycastle.gpg.keybox;
22

33
import java.io.IOException;
4-
import java.util.ArrayList;
5-
import java.util.List;
64

75
/**
86
* A PGP blob holds key material.
@@ -13,163 +11,19 @@ public class CertificateBlob
1311
private CertificateBlob(int base, long length,
1412
BlobType type,
1513
int version,
16-
int blobFlags,
17-
int keyNumber,
18-
List<KeyInformation> keyInformation,
19-
byte[] serialNumber,
20-
int numberOfUserIDs,
21-
List<UserID> userIds,
22-
int numberOfSignatures,
23-
List<Long> expirationTime,
24-
int assignedOwnerTrust,
25-
int allValidity,
26-
long recheckAfter,
27-
long newestTimestamp,
28-
long blobCreatedAt,
29-
byte[] keyBytes,
30-
byte[] reserveBytes,
31-
byte[] sha1Checksum)
14+
KeyBlobContent content)
3215
{
33-
super(base, length, type, version, blobFlags, keyNumber,
34-
keyInformation, serialNumber, numberOfUserIDs, userIds, numberOfSignatures,
35-
expirationTime, assignedOwnerTrust, allValidity, recheckAfter, newestTimestamp, blobCreatedAt,
36-
keyBytes, reserveBytes, sha1Checksum);
16+
super(base, length, type, version, content);
3717
}
3818

3919

4020
static Blob parseContent(int base, long length, BlobType type, int version, KeyBoxByteBuffer buffer, BlobVerifier blobVerifier)
4121
throws IOException
4222
{
4323

44-
//
45-
// u32 Length of this blob (including these 4 bytes)
46-
// byte Blob type
47-
// 2 = OpenPGP
48-
// 3 = X509
49-
// byte Version number of this blob type
50-
// 1 = The only defined value
51-
//
24+
KeyBlobContent content = KeyBlobContent.parse(base, length, buffer, blobVerifier);
5225

53-
54-
//
55-
// Take checksum first.
56-
//
57-
verifyDigest(base, length, buffer, blobVerifier);
58-
59-
60-
int blobFlags = buffer.u16(); // u16 Blob flags
61-
long keyBlockOffset = buffer.u32(); // u32 offset to the OpenPGP keyblock or X509 DER encoded certificate
62-
long keyBlockLength = buffer.u32(); // u32 and its length
63-
64-
int keyNumber = buffer.u16(); // u16 number of keys (at least 1!) [X509: always 1]
65-
66-
67-
// This value defines the length of the space reserved for the AdditionalKeyInformation
68-
int keyInformationStructureSize = buffer.u16(); // u16 size of additional key information
69-
70-
//
71-
// Load the additional key information.
72-
//
73-
List<KeyInformation> keyInformation = new ArrayList<KeyInformation>();
74-
75-
for (int t = keyNumber - 1; t >= 0; t--)
76-
{
77-
keyInformation.add(KeyInformation.getInstance(buffer, keyInformationStructureSize, base));
78-
}
79-
80-
int sizeOfSerialNumber = buffer.u16(); // size of serialnumber(may be zero)
81-
82-
byte[] serialNumber = buffer.bN(sizeOfSerialNumber);
83-
// buffer.bN(serialNumber); // n u16 (see above) bytes of serial number
84-
85-
int numberOfUserIDs = buffer.u16(); // u16 number of user IDs
86-
buffer.u16(); // size of additional user ID information
87-
88-
//
89-
// User IDS.
90-
//
91-
List<UserID> userIds = new ArrayList<UserID>();
92-
long totalUserIdLength = 0;
93-
for (int t = numberOfUserIDs - 1; t >= 0; t--)
94-
{
95-
UserID userID = UserID.getInstance(buffer, base);
96-
// Bound the cumulative user-ID data by the blob length: each entry copies an
97-
// attacker-controlled slice of the blob, so an inflated user-ID count with each entry
98-
// pointing at (almost) the whole blob would otherwise retain ~bufferSize^2 bytes.
99-
totalUserIdLength += userID.getLengthOfUserId();
100-
if (totalUserIdLength > length)
101-
{
102-
throw new IllegalStateException("userID data exceeds blob length");
103-
}
104-
userIds.add(userID);
105-
}
106-
107-
int numberOfSignatures = buffer.u16();
108-
buffer.u16();
109-
110-
111-
List<Long> signatureExpirationTime = new ArrayList<Long>();
112-
for (int t = numberOfSignatures - 1; t >= 0; t--)
113-
{
114-
signatureExpirationTime.add(buffer.u32());
115-
}
116-
117-
int assignedOwnerTrust = buffer.u8(); // din.read();
118-
int allValidity = buffer.u8();
119-
120-
buffer.u16(); // RFU
121-
long recheckAfter = buffer.u32();
122-
long newestTimestamp = buffer.u32();
123-
long blobCreatedAt = buffer.u32();
124-
125-
long sizeOfReservedSpace = buffer.u32();
126-
127-
if (sizeOfReservedSpace > buffer.remaining())
128-
{
129-
throw new IllegalStateException("sizeOfReservedSpace exceeds content remaining in buffer");
130-
}
131-
132-
// Arbitrary reserved space, that may hold X509 V3 certificate IDs.!
133-
byte[] reserveData = buffer.bN((int)sizeOfReservedSpace); // Reserved space of size NRES for future use.
134-
// buffer.bN(reserveData);
135-
136-
137-
//
138-
// Key block is loaded based from the start of the blob rather than
139-
//
140-
141-
byte[] keyData = buffer.rangeOf(
142-
(int)(base + keyBlockOffset),
143-
(int)(base + keyBlockOffset + keyBlockLength)); // Defined near top of structure..
144-
145-
146-
//
147-
// Reserve space.
148-
//
149-
int dataSize = (int)(length - (buffer.position() - base) - 20);
150-
byte[] data = buffer.bN(dataSize);
151-
152-
153-
byte[] sha1Checksum = buffer.rangeOf((int)(base + length - 20), (int)(base + length));
154-
buffer.consume(sha1Checksum.length);
155-
156-
return new CertificateBlob(base, length,
157-
type,
158-
version,
159-
blobFlags,
160-
keyNumber,
161-
keyInformation,
162-
serialNumber,
163-
numberOfUserIDs,
164-
userIds,
165-
numberOfSignatures,
166-
signatureExpirationTime,
167-
assignedOwnerTrust,
168-
allValidity,
169-
recheckAfter,
170-
newestTimestamp,
171-
blobCreatedAt,
172-
keyData, reserveData, sha1Checksum);
26+
return new CertificateBlob(base, length, type, version, content);
17327
}
17428

17529
/**

‎pg/src/main/java/org/bouncycastle/gpg/keybox/KeyBlob.java‎

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.bouncycastle.gpg.keybox;
22

33
import java.io.IOException;
4+
import java.util.ArrayList;
45
import java.util.List;
56

67
/**
@@ -65,6 +66,15 @@ protected KeyBlob(int base, long length,
6566
this.checksum = checksum;
6667
}
6768

69+
KeyBlob(int base, long length, BlobType type, int version, KeyBlobContent content)
70+
{
71+
this(base, length, type, version, content.blobFlags, content.keyNumber,
72+
content.keyInformation, content.serialNumber, content.numberOfUserIDs, content.userIds,
73+
content.numberOfSignatures, content.expirationTime, content.assignedOwnerTrust,
74+
content.allValidity, content.recheckAfter, content.newestTimestamp, content.blobCreatedAt,
75+
content.keyBytes, content.reserveBytes, content.checksum);
76+
}
77+
6878
static void verifyDigest(int base, long length, KeyBoxByteBuffer buffer, BlobVerifier blobVerifier)
6979
throws IOException
7080
{
@@ -77,6 +87,130 @@ static void verifyDigest(int base, long length, KeyBoxByteBuffer buffer, BlobVer
7787
}
7888
}
7989

90+
static final class KeyBlobContent
91+
{
92+
private final int blobFlags;
93+
private final int keyNumber;
94+
private final List<KeyInformation> keyInformation;
95+
private final byte[] serialNumber;
96+
private final int numberOfUserIDs;
97+
private final List<UserID> userIds;
98+
private final int numberOfSignatures;
99+
private final List<Long> expirationTime;
100+
private final int assignedOwnerTrust;
101+
private final int allValidity;
102+
private final long recheckAfter;
103+
private final long newestTimestamp;
104+
private final long blobCreatedAt;
105+
private final byte[] keyBytes;
106+
private final byte[] reserveBytes;
107+
private final byte[] checksum;
108+
109+
private KeyBlobContent(int blobFlags, int keyNumber, List<KeyInformation> keyInformation,
110+
byte[] serialNumber, int numberOfUserIDs, List<UserID> userIds,
111+
int numberOfSignatures, List<Long> expirationTime, int assignedOwnerTrust,
112+
int allValidity, long recheckAfter, long newestTimestamp, long blobCreatedAt,
113+
byte[] keyBytes, byte[] reserveBytes, byte[] checksum)
114+
{
115+
this.blobFlags = blobFlags;
116+
this.keyNumber = keyNumber;
117+
this.keyInformation = keyInformation;
118+
this.serialNumber = serialNumber;
119+
this.numberOfUserIDs = numberOfUserIDs;
120+
this.userIds = userIds;
121+
this.numberOfSignatures = numberOfSignatures;
122+
this.expirationTime = expirationTime;
123+
this.assignedOwnerTrust = assignedOwnerTrust;
124+
this.allValidity = allValidity;
125+
this.recheckAfter = recheckAfter;
126+
this.newestTimestamp = newestTimestamp;
127+
this.blobCreatedAt = blobCreatedAt;
128+
this.keyBytes = keyBytes;
129+
this.reserveBytes = reserveBytes;
130+
this.checksum = checksum;
131+
}
132+
133+
static KeyBlobContent parse(int base, long length, KeyBoxByteBuffer buffer, BlobVerifier blobVerifier)
134+
throws IOException
135+
{
136+
verifyDigest(base, length, buffer, blobVerifier);
137+
138+
int blobFlags = buffer.u16();
139+
long keyBlockOffset = buffer.u32();
140+
long keyBlockLength = buffer.u32();
141+
142+
int keyNumber = buffer.u16();
143+
int keyInformationStructureSize = buffer.u16();
144+
145+
List<KeyInformation> keyInformation = new ArrayList<KeyInformation>();
146+
for (int t = keyNumber - 1; t >= 0; t--)
147+
{
148+
keyInformation.add(KeyInformation.getInstance(buffer, keyInformationStructureSize, base));
149+
}
150+
151+
int sizeOfSerialNumber = buffer.u16();
152+
byte[] serialNumber = buffer.bN(sizeOfSerialNumber);
153+
154+
int numberOfUserIDs = buffer.u16();
155+
buffer.u16();
156+
157+
List<UserID> userIds = new ArrayList<UserID>();
158+
long totalUserIdLength = 0;
159+
for (int t = numberOfUserIDs - 1; t >= 0; t--)
160+
{
161+
UserID userID = UserID.getInstance(buffer, base);
162+
// Bound the cumulative user-ID data by the blob length: each entry copies an
163+
// attacker-controlled slice of the blob, so an inflated user-ID count with each entry
164+
// pointing at (almost) the whole blob would otherwise retain ~bufferSize^2 bytes.
165+
totalUserIdLength += userID.getLengthOfUserId();
166+
if (totalUserIdLength > length)
167+
{
168+
throw new IllegalStateException("userID data exceeds blob length");
169+
}
170+
userIds.add(userID);
171+
}
172+
173+
int numberOfSignatures = buffer.u16();
174+
buffer.u16();
175+
176+
List<Long> signatureExpirationTime = new ArrayList<Long>();
177+
for (int t = numberOfSignatures - 1; t >= 0; t--)
178+
{
179+
signatureExpirationTime.add(buffer.u32());
180+
}
181+
182+
int assignedOwnerTrust = buffer.u8();
183+
int allValidity = buffer.u8();
184+
185+
buffer.u16();
186+
long recheckAfter = buffer.u32();
187+
long newestTimestamp = buffer.u32();
188+
long blobCreatedAt = buffer.u32();
189+
190+
long sizeOfReservedSpace = buffer.u32();
191+
if (sizeOfReservedSpace > buffer.remaining())
192+
{
193+
throw new IllegalStateException("sizeOfReservedSpace exceeds content remaining in buffer");
194+
}
195+
196+
byte[] reserveData = buffer.bN((int)sizeOfReservedSpace);
197+
byte[] keyData = buffer.rangeOf(
198+
(int)(base + keyBlockOffset),
199+
(int)(base + keyBlockOffset + keyBlockLength));
200+
201+
int dataSize = (int)(length - (buffer.position() - base) - 20);
202+
buffer.bN(dataSize);
203+
204+
byte[] checksum = buffer.rangeOf((int)(base + length - 20), (int)(base + length));
205+
buffer.consume(checksum.length);
206+
207+
return new KeyBlobContent(blobFlags, keyNumber, keyInformation, serialNumber,
208+
numberOfUserIDs, userIds, numberOfSignatures, signatureExpirationTime,
209+
assignedOwnerTrust, allValidity, recheckAfter, newestTimestamp, blobCreatedAt,
210+
keyData, reserveData, checksum);
211+
}
212+
}
213+
80214
public int getBlobFlags()
81215
{
82216
return blobFlags;

0 commit comments

Comments
 (0)