11package org .bouncycastle .gpg .keybox ;
22
33import java .io .IOException ;
4+ import java .util .ArrayList ;
45import 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