1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import struct, time, cStringIO, base64, types
17
18
19
20
21 try:
22 import hashlib
23 except ImportError:
24
25 import sha
26 import md5
28
29 @staticmethod
31 if algo == 'md5':
32 return md5.new()
33 if algo == 'sha1':
34 return sha.new()
35 raise ValueError, "Bad checksum type"
36
37 debug = None
38
39
40
41
42 CTB_76_NORMAL = 0x80
43 CTB_76_NEW = 0xc0
44 CTB_76_MASK = 0xc0
45
46
47 CTB_PKTV2_MASK = 0x3c
48 CTB_PKT_MASK = 0x3f
49
50 CTB_PKT_PK_ENC = 1
51 CTB_PKT_SIG = 2
52 CTB_PKT_SK_ENC = 3
53 CTB_PKT_OP_SIG = 4
54 CTB_PKT_SK_CERT = 5
55 CTB_PKT_PK_CERT = 6
56 CTB_PKT_SK_SUB = 7
57 CTB_PKT_COMPRESSED = 8
58 CTB_PKT_ENC = 9
59 CTB_PKT_MARKER = 10
60 CTB_PKT_LIT = 11
61 CTB_PKT_TRUST = 12
62 CTB_PKT_USER_ID = 13
63 CTB_PKT_PK_SUB = 14
64 CTB_PKT_USER_ATTR = 17
65 CTB_PKT_SYM_ENC_INT = 18
66 CTB_PKT_MOD_DETECT = 19
67
68 ctb_pkt_to_str = {
69 CTB_PKT_PK_ENC : 'public-key encrypted session packet',
70 CTB_PKT_SIG : 'signature packet',
71 CTB_PKT_SK_ENC : 'symmetric-key encrypted session packet',
72 CTB_PKT_OP_SIG : 'one-pass signature packet',
73 CTB_PKT_SK_CERT : 'secret-key certificate packet',
74 CTB_PKT_PK_CERT : 'public-key certificate packet',
75 CTB_PKT_SK_SUB : 'secret-key subkey packet',
76 CTB_PKT_COMPRESSED : 'compressed data packet',
77 CTB_PKT_ENC : 'symmetric-key encrypted data packet',
78 CTB_PKT_MARKER : 'marker packet',
79 CTB_PKT_LIT : 'literal data packet',
80 CTB_PKT_TRUST : 'trust packet',
81 CTB_PKT_USER_ID : 'user id packet',
82 CTB_PKT_PK_SUB : 'public subkey packet',
83 CTB_PKT_USER_ATTR : 'user attribute packet',
84 CTB_PKT_SYM_ENC_INT : 'symmetric encrypted integrity packet',
85 CTB_PKT_MOD_DETECT : 'modification detection code packet'
86 }
87
88
89
90 CTB_PKT_LEN_MASK = 0x3
91
92 CTB_PKT_LEN_1 = 0
93 CTB_PKT_LEN_2 = 1
94 CTB_PKT_LEN_4 = 2
95 CTB_PKT_LEN_UNDEF = 3
96
97
98
99
100 ALGO_PK_RSA_ENC_OR_SIGN = 1
101 ALGO_PK_RSA_ENC_ONLY = 2
102 ALGO_PK_RSA_SIGN_ONLY = 3
103 ALGO_PK_ELGAMAL_ENC_ONLY = 16
104 ALGO_PK_DSA = 17
105 ALGO_PK_ELLIPTIC_CURVE = 18
106 ALGO_PK_ECDSA = 19
107 ALGO_PK_ELGAMAL_ENC_OR_SIGN = 20
108 ALGO_PK_DH = 21
109
110 algo_pk_to_str = {
111 ALGO_PK_RSA_ENC_OR_SIGN : 'RSA (Encrypt or Sign)',
112 ALGO_PK_RSA_ENC_ONLY : 'RSA Encrypt-Only',
113 ALGO_PK_RSA_SIGN_ONLY : 'RSA Sign-Only',
114 ALGO_PK_ELGAMAL_ENC_ONLY : 'Elgamal Encrypt-Only',
115 ALGO_PK_DSA : 'DSA (Digital Signature Standard)',
116 ALGO_PK_ELLIPTIC_CURVE : 'Elliptic Curve',
117 ALGO_PK_ECDSA : 'ECDSA',
118 ALGO_PK_ELGAMAL_ENC_OR_SIGN : 'Elgamal (Encrypt or Sign)',
119 ALGO_PK_DH : 'Diffie-Hellman'
120 }
121
122
123 ALGO_SK_PLAIN = 0
124 ALGO_SK_IDEA = 1
125 ALGO_SK_3DES = 2
126 ALGO_SK_CAST5 = 3
127 ALGO_SK_BLOWFISH = 4
128 ALGO_SK_SAFER_SK128 = 5
129 ALGO_SK_DES_SK = 6
130 ALGO_SK_AES_128 = 7
131 ALGO_SK_AES_192 = 8
132 ALGO_SK_AES_256 = 9
133 ALGO_SK_TWOFISH_256 = 10
134
135 algo_sk_to_str = {
136 ALGO_SK_PLAIN : 'Plaintext or unencrypted data',
137 ALGO_SK_IDEA : 'IDEA',
138 ALGO_SK_3DES : 'Triple-DES',
139 ALGO_SK_CAST5 : 'CAST5',
140 ALGO_SK_BLOWFISH : 'Blowfish',
141 ALGO_SK_SAFER_SK128 : 'SAFER-SK128',
142 ALGO_SK_DES_SK : 'DES/SK',
143 ALGO_SK_AES_128 : 'AES 128-bit',
144 ALGO_SK_AES_192 : 'AES 192-bit',
145 ALGO_SK_AES_256 : 'AES 256-bit',
146 ALGO_SK_TWOFISH_256 : 'Twofish 256-bit'
147 }
148
149
150 ALGO_COMP_UNCOMP = 0
151 ALGO_COMP_ZIP = 1
152 ALGO_COMP_ZLIB = 2
153 ALGO_COMP_BZIP2 = 3
154
155 algo_comp_to_str = {
156 ALGO_COMP_UNCOMP : 'Uncompressed',
157 ALGO_COMP_ZIP : 'ZIP',
158 ALGO_COMP_ZLIB : 'ZLIB',
159 ALGO_COMP_BZIP2 : 'BZip2'
160 }
161
162
163 ALGO_HASH_MD5 = 1
164 ALGO_HASH_SHA1 = 2
165 ALGO_HASH_RIPEMD160 = 3
166 ALGO_HASH_SHA_DBL = 4
167 ALGO_HASH_MD2 = 5
168 ALGO_HASH_TIGER192 = 6
169 ALGO_HASH_HAVAL_5_160 = 7
170 ALGO_HASH_SHA256 = 8
171 ALGO_HASH_SHA384 = 9
172 ALGO_HASH_SHA512 = 10
173 ALGO_HASH_SHA224 = 11
174
175 algo_hash_to_str = {
176 ALGO_HASH_MD5 : 'MD5',
177 ALGO_HASH_SHA1 : 'SHA1',
178 ALGO_HASH_RIPEMD160 : 'RIPEMD160',
179 ALGO_HASH_SHA_DBL : 'double-width SHA',
180 ALGO_HASH_MD2 : 'MD2',
181 ALGO_HASH_TIGER192 : 'TIGER192',
182 ALGO_HASH_HAVAL_5_160 : 'HAVAL-5-160',
183 ALGO_HASH_SHA256 : 'SHA256',
184 ALGO_HASH_SHA384 : 'SHA384',
185 ALGO_HASH_SHA512 : 'SHA512',
186 ALGO_HASH_SHA224 : 'SHA224'
187 }
188
189
190 SIG_TYPE_DOCUMENT = 0x00
191 SIG_TYPE_DOCUMENT_CANON = 0x01
192 SIG_TYPE_STANDALONE = 0x02
193 SIG_TYPE_PK_USER_GEN = 0x10
194 SIG_TYPE_PK_USER_PER = 0x11
195 SIG_TYPE_PK_USER_CAS = 0x12
196 SIG_TYPE_PK_USER_POS = 0x13
197 SIG_TYPE_SUBKEY_BIND = 0x18
198 SIG_TYPE_KEY = 0x1F
199 SIG_TYPE_KEY_REVOKE = 0x20
200 SIG_TYPE_SUBKEY_REVOKE = 0x28
201 SIG_TYPE_CERT_REVOKE = 0x30
202 SIG_TYPE_TIMESTAMP = 0x40
203
204 sig_type_to_str = {
205 SIG_TYPE_DOCUMENT : 'document signature, binary image',
206 SIG_TYPE_DOCUMENT_CANON : 'document signature, canonical text',
207 SIG_TYPE_STANDALONE : 'signature over just subpackets',
208 SIG_TYPE_PK_USER_GEN : 'public key packet and user ID packet, generic certification',
209 SIG_TYPE_PK_USER_PER : 'public key packet and user ID packet, persona',
210 SIG_TYPE_PK_USER_CAS : 'public key packet and user ID packet, casual certification',
211 SIG_TYPE_PK_USER_POS : 'public key packet and user ID packet, positive certification',
212 SIG_TYPE_SUBKEY_BIND : 'subkey binding',
213 SIG_TYPE_KEY : 'key signature',
214 SIG_TYPE_KEY_REVOKE : 'key revocation',
215 SIG_TYPE_SUBKEY_REVOKE : 'subkey revocation',
216 SIG_TYPE_CERT_REVOKE : 'certificate revocation',
217 SIG_TYPE_TIMESTAMP : 'timestamp'
218 }
219
220
221 SIG_SUB_TYPE_CREATE_TIME = 2
222 SIG_SUB_TYPE_EXPIRE_TIME = 3
223 SIG_SUB_TYPE_EXPORT_CERT = 4
224 SIG_SUB_TYPE_TRUST_SIG = 5
225 SIG_SUB_TYPE_REGEXP = 6
226 SIG_SUB_TYPE_REVOCABLE = 7
227 SIG_SUB_TYPE_KEY_EXPIRE = 9
228 SIG_SUB_TYPE_PLACEHOLDER = 10
229 SIG_SUB_TYPE_PREF_SYMM_ALGO = 11
230 SIG_SUB_TYPE_REVOKE_KEY = 12
231 SIG_SUB_TYPE_ISSUER_KEY_ID = 16
232 SIG_SUB_TYPE_NOTATION = 20
233 SIG_SUB_TYPE_PREF_HASH_ALGO = 21
234 SIG_SUB_TYPE_PREF_COMP_ALGO = 22
235 SIG_SUB_TYPE_KEY_SRV_PREF = 23
236 SIG_SUB_TYPE_PREF_KEY_SRVR = 24
237 SIG_SUB_TYPE_PRIM_USER_ID = 25
238 SIG_SUB_TYPE_POLICY_URI = 26
239 SIG_SUB_TYPE_KEY_FLAGS = 27
240 SIG_SUB_TYPE_SGNR_USER_ID = 28
241 SIG_SUB_TYPE_REVOKE_REASON = 29
242 SIG_SUB_TYPE_FEATURES = 30
243 SIG_SUB_TYPE_SIG_TARGET = 31
244 SIG_SUB_TYPE_EMBEDDED_SIG = 32
245
246 sig_sub_type_to_str = {
247 SIG_SUB_TYPE_CREATE_TIME : 'signature creation time',
248 SIG_SUB_TYPE_EXPIRE_TIME : 'signature expiration time',
249 SIG_SUB_TYPE_EXPORT_CERT : 'exportable certification',
250 SIG_SUB_TYPE_TRUST_SIG : 'trust signature',
251 SIG_SUB_TYPE_REGEXP : 'regular expression',
252 SIG_SUB_TYPE_REVOCABLE : 'revocable',
253 SIG_SUB_TYPE_KEY_EXPIRE : 'key expiration time',
254 SIG_SUB_TYPE_PLACEHOLDER : 'placeholder for backward compatibility',
255 SIG_SUB_TYPE_PREF_SYMM_ALGO : 'preferred symmetric algorithms',
256 SIG_SUB_TYPE_REVOKE_KEY : 'revocation key',
257 SIG_SUB_TYPE_ISSUER_KEY_ID : 'issuer key ID',
258 SIG_SUB_TYPE_NOTATION : 'notation data',
259 SIG_SUB_TYPE_PREF_HASH_ALGO : 'preferred hash algorithms',
260 SIG_SUB_TYPE_PREF_COMP_ALGO : 'preferred compression algorithms',
261 SIG_SUB_TYPE_KEY_SRV_PREF : 'key server preferences',
262 SIG_SUB_TYPE_PREF_KEY_SRVR : 'preferred key server',
263 SIG_SUB_TYPE_PRIM_USER_ID : 'primary user id',
264 SIG_SUB_TYPE_POLICY_URI : 'policy URI',
265 SIG_SUB_TYPE_KEY_FLAGS : 'key flags',
266 SIG_SUB_TYPE_SGNR_USER_ID : "signer's user id",
267 SIG_SUB_TYPE_REVOKE_REASON : 'reason for revocation',
268 SIG_SUB_TYPE_FEATURES : 'features',
269 SIG_SUB_TYPE_SIG_TARGET : 'signature target',
270 SIG_SUB_TYPE_EMBEDDED_SIG : 'embedded signature'
271 }
272
273
274
275 REVOKE_REASON_NONE = 0
276 REVOKE_REASON_SUPER = 0x01
277 REVOKE_REASON_COMPR = 0x02
278 REVOKE_REASON_NOT_USED = 0x03
279 REVOKE_REASON_ID_INVALID = 0x20
280
281 revoke_reason_to_str = {
282 REVOKE_REASON_NONE : 'No reason specified',
283 REVOKE_REASON_SUPER : 'Key is superceded',
284 REVOKE_REASON_COMPR : 'Key has been compromised',
285 REVOKE_REASON_NOT_USED : 'Key is no longer used',
286 REVOKE_REASON_ID_INVALID : 'user id information is no longer valid'
287 }
288
289
290 KEY_FLAGS1_MAY_CERTIFY = 0x01
291 KEY_FLAGS1_MAY_SIGN = 0x02
292 KEY_FLAGS1_MAY_ENC_COMM = 0x04
293 KEY_FLAGS1_MAY_ENC_STRG = 0x08
294 KEY_FLAGS1_PRIV_MAYBE_SPLIT = 0x10
295 KEY_FLAGS1_GROUP = 0x80
296
297
298 REVOKE_KEY_CLASS_MAND = 0x80
299 REVOKE_KEY_CLASS_SENS = 0x40
300
301
302 PGP_FEATURE_1_MOD_DETECT = 0x01
303
304 pgp_feature_to_str = {
305 PGP_FEATURE_1_MOD_DETECT : 'Modification Detectiobn'
306 }
309 """get_whole_number(msg, idx, numlen)
310 extracts a "whole number" field of length numlen from msg at index idx
311 returns (<whole number>, new_idx) where the whole number is a long integer
312 and new_idx is the index of the next element in the message"""
313 n = 0L
314 while numlen > 0 :
315 b = (struct.unpack("B", msg[idx:idx+1]))[0]
316 n = n * 256L + long(b)
317 idx = idx + 1
318 numlen = numlen - 1
319 return (n, idx)
320
322 """get_whole_int(msg, idx, numlen)
323 same as get_whole_number but returns the number as an int for convenience"""
324 n, idx = get_whole_number(msg, idx, numlen)
325 return int(n), idx
326
328 """pack_long(l)
329 returns big-endian representation of unsigned long integer"""
330 arr = []
331 while l > 0 :
332 arr.insert(0, struct.pack("B", l & 0xff))
333 l >>= 8
334 return ''.join(arr)
335
337 """pack_mpi(l)
338 returns the PGP Multi-Precision Integer representation of unsigned long integer"""
339 s = pack_long(l)
340
341
342 bits = (len(s) - 1) * 8
343 if len(s) > 0 :
344 n = ord(s[0])
345 while n != 0 :
346 bits += 1
347 n >>= 1
348 else :
349 bits = 0
350 return struct.pack(">H", bits) + s
351
353 """get_sig_subpak_len(msg, idx)
354 extracts a signature subpacket length field
355 returns (subpak_len, new_idx)"""
356 plen, idx = get_whole_int(msg, idx, 1)
357 if plen < 192 :
358 return plen, idx
359 if plen < 255 :
360 plen2, idx = get_whole_int(msg, idx, 1)
361 return ((plen - 192) << 8) + plen2 + 192, idx
362 return get_whole_int(msg, idx, 4)
363
365 """get_mpi(msg, idx)
366 extracts a multi-precision integer field from the message msg at index idx
367 returns (n, <mpi>, new_idx) where the mpi is a long integer and new_idx is
368 the index of the next element in the message and n is the number of bits of
369 precision in <mpi>"""
370 ln, idx = get_whole_int(msg, idx, 2)
371 return (ln,) + get_whole_number(msg, idx, (ln+7)/8)
372
374 """get_mpi(msg, idx)
375 extracts a multi-precision integer field from the message msg at index idx
376 returns (<mpi>, new_idx) where the mpi is a long integer and new_idx is
377 the index of the next element in the message"""
378 l = get_n_mpi(msg, idx)
379 return (l[1], l[2])
380
382 return ''.join(map(lambda x : hex(ord(x))[2:].zfill(2), list(s)))
383
385 if s == 0 :
386 return 'never'
387 secs = s % 60
388 s = s / 60
389 mins = s % 60
390 s = s / 60
391 hrs = s % 60
392 s = s / 24
393 days = s
394 return '%d days %02d:%02d:%02d' % (days, hrs, mins, secs)
395
397 slist = []
398
399 if type(vals) != types.ListType and type(vals) != types.TupleType :
400 vals = list((vals,))
401 for i in vals :
402 if m.has_key(i) :
403 slist.append(m[i])
404 else :
405 slist.append('unknown(' + str(i) + ')')
406 return ', '.join(slist)
407
414
422
424
425 if self.fingerprint_ :
426 return self.fingerprint_
427
428
429
430 if self.version == 3 :
431 h = hashlib.new('md5')
432 h.update(pack_long(self.pk_rsa_mod))
433 h.update(pack_long(self.pk_rsa_exp))
434 self.fingerprint_ = h.digest()
435 elif self.version == 4 :
436
437
438 h = hashlib.new('sha1')
439 h.update('\x99')
440
441 buf = self.serialize()
442 h.update(struct.pack(">H", len(buf)))
443 h.update(buf)
444 self.fingerprint_ = h.digest()
445 else :
446 raise RuntimeError("unknown public key version %d" % self.version)
447 return self.fingerprint_
448
454
456 chunks = []
457 if self.version == 3 :
458 chunks.append(struct.pack('>BIHB', self.version, int(self.timestamp), self.validity, self.pk_algo))
459 chunks.append(pack_mpi(self.pk_rsa_mod))
460 chunks.append(pack_mpi(self.pk_rsa_exp))
461 elif self.version == 4 :
462 chunks.append(struct.pack('>BIB', self.version, int(self.timestamp), self.pk_algo))
463 if self.pk_algo == ALGO_PK_RSA_ENC_OR_SIGN or self.pk_algo == ALGO_PK_RSA_SIGN_ONLY :
464 chunks.append(pack_mpi(self.pk_rsa_mod))
465 chunks.append(pack_mpi(self.pk_rsa_exp))
466 elif self.pk_algo == ALGO_PK_DSA :
467 chunks.append(pack_mpi(self.pk_dsa_prime_p))
468 chunks.append(pack_mpi(self.pk_dsa_grp_ord_q))
469 chunks.append(pack_mpi(self.pk_dsa_grp_gen_g))
470 chunks.append(pack_mpi(self.pk_dsa_pub_key))
471 elif self.pk_algo == ALGO_PK_ELGAMAL_ENC_OR_SIGN or self.pk_algo == ALGO_PK_ELGAMAL_ENC_ONLY :
472 chunks.append(pack_mpi(self.pk_elgamal_prime_p))
473 chunks.append(pack_mpi(self.pk_elgamal_grp_gen_g))
474 chunks.append(pack_mpi(self.pk_elgamal_pub_key))
475 else :
476 raise RuntimeError("unknown public key algorithm %d" % (self.pk_algo))
477 return ''.join(chunks)
478
480 idx_save = idx
481 self.version, idx = get_whole_int(msg, idx, 1)
482 if self.version != 2 and self.version != 3 and self.version != 4 :
483 raise RuntimeError('unknown public key packet version %d at %d' % (self.version, idx_save))
484 if self.version == 2 :
485 self.version = 3
486 self.timestamp, idx = get_whole_number(msg, idx, 4)
487 self.timestamp = float(self.timestamp)
488 if self.version == 3 :
489 self.validity, idx = get_whole_number(msg, idx, 2)
490 self.pk_algo, idx = get_whole_int(msg, idx, 1)
491 if self.pk_algo == ALGO_PK_RSA_ENC_OR_SIGN or self.pk_algo == ALGO_PK_RSA_SIGN_ONLY :
492 self.key_size, self.pk_rsa_mod, idx = get_n_mpi(msg, idx)
493 self.pk_rsa_exp, idx = get_mpi(msg, idx)
494 elif self.pk_algo == ALGO_PK_DSA :
495 l1, self.pk_dsa_prime_p, idx = get_n_mpi(msg, idx)
496 self.pk_dsa_grp_ord_q, idx = get_mpi(msg, idx)
497 self.pk_dsa_grp_gen_g, idx = get_mpi(msg, idx)
498 l2, self.pk_dsa_pub_key, idx = get_n_mpi(msg, idx)
499 self.key_size = l1 + l2
500 elif self.pk_algo == ALGO_PK_ELGAMAL_ENC_OR_SIGN or self.pk_algo == ALGO_PK_ELGAMAL_ENC_ONLY :
501 self.key_size, self.pk_elgamal_prime_p, idx = get_n_mpi(msg, idx)
502 self.pk_elgamal_grp_gen_g, idx = get_mpi(msg, idx)
503 self.pk_elgamal_pub_key, idx = get_mpi(msg, idx)
504 else :
505 raise RuntimeError("unknown public key algorithm %d at %d" % (self.pk_algo, idx_save))
506
508 sio = cStringIO.StringIO()
509 sio.write(pgp_packet.__str__(self) + "\n")
510 sio.write("version: " + str(self.version) + "\n")
511 sio.write("timestamp: " + time.ctime(self.timestamp) + "\n")
512 if self.version == 3 :
513 sio.write("validity: " + time.ctime(self.timestamp + self.validity * 24 * 60 * 60) + "\n")
514 sio.write("pubkey algo: " + algo_pk_to_str[self.pk_algo] + "\n")
515 if self.pk_algo == ALGO_PK_RSA_ENC_OR_SIGN or self.pk_algo == ALGO_PK_RSA_SIGN_ONLY :
516 sio.write("pk_rsa_mod: " + hex(self.pk_rsa_mod) + "\n")
517 sio.write("pk_rsa_exp: " + hex(self.pk_rsa_exp) + "\n")
518 elif self.pk_algo == ALGO_PK_DSA :
519 sio.write("pk_dsa_prime_p: " + hex(self.pk_dsa_prime_p) + "\n")
520 sio.write("pk_dsa_grp_ord_q: " + hex(self.pk_dsa_grp_ord_q) + "\n")
521 sio.write("pk_dsa_grp_gen_g: " + hex(self.pk_dsa_grp_gen_g) + "\n")
522 sio.write("pk_dsa_pub_key: " + hex(self.pk_dsa_pub_key) + "\n")
523 elif self.pk_algo == ALGO_PK_ELGAMAL_ENC_OR_SIGN or self.pk_algo == ALGO_PK_ELGAMAL_ENC_ONLY :
524 sio.write("pk_elgamal_prime_p: " + hex(self.pk_elgamal_prime_p) + "\n")
525 sio.write("pk_elgamal_grp_gen_g: " + hex(self.pk_elgamal_grp_gen_g) + "\n")
526 sio.write("pk_elgamal_pub_key: " + hex(self.pk_elgamal_pub_key) + "\n")
527 return sio.getvalue()
528
533
535 self.id = msg[idx:idx + pkt_len]
536
539
545
547 self.sub_type, idx = get_whole_int(msg, idx, 1)
548 pkt_len = pkt_len - 1
549 self.data = msg[idx:idx + pkt_len]
550
553
556 pgp_packet.__init__(self)
557 self.version = None
558 self.sig_type = None
559 self.pk_algo = None
560 self.hash_algo = None
561 self.hash_frag = None
562
574
581
589
591 for i in self.hashed_subpaks :
592 if i[0] == typ :
593 return i
594 return None
595
597 for i in self.unhashed_subpaks :
598 if i[0] == typ :
599 return i
600 return None
601
603 sublen, idx = get_sig_subpak_len(msg, idx)
604 subtype, idx = get_whole_int(msg, idx, 1)
605 if subtype == SIG_SUB_TYPE_CREATE_TIME :
606 tm, idx = get_whole_number(msg, idx, 4)
607 return (subtype, float(tm)), idx
608 if subtype == SIG_SUB_TYPE_EXPIRE_TIME or subtype == SIG_SUB_TYPE_KEY_EXPIRE :
609 s, idx = get_whole_int(msg, idx, 4)
610 return (subtype, s), idx
611 if subtype == SIG_SUB_TYPE_EXPORT_CERT or subtype == SIG_SUB_TYPE_REVOCABLE :
612 bool, idx = get_whole_int(msg, idx, 1)
613 return (subtype, bool), idx
614 if subtype == SIG_SUB_TYPE_TRUST_SIG :
615 trust_lvl, idx = get_whole_int(msg, idx, 1)
616 trust_amt, idx = get_whole_int(msg, idx, 1)
617 return (subtype, trust_lvl, trust_amt), idx
618 if subtype == SIG_SUB_TYPE_REGEXP :
619 expr = msg[idx:idx+sublen-1]
620 idx = idx + sublen - 1
621 return (subtype, expr), idx
622 if subtype == SIG_SUB_TYPE_PREF_SYMM_ALGO or subtype == SIG_SUB_TYPE_PREF_HASH_ALGO or subtype == SIG_SUB_TYPE_PREF_COMP_ALGO or subtype == SIG_SUB_TYPE_KEY_FLAGS :
623 algo_list = map(lambda x : ord(x), list(msg[idx:idx+sublen-1]))
624 idx = idx + sublen - 1
625 return (subtype, algo_list), idx
626 if subtype == SIG_SUB_TYPE_REVOKE_KEY :
627 cls, idx = get_whole_int(msg, idx, 1)
628 algo, idx = get_whole_int(msg, idx, 1)
629 fprint = msg[idx:idx+20]
630 idx = idx + 20
631 return (subtype, cls, algo, fprint), idx
632 if subtype == SIG_SUB_TYPE_ISSUER_KEY_ID :
633 k_id = msg[idx:idx+8]
634 idx = idx + 8
635 return (subtype, k_id), idx
636 if subtype == SIG_SUB_TYPE_NOTATION :
637 flg1, idx = get_whole_int(msg, idx, 1)
638 flg2, idx = get_whole_int(msg, idx, 1)
639 flg3, idx = get_whole_int(msg, idx, 1)
640 flg4, idx = get_whole_int(msg, idx, 1)
641 name_len, idx = get_whole_int(msg, idx, 2)
642 val_len, idx = get_whole_int(msg, idx, 2)
643 nam = msg[idx:idx+name_len]
644 idx = idx + name_len
645 val = msg[idx:idx+val_len]
646 idx = idx + val_len
647 return (subtype, flg1, flg2, flg3, flg4, nam, val), idx
648 if subtype == SIG_SUB_TYPE_KEY_SRV_PREF :
649 prefs = [ ord(x) for x in msg[idx:idx+sublen-1] ]
650 idx = idx + sublen - 1
651 return (subtype, prefs), idx
652 if subtype == SIG_SUB_TYPE_PREF_KEY_SRVR :
653 url = msg[idx:idx+sublen-1]
654 idx = idx + sublen - 1
655 return (subtype, url), idx
656 if subtype == SIG_SUB_TYPE_PRIM_USER_ID :
657 bool, idx = get_whole_int(msg, idx, 1)
658 return (subtype, bool), idx
659 if subtype == SIG_SUB_TYPE_POLICY_URI :
660 uri = msg[idx:idx+sublen-1]
661 idx = idx + sublen - 1
662 return (subtype, uri), idx
663 if subtype == SIG_SUB_TYPE_SGNR_USER_ID :
664 signer_id = msg[idx:idx+sublen-1]
665 idx = idx + sublen - 1
666 return (subtype, signer_id), idx
667 if subtype == SIG_SUB_TYPE_REVOKE_REASON :
668 rev_code, idx = get_whole_int(msg, idx, 1)
669 reas_len = sublen - 2
670 reas = msg[idx:idx+reas_len]
671 idx = idx + reas_len
672 return (subtype, rev_code, reas), idx
673 if subtype == SIG_SUB_TYPE_FEATURES :
674 sublen = sublen - 1
675 l = [subtype]
676 while sublen > 0 :
677 oct, idx = get_whole_int(msg, idx, 1)
678 l.append(oct)
679 sublen = sublen - 1
680 return tuple(l), idx
681 if subtype == SIG_SUB_TYPE_SIG_TARGET :
682 public_key_algo, idx = get_whole_int(msg, idx, 1)
683 hash_algo, idx = get_whole_int(msg, idx, 1)
684 hash = msg[idx:idx+sublen-3]
685 idx = idx + sublen - 3
686 return (subtype, public_key_algo, hash_algo, hash), idx
687 if subtype == SIG_SUB_TYPE_EMBEDDED_SIG :
688
689 dat = msg[idx:idx+sublen-1]
690 idx = idx + sublen - 1
691 return (subtype, dat), idx
692
693
694 dat = msg[idx:idx+sublen-1]
695 idx = idx + sublen - 1
696 return (subtype, dat), idx
697
699 """is_primary_user_id()
700 returns true if this signature contains a primary user id subpacket with value true"""
701 for i in self.hashed_subpaks :
702 if i[0] == SIG_SUB_TYPE_PRIM_USER_ID :
703 return i[1]
704 return 0
705
707 if sp[0] == SIG_SUB_TYPE_CREATE_TIME :
708 return 'creation time: ' + time.ctime(sp[1])
709 if sp[0] == SIG_SUB_TYPE_EXPIRE_TIME :
710 return 'signature expires: ' + duration_to_str(sp[1])
711 if sp[0] == SIG_SUB_TYPE_EXPORT_CERT :
712 if sp[1] :
713 return 'signature exportable: TRUE'
714 else :
715 return 'signature exportable: FALSE'
716 if sp[0] == SIG_SUB_TYPE_TRUST_SIG :
717 if sp[1] == 0 :
718 return 'trust: ordinary'
719 if sp[1] == 1 :
720 return 'trust: introducer (%d)' % sp[2]
721 if sp[1] == 2 :
722 return 'trust: meta-introducer (%d)' % sp[2]
723 return 'trust: %d %d' % (sp[1], sp[2])
724 if sp[0] == SIG_SUB_TYPE_REGEXP :
725 return 'regexp: ' + sp[1]
726 if sp[0] == SIG_SUB_TYPE_REVOCABLE :
727 if sp[1] :
728 return 'signature revocable: TRUE'
729 else :
730 return 'signature revocable: FALSE'
731 if sp[0] == SIG_SUB_TYPE_KEY_EXPIRE :
732 return 'key expires: ' + duration_to_str(sp[1])
733 if sp[0] == SIG_SUB_TYPE_PREF_SYMM_ALGO :
734 return 'preferred symmetric algorithms: ' + map_to_str(algo_sk_to_str, sp[1])
735 if sp[0] == SIG_SUB_TYPE_REVOKE_KEY :
736 s = 'revocation key: '
737 if sp[1] & REVOKE_KEY_CLASS_SENS :
738 s = s + '(sensitive) '
739 return s + map_to_str(algo_pk_to_str, sp[2]) + ' ' + str_to_hex(sp[3])
740 if sp[0] == SIG_SUB_TYPE_ISSUER_KEY_ID :
741 return 'issuer key id: ' + str_to_hex(sp[1])
742 if sp[0] == SIG_SUB_TYPE_NOTATION :
743 return 'notation: flags(%d, %d, %d, %d) name(%s) value(%s)' % sp[1:]
744 if sp[0] == SIG_SUB_TYPE_PREF_HASH_ALGO :
745 return 'preferred hash algorithms: ' + map_to_str(algo_hash_to_str, sp[1])
746 if sp[0] == SIG_SUB_TYPE_PREF_COMP_ALGO :
747 return 'preferred compression algorithms: ' + map_to_str(algo_comp_to_str, sp[1])
748 if sp[0] == SIG_SUB_TYPE_KEY_SRV_PREF :
749 s = 'key server preferences: '
750 prefs = []
751 if sp[1][0] & 0x80 :
752 prefs.append('No-modify')
753 return s + ', '.join(prefs)
754 if sp[0] == SIG_SUB_TYPE_PREF_KEY_SRVR :
755 return 'preferred key server: %s' % sp[1]
756 if sp[0] == SIG_SUB_TYPE_PRIM_USER_ID :
757 if sp[1] :
758 return 'is primary user id'
759 else :
760 return 'is not primary user id'
761 if sp[0] == SIG_SUB_TYPE_POLICY_URI :
762 return 'policy url: %s' % sp[1]
763 if sp[0] == SIG_SUB_TYPE_KEY_FLAGS :
764 flags = []
765 flgs1 = 0
766 if len(sp[1]) >= 1 :
767 flgs1 = sp[1][0]
768 if flgs1 & KEY_FLAGS1_MAY_CERTIFY :
769 flags.append('may certify other keys')
770 if flgs1 & KEY_FLAGS1_MAY_SIGN :
771 flags.append('may sign data')
772 if flgs1 & KEY_FLAGS1_MAY_ENC_COMM :
773 flags.append('may encrypt communications')
774 if flgs1 & KEY_FLAGS1_MAY_ENC_STRG :
775 flags.append('may encrypt storage')
776 if flgs1 & KEY_FLAGS1_PRIV_MAYBE_SPLIT :
777 flags.append('private component may have been secret-sharing split')
778 if flgs1 & KEY_FLAGS1_GROUP :
779 flags.append('group key')
780 return 'key flags: ' + ', '.join(flags)
781 if sp[0] == SIG_SUB_TYPE_SGNR_USER_ID :
782 return 'signer id: ' + sp[1]
783 if sp[0] == SIG_SUB_TYPE_REVOKE_REASON :
784 reas = ''
785 if revoke_reason_to_str.has_key(sp[1]) :
786 reas = revoke_reason_to_str[sp[1]]
787 return 'reason for revocation: %s, %s' % (reas, sp[2])
788 if sp[0] == SIG_SUB_TYPE_FEATURES :
789 features = []
790 if len(sp) > 1 :
791 val = sp[1]
792 if val & PGP_FEATURE_1_MOD_DETECT :
793 features.append('Modification Detection')
794 val = val & ~PGP_FEATURE_1_MOD_DETECT
795 if val != 0 :
796 features.append('[0]=0x%x' % val)
797 for i in range(2, len(sp)) :
798 features.append('[%d]=0x%x' % (i-1,sp[i]))
799 return 'features: ' + ', '.join(features)
800
801 return 'unknown(%d): %s' % (sp[0], str_to_hex(sp[1]))
802
804 self.version, idx = get_whole_int(msg, idx, 1)
805 if self.version == 2 :
806 self.version = 3
807 if self.version == 3 :
808 hash_len, idx = get_whole_number(msg, idx, 1)
809 self.sig_type, idx = get_whole_int(msg, idx, 1)
810 self.timestamp, idx = get_whole_number(msg, idx, 4)
811 self.timestamp = float(self.timestamp)
812 self.key_id_ = msg[idx:idx+8]
813 idx = idx + 8
814 self.pk_algo, idx = get_whole_int(msg, idx, 1)
815 self.hash_algo, idx = get_whole_int(msg, idx, 1)
816 elif self.version == 4:
817 self.sig_type, idx = get_whole_int(msg, idx, 1)
818 self.pk_algo, idx = get_whole_int(msg, idx, 1)
819 self.hash_algo, idx = get_whole_int(msg, idx, 1)
820 sub_paks_len, idx = get_whole_int(msg, idx, 2)
821 sub_paks_end = idx + sub_paks_len
822 self.hashed_subpaks = []
823 while idx < sub_paks_end :
824 sp, idx = self.deserialize_subpacket(msg, idx)
825 self.hashed_subpaks.append(sp)
826 sub_paks_len, idx = get_whole_int(msg, idx, 2)
827 sub_paks_end = idx + sub_paks_len
828 self.unhashed_subpaks = []
829 while idx < sub_paks_end :
830 sp, idx = self.deserialize_subpacket(msg, idx)
831 self.unhashed_subpaks.append(sp)
832 else :
833 raise RuntimeError('unknown signature packet version %d at %d' % (self.version, idx))
834 self.hash_frag, idx = get_whole_number(msg, idx, 2)
835 if self.pk_algo == ALGO_PK_RSA_ENC_OR_SIGN or self.pk_algo == ALGO_PK_RSA_SIGN_ONLY :
836 self.rsa_sig, idx = get_mpi(msg, idx)
837 elif self.pk_algo == ALGO_PK_DSA :
838 self.dsa_sig_r, idx = get_mpi(msg, idx)
839 self.dsa_sig_s, idx = get_mpi(msg, idx)
840 else :
841 raise RuntimeError('unknown public-key algorithm (%d) in signature at %d' % (self.pk_algo, idx))
842 return idx
843
869
877 self.version = None
878 self.public_key = None
879 self.revocations = []
880 self.user_ids = []
881 self.primary_user_id = -1
882
884 sio = cStringIO.StringIO()
885 sio.write("PGP Public Key Certificate v%d\n" % self.version)
886 sio.write("Cert ID: %s\n" % str_to_hex(self.public_key.key_id()))
887 sio.write("Primary ID: %s\n" % self.user_id)
888 sio.write(str(self.public_key))
889 for uid in self.user_ids :
890 sio.write(str(uid[0]))
891 for sig in uid[1:] :
892 sio.write(" " + str(sig))
893 if hasattr(self, 'user_attrs') :
894 for uattr in self.user_attrs :
895 sio.write(' ')
896 sio.write(str(uattr[0]))
897 for sig in uattr[1:] :
898 sio.write(" " + str(sig))
899 return sio.getvalue()
900
902
903
904
905 return self.user_ids[self.primary_user_id][0].id
906
907 user_id = property(get_user_id)
908
925
928
929 - def load(self, pkts) :
930 """load(pkts)
931 Initialize the pgp_certificate with a list of OpenPGP packets. The list of packets will
932 be scanned to make sure they are valid for a pgp certificate."""
933
934
935 if pkts[0].pkt_typ != CTB_PKT_PK_CERT :
936 raise ValueError('first PGP packet should be a public-key packet, not %s' % map_to_str(ctb_pkt_to_str, pkts[0].pkt_typ))
937
938
939
940 self.public_key = pkts[0]
941
942
943 self.version = self.public_key.version
944
945
946 if self.version == 3 :
947 pkt_idx = 1
948
949
950 while pkts[pkt_idx].pkt_typ == CTB_PKT_SIG :
951 if pkts[pkt_idx].version != 3 :
952 raise ValueError('version 3 cert has version %d signature' % pkts[pkt_idx].version)
953 if pkts[pkt_idx].sig_type != SIG_TYPE_KEY_REVOKE :
954 raise ValueError('v3 cert revocation sig has type %s' % map_to_str(sig_type_to_str, pkts[pkt_idx].sig_type))
955
956
957
958 self.revocations.append(pkts[pkt_idx])
959
960
961 pkt_idx = pkt_idx + 1
962
963
964 while pkt_idx < len(pkts) :
965
966 if pkts[pkt_idx].pkt_typ != CTB_PKT_USER_ID :
967 if len(self.user_ids) == 0 :
968 raise ValueError('pgp packet %d is not user id, is %s' % (pkt_idx, map_to_str(ctb_pkt_to_str, pkts[pkt_idx].pkt_typ)))
969 else :
970 break
971
972 user_id = [pkts[pkt_idx]]
973 pkt_idx = pkt_idx + 1
974 is_revoked = 0
975 is_primary_user_id = 0
976
977
978
979 while pkt_idx < len(pkts) and pkts[pkt_idx].pkt_typ == CTB_PKT_SIG :
980 if pkts[pkt_idx].sig_type not in (SIG_TYPE_PK_USER_GEN, SIG_TYPE_PK_USER_PER, SIG_TYPE_PK_USER_CAS, SIG_TYPE_PK_USER_POS, SIG_TYPE_CERT_REVOKE) :
981 raise ValueError('signature %d doesn\'t bind user_id to key, is %s' % (pkt_idx, map_to_str(sig_type_to_str, pkts[pkt_idx].sig_typ)))
982
983 user_id.append(pkts[pkt_idx])
984
985 pkt_idx = pkt_idx + 1
986
987
988 self.user_ids.append(user_id)
989
990 else :
991 pkt_idx = 1
992 self.direct_key_sigs = []
993 self.subkeys = []
994 self.rvkd_subkeys = []
995 self.user_attrs = []
996
997 cert_id = self.public_key.key_id()
998
999
1000 while pkt_idx < len(pkts) and pkts[pkt_idx].pkt_typ == CTB_PKT_SIG :
1001 if pkts[pkt_idx].version != 4 :
1002 raise ValueError('version 4 cert has version %d signature' % pkts[pkt_idx].version)
1003 if pkts[pkt_idx].sig_type == SIG_TYPE_KEY_REVOKE :
1004 self.revocations.append(pkts[pkt_idx])
1005 elif pkts[pkt_idx].sig_type == SIG_TYPE_KEY :
1006 self.direct_key_sigs.append(pkts[pkt_idx])
1007 else :
1008 raise ValueError('v4 cert signature has type %s, supposed to be revocation signature or direct key signature' % map_to_str(sig_type_to_str, pkts[pkt_idx].sig_type))
1009
1010
1011 pkt_idx = pkt_idx + 1
1012
1013
1014
1015
1016
1017 prim_user_id_sig_time = 0
1018
1019 while pkt_idx < len(pkts) :
1020
1021 if pkts[pkt_idx].pkt_typ == CTB_PKT_USER_ID :
1022 user_id = [pkts[pkt_idx]]
1023 is_revoked = 0
1024 is_primary_user_id = 0
1025
1026 pkt_idx = pkt_idx + 1
1027
1028
1029
1030 while pkt_idx < len(pkts) and pkts[pkt_idx].pkt_typ == CTB_PKT_SIG :
1031 if pkts[pkt_idx].sig_type not in (SIG_TYPE_PK_USER_GEN, SIG_TYPE_PK_USER_PER, SIG_TYPE_PK_USER_CAS, SIG_TYPE_PK_USER_POS, SIG_TYPE_CERT_REVOKE) :
1032 raise ValueError('signature %d doesn\'t bind user_id to key, is %s' % (pkt_idx, map_to_str(sig_type_to_str, pkts[pkt_idx].sig_type)))
1033 user_id.append(pkts[pkt_idx])
1034
1035
1036 if pkts[pkt_idx].key_id() == cert_id :
1037 if pkts[pkt_idx].is_primary_user_id() :
1038 ct = pkts[pkt_idx].creation_time()
1039 if ct > prim_user_id_sig_time :
1040 self.primary_user_id = len(self.user_ids)
1041 prim_user_id_sig_time = ct
1042
1043 pkt_idx = pkt_idx + 1
1044
1045
1046 self.user_ids.append(user_id)
1047
1048
1049 elif pkts[pkt_idx].pkt_typ == CTB_PKT_USER_ATTR :
1050 user_attr = [pkts[pkt_idx]]
1051 is_revoked = 0
1052
1053 pkt_idx = pkt_idx + 1
1054
1055
1056
1057 while pkt_idx < len(pkts) and pkts[pkt_idx].pkt_typ == CTB_PKT_SIG :
1058 if pkts[pkt_idx].sig_type not in (SIG_TYPE_PK_USER_GEN, SIG_TYPE_PK_USER_PER, SIG_TYPE_PK_USER_CAS, SIG_TYPE_PK_USER_POS, SIG_TYPE_CERT_REVOKE) :
1059 raise ValueError('signature %d doesn\'t bind user_attr to key, is %s' % (pkt_idx, map_to_str(sig_type_to_str, pkts[pkt_idx].sig_type)))
1060 user_attr.append(pkts[pkt_idx])
1061 pkt_idx = pkt_idx + 1
1062
1063
1064 self.user_attrs.append(user_attr)
1065
1066 elif pkts[pkt_idx].pkt_typ == CTB_PKT_PK_SUB :
1067
1068 subkey = [pkts[pkt_idx]]
1069 pkt_idx = pkt_idx + 1
1070 is_revoked = 0
1071
1072
1073 if pkt_idx >= len(pkts) :
1074 raise ValueError('subkey at index %d was not followed by a signature' % (pkt_idx-1))
1075 if pkts[pkt_idx].pkt_typ != CTB_PKT_SIG or pkts[pkt_idx].sig_type != SIG_TYPE_SUBKEY_BIND :
1076 raise ValueError('signature %d doesn\'t bind subkey to key, type is %s' % (pkt_idx, map_to_str(sig_type_to_str, pkts[pkt_idx].sig_typ)))
1077 subkey.append(pkts[pkt_idx])
1078
1079 pkt_idx = pkt_idx + 1
1080
1081
1082 if pkt_idx < len(pkts) and pkts[pkt_idx].pkt_typ == CTB_PKT_SIG and pkts[pkt_idx].sig_type == SIG_TYPE_SUBKEY_REVOKE :
1083 is_revoked = 1
1084 subkey.append(pkts[pkt_idx])
1085 pkt_idx = pkt_idx + 1
1086
1087
1088 if is_revoked :
1089 self.rvkd_subkeys.append(subkey)
1090 else :
1091 self.subkeys.append(subkey)
1092 else :
1093 break
1094
1095
1096
1097
1098 if len(self.user_ids) == 0 :
1099 raise ValueError('no user id packet was present in the cert %s' % str_to_hex(self.public_key.key_id()))
1100 return pkt_idx
1101
1104 """get_ctb(msg, idx)
1105 extracts a the "cypher type bit" information from message msg at index idx
1106 returns (type, len, new_idx) where type is the enumerated type of the packet,
1107 len is the length of the packet, and new_idx is the index of the next element
1108 in the message"""
1109 b, idx = get_whole_int(msg, idx, 1)
1110 if (b & CTB_76_MASK) == CTB_76_NORMAL :
1111 n_len = 0
1112 if (b & CTB_PKT_LEN_MASK) == CTB_PKT_LEN_1 :
1113 n_len = 1
1114 if (b & CTB_PKT_LEN_MASK) == CTB_PKT_LEN_2 :
1115 n_len = 2
1116 if (b & CTB_PKT_LEN_MASK) == CTB_PKT_LEN_4 :
1117 n_len = 4
1118 if (b & CTB_PKT_LEN_MASK) == CTB_PKT_LEN_UNDEF :
1119 n_len = 0
1120 pkt_len = 0
1121 if n_len > 0 :
1122 pkt_len, idx = get_whole_int(msg, idx, n_len)
1123 return (b & CTB_PKTV2_MASK) >> 2, pkt_len, idx
1124 elif (b & CTB_76_MASK) == CTB_76_NEW :
1125 plen, idx = get_whole_int(msg, idx, 1)
1126 if plen < 192 :
1127 return b & CTB_PKT_MASK, plen, idx
1128 if plen < 224 :
1129 plen2, idx = get_whole_int(msg, idx, 1)
1130 return b & CTB_PKT_MASK, ((plen - 192) << 8) + plen2 + 192, idx
1131 if plen == 255 :
1132 plen, idx = get_whole_int(msg, idx, 4)
1133 return b & CTB_PKT_MASK, plen, idx
1134 else :
1135 raise Exception, 'partial message bodies are not supported by this version (%d)', b
1136 else :
1137 raise Exception, "unknown (not \"normal\") cypher type bit %d at byte %d" % (b, idx)
1138
1140 crc24_init = 0xb704ce
1141 crc24_poly = 0x1864cfb
1142
1143 crc = crc24_init
1144 for i in list(msg) :
1145 crc = crc ^ (ord(i) << 16)
1146 for j in range(0, 8) :
1147 crc = crc << 1
1148 if crc & 0x1000000 :
1149 crc = crc ^ crc24_poly
1150 return crc & 0xffffff
1151
1185
1187 """decode_msg(msg) ==> list of OpenPGP "packet" objects
1188 Takes an ascii-armored PGP block and returns a list of objects each of which
1189 corresponds to a PGP "packets".
1190
1191 A PGP message is a series of packets. You need to understand how packets are
1192 to be combined together in order to know what to do with them. For example
1193 a PGP "certificate" includes a public key, user id(s), and signature.
1194 """
1195
1196
1197 pgpkey_lines = map(lambda x : x.rstrip(), msg.split('\n'))
1198
1199
1200 in_block = 0
1201 in_data = 0
1202
1203 block_buf = cStringIO.StringIO()
1204 for l in pgpkey_lines :
1205 if not in_block :
1206 if l == '-----BEGIN PGP PUBLIC KEY BLOCK-----' :
1207 in_block = 1
1208 continue
1209
1210
1211 if not in_data :
1212 if len(l) == 0 :
1213 in_data = 1
1214 continue
1215
1216
1217 if l[0] == '=' :
1218
1219 csum = base64.decodestring(l[1:5])
1220 i = 0
1221 csum, i = get_whole_number(csum, i, 3)
1222
1223
1224 cert_msg = base64.decodestring(block_buf.getvalue())
1225 block_buf.close()
1226
1227
1228 if csum != crc24(cert_msg) :
1229 raise Exception, 'bad checksum on pgp message'
1230
1231
1232 pkt_list = decode(cert_msg)
1233
1234
1235 cert_list = []
1236 while len(pkt_list) > 0 :
1237 cert = pgp_certificate()
1238 cert.raw_key = msg
1239 pkt_idx = cert.load(pkt_list)
1240 cert_list.append(cert)
1241 pkt_list[0:pkt_idx] = []
1242 return cert_list
1243
1244
1245 block_buf.write(l)
1246
1247 return []
1248
1250
1251 certs = []
1252
1253 pgpkey_lines = map(lambda x : x.rstrip(), msg.split('\n'))
1254 in_block = 0
1255 block = ''
1256 for l in pgpkey_lines :
1257 if not in_block :
1258 if l == '-----BEGIN PGP PUBLIC KEY BLOCK-----' :
1259 in_block = 1
1260 block += '%s\n' % l
1261 continue
1262
1263 block += '%s\n' % l
1264 if l == '-----END PGP PUBLIC KEY BLOCK-----':
1265 in_block = 0
1266 thesecerts = decode_msg(block)
1267 if thesecerts:
1268 certs.extend(thesecerts)
1269 block = ''
1270 continue
1271 return certs
1272
1273
1274 if __name__ == '__main__' :
1275 import sys
1276 for pgp_cert in decode_msg(open(sys.argv[1]).read()) :
1277 print pgp_cert
1278