def get_fprint(self): """Get a key's fingerprint as an uppercase hex string. """ if 3 == self.version: import md5 n_bytes = mpi2str([self.mpi[0]], None)[2:] # chop off MPI length bytes e_bytes = mpi2str([self.mpi[1]], None)[2:] return md5.new(''.join([n_bytes, e_bytes])).hexdigest().upper() elif 4 == self.version: import sha f_data = ''.join([ int2str(self.version), #self._val2str('version'), int2str(self.created), #self._val2str('created'), int2str(self.k_asym), #self._val2str('k_asym'), mpi2str(self.mpi, None) ]) #self._val2str('mpi')]) len_f_data = len(f_data) hi = chr((0xffff & len_f_data) >> 8) # high order packet length lo = chr(0xff & len_f_data) # low order packet length f = ['\x99', hi, lo, f_data] return sha.new(''.join(f)).hexdigest().upper()
def create_SymmetricKeyEncryptedSessionKeyBody(*args, **kwords): """Create a SymmetricKeyEncryptedSessionKeyBody instance. :Parameters: - `args`: parameter list - `kwords`: keyword parameter list Keyword options: - `alg`: integer symmetric key algorithm - `s2k`: `OpenPGP.packet.S2K` instance - `version`: optional integer version number (default 4) :Returns: `SymmetricKeyEncryptedSessionKeyBody` instance """ try: kwords = args[0] except IndexError: pass if 'version' in kwords: if 0 < version < 128: version = kwords['version'] else: raise PGPValueError, "Symmetric session key version %s is out of range." % version else: version = 4 d = [] d.append(STN.int2str(version)[0]) d.append(STN.int2str(kwords['alg'])[0]) d.append(kwords['s2k']._d) return SymmetricKeyEncryptedSessionKeyBody(''.join(d))
def mpi2str(l, limit): """Convert a list of MPIs to a byte-string. :Parameters: - `l`: list of MPI integers - `limit`: dummy parameter to follow svt_*() suit :Returns: MPI byte-string :note: This should probably return some sort of an index like `str2mpi()` since a limit < len(l) might be useful. But it's not useful now. """ d = [] for i in l: i_d = int2str(i) i_length = len(i_d) bit_count = sigbits(i_d[0]) + (8 * (i_length - 1)) i_length_str = int2str(bit_count) if 2 < len(i_length_str): raise ValueError, "MPI integer > two octs: %s octets used>" % str( i_length) elif 1 == len(i_length_str): i_length_str = ''.join(['\x00', i_length_str]) d.append(''.join([i_length_str, i_d])) # since limit checks complete mpi if limit == len(d): break return ''.join(d)
def create_MPI(i): """Create an MPI out of an integer. :Parameters: - `i`: integer :Returns: `MPI` instance """ d = [] i_d = STN.int2str(i) i_length = len(i_d) bit_count = STN.sigbits(i_d[0]) + (8 * (i_length - 1)) i_length_str = STN.int2str(bit_count) if 2 < len(i_length_str): raise ValueError, "int is larger than two octets can specify - int occupies %s octets" % str( i_length) elif 1 == len(i_length_str): i_length_str = ''.join(['\x00', i_length_str]) d.append(i_length_str) d.append(i_d) return MPI(''.join(d))
def decrypt_public(algorithm, key_tuple, cipher_tuple): """Decrypt public key encrypted data. :Parameters: - `algorithm`: integer public key algorithm constant - `key_tuple`: tuple containing a public and private integers of the target key, RSA values (n, d) or ElGamal values (p, x) - `cipher_tuple`: tuple containing the integers of the encrypted data, coerced RSA value (c, ) and ElGamal values (a, b) :Returns: string cleartext `decrypt_public()` works with public key encrypted information (information encrypted to public key values and decrypted using the corresponding secret key values). This function works with tuples of public key values and tuples of values that comprise the "ciphertext." **Use this function to decrypt public key encrypted session key packets.** RSA key tuple (n, d): - `n`: integer RSA product of primes p & q - `d`: integer RSA decryption key RSA cipher tuple (c, ): - `c`: integer m**e mod n ElGamal key tuple (p, x): - `p`: integer ElGamal prime - `x`: integer ElGamal private key ElGamal cipher tuple (a, b): - `a`: integer ElGamal value g**k mod p - `b`: integer ElGamal value m * y**k mod p Use this for decrypting public-key encrypted session keys. """ key_tuple = tuple([long(i) for i in key_tuple]) # long(): fastmath dep if algorithm in [ASYM_RSA_EOS, ASYM_RSA_E]: from Crypto.PublicKey import RSA key = RSA.construct((key_tuple[0], 0L, key_tuple[1])) # L for fastmath a = STN.int2str(cipher_tuple[0]) return key.decrypt((a,)) elif algorithm in [ASYM_ELGAMAL_EOS, ASYM_ELGAMAL_E]: from Crypto.PublicKey import ElGamal key = ElGamal.construct((key_tuple[0], 0, 0, key_tuple[1])) a = STN.int2str(cipher_tuple[0]) b = STN.int2str(cipher_tuple[1]) return key.decrypt((a, b)) else: raise NotImplementedError, "Unsupported asymmetric algorithm:%s" % algorithm
def create_S2K(*args, **kwords): """Create an S2K (string-to-key specifier) instance. :Parameters: - `args`: parameter list - `kwords`: keyword parameter dictionary :Keywords: - `alg_hash`: integer hash algorithm constant (default SHA1) - `salt`: string 8 octet salt **Required for types 1 & 3** - `type`: *optional* integer S2K type (default 3) - `count`: *optional* integer count code (< 128) used for type 3 only (default 99) :Returns: `S2K` instance :note: All keyword parameters can be specified in a dictionary, sent as a single parameter (args[0]). """ try: kwords = args[0] except IndexError: pass if 'type' in kwords: s2ktype = kwords['type'] else: s2ktype = 3 if 'alg_hash' in kwords: alg_hash = kwords['alg_hash'] else: alg_hash = HASH_SHA1 d = [] if 0 == s2ktype: d.append('\x00') d.append(STN.int2str(alg_hash)[0]) elif 1 == s2ktype: d.append('\x01') d.append(STN.int2str(alg_hash)[0]) d.append(kwords['salt'][:8]) elif 3 == s2ktype: d.append('\x03') d.append(STN.int2str(alg_hash)[0]) d.append(kwords['salt'][:8]) if 'count' in kwords: if 0 < kwords['count'] < 128: d.append(STN.int2str(kwords['count'])) else: raise PGPValueError, "S2K count value %s out of range." % kwords['count'] else: d.append('\x63') # 99 return S2K(''.join(d))
def create_OnePassSignatureBody(*args, **kwords): """Create a OnePassSignatureBody instance. :Parameters: - `args`: ordered parameters - `kwords`: parameter keywords :Returns: `OnePassSignatureBody` instance Parameter keywords: `sigtype`: integer signature type constant `alg_hash`: integer hash algorithm constant `alg_pubkey`: integer public key algorithm constant `keyid`: string 16-character hex signing key ID `nest`: integer nested status `version`: integer one-pass version """ if 0 < len(args) and isinstance(args[0], dict): kwords = args[0] sigtype = kwords.get('sigtype') alg_hash = kwords.get('alg_hash') alg_pubkey = kwords.get('alg_pubkey') keyid = kwords.get('keyid') nest = kwords.get('nest') version = kwords.setdefault('version', 3) errmsg = None version_d = STN.int2str(version) if 1 < len(version_d): errmsg = "One-pass version length (%s octs) exceeded 1 octet limit." % len(version_d) sigtype_d = STN.int2str(sigtype) if 1 < len(sigtype_d): errmsg = "One-pass sigtype length (%s octs) exceeded 1 octet limit." % len(type_d) alg_hash_d = STN.int2str(alg_hash) if 1 < len(alg_hash_d): errmsg = "One-pass hash algorithm length (%s octs) exceeded 1 octet limit." % len(alg_hash_d) alg_pubkey_d = STN.int2str(alg_pubkey) if 1 < len(alg_pubkey_d): errmsg = "One-pass public key algorithm length (%s octs) exceeded 1 octet limit." % len(alg_pubkey_d) keyid_d = STN.hex2str(keyid) if 8 != len(keyid_d): errmsg = "One-pass key ID should be a 16 octet (not %s octs) hex string." % len(keyid) nest_d = STN.int2str(nest) if 1 < len(nest_d): errmsg = "One-pass nest length (%s octs) exceeded 1 octet limit." % len(nest_d) if errmsg: raise PGPValueError(errmsg) d = ''.join([version_d, sigtype_d, alg_hash_d, alg_pubkey_d, keyid_d, nest_d]) return OnePassSignatureBody(d)
def sign_DSA(msg, key_tuple, k=None): """Create a DSA signature. :Parameters: - `msg`: string of data signature applies to - `key_tuple`: tuple of DSA integers (y, g, p, q, x) (see `DSA tuple`_) - `k`: random integer 2 < k < q (automatically generated by default) :Returns: tuple (integer, integer) DSA signature values (r, s) .. _DSA tuple: DSA tuple: - `y`: integer DSA public key - `g`: integer DSA group - `p`: integer DSA prime - `q`: integer DSA order - `x`: integer DSA secret value """ import Crypto.PublicKey.DSA as DSA if k is None: # generate our own k value (2 < k < q) import Crypto.Util.number as NUM import Crypto.Util.randpool as RND rnd = RND.RandomPool() q = key_tuple[3] while 1: k = NUM.getRandomNumber(8*len(STN.int2str(q)), rnd.get_bytes) if 2 < k < q: break dsa = DSA.construct(key_tuple) # note change in ordering return dsa.sign(msg, k)
def test01_int2str_str2int(self): "strnum: string/integer inversion (int2str/str2int)" body = self.key.body for i in [ body.DSA_p.value, body.DSA_q.value, body.DSA_g.value, body.DSA_y.value ]: self.assertEqual(i, str2int(int2str(i)))
def __cat_subpkt_block(subpkts): subpkt_d = ''.join([x._d for x in subpkts]) subpkt_d_len = STN.int2str(len(subpkt_d)) if 2 >= len(subpkt_d_len): return STN.prepad(2, subpkt_d_len) + subpkt_d else: raise PGPValueError, "Subpacket block length (%s) is unacceptable." % len(subpkt_d_len)
def create_LiteralDataBody(*args, **kwords): """Create a LiteralDataBody instance. :Parameters: - `args`: parameter list, will accept keyword dictionary as args[0] - `kwords`: keyword parameters :Keywords: - `data`: string of literal data - `modified`: integer timestamp of file modification - `format`: character 'b' or 't' indicating binary or text - `filename`: optional filename associated with data :Returns: `LiteralDataBody` instance Use the filename '_CONSOLE' to signal extra-careful handling of output. """ try: if isinstance(args[0], dict): kwords = args[0] except IndexError: pass data = kwords.get('data') if not data: data = '' modified = kwords.setdefault('modified', 0) format = kwords.setdefault('format', 'b') filename = kwords.setdefault('filename', 'outfile') if format not in ['b', 't']: raise PGPValueError, "Literal data format must be 'b' or 't'. Received->(%s)" % str(format) fnlen_d = STN.int2str(len(filename)) if 1 < len(fnlen_d): raise PGPValueError, "Filename length (%s) exceeded 1 octet capacity." % len(fnlen_d) modified_d = STN.prepad(4, STN.int2str(modified)) d = ''.join([format, fnlen_d, filename, modified_d, data]) return LiteralDataBody(d)
def encrypt_public_session(keypkt, key, symalg): """Create a public-key encrypted session key. :Parameters: - `keypkt`: either an `OpenPGP.packet.PublicKey.PublicKey` (or subclass) instance or encryption passphrase string - `key`: string encryptrion algorithm used for `symalg` - `symalg`: integer symmetric encryption algorithm constant :Returns: `OpenPGP.packet.PublicKeyEncryptedSessionKey.PublicKeyEncryptedSessionKey` instance """ from openpgp.sap.pkt.Packet import create_Packet from openpgp.sap.pkt.PublicKeyEncryptedSessionKey import create_PublicKeyEncryptedSessionKeyBody pubalg = keypkt.body.alg rnd_prefix = [] i = 0 # fixing the "intended length" business to 127 while i <= 63 - len(key): # seems proper, but probably inefficient rnd_byte = gen_random(1) if '\x00' != rnd_byte: rnd_prefix.append(rnd_byte) i += 1 chksum = STN.int2str(STN.checksum(key))[:2] if pubalg in [ASYM_RSA_EOS, ASYM_RSA_E]: key_tup = (keypkt.body.RSA_n.value, keypkt.body.RSA_e.value) elif pubalg in [ASYM_ELGAMAL_EOS, ASYM_ELGAMAL_E]: key_tup = (keypkt.body.ELGAMAL_p.value, keypkt.body.ELGAMAL_g.value, keypkt.body.ELGAMAL_y.value) else: raise NotImplementedError("Unsupported public key algorithm->(%s)." % pubalg) padded_key = ''.join(['\x02', ''.join(rnd_prefix), '\x00', STN.int2str(symalg)[0], key, chksum]) cipher_tup = encrypt_public(pubalg, padded_key, key_tup) sesbody = create_PublicKeyEncryptedSessionKeyBody( keyid=keypkt.body.id, alg=pubalg, mpis=cipher_tup) return create_Packet(PKT_PUBKEYSESKEY, sesbody._d)
def __cat_subpkt_block(subpkts): subpkt_d = ''.join([x._d for x in subpkts]) subpkt_d_len = STN.int2str(len(subpkt_d)) if 2 >= len(subpkt_d_len): return STN.prepad(2, subpkt_d_len) + subpkt_d else: raise PGPValueError, "Subpacket block length (%s) is unacceptable." % len( subpkt_d_len)
def _limit_int2str(i, limit): "Ensure that int2str output has enough ('limit') bytes." s = int2str(i)[:limit] len_s = len(s) if len_s < limit: s = ((limit - len_s) * '\x00') + s return s
def create_MPI(i): """Create an MPI out of an integer. :Parameters: - `i`: integer :Returns: `MPI` instance """ d = [] i_d = STN.int2str(i) i_length = len(i_d) bit_count = STN.sigbits(i_d[0]) + (8 * (i_length - 1)) i_length_str = STN.int2str(bit_count) if 2 < len(i_length_str): raise ValueError, "int is larger than two octets can specify - int occupies %s octets" % str(i_length) elif 1 == len(i_length_str): i_length_str = ''.join(['\x00', i_length_str]) d.append(i_length_str) d.append(i_d) return MPI(''.join(d))
def create_Tag(pkttype): """Create a Tag (packet tag) instance :Parameters: - `pkttype`: integer packet type constant (see `OpenPGP.constant.packets`) :Returns: `OpenPGP.packet.Tag` instance :note: Only "new" version tags will be created. """ if 0 <= pkttype <= 63: tag_d = STN.int2str(192 | pkttype) # 192 forces new version return Tag(tag_d) else: raise PGPFormatError("Tag type must be in the range 0<=t<=63. Received->(%s)." % pkttype)
def create_Tag(pkttype): """Create a Tag (packet tag) instance :Parameters: - `pkttype`: integer packet type constant (see `OpenPGP.constant.packets`) :Returns: `OpenPGP.packet.Tag` instance :note: Only "new" version tags will be created. """ if 0 <= pkttype <= 63: tag_d = STN.int2str(192 | pkttype) # 192 forces new version return Tag(tag_d) else: raise PGPFormatError( "Tag type must be in the range 0<=t<=63. Received->(%s)." % pkttype)
def create_PublicKeyEncryptedSessionKeyBody(*args, **kwords): """ """ try: kwords = args[0] except IndexError: pass version = '\x03' keyid = STN.hex2str(kwords['keyid']) algorithm = STN.int2str(kwords['alg'])[0] if kwords['alg'] in [ASYM_RSA_EOS, ASYM_RSA_E, ASYM_RSA_S]: mpi_d = MPI.create_MPI(kwords['mpis'][0])._d elif ASYM_ELGAMAL_E == kwords['alg']: a_d = MPI.create_MPI(kwords['mpis'][0])._d b_d = MPI.create_MPI(kwords['mpis'][1])._d mpi_d = ''.join([a_d, b_d]) else: raise PGPValueError, "Unsupported public key algorithm. Received alg->(%s)" % alg _d = ''.join([version, keyid, algorithm, mpi_d]) return PublicKeyEncryptedSessionKeyBody(_d)
def create_SignatureSubpacket(type, value): """Create a SignatureSubpacket instance. :Parameters: - `type`: integer signature subpacket type constant (see `OpenPGP.constant.signatures`) - `value`: variable value depending on `type` (see `Subpacket types and values`_) :Returns: `SignatureSubpacket` instance .. _Subpacket types and values: Subpacket types and values: - ``SIGSUB_SIGNERID``: hex key ID string (16 octets) - ``SIGSUB_CREATED``: integer timestamp of signature creation(must resolve to 4 or less octets, restricting value to less than 4294967296) - ``SIGSUB_EXPIRES``, ``SIGSUB_KEYEXPIRES``: integer time past creation of signature or key expiration (must resolve to 4 or less octets, restricting value to less than 4294967296) - ``SIGSUB_EXPORTABLE``, `SIGSUB_REVOCABLE``, ``SIGSUB_PRIMARYUID``: integer 1 or 0 for True or False - ``SIGSUB_TRUST``: tuple(integer level, integer amount) - ``SIGSUB_SYMCODE``, ``SIGSUB_HASHALGS``, ``SIGSUB_COMPALGS``, ``SIGSUB_KEYSERVPREFS``, ``SIGSUB_KEYFLAGS``, ``SIGSUB_FEATURES``: list of integers, see rfc2440 for specifics - ``SIGSUB_REVOKER``: tuple(integer class, integer algorithm, string 40 octet caps hex SHA1 fingerprint hash string - ``SIGSUB_NOTATION``: tuple([list of 4 flags], string name, string value) see rfc2440 for specifics, list of flags may be replaced with None for basic text notation [0x80, 0x0, 0x0, 0x0] - ``SIGSUB_KEYSERV``, ``SIGSUB_POLICYURL``, ``SIGSUB_SIGNERUID``, ``SIGSUB_REGEX``: string see rfc2440 for specifics - ``SIGSUB_REVOCREASON``: tuple (integer code, string reason) - ``SIGSUB_SIGTARGET``: Not Implemented """ from Packet import create_NewLength # set value_d if SIGSUB_SIGNERID == type: value_d = STN.hex2str(value) if len(value_d) != 8: raise SignatureSubpacketValueError("Length of subpacket key id (%s) is not 8 octets." % len(value)) elif type in [SIGSUB_CREATED, SIGSUB_EXPIRES, SIGSUB_KEYEXPIRES]: value_d = STN.int2str(value) while len(value_d) < 4: value_d = '\x00' + value_d if len(value_d) > 4: raise SignatureSubpacketValueError("Length of subpacket time (%s) exceeds 4 octet limit." % len(value_d)) elif type in [SIGSUB_EXPORTABLE, SIGSUB_REVOCABLE, SIGSUB_PRIMARYUID]: if value not in [0, 1]: raise SignatureSubpacketValueError("Subpacket (# %s) value must be 0 or 1." % (str(subtype))) else: value_d = STN.int2str(value) elif SIGSUB_TRUST == type: # level, amount value_d = STN.int2str(value[0]) + STN.int2str(value[1]) elif type in [SIGSUB_SYMCODE, SIGSUB_HASHCODE, SIGSUB_COMPCODE, SIGSUB_KEYSERVPREFS, SIGSUB_KEYFLAGS, SIGSUB_FEATURES]: value_d = ''.join([STN.int2str(x) for x in value]) elif SIGSUB_REVOKER == type: # value_d = STN.int2str(value[0]) + STN.int2str(value[1]) + STN.hex2str(value[2]) elif SIGSUB_NOTE == type: # [f1, f2, f3, f4], name, value # TODO need check for oversized flags if value[0]: # allow for basic text notations w/ "not value[0]" flag_d = ''.join([STN.int2str(x) for x in value[0]]) # first four flag octs else: flag_d = '\x80\x00\x00\x00' nam, val = value[1], value[2] namlen, vallen = STN.int2str(len(nam)), STN.int2str(len(val)) if len(namlen) == 1: namlen = '\x00' + namlen elif len(namlen) > 2: raise SignatureSubpacketValueError("Length (%s) of subpacket notation 'name' exceeds 2 octet limit." % len(nam)) if len(vallen) == 1: vallen = '\x00' + vallen elif len(vallen) > 2: raise SignatureSubpacketValueError("Length (%s) of subpacket notation 'value' exceeds 2 octet limit." % len(val)) value_d = flag_d + namlen + vallen + nam + val elif type in [SIGSUB_KEYSERV, SIGSUB_POLICYURL, SIGSUB_SIGNERUID, SIGSUB_REGEX]: value_d = value elif SIGSUB_REVOCREASON == type: # code, reason value_d = STN.int2str(value[0]) + value[1] elif SIGSUB_SIGTARGET == type: raise NotImplementedError, "SIGTARGET not supported" else: # subpacket is an unknown type, so just pack the data in value_d = value # resolve length string len_d = len(value_d) + 1 # + 1 octet type slice = create_NewLength(len_d)._d # set type string (character) type_d = STN.int2str(type)[0] subpkt_d = ''.join([slice, type_d, value_d]) return SignatureSubpacket(subpkt_d)
def create_SignatureSubpacket(type, value): """Create a SignatureSubpacket instance. :Parameters: - `type`: integer signature subpacket type constant (see `OpenPGP.constant.signatures`) - `value`: variable value depending on `type` (see `Subpacket types and values`_) :Returns: `SignatureSubpacket` instance .. _Subpacket types and values: Subpacket types and values: - ``SIGSUB_SIGNERID``: hex key ID string (16 octets) - ``SIGSUB_CREATED``: integer timestamp of signature creation(must resolve to 4 or less octets, restricting value to less than 4294967296) - ``SIGSUB_EXPIRES``, ``SIGSUB_KEYEXPIRES``: integer time past creation of signature or key expiration (must resolve to 4 or less octets, restricting value to less than 4294967296) - ``SIGSUB_EXPORTABLE``, `SIGSUB_REVOCABLE``, ``SIGSUB_PRIMARYUID``: integer 1 or 0 for True or False - ``SIGSUB_TRUST``: tuple(integer level, integer amount) - ``SIGSUB_SYMCODE``, ``SIGSUB_HASHALGS``, ``SIGSUB_COMPALGS``, ``SIGSUB_KEYSERVPREFS``, ``SIGSUB_KEYFLAGS``, ``SIGSUB_FEATURES``: list of integers, see rfc2440 for specifics - ``SIGSUB_REVOKER``: tuple(integer class, integer algorithm, string 40 octet caps hex SHA1 fingerprint hash string - ``SIGSUB_NOTATION``: tuple([list of 4 flags], string name, string value) see rfc2440 for specifics, list of flags may be replaced with None for basic text notation [0x80, 0x0, 0x0, 0x0] - ``SIGSUB_KEYSERV``, ``SIGSUB_POLICYURL``, ``SIGSUB_SIGNERUID``, ``SIGSUB_REGEX``: string see rfc2440 for specifics - ``SIGSUB_REVOCREASON``: tuple (integer code, string reason) - ``SIGSUB_SIGTARGET``: Not Implemented """ from Packet import create_NewLength # set value_d if SIGSUB_SIGNERID == type: value_d = STN.hex2str(value) if len(value_d) != 8: raise SignatureSubpacketValueError( "Length of subpacket key id (%s) is not 8 octets." % len(value)) elif type in [SIGSUB_CREATED, SIGSUB_EXPIRES, SIGSUB_KEYEXPIRES]: value_d = STN.int2str(value) while len(value_d) < 4: value_d = '\x00' + value_d if len(value_d) > 4: raise SignatureSubpacketValueError( "Length of subpacket time (%s) exceeds 4 octet limit." % len(value_d)) elif type in [SIGSUB_EXPORTABLE, SIGSUB_REVOCABLE, SIGSUB_PRIMARYUID]: if value not in [0, 1]: raise SignatureSubpacketValueError( "Subpacket (# %s) value must be 0 or 1." % (str(subtype))) else: value_d = STN.int2str(value) elif SIGSUB_TRUST == type: # level, amount value_d = STN.int2str(value[0]) + STN.int2str(value[1]) elif type in [ SIGSUB_SYMCODE, SIGSUB_HASHCODE, SIGSUB_COMPCODE, SIGSUB_KEYSERVPREFS, SIGSUB_KEYFLAGS, SIGSUB_FEATURES ]: value_d = ''.join([STN.int2str(x) for x in value]) elif SIGSUB_REVOKER == type: # value_d = STN.int2str(value[0]) + STN.int2str(value[1]) + STN.hex2str( value[2]) elif SIGSUB_NOTE == type: # [f1, f2, f3, f4], name, value # TODO need check for oversized flags if value[0]: # allow for basic text notations w/ "not value[0]" flag_d = ''.join([STN.int2str(x) for x in value[0]]) # first four flag octs else: flag_d = '\x80\x00\x00\x00' nam, val = value[1], value[2] namlen, vallen = STN.int2str(len(nam)), STN.int2str(len(val)) if len(namlen) == 1: namlen = '\x00' + namlen elif len(namlen) > 2: raise SignatureSubpacketValueError( "Length (%s) of subpacket notation 'name' exceeds 2 octet limit." % len(nam)) if len(vallen) == 1: vallen = '\x00' + vallen elif len(vallen) > 2: raise SignatureSubpacketValueError( "Length (%s) of subpacket notation 'value' exceeds 2 octet limit." % len(val)) value_d = flag_d + namlen + vallen + nam + val elif type in [ SIGSUB_KEYSERV, SIGSUB_POLICYURL, SIGSUB_SIGNERUID, SIGSUB_REGEX ]: value_d = value elif SIGSUB_REVOCREASON == type: # code, reason value_d = STN.int2str(value[0]) + value[1] elif SIGSUB_SIGTARGET == type: raise NotImplementedError, "SIGTARGET not supported" else: # subpacket is an unknown type, so just pack the data in value_d = value # resolve length string len_d = len(value_d) + 1 # + 1 octet type slice = create_NewLength(len_d)._d # set type string (character) type_d = STN.int2str(type)[0] subpkt_d = ''.join([slice, type_d, value_d]) return SignatureSubpacket(subpkt_d)
def create_SignatureBody(*args, **kwords): """Assemble signature information into a SignatureBody instance. :Parameters: - `args`: parameter list - `kwords`: keyword parameter dictionary :Keywords: - `sigtype`: integer signature type constant (see `OpenPGP.constant.signatures`) - `alg_pubkey`: integer signature algorithm (see `OpenPGP.constant.algorithms`) - `alg_hash`: integer signature algorithm (see `OpenPGP.constant.algorithms`) - `signature`: algorithm-dependent signature MPIs - DSA MPI tuple (``DSA_r``, ``DSA_s``), single RSA MPI ``RSA``, or ElGamal MPI tuple (``ELGAMAL_a``, ``ELGAMAL_b``) (see `DSA signature tuple`_, `RSA signature value`_, and `ElGamal signature tuple`_) - `hash_frag`: 2 octet string of signed hash fragment - `hashed_subpkts`: list of `SignatureSubpacket` instances included in the hashed (protected) portion of the signature - `unhashed_subpkts`: list of `SignatureSubpacket` instances included in the unhashed (unprotected) portion of the signature - `created`: integer timestamp of signature creation, **v3 only** - `keyid`: string ID of signing key **v3 only** :Returns: `SignatureBody` instance .. _DSA signature tuple: DSA signature tuple (``DSA_r``, ``DSA_s``): - `DSA_r`: `OpenPGP.packet.MPI` instance - `DSA_s`: `OpenPGP.packet.MPI` instance .. _RSA signature value: RSA signature value ``RSA``: - ``RSA``: `OpenPGP.packet.MPI` instance .. _ElGamal signature tuple: ElGamal signature tuple (``ELGAMAL_a``, ``ELGAMAL_b``): - ``ELGAMAL_a``: `OpenPGP.packet.MPI` instance - ``ELGAMAL_b``: `OpenPGP.packet.MPI` instance :note: This function only assembles the provided information into a signature packet, it does not reconcile anything - namely, the values of the MPIs with the signed data and hash fragment. :note: All keyword parameters can be specified in a dictionary, sent as a single parameter (args[0]). """ try: kwords = args[0] except IndexError: pass version = kwords['version'] sigtype = kwords['sigtype'] alg_pubkey = kwords['alg_pubkey'] alg_hash = kwords['alg_hash'] signature = kwords['signature'] hash_frag = kwords['hash_frag'] hashed_subpkts = kwords.get('hashed_subpkts') # optional unhashed_subpkts = kwords.get('unhashed_subpkts') # optional _d = [] _d.append(STN.int2str(version)[0]) ################# version if 3 == version: ################# 3 _d.append('\x05') # hash length (required) _d.append(STN.int2str(sigtype)[0]) # signature type _d.append(STN.int2str(kwords['created'])[:4]) # creation timestamp _d.append(STN.hex2str(kwords['keyid'])[:8]) # signing key ID _d.append(STN.int2str(alg_pubkey)[0]) # public key algorithm _d.append(STN.int2str(alg_hash)[0]) # hash algorithm elif 4 == version: ################# 4 _d.append(STN.int2str(sigtype)[0]) # signature type _d.append(STN.int2str(alg_pubkey)[0]) # public key algorithm _d.append(STN.int2str(alg_hash)[0]) # hash algorithm if hashed_subpkts: # hashed subpackets _d.append(__cat_subpkt_block(hashed_subpkts)) else: _d.append('\x00\x00') if unhashed_subpkts: # unhashed subpackets _d.append(__cat_subpkt_block(unhashed_subpkts)) else: _d.append('\x00\x00') _d.append(hash_frag[:2]) # hash fragment if alg_pubkey in [ASYM_RSA_S, ASYM_RSA_EOS]: # RSA MPI _d.append(''.join([x._d for x in signature])) elif alg_pubkey in [ASYM_DSA, ASYM_ELGAMAL_EOS]: # DSA MPIs _d.append(''.join([x._d for x in signature])) else: raise PGPValueError, "Unsupported signature algorithm %s." % algorithm return SignatureBody(''.join(_d))
def test01_int2str_str2int(self): "strnum: string/integer inversion (int2str/str2int)" body = self.key.body for i in [body.DSA_p.value, body.DSA_q.value, body.DSA_g.value, body.DSA_y.value]: self.assertEqual(i, str2int(int2str(i)))
def sign_ElGamal(msg, key_tuple, k=None): """Create an ElGamal signature. :Parameters: - `msg`: string of data signature applies to - `key_tuple`: tuple ElGamal key integers (p, g, x) (see `ElGamal key tuple`_) - `k`: integer (must be relatively prime to p-1) :Returns: tuple (integer, integer) ElGamal signature values (a, b) .. _ElGamal key tuple: ElGamal key tuple: - `p`: integer ElGamal prime - `g`: integer ElGamal random "g" value - `x`: integer ElGamal private key """ import Crypto.PublicKey.ElGamal as ELG if k is None: # generate our own prime k value (k relatively prime to p-1) import Crypto.Util.number as NUM import Crypto.Util.randpool as RND rnd = RND.RandomPool() q = key_tuple[0] # no restrictions on bit length for k, good enough? k = NUM.getPrime(8*len(STN.int2str(q)), rnd.get_bytes) elg = ELG.construct((key_tuple[0], key_tuple[1], 0, key_tuple[2])) return elg.sign(msg, k)