def detach_sign_with_pgp_key(file_to_sign_path, pgp_private_key_fingerprint, output_signature_path): """Does a detached sign of a given file using a given users private key. Parameters ---------- file_to_sign_path : str Path to file to create a detached PGP signature for. pgp_private_key_fingerprint : str PGP private key fingerprint to sign the given file with. output_signature_path : str File path to put the detached signature in. Raises ------ RuntimeError If error signing given file with given key. """ try: sh.gpg( # pylint: disable=no-member '--armor', '--local-user', pgp_private_key_fingerprint, '--output', output_signature_path, '--detach-sign', file_to_sign_path ) except sh.ErrorReturnCode as error: raise RuntimeError( f"Error performing detached signature of file ({file_to_sign_path})" \ f" with PGP key ({pgp_private_key_fingerprint}): {error}" ) from error
def install_gpg_key(self): # install private key gpg_private_key_path = os.path.join( os.path.dirname(__file__), 'files', 'ploigos-step-runner-tests-private.asc') sh.gpg( # pylint: disable=no-member '--import', gpg_private_key_path)
def install_gpg_key(self): # install private key gpg_private_key_path = os.path.join( os.path.dirname(__file__), 'files', 'tssc-python-package-tests-private.asc') sh.gpg( # pylint: disable=no-member '--import', gpg_private_key_path)
def delete_gpg_key(self): try: # uninstall private key sh.gpg( # pylint: disable=no-member '--batch', '--pinentry-mode', 'loopback', '--yes', '--delete-secret-keys', SOPSIntegrationTestCase.TESTS_GPG_KEY_FINGERPRINT) except: # don't care if this fails really # could fail cuz the test uninstaleld it already pass
def import_gpg_from_secret_env_var(self: object, env_var: str): gpg_home = helpers.NormalizeSubdir(self.get_gpg_home())() sys.stderr.write("GPG home: {}\n".format(gpg_home)) secret = self.env(env_var) sh.gpg("-v", "--batch", "--import", "-", _in=secret, _out=sys.stdout.buffer, _err=sys.stderr.buffer, _cwd=self.normalized_path)
def init(): key_fingerprint = input("enter your key fingerprint") pub_key = gpg("--export", "--armor", key_fingerprint).stdout return {"public_key": pub_key, "key_fingerprint": key_fingerprint}
def __import_pgp_key( pgp_private_key ): print("Import PGP private key to sign container image(s) with") try: # import the key # NOTE: GPG is weird in that it sends "none error" output to stderr even on success... # so merge the stderr into stdout gpg_import_stdout_result = StringIO() gpg_import_stdout_callback = create_sh_redirect_to_multiple_streams_fn_callback([ sys.stdout, gpg_import_stdout_result ]) sh.gpg( # pylint: disable=no-member '--import', '--fingerprint', '--with-colons', '--import-options=import-show', _in=pgp_private_key, _out=gpg_import_stdout_callback, _err_to_out=True, _tee='out' ) # get the fingerprint of the imported key # # NOTE: if more then one match just using first one... gpg_imported_pgp_private_key_fingerprints = re.findall( PodmanSign.GPG_IMPORT_FINGER_PRINT_REGEX, gpg_import_stdout_result.getvalue() ) if len(gpg_imported_pgp_private_key_fingerprints) < 1: raise RuntimeError( "Unexpected error getting PGP fingerprint for PGP key" " to sign container image(s) with. See stdout and stderr for more info." ) pgp_private_key_fingerprint = gpg_imported_pgp_private_key_fingerprints[0] print( "Imported PGP private key to sign container image(s) with: " f"fingerprint='{pgp_private_key_fingerprint}'" ) except sh.ErrorReturnCode as error: raise RuntimeError(f"Unexpected error importing pgp private key: {error}") from error return pgp_private_key_fingerprint
def lock_key_slot(data, nonce): """encode the nonce with the public key""" data["enc_nonce"] = gpg("--encrypt", "--armor", "--recipient", data["key_fingerprint"], _in=nonce).stdout
def UPLOADPK(msg, address=None, host=None): res={} sender=collapse_rfc2231_value(msg['from']) m=sendere.match(sender) if m: res['sender_name'], res['sender_mail']=m.groups() else: res['sender_mail']=sender for mpart in msg.walk(): ispgp=False part=to_message(mpart) if part.get_content_type()=='text/plain': # cut of preamble inblock=False lines=part.get_payload(decode=True).split('\n') i=0 while i<len(lines): if not inblock: if lines[i].strip()=='-----BEGIN PGP PUBLIC KEY BLOCK-----': inblock=True i+=2 else: if lines[i].strip()=='-----END PGP PUBLIC KEY BLOCK-----': break i+=1 if i<len(lines): ispgp=True elif part.get_content_type()=='application/pgp-keys': ispgp=True if ispgp: res=getpgpmeta(part.get_payload(decode=True)) ret=gpg('--import', _err_to_out=True, _in=part.get_payload(decode=True)) #logging.info(ret) modifiers={'fresh': False, 'abbreved': False, 'singleid': False, 'tidy': False, } if res['datetime']>datetime.datetime.utcnow()-datetime.timedelta(days=10): modifiers['fresh']=True if len(res['ids'])<2: modifiers['singleid']=True if len(res['ids'][0]['email'].split('@')[0])<9: modifiers['abbreved']=True if len([1 for x in res['sigs'] if x['st'] not in ['Positive certification of a User ID and Public Key packet', 'Subkey Binding Signature']])==0: modifiers['tidy']=True res['award']=award("You uploaded your public key.\n%s" % '\n'.join(["%s [%s]" % (k,'X' if v else ' ') for k,v in modifiers.items()])) #logging.info(res) welcome = view.respond(res, "pkuploaded.msg", From=sendermail, To=sender, Subject="Welcome to the Privacy Challenge") view.attach(welcome, {}, "pubkey.asc", filename="my key", content_type="application/pgp-keys") relay.deliver(welcome)
def __verify_sig( self, signature_file_path, artifact_file_path, private_key_fingerprint, ): # GPG_OUTPUT_REGEX = re.compile(r"using RSA key ([A-Za-z0-9]+).*(Good signature)", re.DOTALL) GPG_OUTPUT_REGEX = re.compile(f"using RSA key {private_key_fingerprint}.*Good signature", re.DOTALL) with TempDirectory() as temp_dir: signature_file_path=Path(signature_file_path) try: stdout_result = StringIO() stdout_callback = create_sh_redirect_to_multiple_streams_fn_callback([ sys.stdout, stdout_result ]) sh.gpg( '--verify', signature_file_path, artifact_file_path, _out=stdout_callback, _err_to_out=True, _tee='out' ) verify_matches = re.findall( GPG_OUTPUT_REGEX, stdout_result.getvalue() ) if len(verify_matches) < 1: return False except sh.ErrorReturnCode as error: print( f"Error verifying sig with gpg: {error}" ) return False # if here, then verification successful return True
def export_pgp_public_key(pgp_private_key_fingerprint): """Exports a PGP public key given a private key fingerprint. Parameters ---------- pgp_private_key_fingerprint : str PGP fingerprint. Returns ------- str Public key from the private key fingerprint. Raises ------ RuntimeError If error getting exported PGP public key """ try: gpg_export_stdout_result = StringIO() sh.gpg( # pylint: disable=no-member '--armor', '--export', pgp_private_key_fingerprint, _out=gpg_export_stdout_result, _err_to_out=False, _tee='out' ) gpg_public_key = gpg_export_stdout_result.getvalue() except sh.ErrorReturnCode as error: raise RuntimeError( f"Error exporting pgp public key: {error}" ) from error return gpg_public_key
def test_create_rekor_entry(self): with TempDirectory() as temp_dir: parent_work_dir_path = os.path.join(temp_dir.path, 'working') signer_pgp_public_key_path = os.path.join( os.path.dirname(__file__), '../../helpers', 'files', 'ploigos-step-runner-tests-public.key') try: sh.gpg('--import', signer_pgp_public_key_path) except sh.ErrorReturnCode_2: print("Key already imported.") # Write empty WorkflowResult to json file to use as Rekor extra data extra_data_file = os.path.join(parent_work_dir_path, 'automated-governance', 'automated-governance.json') extra_data_file_path = Path(extra_data_file) WorkflowResult().write_results_to_json_file(extra_data_file_path) sig_file = extra_data_file + '.asc' sig_file_path = Path(sig_file) sig_file_path.touch() result = Rekor.create_rekor_entry( signer_pgp_public_key_path=signer_pgp_public_key_path, signer_pgp_private_key_user= TestStepImplementerAutomatedGovernanceRekor. TEST_signer_pgp_private_key_user, extra_data_file=extra_data_file) self.assertEqual( result['spec']['data']['hash']['value'], TestStepImplementerAutomatedGovernanceRekor. TEST_REKOR_ENTRY['spec']['data']['hash']['value']) self.assertEqual( result['spec']['extraData'], TestStepImplementerAutomatedGovernanceRekor. TEST_REKOR_ENTRY['spec']['extraData'])
def extract_gpg_keyid_from_secret_env_var(self: object, env_var: str): gpg_home = helpers.NormalizeSubdir(self.get_gpg_home())() sys.stderr.write("GPG home: {}\n".format(gpg_home)) secret = self.env(env_var) output = sh.gpg("-v", "--batch", "--import", "--import-options=show-only", "--dry-run", "--with-colons", "-", _in=secret, _cwd=self.normalized_path) for line in output: if line.startswith('sec'): fields = line.split(':') return fields[4]
def doxer(msg, mailid=None, host=None): try: keyid=get_val('dox-mailid',":%s" % mailid, second)[:-(len(mailid)+1)] except TypeError: #print >>sys.stderr, 'nomailid' return # no such mailid pwd=get_state(keyid, 'prod.pdf.pass') #logging.info("pwd "+pwd) if not pwd: add_state(keyid, 'prod.pdf.err',"I got a mail, but i was not aware of the password at that time. try to resend it after telling me the password please.") return err = None for mpart in msg.walk(): part=to_message(mpart) #if part.get_content_maintype() == 'multipart' or len(part.get_payload(decode=True)) < 268125: if part.get_content_maintype() == 'multipart': #print >>sys.stderr, 'skip', len(part.get_payload(decode=True) or ''), part.get_content_maintype() continue size = len(part.get_payload(decode=True)) if (size < 200000 or size > 310000): continue hash=hashlib.sha256() def hupdate(data): # workaround for http://bugs.python.org/issue17481 hash.update(data) ret=gpg('-d', '--passphrase', pwd, _ok_code=[0,2], _in=part.get_payload(decode=True), _out=hupdate) if ret.exit_code!=0: add_state(keyid, 'prod.pdf.err',"got a mail, but gpg had problems, try fixing the problem and resend the mail. gpg said this\n"+err) break #logging.info('ret '+str(ret)) #logging.info('stderr '+ret.stderr) err=str(ret.stderr) # for flushing the process? #print >>sys.stderr, 'err', err if hash.hexdigest() == '658be96015645fe1d646fd167c1ac3bd372360530191d574ace5870c5aeb132f': add_state(keyid, 'prod.pdf.done','1') break else: add_state(keyid, 'prod.pdf.err',"got a mail, but it wasn't quite what i expected, so i dropped it.") break else: add_state(keyid, 'prod.pdf.err',"got a mail, but there was nothing found that looked like a reasonably sized pgp payload")
def import_pgp_key(pgp_private_key): """Imports a PGP key. Parameters ---------- pgp_private_key : str PGP key to import. Returns ------- str Fingerprint of the imported PGP key. Raises ------ RuntimeError If error getting PGP fingerprint for imported PGP key If error importing PGP key. """ # Example input to match on: # sec:-:3072:1:CF4AC14A3D109637:1601483310:1664555310::-:::scESC::::::23::0: # fpr:::::::::DD7208BA0A6359F65B906B29CF4AC14A3D109637: # grp:::::::::A483EE079EC1D58A954E3AAF3BCC61EDD7596BF0: gpg_regex = re.compile(r"^fpr:+([^:]+):$", re.MULTILINE) print("Import PGP private key to sign artifacts with") try: # import the key # NOTE: GPG is weird in that it sends "none error" output to stderr even on success... # so merge the stderr into stdout gpg_import_stdout_result = StringIO() gpg_import_stdout_callback = create_sh_redirect_to_multiple_streams_fn_callback([ sys.stdout, gpg_import_stdout_result ]) sh.gpg( # pylint: disable=no-member '--import', '--fingerprint', '--with-colons', '--import-options=import-show', _in=pgp_private_key, _out=gpg_import_stdout_callback, _err_to_out=True, _tee='out' ) # get the fingerprint of the imported key # # NOTE: if more then one match just using first one... gpg_imported_pgp_private_key_fingerprints = re.findall( gpg_regex, gpg_import_stdout_result.getvalue() ) if len(gpg_imported_pgp_private_key_fingerprints) < 1: raise RuntimeError( "Error getting PGP fingerprint for PGP key" " to sign container image(s) with. See stdout and stderr for more info." ) pgp_private_key_fingerprint = gpg_imported_pgp_private_key_fingerprints[0] print( "Imported PGP private key to sign artifacts with: " f"fingerprint='{pgp_private_key_fingerprint}'" ) except sh.ErrorReturnCode as error: raise RuntimeError( f"Error importing pgp private key: {error}" ) from error return pgp_private_key_fingerprint
def award(text): return gpg('--clearsign', '--armor', '--default-key', sendermail, _in="Achievement unlocked %s\n%s" % (text, datetime.datetime.utcnow().isoformat()))
def otrfp(msg, address=None, host=None): sender=collapse_rfc2231_value(msg['from']) m=sendere.match(sender) res={} if m: res['sender_name'], res['sender_mail']=m.groups() else: res['sender_mail']=sender for mpart in msg.walk(): part=to_message(mpart) # cut of preamble inblock=False lines=part.get_payload(decode=True).split('\n') i=0 #logging.info(lines) while i<len(lines): if not inblock: if lines[i].strip()=='-----BEGIN PGP SIGNED MESSAGE-----' or lines[i].strip()=='-----BEGIN PGP MESSAGE-----': inblock=True i+=2 else: if lines[i].strip()=='-----END PGP SIGNATURE-----' or lines[i].strip()=='-----END PGP MESSAGE-----': break i+=1 #logging.info(i) if i<len(lines): res.update(getpgpmeta(part.get_payload(decode=True))) ret=gpg('-d', _ok_code=[0,2], _in=part.get_payload(decode=True)) #logging.info('ret '+str(ret)) #logging.info('stderr '+ret.stderr) res['msg']='\n'.join(["> %s" % x for x in ret.stdout.split('\n')]) # extra points, # - no named recipient # - signed #logging.info(res['keys']) modifiers={'sekrit': False, 'signed': False} if len([x for x in res['keys'] if x['key_id']!="0000000000000000"])==0: modifiers['sekrit']=True else: logging.warn([x for x in res['keys'] if x['key_id']!="0000000000000000"]) signed={} for line in ret.stderr.split('\n'): if line.startswith('gpg: Signature made '): # gpg: Signature made Fri 11 May 2012 04:43:04 PM CEST using RSA key ID XXXXXX m=signed1re.match(line) if m: #logging.info(m.groups()) signed['date']=dparse(str(m.group(1))) signed['algo']=m.group(2) signed['key_id']=m.group(3) elif line.startswith('gpg: Good signature from '): # gpg: Good signature from "name <mail>" m=signed2re.match(line) if m: #logging.info(m.groups()) signed['name']=m.group(1) signed['mail']=m.group(2) modifiers['signed']=True if not signed: plssign = view.respond(res, "plssign.msg", From=sendermail, To=sender, Subject="OTR fingerprint help") relay.deliver(plssign) continue res['signed']=signed res['award']=award("you bootstrapped OTR trust using PGP.\n%s" % '\n'.join(["%s [%s]" % (k,'X' if v else ' ') for k,v in modifiers.items()])) #logging.info(res) jid=None fp=None secret=None for line in to_message(from_string(ret.stdout)).get_payload(decode=True).split('\n'): if not line.strip(): continue if line=='-- ': break if jid and fp: secret=line break #logging.info("line "+line) m=otrfpre.match(line) if m: #logging.info(m.groups()) jid, fp = m.group(1), m.group(2) if jid and fp: with FileLock('%s/otr/otr/%s.fpr' % (basepath, botjid)): fr=open('%s/otr/otr/%s.fpr' % (basepath, botjid), 'r') fw=open('%s/otr/otr/%s.fpr.new' % (basepath, botjid), 'w') for line in fr: #logging.info(line) #logging.info("%s\t%s\tjabber\t%s" % (jid, # botjid, # fp.lower().replace(' ',''))) if line.startswith("%s\t%s\tjabber\t%s" % (jid, botjid, fp.lower().replace(' ',''))): fw.write("%s\t%s\tjabber\t%s\ttrust\n" % (jid, botjid, fp.lower().replace(' ',''))) else: fw.write(line) fw.close() fr.close() os.unlink('%s/otr/otr/%s.fpr' % (basepath, botjid)) shutil.move('%s/otr/otr/%s.fpr.new' % (basepath, botjid), '%s/otr/otr/%s.fpr' % (basepath, botjid)) if secret: fs=open('%s/otr/otr/%s.s' % (basepath, jid), 'w') fs.write("%s %s" % (signed['key_id'], secret)) fs.close() welcome = view.respond(res, "otrtrust.msg", From=sendermail, To=sender, Subject="OTR fingerprint received") relay.deliver(welcome)
def get_gpg_home(self: object): gpg_help = str(sh.gpg("--help", _tty_out=False)) match = self.gpg_home_regex.search(gpg_help) return match.group(1)
def init_auth(data): pub_key = gpg("--export", "--armor", data["key_fingerprint"]).stdout data["public_key"] = pub_key
def unlock_key_slot(data, unlock_data): return gpg("--decrypt", _in=data["enc_nonce"]).stdout
def DECODER(msg, address=None, host=None): sender=collapse_rfc2231_value(msg['from']) m=sendere.match(sender) res={} if m: res['sender_name'], res['sender_mail']=m.groups() else: res['sender_mail']=sender for mpart in msg.walk(): part=to_message(mpart) # cut of preamble inblock=False lines=part.get_payload(decode=True).split('\n') i=0 #logging.info(lines) while i<len(lines): if not inblock: if lines[i].strip()=='-----BEGIN PGP MESSAGE-----': inblock=True i+=2 else: if lines[i].strip()=='-----END PGP MESSAGE-----': break i+=1 #logging.info(i) if i<len(lines): res.update(getpgpmeta(part.get_payload(decode=True))) ret=gpg('-d', _ok_code=[0,2], _in=part.get_payload(decode=True)) #logging.info('ret '+str(ret)) #logging.info('stderr '+ret.stderr) res['msg']='\n'.join(["> %s" % x for x in ret.stdout.split('\n')]) # extra points, # - no named recipient # - signed modifiers={'sekrit': False, 'signed': False} #logging.info(res['keys']) if len([x for x in res['keys'] if x['key_id']!="0000000000000000"])==0: modifiers['sekrit']=True signed={} for line in ret.stderr.split('\n'): if line.startswith('gpg: Signature made '): # gpg: Signature made Fri 11 May 2012 04:43:04 PM CEST using RSA key ID XXXXXX m=signed1re.match(line) if m: #logging.info(m.groups()) signed['date']=dparse(str(m.group(1))) signed['algo']=m.group(2) signed['key_id']=m.group(3) elif line.startswith('gpg: Good signature from '): # gpg: Good signature from "name <mail>" m=signed2re.match(line) if m: #logging.info(m.groups()) signed['name']=m.group(1) signed['mail']=m.group(2) modifiers['signed']=True if signed: res['signed']=signed res['award']=award("You sent an encrypted mail.\n%s" % '\n'.join(["%s [%s]" % (k,'X' if v else ' ') for k,v in modifiers.items()])) #logging.info(res) welcome = view.respond(res, "pgpmail.msg", From=sendermail, To=sender, Subject="Encrypted mail received") relay.deliver(welcome)
def unlock_key_slot(data, enc_nonce): return gpg("--decrypt", _in=enc_nonce).stdout
def verify_sig(self, path, sig): try: gpg("--batch", "--no-tty", "--verify", sig, path) return True except: return False