class reqPGP(object): def __init__(self, path=None): self.gpg = GPG(gpgbinary=('../gpg.exe' if osName == 'nt' else 'gpg')) if not path: try: self.path = environ["HOME"] + '/' except KeyError: self.path = environ["HOMEPATH"] + '\\' else: if path[-1] != '\\' and osName == 'nt': path += '\\' elif path[-1] != '/' and osName == 'posix': path += '/' self.path = path def genKey(self, account, passphrase): input_data = self.gpg.gen_key_input(name_email=account, passphrase=passphrase) self.gpg.gen_key(input_data) def encryptFile(self, account, data): encryptedData = str(self.gpg.encrypt(data, account)) with open(self.path + '.' + account + '.req', 'w') as f: f.write(encryptedData) def decryptFile(self, account, passphrase): with open(self.path + '.' + account + '.req', 'rb') as f: decryptedData = str(self.gpg.decrypt_file(f, passphrase=passphrase)) return decryptedData def deleteKey(self, keyId): self.gpg.delete_keys(keyId, True) self.gpg.delete_keys(keyId)
class reqPGP(object): def __init__(self, path=None): self.gpg = GPG(gpgbinary=('../gpg.exe' if osName == 'nt' else 'gpg')) if not path: try: self.path = environ["HOME"] + '/' except KeyError: self.path = environ["HOMEPATH"] + '\\' else: if path[-1] != '\\' and osName == 'nt': path += '\\' elif path[-1] != '/' and osName == 'posix': path += '/' self.path = path def genKey(self, account, passphrase): input_data = self.gpg.gen_key_input( name_email=account, passphrase=passphrase) self.gpg.gen_key(input_data) def encryptFile(self, account, data): encryptedData = str(self.gpg.encrypt(data, account)) with open(self.path + '.' + account + '.req', 'w') as f: f.write(encryptedData) def decryptFile(self, account, passphrase): with open(self.path + '.' + account + '.req', 'rb') as f: decryptedData = str(self.gpg.decrypt_file(f, passphrase=passphrase)) return decryptedData def deleteKey(self, keyId): self.gpg.delete_keys(keyId, True) self.gpg.delete_keys(keyId)
def delete(self): """ Remove any keys for this address. """ gpg = GPG(gnupghome=GNUPG_HOME) for key in gpg.list_keys(): if self.address in addresses_for_key(gpg, key): gpg.delete_keys(key["fingerprint"], True) gpg.delete_keys(key["fingerprint"]) super(Address, self).delete()
def delete(self): """ remove any keys for this address """ from email_extras.utils import addresses_for_key gpg = GPG(gnupghome=GNUPG_HOME) for key in gpg.list_keys(): if self.address in addresses_for_key(gpg, key): gpg.delete_keys(key["fingerprint"], True) gpg.delete_keys(key["fingerprint"]) super(Address, self).delete()
def key_edit(key, password, path_to_user_folder): try: gpg = GPG() gpg.delete_keys(key, True, passphrase=password) gpg.delete_keys(key) key_data = open(path_to_user_folder+'/main/gpg_private_key.asc').read() new_key = gpg.import_keys(key_data) new_key = new_key.results[0]['fingerprint'] gpg.trust_keys(new_key, "TRUST_ULTIMATE") system('echo RELOADAGENT | gpg-connect-agent') copy2(path_to_user_folder+'/main/gpg_private_key.asc', path_to_user_folder+'/user_data/gpg_private_key.asc') return new_key except: msg = bot.send_message(message.chat.id, 'Произошла ошибка.', reply_markup = types.ReplyKeyboardRemove(selective=False)) del_mess(msg, bot, 6) return key
class TempGPGWrapper(object): """ A context manager that wraps a temporary GPG keyring which only contains the keys given at object creation. """ def __init__(self, keys=None, gpgbinary=None): """ Create an empty temporary keyring and import any given C{keys} into it. :param keys: OpenPGP key, or list of. :type keys: OpenPGPKey or list of OpenPGPKeys :param gpgbinary: Name for GnuPG binary executable. :type gpgbinary: C{str} """ self._gpg = None self._gpgbinary = gpgbinary if not keys: keys = list() if not isinstance(keys, list): keys = [keys] self._keys = keys for key in keys: leap_assert_type(key, OpenPGPKey) def __enter__(self): """ Build and return a GPG keyring containing the keys given on object creation. :return: A GPG instance containing the keys given on object creation. :rtype: gnupg.GPG """ self._build_keyring() return self._gpg def __exit__(self, exc_type, exc_value, traceback): """ Ensure the gpg is properly destroyed. """ # TODO handle exceptions and log here self._destroy_keyring() def _build_keyring(self): """ Create a GPG keyring containing the keys given on object creation. :return: A GPG instance containing the keys given on object creation. :rtype: gnupg.GPG """ privkeys = [key for key in self._keys if key and key.private is True] publkeys = [key for key in self._keys if key and key.private is False] # here we filter out public keys that have a correspondent # private key in the list because the private key_data by # itself is enough to also have the public key in the keyring, # and we want to count the keys afterwards. privfps = map(lambda privkey: privkey.fingerprint, privkeys) publkeys = filter( lambda pubkey: pubkey.fingerprint not in privfps, publkeys) listkeys = lambda: self._gpg.list_keys() listsecretkeys = lambda: self._gpg.list_keys(secret=True) self._gpg = GPG(binary=self._gpgbinary, homedir=tempfile.mkdtemp()) leap_assert(len(listkeys()) is 0, 'Keyring not empty.') # import keys into the keyring: # concatenating ascii-armored keys, which is correctly # understood by GPG. self._gpg.import_keys("".join( [x.key_data for x in publkeys + privkeys])) # assert the number of keys in the keyring leap_assert( len(listkeys()) == len(publkeys) + len(privkeys), 'Wrong number of public keys in keyring: %d, should be %d)' % (len(listkeys()), len(publkeys) + len(privkeys))) leap_assert( len(listsecretkeys()) == len(privkeys), 'Wrong number of private keys in keyring: %d, should be %d)' % (len(listsecretkeys()), len(privkeys))) def _destroy_keyring(self): """ Securely erase the keyring. """ # TODO: implement some kind of wiping of data or a more # secure way that # does not write to disk. try: for secret in [True, False]: for key in self._gpg.list_keys(secret=secret): self._gpg.delete_keys( key['fingerprint'], secret=secret) leap_assert(len(self._gpg.list_keys()) is 0, 'Keyring not empty!') except: raise finally: leap_assert(self._gpg.homedir != os.path.expanduser('~/.gnupg'), "watch out! Tried to remove default gnupg home!") shutil.rmtree(self._gpg.homedir)
class Test(unittest.TestCase): '''Unit test cases for signing envelopes from the Learning Registry''' def __init__(self, methodName="runTest"): self.gpgbin="/usr/local/bin/gpg" self.gnupgHome = os.path.expanduser(os.path.join("~", ".gnupg")) self.gpg = GPG(gpgbinary=self.gpgbin, gnupghome=self.gnupgHome) unittest.TestCase.__init__(self, methodName) self.testDataDir = None configFile = os.path.join("config.cfg") if os.path.exists(configFile): config = json.load(file(configFile)) if config.has_key("global"): if config["global"].has_key("testdata") and os.path.exists(config["global"]["testdata"]): self.testDataDir = config["global"]["testdata"] def setUp(self): now = time.localtime() now = calendar.timegm(now) self.goodEmail = "signTest-{0}@learningregistry.org".format(now) self.goodRealName = "Autogenerated Sign Test" self.goodpassphrase = "supersecret" input = self.gpg.gen_key_input(name_real=self.goodRealName, name_email=self.goodEmail, passphrase=self.goodpassphrase) self.goodPrivateKey = self.gpg.gen_key(input) privateKeyAvailable = False privateKeys = self.gpg.list_keys(secret=True) for skey in privateKeys: if skey["keyid"] == self.goodPrivateKey.fingerprint: privateKeyAvailable = True self.privateKeyInfo = skey break if skey["fingerprint"] == self.goodPrivateKey.fingerprint: privateKeyAvailable = True self.privateKeyInfo = skey break assert privateKeyAvailable == True, "Could not locate generated Private Key" self.goodkeyid = self.privateKeyInfo["keyid"] self.goodowner = self.privateKeyInfo["uids"][0] self.badkeyid = "XXXXXXXXXXXXXXXX" self.badpassphrase = "bad passphrase" self.sampleJSON = ''' { "_id":"00e3f67232e743b6bc2a079bd98ff55a", "_rev":"1-8163d32f6cc9996f2b7228d8b5db7962", "doc_type":"resource_data", "update_timestamp":"2011-03-14 13:36:04.617999", "resource_data":"<oai_dc:dc xmlns:oai_dc=\\"http://www.openarchives.org/OAI/2.0/oai_dc/\\" xmlns:dc=\\"http://purl.org/dc/elements/1.1/\\" xmlns:xsi=\\"http://www.w3.org/2001/XMLSchema-instance\\" xmlns=\\"http://www.openarchives.org/OAI/2.0/\\" xsi:schemaLocation=\\"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd\\">\\n<dc:title>A chat about America. October and November, 1884.</dc:title>\\n<dc:creator>J. P.</dc:creator>\\n<dc:subject>United States--Description and travel.</dc:subject>\\n<dc:description>\\"Printed for private circulation only.\\"</dc:description>\\n<dc:description>Electronic reproduction. Washington, D.C. : Library of Congress, [2002-2003]</dc:description>\\n<dc:publisher>Manchester, Palmer & Howe</dc:publisher>\\n<dc:date>1885</dc:date>\\n<dc:type>text</dc:type>\\n<dc:identifier>http://hdl.loc.gov/loc.gdc/lhbtn.12281</dc:identifier>\\n<dc:language>eng</dc:language>\\n<dc:coverage>United States</dc:coverage>\\n</oai_dc:dc>\\n ", "keys":["United States--Description and travel.","eng"], "submitter_type":"agent", "resource_data_type":"metadata", "payload_schema_locator":"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_placement":"inline", "submitter":"NSDL 2 LR Data Pump", "payload_schema":["oai_dc"], "node_timestamp":"2011-03-14 13:36:04.617999", "doc_version":"0.10.0", "create_timestamp":"2011-03-14 13:36:04.617999", "active":true, "publishing_node":"string", "resource_locator":"http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_ID":"00e3f67232e743b6bc2a079bd98ff55a", "TOS": { "submission_TOS": "http://example.com/tos/unknown", "submission_attribution": "unidentified" } } ''' self.sampleJSON_strip = '''{"keys": ["United States--Description and travel.", "eng"], "TOS": {"submission_attribution": "unidentified", "submission_TOS": "http://example.com/tos/unknown"}, "payload_placement": "inline", "active": true, "resource_locator": "http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_type": "resource_data", "resource_data": "<oai_dc:dc xmlns:oai_dc=\\"http://www.openarchives.org/OAI/2.0/oai_dc/\\" xmlns:dc=\\"http://purl.org/dc/elements/1.1/\\" xmlns:xsi=\\"http://www.w3.org/2001/XMLSchema-instance\\" xmlns=\\"http://www.openarchives.org/OAI/2.0/\\" xsi:schemaLocation=\\"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd\\">\\n<dc:title>A chat about America. October and November, 1884.</dc:title>\\n<dc:creator>J. P.</dc:creator>\\n<dc:subject>United States--Description and travel.</dc:subject>\\n<dc:description>\\"Printed for private circulation only.\\"</dc:description>\\n<dc:description>Electronic reproduction. Washington, D.C. : Library of Congress, [2002-2003]</dc:description>\\n<dc:publisher>Manchester, Palmer & Howe</dc:publisher>\\n<dc:date>1885</dc:date>\\n<dc:type>text</dc:type>\\n<dc:identifier>http://hdl.loc.gov/loc.gdc/lhbtn.12281</dc:identifier>\\n<dc:language>eng</dc:language>\\n<dc:coverage>United States</dc:coverage>\\n</oai_dc:dc>\\n ", "submitter_type": "agent", "resource_data_type": "metadata", "payload_schema_locator": "http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_schema": ["oai_dc"], "doc_version": "0.10.0", "submitter": "NSDL 2 LR Data Pump"}''' self.sampleJSON_strip_normal = '''{"keys": ["United States--Description and travel.", "eng"], "TOS": {"submission_attribution": "unidentified", "submission_TOS": "http://example.com/tos/unknown"}, "payload_placement": "inline", "active": "true", "resource_locator": "http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_type": "resource_data", "resource_data": "<oai_dc:dc xmlns:oai_dc=\\"http://www.openarchives.org/OAI/2.0/oai_dc/\\" xmlns:dc=\\"http://purl.org/dc/elements/1.1/\\" xmlns:xsi=\\"http://www.w3.org/2001/XMLSchema-instance\\" xmlns=\\"http://www.openarchives.org/OAI/2.0/\\" xsi:schemaLocation=\\"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd\\">\\n<dc:title>A chat about America. October and November, 1884.</dc:title>\\n<dc:creator>J. P.</dc:creator>\\n<dc:subject>United States--Description and travel.</dc:subject>\\n<dc:description>\\"Printed for private circulation only.\\"</dc:description>\\n<dc:description>Electronic reproduction. Washington, D.C. : Library of Congress, [2002-2003]</dc:description>\\n<dc:publisher>Manchester, Palmer & Howe</dc:publisher>\\n<dc:date>1885</dc:date>\\n<dc:type>text</dc:type>\\n<dc:identifier>http://hdl.loc.gov/loc.gdc/lhbtn.12281</dc:identifier>\\n<dc:language>eng</dc:language>\\n<dc:coverage>United States</dc:coverage>\\n</oai_dc:dc>\\n ", "submitter_type": "agent", "resource_data_type": "metadata", "payload_schema_locator": "http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_schema": ["oai_dc"], "doc_version": "0.10.0", "submitter": "NSDL 2 LR Data Pump"}''' self.sampleJSON_strip_normal_bencode = '''d3:TOSd14:submission_TOS30:http://example.com/tos/unknown22:submission_attribution12:unidentifiede6:active4:true8:doc_type13:resource_data11:doc_version6:0.10.04:keysl38:United States--Description and travel.3:enge17:payload_placement6:inline14:payload_schemal6:oai_dce22:payload_schema_locator90:http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd13:resource_data968:<oai_dc:dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.openarchives.org/OAI/2.0/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd">\n<dc:title>A chat about America. October and November, 1884.</dc:title>\n<dc:creator>J. P.</dc:creator>\n<dc:subject>United States--Description and travel.</dc:subject>\n<dc:description>"Printed for private circulation only."</dc:description>\n<dc:description>Electronic reproduction. Washington, D.C. : Library of Congress, [2002-2003]</dc:description>\n<dc:publisher>Manchester, Palmer & Howe</dc:publisher>\n<dc:date>1885</dc:date>\n<dc:type>text</dc:type>\n<dc:identifier>http://hdl.loc.gov/loc.gdc/lhbtn.12281</dc:identifier>\n<dc:language>eng</dc:language>\n<dc:coverage>United States</dc:coverage>\n</oai_dc:dc>\n 18:resource_data_type8:metadata16:resource_locator38:http://hdl.loc.gov/loc.gdc/lhbtn.122819:submitter19:NSDL 2 LR Data Pump14:submitter_type5:agente''' self.sampleJSON_sha256 = '''ef1b3b63adc663602c7a3c7595951b2761b34f5f6490ea1acee3df0fd97db03c''' self.sampleKeyLocations = [ "http://example.com/mykey", "http://example2.com/mykey" ] self.signatureTemplate = '{{"key_location": [{0}], "key_owner": "'+self.goodowner+'", "signing_method": "LR-PGP.1.0", "signature": "{1}"}}' def tearDown(self): self.gpg.delete_keys([self.goodPrivateKey.fingerprint, ], secret=True) self.gpg.delete_keys([self.goodPrivateKey.fingerprint, ], secret=False) pass def testMissingPrivateKey(self): def missingKey(): try: sign = Sign_0_21(self.badkeyid) except UnknownKeyException as e: assert e.keyid == self.badkeyid, "keyid in exception doesn't match key passed to sign." raise e self.assertRaises(UnknownKeyException, missingKey) def testPresentPrivateKey(self): sign = Sign_0_21(self.goodkeyid) assert sign.privateKeyID == self.goodkeyid def testStrip(self): origJson = json.loads(self.sampleJSON) benchmark = json.loads(self.sampleJSON_strip) signer = Sign_0_21(self.goodkeyid) stripJson = signer._stripEnvelope(origJson) assert benchmark == stripJson def testStripNormal(self): origJson = json.loads(self.sampleJSON) benchmark = json.loads(self.sampleJSON_strip_normal) signer = Sign_0_21(self.goodkeyid) stripJson = signer._stripEnvelope(origJson) normalJson = signer._bnormal(stripJson) assert benchmark == normalJson def testStripNormalBencode(self): origJson = json.loads(self.sampleJSON) benchmark = self.sampleJSON_strip_normal_bencode signer = Sign_0_21(self.goodkeyid) stripJson = signer._stripEnvelope(origJson) normalJson = signer._bnormal(stripJson) bencodeJson = signer._buildCanonicalString(normalJson) assert benchmark == bencodeJson def testStripNormalBencodeHash(self): origJson = json.loads(self.sampleJSON) benchmark = self.sampleJSON_sha256 signer = Sign_0_21(self.goodkeyid) stripJson = signer._stripEnvelope(origJson) normalJson = signer._bnormal(stripJson) bencodeJson = signer._buildCanonicalString(normalJson) hash = signer._hash(bencodeJson) assert benchmark == hash def testGetMessage(self): origJson = json.loads(self.sampleJSON) benchmark = self.sampleJSON_sha256 signer = Sign_0_21(self.goodkeyid) message = signer.get_message(origJson) assert benchmark == message def testPrivateKeyOwner(self): benchmark = self.goodowner signer = Sign_0_21(self.goodkeyid) assert benchmark == signer._get_privatekey_owner() def testSigBlock(self): origJson = json.loads(self.sampleJSON) arbitrarySigdata = "ABCDEF0123456789-abcdef" arbitraryKeyLoc = self.sampleKeyLocations keyloc = ",".join(map(lambda x: '"{0}"'.format(x), arbitraryKeyLoc)) benchmark = json.loads(self.signatureTemplate.format(keyloc, arbitrarySigdata)) signer = Sign_0_21(self.goodkeyid, passphrase=self.goodpassphrase, publicKeyLocations=arbitraryKeyLoc) assert benchmark == signer._get_sig_block(arbitrarySigdata) def testSign(self): origJson = json.loads(self.sampleJSON) arbitraryKeyLoc = self.sampleKeyLocations signer = Sign_0_21(self.goodkeyid, passphrase=self.goodpassphrase, publicKeyLocations=arbitraryKeyLoc) signed = signer.sign(origJson) assert signed.has_key("digital_signature") sig = signed["digital_signature"] assert sig.has_key("signature") assert sig["signature"] != None and len(sig["signature"]) > 0 def testSignUnicode(self): if self.testDataDir == None: log.info("Skipping test, test data directory not set.") return import codecs fileName = "2011-02-28Metadata1004.json" unsigned = json.load(codecs.open(os.path.join(self.testDataDir, fileName), "r", "utf-8-sig")) arbitraryKeyLoc = self.sampleKeyLocations signer = Sign_0_21(self.goodkeyid, passphrase=self.goodpassphrase, publicKeyLocations=arbitraryKeyLoc) signed = signer.sign(unsigned) assert signed.has_key("digital_signature") sig = signed["digital_signature"] assert sig.has_key("signature") assert sig["signature"] != None and len(sig["signature"]) > 0 def testSignLRTestData(self): if self.testDataDir == None: log.info("Skipping test, test data directory not set.") return import codecs allfiles = os.listdir(self.testDataDir) for fileName in allfiles: log.info("Trying to sign %s" % (fileName, )) unsigned = json.load(codecs.open(os.path.join(self.testDataDir, fileName), "r", "utf-8-sig")) arbitraryKeyLoc = self.sampleKeyLocations signer = Sign_0_21(self.goodkeyid, passphrase=self.goodpassphrase, publicKeyLocations=arbitraryKeyLoc) signed = signer.sign(unsigned) assert signed.has_key("digital_signature") sig = signed["digital_signature"] assert sig.has_key("signature") assert sig["signature"] != None and len(sig["signature"]) > 0
class Test(unittest.TestCase): '''Unit tests for validating signed envelopes from the Learning Registry''' def __init__(self, methodName="runTest"): self.sampleJSON = ''' { "_id":"00e3f67232e743b6bc2a079bd98ff55a", "_rev":"1-8163d32f6cc9996f2b7228d8b5db7962", "doc_type":"resource_data", "update_timestamp":"2011-03-14 13:36:04.617999", "resource_data":"<oai_dc:dc xmlns:oai_dc=\\"http://www.openarchives.org/OAI/2.0/oai_dc/\\" xmlns:dc=\\"http://purl.org/dc/elements/1.1/\\" xmlns:xsi=\\"http://www.w3.org/2001/XMLSchema-instance\\" xmlns=\\"http://www.openarchives.org/OAI/2.0/\\" xsi:schemaLocation=\\"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd\\">\\n<dc:title>A chat about America. October and November, 1884.</dc:title>\\n<dc:creator>J. P.</dc:creator>\\n<dc:subject>United States--Description and travel.</dc:subject>\\n<dc:description>\\"Printed for private circulation only.\\"</dc:description>\\n<dc:description>Electronic reproduction. Washington, D.C. : Library of Congress, [2002-2003]</dc:description>\\n<dc:publisher>Manchester, Palmer & Howe</dc:publisher>\\n<dc:date>1885</dc:date>\\n<dc:type>text</dc:type>\\n<dc:identifier>http://hdl.loc.gov/loc.gdc/lhbtn.12281</dc:identifier>\\n<dc:language>eng</dc:language>\\n<dc:coverage>United States</dc:coverage>\\n</oai_dc:dc>\\n ", "keys":["United States--Description and travel.","eng"], "submitter_type":"agent", "resource_data_type":"metadata", "payload_schema_locator":"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_placement":"inline", "submitter":"NSDL 2 LR Data Pump", "payload_schema":["oai_dc"], "node_timestamp":"2011-03-14 13:36:04.617999", "doc_version":"0.10.0", "create_timestamp":"2011-03-14 13:36:04.617999", "active":true, "publishing_node":"string", "resource_locator":"http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_ID":"00e3f67232e743b6bc2a079bd98ff55a", "TOS": { "submission_TOS": "http://example.com/tos/unknown", "submission_attribution": "unidentified" } } ''' self.sampleKeyLocations = [ "http://example.com/mykey", "http://example2.com/mykey" ] self.gpgbin = "gpg" self.gnupgHome = os.path.expanduser( os.path.abspath(os.path.join("..", "gnupg_home"))) self.gpg = None self.testDataDir = None self.testDataUnicode = None configFile = os.path.join("config.cfg") if os.path.exists(configFile): config = json.load(file(configFile)) if "global" in config: if "testdata" in config["global"] and os.path.exists( config["global"]["testdata"]): self.testDataDir = config["global"]["testdata"] if "testdata_unicode" in config["global"] and os.path.exists( config["global"]["testdata_unicode"]): self.testDataUnicode = config["global"]["testdata_unicode"] unittest.TestCase.__init__(self, methodName) def setUp(self): now = time.localtime() now = calendar.timegm(now) try: for root, dirs, files in os.walk(self.gnupgHome): for filename in files: try: os.unlink(os.path.join(root, filename)) except: pass os.removedirs(root) except: pass os.makedirs(self.gnupgHome) self.gpg = GPG(gpgbinary=self.gpgbin, gnupghome=self.gnupgHome) self.privateEmail = "privateTest-{0}@learningregistry.org".format(now) self.privateEmail2 = "privateTest2-{0}@learningregistry.org".format( now) self.genericPassphrase = "supersecret" input = self.gpg.gen_key_input(name_email=self.privateEmail, passphrase=self.genericPassphrase) self.privateKey = self.gpg.gen_key(input) input = self.gpg.gen_key_input(name_email=self.privateEmail2, passphrase=self.genericPassphrase) self.privateKey2 = self.gpg.gen_key(input) self.privExport = self.gpg.export_keys( [self.privateKey.fingerprint, self.privateKey2.fingerprint], secret=True) self.pubExport = self.gpg.export_keys( [self.privateKey.fingerprint, self.privateKey2.fingerprint], secret=False) pass def tearDown(self): self.gpg.delete_keys( [self.privateKey.fingerprint, self.privateKey2.fingerprint], secret=True) self.gpg.delete_keys( [self.privateKey.fingerprint, self.privateKey2.fingerprint], secret=False) pass def testGetSignatureBlock(self): '''Check that signature block validation correctly returns a structurally valid response''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "envelope did not sign correctly" verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) sigInfo = verifytool._getSignatureInfo(signed) assert sigInfo != None, "signature extraction from envelope failed" assert "signing_method" in sigInfo and sigInfo[ "signing_method"] == verifytool.signatureMethod, "signing_method is missing from signature block" assert "signature" in sigInfo and sigInfo["signature"] != None and len( sigInfo["signature"] ) > 0, "signature field is missing, null or is empty" assert "key_location" in sigInfo and sigInfo[ "key_location"] == self.sampleKeyLocations, "key_location field is not correct" assert "key_owner" in sigInfo and sigInfo[ "key_owner"] == signtool._get_privatekey_owner( ), "key_owner field does not match signing key" def testBadSignatureBlockMissingLocation(self): '''Check that signature block validation correctly checks for missing key_location field''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=None) signed = signtool.sign(unsigned) assert signed != None, "Envelope not signed correctly" verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) with self.assertRaises(errors.BadSignatureFormat, msg="Expected exception not raised.") as caught: sigInfo = verifytool._getSignatureInfo(signed) assert caught.exception.message == errors.MISSING_KEY_LOCATION, "Exception not formatted with correct message" def testBadSignatureBlockMissingSignatureMethod(self): '''Check signature block for missing signature_method detection''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "Envelope not signed correctly." del signed["digital_signature"]["signing_method"] verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) with self.assertRaises(errors.UnsupportedSignatureAlgorithm, msg="Expected exception not raised.") as caught: sigInfo = verifytool._getSignatureInfo(signed) assert caught.exception.alg == None, "Raised exception not formatted correctly" def testBadSignatureBlockBadSignatureMethod(self): '''Check signature block for detecting unsupported algorithm use''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None signed["digital_signature"][ "signing_method"] = signtool.signatureMethod + "+BAD_SIGNATURE_METHOD" verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) with self.assertRaises(errors.UnsupportedSignatureAlgorithm, msg="Expected exception not raised.") as caught: sigInfo = verifytool._getSignatureInfo(signed) assert caught.exception.alg == signed["digital_signature"][ "signing_method"], "Exception not raised correctly." def testBadSignatureBlockBadKeyOwner(self): '''Check signature block for a bad key_owner field''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "Envelope not signed correctly" signed["digital_signature"]["key_owner"] = [ "John Q. Public <*****@*****.**>" ] verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) with self.assertRaises(errors.BadSignatureFormat, msg="Expected exception not raised.") as caught: sigInfo = verifytool._getSignatureInfo(signed) assert caught.exception.message == errors.BAD_KEY_OWNER, "Wrong exception" def testBadSignatureBlockMissingNullEmptySignature(self): '''Check signature block validation with missing/null or empty signature''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "Envelope signing did not succeed correctly." verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) signed["digital_signature"]["signature"] = "" with self.assertRaises(errors.BadSignatureFormat, msg="Expected exception not raised.") as caught: sigInfo = verifytool._getSignatureInfo(signed) assert caught.exception.message == errors.MISSING_SIGNATURE, "Wrong exception raised" signed["digital_signature"]["signature"] = None with self.assertRaises(errors.BadSignatureFormat, msg="Expected exception not raised.") as caught: sigInfo = verifytool._getSignatureInfo(signed) assert caught.exception.message == errors.MISSING_SIGNATURE, "Wrong exception raised" del signed["digital_signature"]["signature"] with self.assertRaises(errors.BadSignatureFormat, msg="Expected exception not raised.") as caught: sigInfo = verifytool._getSignatureInfo(signed) assert caught.exception.message == errors.MISSING_SIGNATURE, "Wrong exception raised" def testValidSignature(self): '''Check for valid signature''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "Envelope did not sign correctly" verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) verified = verifytool.verify(signed) assert verified == True, "Envelope signature verification did not succeed, even though it should" def testMissingPublicKey(self): '''Check for appropriate response when public key for signature is missing''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "Envelope did not sign correctly" self.gpg.delete_keys([self.privateKey.fingerprint], secret=True) self.gpg.delete_keys([self.privateKey.fingerprint], secret=False) verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) with self.assertRaises(errors.MissingPublicKey, msg="Expected exception not raised.") as caught: verified = verifytool.verify(signed) assert verified == False, "Envelope verified, despite missing public key." def testCorruptEnvelope(self): '''Modify a signed envelope and check for validity''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "envelope did not get signed correctly" signed["X_corrupted"] = "Corrupted Envelope" verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) verified = verifytool.verify(signed) assert verified == False, "corrupted envelope verified as good" def testWrongSignature(self): '''Test using a mis-matched signature, using a signature from a different valid envelope''' unsigned = json.loads(self.sampleJSON) altered = copy.deepcopy(unsigned) altered["X_corrupted"] = "Altered Envelope" signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signtool2 = Sign_0_21(privateKeyID=self.privateKey2.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) alt_signed = signtool2.sign(altered) assert signed != None, "original did not get signed" assert alt_signed != None, "modified copy did not get signed" verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) verified = verifytool.verify(signed) assert verified == True, "signature did not verify, even though it should." verified = verifytool.verify(alt_signed) assert verified == True, "signature did not verify, even though it should." signed["digital_signature"] = alt_signed["digital_signature"] verified = verifytool.verify(signed) assert verified == False, "swapped signature block validated envelope as good." def testCorruptSignature(self): '''Test using a corrupted signature, replace the hash within a signature with a hash from a different envelope''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "baseline signing failed" # validate original with good signature verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) verified = verifytool.verify(signed) assert verified == True, "baseline validation failed" # manipulate the hash portion of a signature block altered = copy.deepcopy(unsigned) altered["X_corrupted"] = "Altered Envelope" altered_hash = signtool.get_message(altered) validHash = verifytool._extractHashFromSignature( signed["digital_signature"]["signature"]) alt_signed = copy.deepcopy(signed) alt_signed["digital_signature"]["signature"] = signed[ "digital_signature"]["signature"].replace(validHash, altered_hash) assert alt_signed["digital_signature"]["signature"] != signed[ "digital_signature"][ "signature"], "envelopes should not be equal after deliberate modificaton" verified = verifytool.verify(alt_signed) assert verified == False, "verification failed, corrupted signature block verified as good" def testSignLRTestData(self): '''Test using LR Test Data, if available''' if self.testDataDir == None: log.info("Skipping test, test data directory not set.") return import codecs signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) allfiles = os.listdir(self.testDataDir) for root, dirs, files in os.walk(self.testDataDir): for fileName in files: log.info("Trying to sign %s" % (fileName, )) unsigned = json.load( codecs.open(os.path.join(root, fileName), "r", "utf-8-sig")) signed = signtool.sign(unsigned) assert "digital_signature" in signed, "missing digital_signature" verified = verifytool.verify(signed) assert verified == True, "baseline validation failed"
class TempGPGWrapper(object): """ A context manager that wraps a temporary GPG keyring which only contains the keys given at object creation. """ log = Logger() def __init__(self, keys=None, gpgbinary=None): """ Create an empty temporary keyring and import any given C{keys} into it. :param keys: OpenPGP key, or list of. :type keys: OpenPGPKey or list of OpenPGPKeys :param gpgbinary: Name for GnuPG binary executable. :type gpgbinary: C{str} """ self._gpg = None self._gpgbinary = gpgbinary if not keys: keys = list() if not isinstance(keys, list): keys = [keys] self._keys = keys def __enter__(self): """ Build and return a GPG keyring containing the keys given on object creation. :return: A GPG instance containing the keys given on object creation. :rtype: gnupg.GPG """ self._build_keyring() return self._gpg def __exit__(self, exc_type, exc_value, traceback): """ Ensure the gpg is properly destroyed. """ # TODO handle exceptions and log here self._destroy_keyring() def _build_keyring(self): """ Create a GPG keyring containing the keys given on object creation. :return: A GPG instance containing the keys given on object creation. :rtype: gnupg.GPG """ privkeys = [key for key in self._keys if key and key.private is True] publkeys = [key for key in self._keys if key and key.private is False] # here we filter out public keys that have a correspondent # private key in the list because the private key_data by # itself is enough to also have the public key in the keyring, # and we want to count the keys afterwards. privfps = map(lambda privkey: privkey.fingerprint, privkeys) publkeys = filter( lambda pubkey: pubkey.fingerprint not in privfps, publkeys) listkeys = lambda: self._gpg.list_keys() listsecretkeys = lambda: self._gpg.list_keys(secret=True) try: self._gpg = GPG(binary=self._gpgbinary, homedir=tempfile.mkdtemp()) except TypeError: # compat-mode with python-gnupg until windows # support is fixed in gnupg-ng self._gpg = GPG(gpgbinary=self._gpgbinary, gnupghome=tempfile.mkdtemp(), options=[]) leap_assert(len(listkeys()) is 0, 'Keyring not empty.') # import keys into the keyring: # concatenating ascii-armored keys, which is correctly # understood by GPG. self._gpg.import_keys("".join( [x.key_data for x in publkeys + privkeys])) # assert the number of keys in the keyring leap_assert( len(listkeys()) == len(publkeys) + len(privkeys), 'Wrong number of public keys in keyring: %d, should be %d)' % (len(listkeys()), len(publkeys) + len(privkeys))) leap_assert( len(listsecretkeys()) == len(privkeys), 'Wrong number of private keys in keyring: %d, should be %d)' % (len(listsecretkeys()), len(privkeys))) def _destroy_keyring(self): """ Securely erase the keyring. """ # TODO: implement some kind of wiping of data or a more # secure way that # does not write to disk. try: for secret in [True, False]: for key in self._gpg.list_keys(secret=secret): self._gpg.delete_keys( key['fingerprint'], secret=secret) leap_assert(len(self._gpg.list_keys()) is 0, 'Keyring not empty!') except: raise finally: try: homedir = self._gpg.homedir except AttributeError: homedir = self._gpg.gnupghome leap_assert(homedir != os.path.expanduser('~/.gnupg'), "watch out! Tried to remove default gnupg home!") # TODO some windows debug .... homedir = os.path.normpath(homedir).replace("\\", "/") homedir = str(homedir.replace("c:/", "c://")) if platform.system() == "Windows": self.log.error("BUG! Not erasing folder in Windows") return shutil.rmtree(homedir)
class Test(unittest.TestCase): '''Unit test cases for signing envelopes from the Learning Registry''' def __init__(self, methodName="runTest"): self.gpgbin = "gpg" self.gnupgHome = os.path.expanduser( os.path.abspath(os.path.join("..", "gnupg_home"))) try: os.makedirs(self.gnupghome) except: pass self.gpg = None unittest.TestCase.__init__(self, methodName) self.testDataDir = None self.testDataUnicode = None configFile = os.path.join("config.cfg") if os.path.exists(configFile): config = json.load(file(configFile)) if "global" in config: if "testdata" in config["global"] and os.path.exists( config["global"]["testdata"]): self.testDataDir = config["global"]["testdata"] if "testdata_unicode" in config["global"] and os.path.exists( config["global"]["testdata_unicode"]): self.testDataUnicode = config["global"]["testdata_unicode"] def setUp(self): now = time.localtime() now = calendar.timegm(now) self.goodEmail = "signTest-{0}@learningregistry.org".format(now) self.goodRealName = "Autogenerated Sign Test" self.goodpassphrase = "supersecret" try: for root, dirs, files in os.walk(self.gnupgHome): for filename in files: try: os.unlink(os.path.join(root, filename)) except: pass os.removedirs(root) except: pass os.makedirs(self.gnupgHome) self.gpg = GPG(gpgbinary=self.gpgbin, gnupghome=self.gnupgHome) input = self.gpg.gen_key_input(name_real=self.goodRealName, name_email=self.goodEmail, passphrase=self.goodpassphrase) self.goodPrivateKey = self.gpg.gen_key(input) privateKeyAvailable = False privateKeys = self.gpg.list_keys(secret=True) for skey in privateKeys: if skey["keyid"] == self.goodPrivateKey.fingerprint: privateKeyAvailable = True self.privateKeyInfo = skey break if skey["fingerprint"] == self.goodPrivateKey.fingerprint: privateKeyAvailable = True self.privateKeyInfo = skey break assert privateKeyAvailable == True, "Could not locate generated Private Key" self.goodkeyid = self.privateKeyInfo["keyid"] self.goodowner = self.privateKeyInfo["uids"][0] self.badkeyid = "XXXXXXXXXXXXXXXX" self.badpassphrase = "bad passphrase" self.sampleJSON = ''' { "_id":"00e3f67232e743b6bc2a079bd98ff55a", "_rev":"1-8163d32f6cc9996f2b7228d8b5db7962", "doc_type":"resource_data", "update_timestamp":"2011-03-14 13:36:04.617999", "resource_data":"<oai_dc:dc xmlns:oai_dc=\\"http://www.openarchives.org/OAI/2.0/oai_dc/\\" xmlns:dc=\\"http://purl.org/dc/elements/1.1/\\" xmlns:xsi=\\"http://www.w3.org/2001/XMLSchema-instance\\" xmlns=\\"http://www.openarchives.org/OAI/2.0/\\" xsi:schemaLocation=\\"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd\\">\\n<dc:title>A chat about America. October and November, 1884.</dc:title>\\n<dc:creator>J. P.</dc:creator>\\n<dc:subject>United States--Description and travel.</dc:subject>\\n<dc:description>\\"Printed for private circulation only.\\"</dc:description>\\n<dc:description>Electronic reproduction. Washington, D.C. : Library of Congress, [2002-2003]</dc:description>\\n<dc:publisher>Manchester, Palmer & Howe</dc:publisher>\\n<dc:date>1885</dc:date>\\n<dc:type>text</dc:type>\\n<dc:identifier>http://hdl.loc.gov/loc.gdc/lhbtn.12281</dc:identifier>\\n<dc:language>eng</dc:language>\\n<dc:coverage>United States</dc:coverage>\\n</oai_dc:dc>\\n ", "keys":["United States--Description and travel.","eng"], "submitter_type":"agent", "resource_data_type":"metadata", "payload_schema_locator":"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_placement":"inline", "submitter":"NSDL 2 LR Data Pump", "payload_schema":["oai_dc"], "node_timestamp":"2011-03-14 13:36:04.617999", "doc_version":"0.10.0", "create_timestamp":"2011-03-14 13:36:04.617999", "active":true, "publishing_node":"string", "resource_locator":"http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_ID":"00e3f67232e743b6bc2a079bd98ff55a", "TOS": { "submission_TOS": "http://example.com/tos/unknown", "submission_attribution": "unidentified" } } ''' self.sampleJSON__0_23_0 = ''' { "_id":"00e3f67232e743b6bc2a079bd98ff55a", "_rev":"1-8163d32f6cc9996f2b7228d8b5db7962", "doc_type":"resource_data", "update_timestamp":"2011-03-14 13:36:04.617999", "resource_data":"<oai_dc:dc xmlns:oai_dc=\\"http://www.openarchives.org/OAI/2.0/oai_dc/\\" xmlns:dc=\\"http://purl.org/dc/elements/1.1/\\" xmlns:xsi=\\"http://www.w3.org/2001/XMLSchema-instance\\" xmlns=\\"http://www.openarchives.org/OAI/2.0/\\" xsi:schemaLocation=\\"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd\\">\\n<dc:title>A chat about America. October and November, 1884.</dc:title>\\n<dc:creator>J. P.</dc:creator>\\n<dc:subject>United States--Description and travel.</dc:subject>\\n<dc:description>\\"Printed for private circulation only.\\"</dc:description>\\n<dc:description>Electronic reproduction. Washington, D.C. : Library of Congress, [2002-2003]</dc:description>\\n<dc:publisher>Manchester, Palmer & Howe</dc:publisher>\\n<dc:date>1885</dc:date>\\n<dc:type>text</dc:type>\\n<dc:identifier>http://hdl.loc.gov/loc.gdc/lhbtn.12281</dc:identifier>\\n<dc:language>eng</dc:language>\\n<dc:coverage>United States</dc:coverage>\\n</oai_dc:dc>\\n ", "keys":["United States--Description and travel.","eng"], "identity":{ "submitter_type":"agent", "submitter":"NSDL 2 LR Data Pump" }, "resource_data_type":"metadata", "payload_schema_locator":"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_placement":"inline", "payload_schema":["oai_dc"], "node_timestamp":"2011-03-14 13:36:04.617999", "doc_version":"0.23.0", "create_timestamp":"2011-03-14 13:36:04.617999", "active":true, "publishing_node":"string", "resource_locator":"http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_ID":"00e3f67232e743b6bc2a079bd98ff55a", "TOS": { "submission_TOS": "http://example.com/tos/unknown", "submission_attribution": "unidentified" } } ''' self.sampleJSON__0_51_0 = ''' { "_id":"00e3f67232e743b6bc2a079bd98ff55a", "_rev":"1-8163d32f6cc9996f2b7228d8b5db7962", "doc_type":"resource_data", "update_timestamp":"2014-04-01 13:36:04.617999", "resource_data":"<oai_dc:dc xmlns:oai_dc=\\"http://www.openarchives.org/OAI/2.0/oai_dc/\\" xmlns:dc=\\"http://purl.org/dc/elements/1.1/\\" xmlns:xsi=\\"http://www.w3.org/2001/XMLSchema-instance\\" xmlns=\\"http://www.openarchives.org/OAI/2.0/\\" xsi:schemaLocation=\\"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd\\">\\n<dc:title>A chat about America. October and November, 1884.</dc:title>\\n<dc:creator>J. P.</dc:creator>\\n<dc:subject>United States--Description and travel.</dc:subject>\\n<dc:description>\\"Printed for private circulation only.\\"</dc:description>\\n<dc:description>Electronic reproduction. Washington, D.C. : Library of Congress, [2002-2003]</dc:description>\\n<dc:publisher>Manchester, Palmer & Howe</dc:publisher>\\n<dc:date>1885</dc:date>\\n<dc:type>text</dc:type>\\n<dc:identifier>http://hdl.loc.gov/loc.gdc/lhbtn.12281</dc:identifier>\\n<dc:language>eng</dc:language>\\n<dc:coverage>United States</dc:coverage>\\n</oai_dc:dc>\\n ", "keys":["United States--Description and travel.","eng"], "identity":{ "submitter_type":"agent", "submitter":"NSDL 2 LR Data Pump" }, "resource_data_type":"metadata", "payload_schema_locator":"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_placement":"inline", "payload_schema":["oai_dc"], "node_timestamp":"2014-04-01 13:32:04.617999", "doc_version":"0.51.0", "create_timestamp":"2014-04-01 13:36:04.617999", "active":true, "publishing_node":"string", "resource_locator":"http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_ID":"00e3f67232e743b6bc2a079bd98ff55a", "TOS": { "submission_TOS": "http://example.com/tos/unknown", "submission_attribution": "unidentified" } } ''' self.sampleJSON_no_coercion = ''' { "_id":"00e3f67232e743b6bc2a079bd98ff55a", "_rev":"1-8163d32f6cc9996f2b7228d8b5db7962", "doc_type":"resource_data", "update_timestamp":"2011-03-14 13:36:04.617999", "resource_data": { "name": "Test coersion", "nullable": null, "booleanT": true, "booleanF": false }, "keys":["United States--Description and travel.","eng"], "identity":{ "submitter_type":"agent", "submitter":"NSDL 2 LR Data Pump" }, "resource_data_type":"metadata", "payload_schema_locator":"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_placement":"inline", "payload_schema":["oai_dc"], "node_timestamp":"2011-03-14 13:36:04.617999", "doc_version":"0.23.0", "create_timestamp":"2011-03-14 13:36:04.617999", "active":true, "publishing_node":"string", "resource_locator":"http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_ID":"00e3f67232e743b6bc2a079bd98ff55a", "TOS": { "submission_TOS": "http://example.com/tos/unknown", "submission_attribution": "unidentified" } } ''' self.sampleJSON_lossy_sha256 = [ ''' { "_id":"00e3f67232e743b6bc2a079bd98ff55a", "_rev":"1-8163d32f6cc9996f2b7228d8b5db7962", "doc_type":"resource_data", "update_timestamp":"2011-03-14 13:36:04.617999", "resource_data": { "name": "Test Lossy" "integer": 1 }, "keys":["United States--Description and travel.","eng"], "identity":{ "submitter_type":"agent", "submitter":"NSDL 2 LR Data Pump" }, "resource_data_type":"metadata", "payload_schema_locator":"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_placement":"inline", "payload_schema":["oai_dc"], "node_timestamp":"2011-03-14 13:36:04.617999", "doc_version":"0.23.0", "create_timestamp":"2011-03-14 13:36:04.617999", "active":true, "publishing_node":"string", "resource_locator":"http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_ID":"00e3f67232e743b6bc2a079bd98ff55a", "TOS": { "submission_TOS": "http://example.com/tos/unknown", "submission_attribution": "unidentified" } } ''', ''' { "_id":"00e3f67232e743b6bc2a079bd98ff55a", "_rev":"1-8163d32f6cc9996f2b7228d8b5db7962", "doc_type":"resource_data", "update_timestamp":"2011-03-14 13:36:04.617999", "resource_data": { "name": "Test Lossy" "float": 1.1 }, "keys":["United States--Description and travel.","eng"], "identity":{ "submitter_type":"agent", "submitter":"NSDL 2 LR Data Pump" }, "resource_data_type":"metadata", "payload_schema_locator":"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_placement":"inline", "payload_schema":["oai_dc"], "node_timestamp":"2011-03-14 13:36:04.617999", "doc_version":"0.23.0", "create_timestamp":"2011-03-14 13:36:04.617999", "active":true, "publishing_node":"string", "resource_locator":"http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_ID":"00e3f67232e743b6bc2a079bd98ff55a", "TOS": { "submission_TOS": "http://example.com/tos/unknown", "submission_attribution": "unidentified" } } ''', ''' { "_id":"00e3f67232e743b6bc2a079bd98ff55a", "_rev":"1-8163d32f6cc9996f2b7228d8b5db7962", "doc_type":"resource_data", "update_timestamp":"2011-03-14 13:36:04.617999", "resource_data": { "name": "Test Lossy" "max_int": 9007199254740990 }, "keys":["United States--Description and travel.","eng"], "identity":{ "submitter_type":"agent", "submitter":"NSDL 2 LR Data Pump" }, "resource_data_type":"metadata", "payload_schema_locator":"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_placement":"inline", "payload_schema":["oai_dc"], "node_timestamp":"2011-03-14 13:36:04.617999", "doc_version":"0.23.0", "create_timestamp":"2011-03-14 13:36:04.617999", "active":true, "publishing_node":"string", "resource_locator":"http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_ID":"00e3f67232e743b6bc2a079bd98ff55a", "TOS": { "submission_TOS": "http://example.com/tos/unknown", "submission_attribution": "unidentified" } } ''', ''' { "_id":"00e3f67232e743b6bc2a079bd98ff55a", "_rev":"1-8163d32f6cc9996f2b7228d8b5db7962", "doc_type":"resource_data", "update_timestamp":"2011-03-14 13:36:04.617999", "resource_data": { "name": "Test Lossy" "min_int": -9007199254740990 }, "keys":["United States--Description and travel.","eng"], "identity":{ "submitter_type":"agent", "submitter":"NSDL 2 LR Data Pump" }, "resource_data_type":"metadata", "payload_schema_locator":"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_placement":"inline", "payload_schema":["oai_dc"], "node_timestamp":"2011-03-14 13:36:04.617999", "doc_version":"0.23.0", "create_timestamp":"2011-03-14 13:36:04.617999", "active":true, "publishing_node":"string", "resource_locator":"http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_ID":"00e3f67232e743b6bc2a079bd98ff55a", "TOS": { "submission_TOS": "http://example.com/tos/unknown", "submission_attribution": "unidentified" } } ''' ] self.sampleJSON_strip = '''{"keys": ["United States--Description and travel.", "eng"], "TOS": {"submission_attribution": "unidentified", "submission_TOS": "http://example.com/tos/unknown"}, "payload_placement": "inline", "active": true, "resource_locator": "http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_type": "resource_data", "resource_data": "<oai_dc:dc xmlns:oai_dc=\\"http://www.openarchives.org/OAI/2.0/oai_dc/\\" xmlns:dc=\\"http://purl.org/dc/elements/1.1/\\" xmlns:xsi=\\"http://www.w3.org/2001/XMLSchema-instance\\" xmlns=\\"http://www.openarchives.org/OAI/2.0/\\" xsi:schemaLocation=\\"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd\\">\\n<dc:title>A chat about America. October and November, 1884.</dc:title>\\n<dc:creator>J. P.</dc:creator>\\n<dc:subject>United States--Description and travel.</dc:subject>\\n<dc:description>\\"Printed for private circulation only.\\"</dc:description>\\n<dc:description>Electronic reproduction. Washington, D.C. : Library of Congress, [2002-2003]</dc:description>\\n<dc:publisher>Manchester, Palmer & Howe</dc:publisher>\\n<dc:date>1885</dc:date>\\n<dc:type>text</dc:type>\\n<dc:identifier>http://hdl.loc.gov/loc.gdc/lhbtn.12281</dc:identifier>\\n<dc:language>eng</dc:language>\\n<dc:coverage>United States</dc:coverage>\\n</oai_dc:dc>\\n ", "submitter_type": "agent", "resource_data_type": "metadata", "payload_schema_locator": "http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_schema": ["oai_dc"], "doc_version": "0.10.0", "submitter": "NSDL 2 LR Data Pump"}''' self.sampleJSON_strip_normal = '''{"keys": ["United States--Description and travel.", "eng"], "TOS": {"submission_attribution": "unidentified", "submission_TOS": "http://example.com/tos/unknown"}, "payload_placement": "inline", "active": "true", "resource_locator": "http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_type": "resource_data", "resource_data": "<oai_dc:dc xmlns:oai_dc=\\"http://www.openarchives.org/OAI/2.0/oai_dc/\\" xmlns:dc=\\"http://purl.org/dc/elements/1.1/\\" xmlns:xsi=\\"http://www.w3.org/2001/XMLSchema-instance\\" xmlns=\\"http://www.openarchives.org/OAI/2.0/\\" xsi:schemaLocation=\\"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd\\">\\n<dc:title>A chat about America. October and November, 1884.</dc:title>\\n<dc:creator>J. P.</dc:creator>\\n<dc:subject>United States--Description and travel.</dc:subject>\\n<dc:description>\\"Printed for private circulation only.\\"</dc:description>\\n<dc:description>Electronic reproduction. Washington, D.C. : Library of Congress, [2002-2003]</dc:description>\\n<dc:publisher>Manchester, Palmer & Howe</dc:publisher>\\n<dc:date>1885</dc:date>\\n<dc:type>text</dc:type>\\n<dc:identifier>http://hdl.loc.gov/loc.gdc/lhbtn.12281</dc:identifier>\\n<dc:language>eng</dc:language>\\n<dc:coverage>United States</dc:coverage>\\n</oai_dc:dc>\\n ", "submitter_type": "agent", "resource_data_type": "metadata", "payload_schema_locator": "http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_schema": ["oai_dc"], "doc_version": "0.10.0", "submitter": "NSDL 2 LR Data Pump"}''' self.sampleJSON_strip_normal_bencode = '''d3:TOSd14:submission_TOS30:http://example.com/tos/unknown22:submission_attribution12:unidentifiede6:active4:true8:doc_type13:resource_data11:doc_version6:0.10.04:keysl38:United States--Description and travel.3:enge17:payload_placement6:inline14:payload_schemal6:oai_dce22:payload_schema_locator90:http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd13:resource_data968:<oai_dc:dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.openarchives.org/OAI/2.0/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd">\n<dc:title>A chat about America. October and November, 1884.</dc:title>\n<dc:creator>J. P.</dc:creator>\n<dc:subject>United States--Description and travel.</dc:subject>\n<dc:description>"Printed for private circulation only."</dc:description>\n<dc:description>Electronic reproduction. Washington, D.C. : Library of Congress, [2002-2003]</dc:description>\n<dc:publisher>Manchester, Palmer & Howe</dc:publisher>\n<dc:date>1885</dc:date>\n<dc:type>text</dc:type>\n<dc:identifier>http://hdl.loc.gov/loc.gdc/lhbtn.12281</dc:identifier>\n<dc:language>eng</dc:language>\n<dc:coverage>United States</dc:coverage>\n</oai_dc:dc>\n 18:resource_data_type8:metadata16:resource_locator38:http://hdl.loc.gov/loc.gdc/lhbtn.122819:submitter19:NSDL 2 LR Data Pump14:submitter_type5:agente''' self.sampleJSON_sha256 = '''ef1b3b63adc663602c7a3c7595951b2761b34f5f6490ea1acee3df0fd97db03c''' self.sampleKeyLocations = [ "http://example.com/mykey", "http://example2.com/mykey" ] self.signatureTemplate = '{{"key_location": [{0}], "key_owner": "' + self.goodowner + '", "signing_method": "LR-PGP.1.0", "signature": "{1}"}}' def tearDown(self): self.gpg.delete_keys([ self.goodPrivateKey.fingerprint, ], secret=True) self.gpg.delete_keys([ self.goodPrivateKey.fingerprint, ], secret=False) pass def testMissingPrivateKey(self): def missingKey(): try: sign = Sign_0_21(self.badkeyid, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) except UnknownKeyException as e: assert e.keyid == self.badkeyid, "keyid in exception doesn't match key passed to sign." raise e self.assertRaises(UnknownKeyException, missingKey) def testPresentPrivateKey(self): sign = Sign_0_21(self.goodkeyid, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) assert sign.privateKeyID == self.goodkeyid def testStrip(self): origJson = json.loads(self.sampleJSON) benchmark = json.loads(self.sampleJSON_strip) signer = Sign_0_21(self.goodkeyid, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) stripJson = signer._stripEnvelope(origJson) assert benchmark == stripJson def testStripNormal(self): origJson = json.loads(self.sampleJSON) benchmark = json.loads(self.sampleJSON_strip_normal) signer = Sign_0_21(self.goodkeyid, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) stripJson = signer._stripEnvelope(origJson) normalJson = signer._bnormal(stripJson) assert benchmark == normalJson def testStripNormalBencode(self): origJson = json.loads(self.sampleJSON) benchmark = self.sampleJSON_strip_normal_bencode signer = Sign_0_21(self.goodkeyid, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) stripJson = signer._stripEnvelope(origJson) normalJson = signer._bnormal(stripJson) bencodeJson = signer._buildCanonicalString(normalJson) assert benchmark == bencodeJson def testStripNormalBencodeHash(self): origJson = json.loads(self.sampleJSON) benchmark = self.sampleJSON_sha256 signer = Sign_0_21(self.goodkeyid, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) stripJson = signer._stripEnvelope(origJson) normalJson = signer._bnormal(stripJson) bencodeJson = signer._buildCanonicalString(normalJson) hashed = signer._hash(bencodeJson) assert benchmark == hashed def testKnownLossyHash(self): for idx, lossyJSON in enumerate(self.sampleJSON_lossy_sha256): origJson = json.loads(self.sampleJSON) signer = Sign_0_21(self.goodkeyid, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) stripJson = signer._stripEnvelope(origJson) normalJson = signer._bnormal(stripJson) bencodeJson = signer._buildCanonicalString(normalJson) hashed = signer._hash(bencodeJson) if idx > 0: assert prev_hashed == hashed, "Hashes should match - algorithm is lossy" assert prev_bencodeJson == bencodeJson, "Bencode strings should match - algorithm is lossy" prev_bencodeJson = bencodeJson prev_hashed = hashed def testGetMessage(self): origJson = json.loads(self.sampleJSON) benchmark = self.sampleJSON_sha256 signer = Sign_0_21(self.goodkeyid, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) message = signer.get_message(origJson) assert benchmark == message def testPrivateKeyOwner(self): benchmark = self.goodowner signer = Sign_0_21(self.goodkeyid, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) assert benchmark == signer._get_privatekey_owner() def testSigBlock(self): origJson = json.loads(self.sampleJSON) arbitrarySigdata = "ABCDEF0123456789-abcdef" arbitraryKeyLoc = self.sampleKeyLocations keyloc = ",".join(['"{0}"'.format(x) for x in arbitraryKeyLoc]) benchmark = json.loads( self.signatureTemplate.format(keyloc, arbitrarySigdata)) signer = Sign_0_21(self.goodkeyid, passphrase=self.goodpassphrase, publicKeyLocations=arbitraryKeyLoc, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) assert benchmark == signer._get_sig_block(arbitrarySigdata) def testSignNoEnvelopeCoercion(self): origJson = json.loads(self.sampleJSON_no_coercion) arbitraryKeyLoc = self.sampleKeyLocations assert origJson["resource_data"]["nullable"] == None, "Expected null" assert origJson["resource_data"]["booleanT"] == True, "Expected true" assert origJson["resource_data"]["booleanF"] == False, "Expected false" signer = Sign_0_21(self.goodkeyid, passphrase=self.goodpassphrase, publicKeyLocations=arbitraryKeyLoc, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) signed = signer.sign(origJson) assert "digital_signature" in signed assert origJson["resource_data"]["nullable"] == None, "Expected null" assert origJson["resource_data"]["booleanT"] == True, "Expected true" assert origJson["resource_data"]["booleanF"] == False, "Expected false" def testSign_0_10__no_passthru(self): origJson = json.loads(self.sampleJSON) arbitraryKeyLoc = self.sampleKeyLocations signer = Sign_0_21(self.goodkeyid, passphrase=self.goodpassphrase, publicKeyLocations=arbitraryKeyLoc, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) signed = signer.sign(origJson) assert "digital_signature" in signed sig = signed["digital_signature"] assert "signature" in sig assert sig["signature"] != None and len(sig["signature"]) > 0 def test_Sign__0_10__passthru(self): origJson = json.loads(self.sampleJSON) arbitraryKeyLoc = self.sampleKeyLocations signer = Sign_0_21(self.goodkeyid, passphrase=self.goodpassphrase, publicKeyLocations=arbitraryKeyLoc, sign_everything=False, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) signed = signer.sign(origJson) assert "digital_signature" not in signed # sig = signed["digital_signature"] # assert sig.has_key("signature") # assert sig["signature"] != None and len(sig["signature"]) > 0 def test_Sign__0_23__passthru(self): origJson = json.loads(self.sampleJSON__0_23_0) arbitraryKeyLoc = self.sampleKeyLocations signer = Sign_0_23(self.goodkeyid, passphrase=self.goodpassphrase, publicKeyLocations=arbitraryKeyLoc, sign_everything=False, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) signed = signer.sign(origJson) assert "digital_signature" in signed sig = signed["digital_signature"] assert "signature" in sig assert sig["signature"] != None and len(sig["signature"]) > 0 def test_Sign__0_23__no_passthru(self): origJson = json.loads(self.sampleJSON__0_23_0) arbitraryKeyLoc = self.sampleKeyLocations signer = Sign_0_23(self.goodkeyid, passphrase=self.goodpassphrase, publicKeyLocations=arbitraryKeyLoc, sign_everything=True, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) signed = signer.sign(origJson) assert "digital_signature" in signed sig = signed["digital_signature"] assert "signature" in sig assert sig["signature"] != None and len(sig["signature"]) > 0 def test_Sign__0_51__no_passthru(self): origJson = json.loads(self.sampleJSON__0_51_0) arbitraryKeyLoc = self.sampleKeyLocations signer = Sign_0_51(self.goodkeyid, passphrase=self.goodpassphrase, publicKeyLocations=arbitraryKeyLoc, sign_everything=True, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) signed = signer.sign(origJson) assert "digital_signature" in signed sig = signed["digital_signature"] assert "signature" in sig assert sig["signature"] != None and len(sig["signature"]) > 0 def testSignUnicode(self): if self.testDataUnicode == None: log.info("Skipping test, unicode test data file not set.") return import codecs # fileName = "2011-02-28Metadata1004.json" # unsigned = json.load(codecs.open(os.path.join(self.testDataDir, fileName), "r", "utf-8-sig")) unsigned = json.load( codecs.open(self.testDataUnicode, "r", "utf-8-sig")) arbitraryKeyLoc = self.sampleKeyLocations signer = Sign_0_21(self.goodkeyid, passphrase=self.goodpassphrase, publicKeyLocations=arbitraryKeyLoc, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) signed = signer.sign(unsigned) assert "digital_signature" in signed sig = signed["digital_signature"] assert "signature" in sig assert sig["signature"] != None and len(sig["signature"]) > 0 def testSignLRTestData(self): if self.testDataDir == None: log.info("Skipping test, test data directory not set.") return import codecs # allfiles = os.listdir(self.testDataDir) for root, dirs, files in os.walk(self.testDataDir): for fileName in files: log.info("Trying to sign %s" % (fileName, )) unsigned = json.load( codecs.open(os.path.join(root, fileName), "r", "utf-8-sig")) arbitraryKeyLoc = self.sampleKeyLocations signer = Sign_0_21(self.goodkeyid, passphrase=self.goodpassphrase, publicKeyLocations=arbitraryKeyLoc, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin) signed = signer.sign(unsigned) assert "digital_signature" in signed sig = signed["digital_signature"] assert "signature" in sig assert sig["signature"] != None and len(sig["signature"]) > 0
class Test(unittest.TestCase): '''Unit tests for validating signed envelopes from the Learning Registry''' def __init__(self, methodName="runTest"): self.sampleJSON = ''' { "_id":"00e3f67232e743b6bc2a079bd98ff55a", "_rev":"1-8163d32f6cc9996f2b7228d8b5db7962", "doc_type":"resource_data", "update_timestamp":"2011-03-14 13:36:04.617999", "resource_data":"<oai_dc:dc xmlns:oai_dc=\\"http://www.openarchives.org/OAI/2.0/oai_dc/\\" xmlns:dc=\\"http://purl.org/dc/elements/1.1/\\" xmlns:xsi=\\"http://www.w3.org/2001/XMLSchema-instance\\" xmlns=\\"http://www.openarchives.org/OAI/2.0/\\" xsi:schemaLocation=\\"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd\\">\\n<dc:title>A chat about America. October and November, 1884.</dc:title>\\n<dc:creator>J. P.</dc:creator>\\n<dc:subject>United States--Description and travel.</dc:subject>\\n<dc:description>\\"Printed for private circulation only.\\"</dc:description>\\n<dc:description>Electronic reproduction. Washington, D.C. : Library of Congress, [2002-2003]</dc:description>\\n<dc:publisher>Manchester, Palmer & Howe</dc:publisher>\\n<dc:date>1885</dc:date>\\n<dc:type>text</dc:type>\\n<dc:identifier>http://hdl.loc.gov/loc.gdc/lhbtn.12281</dc:identifier>\\n<dc:language>eng</dc:language>\\n<dc:coverage>United States</dc:coverage>\\n</oai_dc:dc>\\n ", "keys":["United States--Description and travel.","eng"], "submitter_type":"agent", "resource_data_type":"metadata", "payload_schema_locator":"http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd", "payload_placement":"inline", "submitter":"NSDL 2 LR Data Pump", "payload_schema":["oai_dc"], "node_timestamp":"2011-03-14 13:36:04.617999", "doc_version":"0.10.0", "create_timestamp":"2011-03-14 13:36:04.617999", "active":true, "publishing_node":"string", "resource_locator":"http://hdl.loc.gov/loc.gdc/lhbtn.12281", "doc_ID":"00e3f67232e743b6bc2a079bd98ff55a", "TOS": { "submission_TOS": "http://example.com/tos/unknown", "submission_attribution": "unidentified" } } ''' self.sampleKeyLocations = [ "http://example.com/mykey", "http://example2.com/mykey" ] self.gpgbin="/usr/local/bin/gpg" self.gnupgHome = os.path.expanduser(os.path.join("~", ".gnupg")) self.gpg = GPG(gpgbinary=self.gpgbin, gnupghome=self.gnupgHome) self.testDataDir = None configFile = os.path.join("config.cfg") if os.path.exists(configFile): config = json.load(file(configFile)) if config.has_key("global"): if config["global"].has_key("testdata") and os.path.exists(config["global"]["testdata"]): self.testDataDir = config["global"]["testdata"] unittest.TestCase.__init__(self, methodName) def setUp(self): now = time.localtime() now = calendar.timegm(now) self.privateEmail = "privateTest-{0}@learningregistry.org".format(now) self.privateEmail2 = "privateTest2-{0}@learningregistry.org".format(now) self.genericPassphrase = "supersecret" input = self.gpg.gen_key_input(name_email=self.privateEmail, passphrase=self.genericPassphrase) self.privateKey = self.gpg.gen_key(input) input = self.gpg.gen_key_input(name_email=self.privateEmail2, passphrase=self.genericPassphrase) self.privateKey2 = self.gpg.gen_key(input) self.privExport = self.gpg.export_keys([self.privateKey.fingerprint, self.privateKey2.fingerprint], secret=True) self.pubExport = self.gpg.export_keys([self.privateKey.fingerprint, self.privateKey2.fingerprint], secret=False) pass def tearDown(self): self.gpg.delete_keys([self.privateKey.fingerprint, self.privateKey2.fingerprint], secret=True) self.gpg.delete_keys([self.privateKey.fingerprint, self.privateKey2.fingerprint], secret=False) pass def testGetSignatureBlock(self): '''Check that signature block validation correctly returns a structurally valid response''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "envelope did not sign correctly" verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) sigInfo = verifytool._getSignatureInfo(signed) assert sigInfo != None, "signature extraction from envelope failed" assert sigInfo.has_key("signing_method") and sigInfo["signing_method"] == verifytool.signatureMethod, "signing_method is missing from signature block" assert sigInfo.has_key("signature") and sigInfo["signature"] != None and len(sigInfo["signature"]) > 0, "signature field is missing, null or is empty" assert sigInfo.has_key("key_location") and sigInfo["key_location"] == self.sampleKeyLocations, "key_location field is not correct" assert sigInfo.has_key("key_owner") and sigInfo["key_owner"] == signtool._get_privatekey_owner(), "key_owner field does not match signing key" def testBadSignatureBlockMissingLocation(self): '''Check that signature block validation correctly checks for missing key_location field''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=None) signed = signtool.sign(unsigned) assert signed != None, "Envelope not signed correctly" verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) with self.assertRaises(errors.BadSignatureFormat, msg="Expected exception not raised.") as caught: sigInfo = verifytool._getSignatureInfo(signed) assert caught.exception.message == errors.MISSING_KEY_LOCATION, "Exception not formatted with correct message" def testBadSignatureBlockMissingSignatureMethod(self): '''Check signature block for missing signature_method detection''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "Envelope not signed correctly." del signed["digital_signature"]["signing_method"] verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) with self.assertRaises(errors.UnsupportedSignatureAlgorithm, msg="Expected exception not raised.") as caught: sigInfo = verifytool._getSignatureInfo(signed) assert caught.exception.alg == None, "Raised exception not formatted correctly" def testBadSignatureBlockBadSignatureMethod(self): '''Check signature block for detecting unsupported algorithm use''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None signed["digital_signature"]["signing_method"] = signtool.signatureMethod+"+BAD_SIGNATURE_METHOD" verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) with self.assertRaises(errors.UnsupportedSignatureAlgorithm, msg="Expected exception not raised.") as caught: sigInfo = verifytool._getSignatureInfo(signed) assert caught.exception.alg == signed["digital_signature"]["signing_method"], "Exception not raised correctly." def testBadSignatureBlockBadKeyOwner(self): '''Check signature block for a bad key_owner field''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "Envelope not signed correctly" signed["digital_signature"]["key_owner"] = ["John Q. Public <*****@*****.**>"] verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) with self.assertRaises(errors.BadSignatureFormat, msg="Expected exception not raised.") as caught: sigInfo = verifytool._getSignatureInfo(signed) assert caught.exception.message == errors.BAD_KEY_OWNER, "Wrong exception" def testBadSignatureBlockMissingNullEmptySignature(self): '''Check signature block validation with missing/null or empty signature''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "Envelope signing did not succeed correctly." verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) signed["digital_signature"]["signature"] = "" with self.assertRaises(errors.BadSignatureFormat, msg="Expected exception not raised.") as caught: sigInfo = verifytool._getSignatureInfo(signed) assert caught.exception.message == errors.MISSING_SIGNATURE, "Wrong exception raised" signed["digital_signature"]["signature"] = None with self.assertRaises(errors.BadSignatureFormat, msg="Expected exception not raised.") as caught: sigInfo = verifytool._getSignatureInfo(signed) assert caught.exception.message == errors.MISSING_SIGNATURE, "Wrong exception raised" del signed["digital_signature"]["signature"] with self.assertRaises(errors.BadSignatureFormat, msg="Expected exception not raised.") as caught: sigInfo = verifytool._getSignatureInfo(signed) assert caught.exception.message == errors.MISSING_SIGNATURE, "Wrong exception raised" def testValidSignature(self): '''Check for valid signature''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "Envelope did not sign correctly" verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) verified = verifytool.verify(signed) assert verified == True, "Envelope signature verification did not succeed, even though it should" def testMissingPublicKey(self): '''Check for appropriate response when public key for signature is missing''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "Envelope did not sign correctly" self.gpg.delete_keys([self.privateKey.fingerprint], secret=True) self.gpg.delete_keys([self.privateKey.fingerprint], secret=False) verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) with self.assertRaises(errors.MissingPublicKey, msg="Expected exception not raised.") as caught: verified = verifytool.verify(signed) assert verified == False, "Envelope verified, despite missing public key." def testCorruptEnvelope(self): '''Modify a signed envelope and check for validity''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "envelope did not get signed correctly" signed["X_corrupted"] = "Corrupted Envelope" verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) verified = verifytool.verify(signed) assert verified == False, "corrupted envelope verified as good" def testWrongSignature(self): '''Test using a mis-matched signature, using a signature from a different valid envelope''' unsigned = json.loads(self.sampleJSON) altered = copy.copy(unsigned) altered["X_corrupted"] = "Altered Envelope" signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signtool2 = Sign_0_21(privateKeyID=self.privateKey2.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) alt_signed = signtool2.sign(altered) assert signed != None, "original did not get signed" assert alt_signed != None, "modified copy did not get signed" verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) verified = verifytool.verify(signed) assert verified == True, "signature did not verify, even though it should." verified = verifytool.verify(alt_signed) assert verified == True, "signature did not verify, even though it should." signed["digital_signature"] = alt_signed["digital_signature"] verified = verifytool.verify(signed) assert verified == False, "swapped signature block validated envelope as good." def testCorruptSignature(self): '''Test using a corrupted signature, replace the hash within a signature with a hash from a different envelope''' unsigned = json.loads(self.sampleJSON) signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) signed = signtool.sign(unsigned) assert signed != None, "baseline signing failed" # validate original with good signature verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) verified = verifytool.verify(signed) assert verified == True, "baseline validation failed" # manipulate the hash portion of a signature block altered = copy.copy(unsigned) altered["X_corrupted"] = "Altered Envelope" altered_hash = signtool.get_message(altered) validHash = verifytool._extractHashFromSignature(signed["digital_signature"]["signature"]) alt_signed = copy.deepcopy(signed) alt_signed["digital_signature"]["signature"] = signed["digital_signature"]["signature"].replace(validHash, altered_hash) assert alt_signed["digital_signature"]["signature"] != signed["digital_signature"]["signature"], "envelopes should not be equal after deliberate modificaton" verified = verifytool.verify(alt_signed) assert verified == False, "verification failed, corrupted signature block verified as good" def testSignLRTestData(self): '''Test using LR Test Data, if available''' if self.testDataDir == None: log.info("Skipping test, test data directory not set.") return import codecs signtool = Sign_0_21(privateKeyID=self.privateKey.fingerprint, passphrase=self.genericPassphrase, gnupgHome=self.gnupgHome, gpgbin=self.gpgbin, publicKeyLocations=self.sampleKeyLocations) verifytool = Verify_0_21(gpgbin=self.gpgbin, gnupgHome=self.gnupgHome) allfiles = os.listdir(self.testDataDir) for fileName in allfiles: log.info("Trying to sign %s" % (fileName, )) unsigned = json.load(codecs.open(os.path.join(self.testDataDir, fileName), "r", "utf-8-sig")) signed = signtool.sign(unsigned) assert signed.has_key("digital_signature"), "missing digital_signature" verified = verifytool.verify(signed) assert verified == True, "baseline validation failed"
def rm_key(key, password): gpg = GPG() gpg.delete_keys(key, True, passphrase=password) gpg.delete_keys(key) system('echo RELOADAGENT | gpg-connect-agent')