def testF02EncryptPublicArmor(self): "encrypt_str()/decrypt_str() ElGamal via user ID (armored)" pubkey_d = read_test_file(['pgpfiles','key','DSAELG1.pub.asc']) seckey_d = read_test_file(['pgpfiles','key','DSAELG1.sec.asc']) cphtxt = encrypt_str(self.lit_data,keys=pubkey_d, use_userid=[(None,"Tester")], armor=True) self.assertEqual(True, looks_armored(cphtxt))
def testC03DecryptDSA_armor(self): "decrypt_str() DSA AES256 w/integrity (armored, decompressed)" enc_d = read_test_file(['pgpfiles','enc','pub.elg.aes256.clrtxt.gpg']) key_d = read_test_file(['pgpfiles','key','DSAELG1.sec.asc']) clrtxt = decrypt_str(enc_d, passphrase='test', keys=key_d, decompress=True, armor=True) self.assertEqual(True, looks_armored(clrtxt)) # check armoring litmsg = list_as_signed(clrtxt)[0] self.assertEqual(self.lit_data, litmsg.literals[0].body.data)
def testF02EncryptPublicArmor(self): "encrypt_str()/decrypt_str() ElGamal via user ID (armored)" pubkey_d = read_test_file(['pgpfiles', 'key', 'DSAELG1.pub.asc']) seckey_d = read_test_file(['pgpfiles', 'key', 'DSAELG1.sec.asc']) cphtxt = encrypt_str(self.lit_data, keys=pubkey_d, use_userid=[(None, "Tester")], armor=True) self.assertEqual(True, looks_armored(cphtxt))
def testC03DecryptDSA_armor(self): "decrypt_str() DSA AES256 w/integrity (armored, decompressed)" enc_d = read_test_file( ['pgpfiles', 'enc', 'pub.elg.aes256.clrtxt.gpg']) key_d = read_test_file(['pgpfiles', 'key', 'DSAELG1.sec.asc']) clrtxt = decrypt_str(enc_d, passphrase='test', keys=key_d, decompress=True, armor=True) self.assertEqual(True, looks_armored(clrtxt)) # check armoring litmsg = list_as_signed(clrtxt)[0] self.assertEqual(self.lit_data, litmsg.literals[0].body.data)
def slice_pkt_str(msg_d, slice_d): """Return a packet slice from a string of OpenPGP data. :Parameters: - `msg_d`: string OpenPGP data - `slice_d`: string slice notation (see `Slice syntax`_) :Returns: list of sliced items (message or packet instances) .. _Slice syntax: Slice syntax The slice string looks like ``M[x:y:z]`` where ``M`` is either an integer message index or the (capital) letter "L" and ``[x:y:z]`` is a normal Python slice. An integer value ``M`` specifies the index of a message in the list of messages to slice packets from. The value "L" is used to take a slice from the list of leftover packets. :note: This uses `eval()`, so tread lightly. :note: If you shake it, it will break. Play nice. :todo: Add deep msg slicing (ex.signed contains literal - 0[0][1:3]). """ if looks_armored(msg_d): msg_d = ''.join([a.data for a in list_armored(msg_d)]) l = [] players = list_msgs(list_pkts(msg_d), leftover=l) if 'L' == slice_d[0]: items = l saplog.info("Slicing leftover packets (%s total)." % len(items)) else: msg_idx = int(slice_d[0]) items = players[msg_idx].seq() saplog.info("Slicing message %s of %s." % (msg_idx, len(players)-1)) pkt_slice = eval("items%s" % slice_d[1:]) if isinstance(pkt_slice, list): return pkt_slice else: return [pkt_slice]
def show_pkts(d): """Show OpenPGP packet information. :Parameters: - `d`: variable OpenPGP data - may be a string containing OpenPGP packets (native or armored), a list of OpenPGP packets, or a single OpenPGP packet instance :Returns: string of packet information found in `d` :note: Data `d` can either be an unbroken string of native OpenPGP data or normal text with one or more ASCII-armored blocks (but not both). """ if isinstance(d, str): if looks_armored(d): armored = list_armored(d) d = ''.join([a.data for a in armored]) # we may have many armored instances pkts = list_pkts(d) elif isinstance(d, list): pkts = d elif isinstance(d, PKT.Packet): pkts = [d] r = [] for p in pkts: r.append(TXT.pkt_msg(p.tag.type)) l = p.length.size if l == 'UNDEFINED': l = "Undefined (found %s octets)" % len(p.body.rawstr()) r.append(" version: %s type: %s bodylength: %s" % (p.tag.version, p.tag.type, l)) r.append(report_body(p)) ### packet byte string #import OpenPGP.util.strnum as STN #r.append("Packet body byte string:") #r.append(STN.str2pyhex(p.rawstr())) ### ### packet body byte string #r.append("Packet body byte string:") #r.append(STN.str2pyhex(p.body._d)) ### r.append("Pau.") return linesep.join(r)
def list_pkts(s, **kw): """Create a list of OpenPGP packet instances given a string of data. :Parameters: - `s`: string of native OpenPGP data (native or ASCII-armored) :Keywords: - `code`: optional packet type code to match listed packets :Returns: list of packet instances `list_pkts()` will return all OpenPGP packets found as various packet type instances. :TODO: It looks like an incomplete packet may be returned at the end of the list. Check this. """ code = kw.get('code', None) if looks_armored(s): arm_d = [] # native data for arm in list_armored(s): # all armored data is caught, including clearsigned sigs # clearsigned text is ignored arm_d.append(arm.data) s = ''.join(arm_d) pkts = [] idx = 0 len_d = len(s) while idx < len_d: tag = Tag(s[idx]) # assume first octet starts packet tag (header) packet = pktclass(tag.type)(s[idx:]) idx = idx + packet.size pkts.append(packet) if code: pkts = filter(lambda p: p.tag.type == code, pkts) return pkts
def cat_pkt_str(pkt_str_list, armor=False): """Concatenate all the packets found in a list of strings. :Parameters: - `pkt_str_list`: list of OpenPGP strings (native or ASCII-armored) :Keywords: - `armor`: set to True to return an armored string :Returns: string of packets :note: ASCII-armored blocks are OK. Native OpenPGP packets are OK. Just make sure that both aren't present in a single element in `pkt_str_list`. """ from openpgp.sap.armory import looks_armored, list_armored, apply_armor saplog.info("Concatenating packets from %s chunks." % len(pkt_str_list)) pkts = [] for pkt_d in pkt_str_list: if looks_armored(pkt_d): pgp_d = ''.join([a.data for a in list_armored(pkt_d)]) else: pgp_d = pkt_d pkts.extend(list_pkts(pgp_d)) if armor: s = apply_armor(pkts) else: s = ''.join([p.rawstr() for p in pkts]) return s
def testB02VerifyDSANativeOnePass(self): "verify_str() DSA one-pass sig (armored output)" sig_d = read_test_file(['pgpfiles','sig','sig.DSAELG1.onepass.gpg']) key_d = read_test_file(['pgpfiles','key','DSAELG1.pub.asc']) armored = verify_str(sig_d, key_d, armor=True) self.assertEqual(True, looks_armored(armored))
def show_msgs(d, m_idx=0): """Show a synopsis of the packets in a message. :Parameters: - `d`: variable OpenPGP data - may be a string contaning OpenPGP packets, a list of OpenPGP messages, or a single OpenPGP message instance - `m_idx`: counter used to keep track of indentation - should just leave it alone :Returns: string of message information found in `d` Data `d` can either be an unbroken string of native OpenPGP data or normal text with one or more ASCII-armored blocks (but not both). """ from openpgp.sap.msg.Msg import Msg l = [] if isinstance(d, str): if looks_armored(d): armored = list_armored(d) d = ''.join([a.data for a in armored ]) # we may have many armored instances msgs = list_msgs(list_pkts(d), leftover=l) elif isinstance(d, list): # assume a list of messages msgs = d l = None elif isinstance(d, MSG.Msg.Msg): msgs = [d] l = None r = [] # report, lines of msg/pkt info tab = ' ' if 0 == m_idx: m_tab = '' else: m_tab = tab for m in msgs: r.append("%s%s %s" % (m_tab, m_idx, TXT.msg_msg(m.type))) i_idx = 0 i_list = [] for i in m.seq(): if isinstance(i, Msg): r.append(show_msgs(i.rawstr(), i_idx)) else: # assume packet r.append("%s%s %s" % (m_tab + tab, i_idx, show_simple_packet(i))) i_idx += 1 m_idx += 1 if l: r.append("Leftover (non-message) packets") for p in l: r.append(" %s" % show_simple_packet(p)) return linesep.join(r)
def testB02VerifyDSANativeOnePass(self): "verify_str() DSA one-pass sig (armored output)" sig_d = read_test_file(['pgpfiles', 'sig', 'sig.DSAELG1.onepass.gpg']) key_d = read_test_file(['pgpfiles', 'key', 'DSAELG1.pub.asc']) armored = verify_str(sig_d, key_d, armor=True) self.assertEqual(True, looks_armored(armored))
def show_msgs(d, m_idx=0): """Show a synopsis of the packets in a message. :Parameters: - `d`: variable OpenPGP data - may be a string contaning OpenPGP packets, a list of OpenPGP messages, or a single OpenPGP message instance - `m_idx`: counter used to keep track of indentation - should just leave it alone :Returns: string of message information found in `d` Data `d` can either be an unbroken string of native OpenPGP data or normal text with one or more ASCII-armored blocks (but not both). """ from openpgp.sap.msg.Msg import Msg l = [] if isinstance(d, str): if looks_armored(d): armored = list_armored(d) d = ''.join([a.data for a in armored]) # we may have many armored instances msgs = list_msgs(list_pkts(d), leftover=l) elif isinstance(d, list): # assume a list of messages msgs = d l = None elif isinstance(d, MSG.Msg.Msg): msgs = [d] l = None r = [] # report, lines of msg/pkt info tab = ' ' if 0 == m_idx: m_tab = '' else: m_tab = tab for m in msgs: r.append("%s%s %s" % (m_tab, m_idx, TXT.msg_msg(m.type))) i_idx = 0 i_list = [] for i in m.seq(): if isinstance(i, Msg): r.append(show_msgs(i.rawstr(), i_idx)) else: # assume packet r.append("%s%s %s" % (m_tab+tab, i_idx, show_simple_packet(i))) i_idx += 1 m_idx += 1 if l: r.append("Leftover (non-message) packets") for p in l: r.append(" %s" % show_simple_packet(p)) return linesep.join(r)
def list_as_signed(pgp_d, **kw): """With respect to the API, list useful OpenPGP items. :Parameters: - `pgp_d`: str either ASCII-armored text or native OpenPGP data - armored text may contain multiple armored blocks, while native OpenPGP data should exist as a single unbroken string :Keywords: - `leftover`: *optional* list to which leftover (non-message) packets will be appended - this will include repeats of detached or standalone signature packets - `decompress`: set to True to auto-decompress compressed messages - `detached`: detached string, signed by signatures in `pgp_d` :Returns: list of message instances and special signature tuples This function takes OpenPGP data and returns a list of items that are "useful" for the API. In particular, this includes detached, standalone, and key signature tuples (hence, "as_signed") which are returned as:: ([det_sig1, det_sig2, ..], string) # detached signatures ([key_sig1, key_sig2, ..], key) # key_sigs within a key message ([standalone_sig], None) # standalone signature :note: This is basically a `list_msgs()` which works with armored strings. One important difference is that the focus is turned away from packets (leftover) toward 'usable things' with respect to the API. :note: I'm debating whether or not to scrap the whole ([sigs], target) tuple deal and automagically convert detached sigs and clearsigned sigs to signed messages, using filename='' and modified='' for both, format='b' for detached and format='t' for clearsigned. Higher up, this will have the consequence of spitting out the signed stuff (could be a lot) with some literal packet header junk prepended. Right now, only detached and clearsigned stuff are sent out as tuples. It's just ugly worrying about whether we're working with instances or tuples all over the place (or ignoring the possibilities and just letting an exception be raised). """ saplog = logging.getLogger("saplog") det_d = kw.get('detached', None) # stringify pgp_d and det_d if not pgp_d: return [] # det_d requires detached signatures, so it's safe to return #elif hasattr(pgp_d, 'read'): #elif isinstance(pgp_d, (StringIO, file)): # pgp_d = pgp_d.read() elif isinstance(pgp_d, str): pass else: raise TypeError("Invalid OpenPGP type. Please send string, file, or StringIO.") if det_d: if hasattr(det_d, 'read'): det_d = det_d.read() elif isinstance(det_d, str): pass else: raise Hell players = [] # bona fide messages and detached signature tuples # similar "looks_armored" deal in list_pkts(), perhaps can consolidate if looks_armored(pgp_d): arm_d = [] # native data for arm in list_armored(pgp_d): if 'signed' in arm.__dict__: # distinguished a clearsigned message # this is where we can just create a signed message, # literalname = '', modified = 0, format = 't' sigs = [p for p in list_pkts(arm.data) if PKT_SIGNATURE == p.tag.type] players.append((sigs, arm.signed)) else: arm_d.append(arm.data) pgp_d = ''.join(arm_d) leftover = [] msgs = list_msgs(list_pkts(pgp_d), leftover=leftover) detached_sigs = [] detached_types = [SIG_BINARY, SIG_TEXT] standalone_types = [SIG_STANDALONE, SIG_TIMESTAMP, SIG_THIRDPARTY] for pkt in leftover: # distinguish leftover.. if PKT_SIGNATURE == pkt.tag.type: if pkt.body.type in detached_types: # ..detached sigs from.. detached_sigs.append(pkt) elif pkt.body.type in standalone_types: #..standalone sigs standalone_sigs.append(pkt) players.append(([pkt],None)) if kw.get('decompress'): for i in range(len(msgs)): if MSG_COMPRESSED == msgs[i].type: saplog.info("Decompressing compressed message (%r)." % TXT.alg_comp_msg(msgs[i].compressed.body.alg)) compmsg = msgs.pop(i) compplayers = list_as_signed(compmsg.compressed.body.data) for j in range(len(compplayers)): # to preserve the order in.. msgs.insert(i+j, compplayers[j]) # ..which they appeared if detached_sigs: players.append((detached_sigs, det_d)) players += msgs if isinstance(kw.get('leftover'), list): kw['leftover'].extend(leftover) return players