Package yum :: Module pgpmsg
[hide private]
[frames] | no frames]

Source Code for Module yum.pgpmsg

   1  ##Copyright (C) 2003,2005,2009  Jens B. Jorgensen <jbj1@ultraemail.net> 
   2  ## 
   3  ##This program is free software; you can redistribute it and/or 
   4  ##modify it under the terms of the GNU General Public License 
   5  ##as published by the Free Software Foundation; either version 2 
   6  ##of the License, or (at your option) any later version. 
   7  ## 
   8  ##This program is distributed in the hope that it will be useful, 
   9  ##but WITHOUT ANY WARRANTY; without even the implied warranty of 
  10  ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  11  ##GNU General Public License for more details. 
  12  ## 
  13  ##You should have received a copy of the GNU General Public License 
  14  ##along with this program; if not, write to the Free Software 
  15  ##Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
  16  import struct, time, cStringIO, base64, types 
  17   
  18  #  We use this so that we can work on python-2.4 and python-2.6, and thus. 
  19  # use import md5/import sha on the older one and import hashlib on the newer. 
  20  #  Stupid deprecation warnings. 
  21  try: 
  22      import hashlib 
  23  except ImportError: 
  24      # Python-2.4.z ... gah! 
  25      import sha 
  26      import md5 
27 - class hashlib:
28 29 @staticmethod
30 - def new(algo):
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 # Cypher Type Byte 40 # bits 7,6 of the CTB say what kind it is 41 # we only have reserved defined 42 CTB_76_NORMAL = 0x80 43 CTB_76_NEW = 0xc0 44 CTB_76_MASK = 0xc0 45 46 # CTB packet type, bits 5,4,3,2 47 CTB_PKTV2_MASK = 0x3c # 1111 - mask for this field 48 CTB_PKT_MASK = 0x3f # 111111 - all the lower bits 49 50 CTB_PKT_PK_ENC = 1 # 0001 - public-key encrypted session packet 51 CTB_PKT_SIG = 2 # 0010 - signature packet 52 CTB_PKT_SK_ENC = 3 # 0011 - symmetric-key encrypted session packet 53 CTB_PKT_OP_SIG = 4 # 0100 - one-pass signature packet 54 CTB_PKT_SK_CERT = 5 # 0101 - secret-key certificate packet 55 CTB_PKT_PK_CERT = 6 # 0110 - public-key certificate packet 56 CTB_PKT_SK_SUB = 7 # 0111 - secret-key subkey packet 57 CTB_PKT_COMPRESSED = 8 # 1000 - compressed data packet 58 CTB_PKT_ENC = 9 # 1001 - symmetric-key encrypted data packet 59 CTB_PKT_MARKER = 10 # 1010 - marker packet 60 CTB_PKT_LIT = 11 # 1011 - literal data packet 61 CTB_PKT_TRUST = 12 # 1100 - trust packet 62 CTB_PKT_USER_ID = 13 # 1101 - user id packet 63 CTB_PKT_PK_SUB = 14 # 1110 - public subkey packet 64 CTB_PKT_USER_ATTR = 17 # 10001 - user attribute packet 65 CTB_PKT_SYM_ENC_INT = 18 # 10010 - symmetric encrypted integrity packet 66 CTB_PKT_MOD_DETECT = 19 # 10011 - modification detection code packet 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 # CTB packet-length 90 CTB_PKT_LEN_MASK = 0x3 # 11 - mask 91 92 CTB_PKT_LEN_1 = 0 # 00 - 1 byte 93 CTB_PKT_LEN_2 = 1 # 01 - 2 bytes 94 CTB_PKT_LEN_4 = 2 # 10 - 4 bytes 95 CTB_PKT_LEN_UNDEF = 3 # 11 - no packet length supplied 96 97 # Algorithms 98 99 # Public Key Algorithms 100 ALGO_PK_RSA_ENC_OR_SIGN = 1 # RSA (Encrypt or Sign) 101 ALGO_PK_RSA_ENC_ONLY = 2 # RSA Encrypt-Only 102 ALGO_PK_RSA_SIGN_ONLY = 3 # RSA Sign-Only 103 ALGO_PK_ELGAMAL_ENC_ONLY = 16 # Elgamal (Encrypt-Only) 104 ALGO_PK_DSA = 17 # DSA (Digital Signature Standard) 105 ALGO_PK_ELLIPTIC_CURVE = 18 # Elliptic Curve 106 ALGO_PK_ECDSA = 19 # ECDSA 107 ALGO_PK_ELGAMAL_ENC_OR_SIGN = 20 # Elgamal (Encrypt or Sign) 108 ALGO_PK_DH = 21 # Diffie-Hellman 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 # Symmetric Key Algorithms 123 ALGO_SK_PLAIN = 0 # Plaintext or unencrypted data 124 ALGO_SK_IDEA = 1 # IDEA 125 ALGO_SK_3DES = 2 # Triple-DES 126 ALGO_SK_CAST5 = 3 # CAST5 127 ALGO_SK_BLOWFISH = 4 # Blowfish 128 ALGO_SK_SAFER_SK128 = 5 # SAFER-SK128 129 ALGO_SK_DES_SK = 6 # DES/SK 130 ALGO_SK_AES_128 = 7 # AES 128-bit 131 ALGO_SK_AES_192 = 8 # AES 192-bit 132 ALGO_SK_AES_256 = 9 # AES 256-bit 133 ALGO_SK_TWOFISH_256 = 10 # Twofish 256 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 # Compression Algorithms 150 ALGO_COMP_UNCOMP = 0 # Uncompressed 151 ALGO_COMP_ZIP = 1 # ZIP 152 ALGO_COMP_ZLIB = 2 # ZLIB 153 ALGO_COMP_BZIP2 = 3 # BZip2 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 # Hash Algorithms 163 ALGO_HASH_MD5 = 1 # MD5 164 ALGO_HASH_SHA1 = 2 # SHA1 165 ALGO_HASH_RIPEMD160 = 3 # RIPEMD160 166 ALGO_HASH_SHA_DBL = 4 # double-width SHA 167 ALGO_HASH_MD2 = 5 # MD2 168 ALGO_HASH_TIGER192 = 6 # TIGER192 169 ALGO_HASH_HAVAL_5_160 = 7 # HAVAL-5-160 170 ALGO_HASH_SHA256 = 8 # SHA256 171 ALGO_HASH_SHA384 = 9 # SHA384 172 ALGO_HASH_SHA512 = 10 # SHA512 173 ALGO_HASH_SHA224 = 11 # SHA224 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 # Signature types 190 SIG_TYPE_DOCUMENT = 0x00 # document signature, binary image 191 SIG_TYPE_DOCUMENT_CANON = 0x01 # document signature, canonical text 192 SIG_TYPE_STANDALONE = 0x02 # signature over just subpackets 193 SIG_TYPE_PK_USER_GEN = 0x10 # public key packet and user ID packet, generic certification 194 SIG_TYPE_PK_USER_PER = 0x11 # public key packet and user ID packet, persona 195 SIG_TYPE_PK_USER_CAS = 0x12 # public key packet and user ID packet, casual certification 196 SIG_TYPE_PK_USER_POS = 0x13 # public key packet and user ID packet, positive certification 197 SIG_TYPE_SUBKEY_BIND = 0x18 # subkey binding 198 SIG_TYPE_KEY = 0x1F # key signature 199 SIG_TYPE_KEY_REVOKE = 0x20 # key revocation 200 SIG_TYPE_SUBKEY_REVOKE = 0x28 # subkey revocation 201 SIG_TYPE_CERT_REVOKE = 0x30 # certificate revocation 202 SIG_TYPE_TIMESTAMP = 0x40 # timestamp 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 # Signature sub-packet types 221 SIG_SUB_TYPE_CREATE_TIME = 2 # signature creation time 222 SIG_SUB_TYPE_EXPIRE_TIME = 3 # signature expiration time 223 SIG_SUB_TYPE_EXPORT_CERT = 4 # exportable certification 224 SIG_SUB_TYPE_TRUST_SIG = 5 # trust signature 225 SIG_SUB_TYPE_REGEXP = 6 # regular expression 226 SIG_SUB_TYPE_REVOCABLE = 7 # revocable 227 SIG_SUB_TYPE_KEY_EXPIRE = 9 # key expiration time 228 SIG_SUB_TYPE_PLACEHOLDER = 10 # placeholder for backward compatibility 229 SIG_SUB_TYPE_PREF_SYMM_ALGO = 11 # preferred symmetric algorithms 230 SIG_SUB_TYPE_REVOKE_KEY = 12 # revocation key 231 SIG_SUB_TYPE_ISSUER_KEY_ID = 16 # issuer key ID 232 SIG_SUB_TYPE_NOTATION = 20 # notation data 233 SIG_SUB_TYPE_PREF_HASH_ALGO = 21 # preferred hash algorithms 234 SIG_SUB_TYPE_PREF_COMP_ALGO = 22 # preferred compression algorithms 235 SIG_SUB_TYPE_KEY_SRV_PREF = 23 # key server preferences 236 SIG_SUB_TYPE_PREF_KEY_SRVR = 24 # preferred key server 237 SIG_SUB_TYPE_PRIM_USER_ID = 25 # primary user id 238 SIG_SUB_TYPE_POLICY_URI = 26 # policy URI 239 SIG_SUB_TYPE_KEY_FLAGS = 27 # key flags 240 SIG_SUB_TYPE_SGNR_USER_ID = 28 # signer's user id 241 SIG_SUB_TYPE_REVOKE_REASON = 29 # reason for revocation 242 SIG_SUB_TYPE_FEATURES = 30 # features 243 SIG_SUB_TYPE_SIG_TARGET = 31 # signature target 244 SIG_SUB_TYPE_EMBEDDED_SIG = 32 # embedded signature 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 # in a signature subpacket there may be a revocation reason, these codes indicate 274 # the reason 275 REVOKE_REASON_NONE = 0 # No reason specified 276 REVOKE_REASON_SUPER = 0x01 # Key is superceded 277 REVOKE_REASON_COMPR = 0x02 # Key has been compromised 278 REVOKE_REASON_NOT_USED = 0x03 # Key is no longer used 279 REVOKE_REASON_ID_INVALID = 0x20 # user id information is no longer valid 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 # These flags are used in a 'key flags' signature subpacket 290 KEY_FLAGS1_MAY_CERTIFY = 0x01 # This key may be used to certify other keys 291 KEY_FLAGS1_MAY_SIGN = 0x02 # This key may be used to sign data 292 KEY_FLAGS1_MAY_ENC_COMM = 0x04 # This key may be used to encrypt communications 293 KEY_FLAGS1_MAY_ENC_STRG = 0x08 # This key may be used to encrypt storage 294 KEY_FLAGS1_PRIV_MAYBE_SPLIT = 0x10 # Private component have be split through secret-sharing mech. 295 KEY_FLAGS1_GROUP = 0x80 # Private component may be among group 296 297 # A revocation key subpacket has these class values 298 REVOKE_KEY_CLASS_MAND = 0x80 # this bit must always be set 299 REVOKE_KEY_CLASS_SENS = 0x40 # sensitive 300 301 # Features may be indicated in a signature hashed subpacket 302 PGP_FEATURE_1_MOD_DETECT = 0x01 # Modification detection 303 304 pgp_feature_to_str = { 305 PGP_FEATURE_1_MOD_DETECT : 'Modification Detectiobn' 306 }
307 308 -def get_whole_number(msg, idx, numlen) :
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
321 -def get_whole_int(msg, idx, numlen) :
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
327 -def pack_long(l) :
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
336 -def pack_mpi(l) :
337 """pack_mpi(l) 338 returns the PGP Multi-Precision Integer representation of unsigned long integer""" 339 s = pack_long(l) 340 # the len is the number of bits, counting only from the MSB, 341 # so we need to account for that 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 # otherwise bits == -8 350 return struct.pack(">H", bits) + s
351
352 -def get_sig_subpak_len(msg, idx) :
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
364 -def get_n_mpi(msg, idx) :
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
373 -def get_mpi(msg, idx) :
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
381 -def str_to_hex(s) :
382 return ''.join(map(lambda x : hex(ord(x))[2:].zfill(2), list(s)))
383
384 -def duration_to_str(s) :
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
396 -def map_to_str(m, vals) :
397 slist = [] 398 # change to a list if it's a single value 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
408 -class pgp_packet(object) :
409 - def __init__(self) :
410 self.pkt_typ = None
411
412 - def __str__(self) :
413 return map_to_str(ctb_pkt_to_str, self.pkt_typ)
414
415 -class public_key(pgp_packet) :
416 - def __init__(self) :
417 pgp_packet.__init__(self) 418 self.version = None 419 self.pk_algo = None 420 self.key_size = 0 421 self.fingerprint_ = None # we cache this upon calculation
422
423 - def fingerprint(self) :
424 # return cached value if we have it 425 if self.fingerprint_ : 426 return self.fingerprint_ 427 428 # otherwise calculate it now and cache it 429 # v3 and v4 are calculated differently 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 # we hash what would be the whole PGP message containing 437 # the pgp certificate 438 h = hashlib.new('sha1') 439 h.update('\x99') 440 # we need to has the length of the packet as well 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
449 - def key_id(self) :
450 if self.version == 3 : 451 return pack_long(self.pk_rsa_mod & 0xffffffffffffffffL) 452 elif self.version == 4 : 453 return self.fingerprint()[-8:]
454
455 - def serialize(self) :
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
479 - def deserialize(self, msg, idx, pkt_len) :
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 : # map v2 into v3 for coding simplicity since they're structurally the same 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
507 - def __str__(self) :
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
529 -class user_id(pgp_packet) :
530 - def __init__(self) :
531 pgp_packet.__init__(self) 532 self.id = None
533
534 - def deserialize(self, msg, idx, pkt_len) :
535 self.id = msg[idx:idx + pkt_len]
536
537 - def __str__(self) :
538 return pgp_packet.__str__(self) + "\n" + "id: " + self.id + "\n"
539
540 -class user_attribute(pgp_packet) :
541 - def __init__(self) :
542 pgp_packet.__init__(self) 543 self.sub_type = None 544 self.data = None
545
546 - def deserialize(self, msg, idx, pkt_len) :
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
551 - def __str__(self) :
552 return pgp_packet.__str__(self) + "\n" + "sub_type: " + str(self.sub_type) + "\ndata: " + str_to_hex(self.data)
553
554 -class signature(pgp_packet) :
555 - def __init__(self) :
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
563 - def key_id(self) :
564 if self.version == 3 : 565 return self.key_id_ 566 else : 567 i = self.get_hashed_subpak(SIG_SUB_TYPE_ISSUER_KEY_ID) 568 if i : 569 return i[1] 570 i = self.get_unhashed_subpak(SIG_SUB_TYPE_ISSUER_KEY_ID) 571 if i : 572 return i[1] 573 return None
574
575 - def creation_time(self) :
576 if self.version == 3 : 577 return self.timestamp 578 else : 579 i = self.get_hashed_subpak(SIG_SUB_TYPE_CREATE_TIME) 580 return i[1]
581
582 - def expiration(self) :
583 if self.version != 4 : 584 raise ValueError('v3 signatures don\'t have expirations') 585 i = self.get_hashed_subpak(SIG_SUB_TYPE_KEY_EXPIRE) 586 if i : 587 return i[1] 588 return 0 # if not present then it never expires
589
590 - def get_hashed_subpak(self, typ) :
591 for i in self.hashed_subpaks : 592 if i[0] == typ : 593 return i 594 return None
595
596 - def get_unhashed_subpak(self, typ) :
597 for i in self.unhashed_subpaks : 598 if i[0] == typ : 599 return i 600 return None
601
602 - def deserialize_subpacket(self, msg, idx) :
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 : # key creation 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 : # trust signature 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 : # regular expression 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 : # revocation 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 : # 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 : # notation data 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 : # key server preferences 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 : # preferred key server 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 : # primary user id 657 bool, idx = get_whole_int(msg, idx, 1) 658 return (subtype, bool), idx 659 if subtype == SIG_SUB_TYPE_POLICY_URI : # 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 : # signer's 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 : # reason for revocation 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 : # 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 : # signature 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 : # embedded signature 688 # don't do anything fancy, just the raw bits 689 dat = msg[idx:idx+sublen-1] 690 idx = idx + sublen - 1 691 return (subtype, dat), idx 692 693 # otherwise the subpacket is an unknown type, so we just pack the data in it 694 dat = msg[idx:idx+sublen-1] 695 idx = idx + sublen - 1 696 return (subtype, dat), idx
697
698 - def is_primary_user_id(self) :
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
706 - def subpacket_to_str(self, sp) :
707 if sp[0] == SIG_SUB_TYPE_CREATE_TIME : # signature creation time 708 return 'creation time: ' + time.ctime(sp[1]) 709 if sp[0] == SIG_SUB_TYPE_EXPIRE_TIME : # signature expiration time 710 return 'signature expires: ' + duration_to_str(sp[1]) 711 if sp[0] == SIG_SUB_TYPE_EXPORT_CERT : # exportable certification 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 : # trust signature 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 : # regular expression 725 return 'regexp: ' + sp[1] 726 if sp[0] == SIG_SUB_TYPE_REVOCABLE : # 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 : # key expiration time 732 return 'key expires: ' + duration_to_str(sp[1]) 733 if sp[0] == SIG_SUB_TYPE_PREF_SYMM_ALGO : # preferred symmetric algorithms 734 return 'preferred symmetric algorithms: ' + map_to_str(algo_sk_to_str, sp[1]) 735 if sp[0] == SIG_SUB_TYPE_REVOKE_KEY : # revocation 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 : # issuer key ID 741 return 'issuer key id: ' + str_to_hex(sp[1]) 742 if sp[0] == SIG_SUB_TYPE_NOTATION : # notation data 743 return 'notation: flags(%d, %d, %d, %d) name(%s) value(%s)' % sp[1:] 744 if sp[0] == SIG_SUB_TYPE_PREF_HASH_ALGO : # preferred hash algorithms 745 return 'preferred hash algorithms: ' + map_to_str(algo_hash_to_str, sp[1]) 746 if sp[0] == SIG_SUB_TYPE_PREF_COMP_ALGO : # preferred compression algorithms 747 return 'preferred compression algorithms: ' + map_to_str(algo_comp_to_str, sp[1]) 748 if sp[0] == SIG_SUB_TYPE_KEY_SRV_PREF : # key server preferences 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 : # preferred key server 755 return 'preferred key server: %s' % sp[1] 756 if sp[0] == SIG_SUB_TYPE_PRIM_USER_ID : # primary 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 : # policy URL 762 return 'policy url: %s' % sp[1] 763 if sp[0] == SIG_SUB_TYPE_KEY_FLAGS : # 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 : # signer's user id 782 return 'signer id: ' + sp[1] 783 if sp[0] == SIG_SUB_TYPE_REVOKE_REASON : # reason for revocation 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 : # featues 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 # this means we don't know what the thing is so we just have raw data 801 return 'unknown(%d): %s' % (sp[0], str_to_hex(sp[1]))
802
803 - def deserialize(self, msg, idx, pkt_len) :
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
844 - def __str__(self) :
845 sio = cStringIO.StringIO() 846 sio.write(pgp_packet.__str__(self) + "\n") 847 sio.write("version: " + str(self.version) + "\n") 848 sio.write("type: " + sig_type_to_str[self.sig_type] + "\n") 849 if self.version == 3 : 850 sio.write("timestamp: " + time.ctime(self.timestamp) + "\n") 851 sio.write("key_id: " + str_to_hex(self.key_id_) + "\n") 852 elif self.version == 4 : 853 sio.write("hashed subpackets:\n") 854 for i in self.hashed_subpaks : 855 sio.write(" " + self.subpacket_to_str(i) + "\n") 856 sio.write("unhashed subpackets:\n") 857 for i in self.unhashed_subpaks : 858 sio.write(" " + self.subpacket_to_str(i) + "\n") 859 sio.write("hash_algo: " + algo_hash_to_str[self.hash_algo] + "\n") 860 sio.write("hash_frag: " + hex(self.hash_frag) + "\n") 861 if self.pk_algo == ALGO_PK_RSA_ENC_OR_SIGN or self.pk_algo == ALGO_PK_RSA_SIGN_ONLY : 862 sio.write("pk_algo: RSA\n") 863 sio.write("rsa_sig: " + hex(self.rsa_sig) + "\n") 864 elif self.pk_algo == ALGO_PK_DSA : 865 sio.write("pk_algo: DSA\n") 866 sio.write("dsa_sig_r: " + hex(self.dsa_sig_r) + "\n") 867 sio.write("dsa_sig_s: " + hex(self.dsa_sig_s) + "\n") 868 return sio.getvalue()
869
870 # 871 # This class encapsulates an openpgp public "certificate", which is formed in a message as 872 # a series of PGP packets of certain types in certain orders 873 # 874 875 -class pgp_certificate(object):
876 - def __init__(self) :
877 self.version = None 878 self.public_key = None 879 self.revocations = [] 880 self.user_ids = [] 881 self.primary_user_id = -1 # index of the primary user id
882
883 - def __str__(self) :
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
901 - def get_user_id(self):
902 # take the LAST one in the list, not first 903 # they appear to be ordered FIFO from the key and that means if you 904 # added a key later then it won't show the one you expect 905 return self.user_ids[self.primary_user_id][0].id
906 907 user_id = property(get_user_id) 908
909 - def expiration(self) :
910 if self.version == 3 : 911 if self.public_key.validity == 0 : 912 return 0 913 return self.public_key.timestamp + self.public_key.validity * 24 * 60 * 60 914 else : # self.version == 4 915 # this is a bit more complex, we need to find the signature on the 916 # key and get its expiration 917 u_id = self.user_ids[0] 918 for i in u_id[1:] : 919 if i.sig_type == SIG_TYPE_PK_USER_GEN : 920 exp = i.expiration() 921 if exp == 0 : 922 return 0 923 return self.public_key.timestamp + exp 924 return 0
925
926 - def key_size(self) :
927 return 0
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 # each certificate should begin with a public key packet 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 # all versions have a public key although in a v4 cert the main key is only 939 # used for signing, never encryption 940 self.public_key = pkts[0] 941 942 # ok, then what's the version 943 self.version = self.public_key.version 944 945 # now the behavior splits a little depending on the version 946 if self.version == 3 : 947 pkt_idx = 1 948 949 # zero or more revocations 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 # ok, well at least the type is good, we'll assume the cert is 957 # revoked 958 self.revocations.append(pkts[pkt_idx]) 959 960 # increment the pkt_idx to go to the next one 961 pkt_idx = pkt_idx + 1 962 963 # the following packets are User ID, Signature pairs 964 while pkt_idx < len(pkts) : 965 # this packet is supposed to be a user id 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 # there may be a sequence of signatures following the user id which 978 # bind it to the key 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 # append the user ID and signature(s) onto a list 988 self.user_ids.append(user_id) 989 990 else : # self.version == 4 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 # second packet could be a revocation (or a direct key self signature) 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 # increment the pkt_idx to go to the next one 1011 pkt_idx = pkt_idx + 1 1012 1013 # the following packets are: 1014 # User ID, signature... sets or 1015 # subkey, signature... sets or 1016 # user attribute, signature... sets 1017 prim_user_id_sig_time = 0 1018 1019 while pkt_idx < len(pkts) : 1020 # this packet is supposed to be a user id 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 # there may be a sequence of signatures following the user id which 1029 # bind it to the key 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 # is this the primary user id? 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 # append the user ID and signature(s) onto the list 1046 self.user_ids.append(user_id) 1047 1048 # this packet is supposed to be a user id 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 # there may be a sequence of signatures following the user id which 1056 # bind it to the key 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 # append the user ID and signature(s) onto the list 1064 self.user_attrs.append(user_attr) 1065 1066 elif pkts[pkt_idx].pkt_typ == CTB_PKT_PK_SUB : 1067 # collect this list of subkey + signature [ + revocation ] 1068 subkey = [pkts[pkt_idx]] 1069 pkt_idx = pkt_idx + 1 1070 is_revoked = 0 1071 1072 # there must be one signature following the subkey that binds it to the main key 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 # there may optionally be a revocation 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 # append the user ID and signature(s) onto the list 1088 if is_revoked : 1089 self.rvkd_subkeys.append(subkey) 1090 else : 1091 self.subkeys.append(subkey) 1092 else : 1093 break 1094 1095 # did we get all the things we needed? 1096 #if not self.user_id : 1097 # just take the first valid user id we encountered then 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
1102 1103 -def get_ctb(msg, idx) :
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 # undefined length 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
1139 -def crc24(msg) :
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
1152 -def decode(msg) :
1153 # each message is a sequence of packets so we go through the message 1154 # and generate a list of packets and return that 1155 pkt_list = [] 1156 idx = 0 1157 msg_len = len(msg) 1158 while idx < msg_len : 1159 pkt_typ, pkt_len, idx = get_ctb(msg, idx) 1160 pkt = None 1161 if pkt_typ == CTB_PKT_PK_CERT or pkt_typ == CTB_PKT_PK_SUB : 1162 pkt = public_key() 1163 1164 elif pkt_typ == CTB_PKT_USER_ID : 1165 pkt = user_id() 1166 1167 elif pkt_typ == CTB_PKT_SIG : 1168 pkt = signature() 1169 1170 elif pkt_typ == CTB_PKT_USER_ATTR : 1171 pkt = user_attribute() 1172 1173 if pkt : 1174 pkt.pkt_typ = pkt_typ 1175 pkt.deserialize(msg, idx, pkt_len) 1176 if debug : 1177 debug.write(pkt.__str__() + "\n") 1178 else : 1179 raise ValueError('unexpected pgp packet type %s at %d' % (map_to_str(ctb_pkt_to_str, pkt_typ), idx)) 1180 1181 pkt_list.append(pkt) 1182 1183 idx = idx + pkt_len 1184 return pkt_list
1185
1186 -def decode_msg(msg) :
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 # first we'll break the block up into lines and trim each line of any 1196 # carriage return chars 1197 pgpkey_lines = map(lambda x : x.rstrip(), msg.split('\n')) 1198 1199 # check out block 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 # are we at the actual data yet? 1211 if not in_data : 1212 if len(l) == 0 : 1213 in_data = 1 1214 continue 1215 1216 # are we at the checksum line? 1217 if l[0] == '=' : 1218 # get the checksum number 1219 csum = base64.decodestring(l[1:5]) 1220 i = 0 1221 csum, i = get_whole_number(csum, i, 3) 1222 1223 # convert the base64 cert data to binary data 1224 cert_msg = base64.decodestring(block_buf.getvalue()) 1225 block_buf.close() 1226 1227 # check the checksum 1228 if csum != crc24(cert_msg) : 1229 raise Exception, 'bad checksum on pgp message' 1230 1231 # ok, the sum looks ok so we'll actually decode the thing 1232 pkt_list = decode(cert_msg) 1233 1234 # turn it into a real cert 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 # add the data to our buffer then 1245 block_buf.write(l) 1246 1247 return []
1248
1249 -def decode_multiple_keys(msg):
1250 #ditto of above - but handling multiple certs/keys per file 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