def __init__(self, config, secrets): try: dataKeyBytes = config.get("key", None) except AttributeError as e: raise InvalidConfig(original=e) if not dataKeyBytes or dataKeyBytes == "": msg = ("crypto.key is not a string, " "Remove the crypto property if encryption is not needed") raise InvalidConfig(message=msg) privateKey = secrets.get("privateKey", None) if not privateKey or privateKey == "": msg = ("No gpg private key provided for decryption. " "Remove the crypto property if encryption is not needed") raise InvalidConfig(message=msg) gpgKey = PGPKey() gpgKey.parse(privateKey) password = secrets.get("privateKeyPassword", None) if password: try: gpgKey.unlock(password) except PGPDecryptionError as err: raise BadGPGKeyPasswordError(gpgKey.userids[0]) with warnings.catch_warnings(): # prevents warning of type `UserWarning: Message was encrypted with this key's subkey: ...` warnings.simplefilter("ignore", category=UserWarning) dataKey = gpgKey.decrypt( PGPMessage.from_blob(dataKeyBytes)).message DataKeyService.__init__(self, dataKey)
def test_parse_wrong_crc24(self, recwarn): keytext = _read('tests/testdata/keys/rsa.1.sec.asc').splitlines() keytext[-2] = "=abcd" keytext = '\n'.join(keytext) key = PGPKey() key.parse(keytext) w = recwarn.pop(UserWarning) assert str(w.message) == "Incorrect crc24" assert w.filename == __file__
def _nosecrete_private_key(pubkey): """ Create the parent certify key without the private parts """ km = OpaquePrivKey() km.parse(pubkey.keymaterial.__bytearray__()) # S2K GNU extension: private key without secrete parts km.s2k.parse(bytearray(b'\xff\x00\x65\x00GNU\x01')) # wrap key material in PrivKeyV4 containing info about algo, creation timestamp, ... privkey = PrivKeyV4() privkey.pkalg = pubkey.pkalg privkey.keymaterial = km privkey.created = pubkey.created privkey.update_hlen() # wrap private key in a PGPKey object key = PGPKey() key._key = privkey return key
def test_empty_key_action(self): key = PGPKey() with pytest.raises(PGPError): key.sign('asdf')
def test_parse_wrong_magic(self): keytext = _read('tests/testdata/keys/rsa.1.sec.asc').replace( 'KEY', 'EKY') key = PGPKey() with pytest.raises(ValueError): key.parse(keytext)
def parse_key(key_str): key = PGPKey() key.parse(key_str) return key
def test_reg_bug_56(): # some imports only used by this regression test import hashlib from datetime import datetime from pgpy.pgp import PGPSignature from pgpy.constants import HashAlgorithm from pgpy.constants import PubKeyAlgorithm from pgpy.constants import SignatureType from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import padding # do a regression test on issue #56 # re-create a signature that would have been encoded improperly as with issue #56 # and see if it fails to verify or not # this was the old seckeys/TestRSA-2048.key sec = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" \ "\n" \ "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\n" \ "iz54ZFyNdQ==\n" \ "=WLpc\n" \ "-----END PGP PRIVATE KEY BLOCK-----\n" pub = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" \ "\n" \ "mQENBFOaNYoBCAC9FDOrASOxJoo3JhwCCln4wPy+A/UY1H0OYlc+MMolSwev2uDj\n" \ "nnwmt8ziNTjLuLEh3AnKjDWA9xvY5NW2ZkB6Edo6HQkquKLH7AkpLd82kiTWHdOw\n" \ "OH7OWQpz7z2z6e40wwHEduhyGcZ/Ja/A0+6GIb/2YFKlkwfnT92jtB94W//mL6wu\n" \ "LOkZMoU/CS/QatervzAf9VCemvAR9NI0UJc7Y0RC1B/1cBTQAUg70EhjnmJkqyYx\n" \ "yWqaXyfX10dsEX3+MyiP1kvUDfFwhdeL7E2H9sbFE5+MC9Eo99/Qezv3QoXzH2Tj\n" \ "bTun+QMVkbM92dj70KiExAJya9lSLZGCoOrDABEBAAG0M1Rlc3RSU0EtMjA0OCAo\n" \ "VEVTVElORy1VU0UtT05MWSkgPGVtYWlsQGFkZHJlc3MudGxkPokBNwQTAQoAIQUC\n" \ "U5o1igIbIwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRDA8iEODxk9zYQPB/4+\n" \ "kZlIwLE27J7IiZSkk+4T5CPrASxoSsRMadUvoHc0eiZIlQD2Gu05oQcm4kZojJAz\n" \ "Mv12rLtk+ZPwVOZU/TUxPYwuEyJP4keFJEW9P0GiURAvYQRQCbQ5IOlIkZ0tPotb\n" \ "010Ej3u5rHAiVCvh/cxF16UhkXknf/wgDDWErfGIMaaruAIr0G05p4Q2G/NLgBcc\n" \ "owSgFFfWprg3zfNPEQhH/qNs8O5mByniMZk4n2TsKGlX6eT9RrfJVQhSLoQXxYik\n" \ "MtiZTki4yPUhTQev62KWHQcY6zNV2p9VQ24NUhVCIBnZ0CLkm38QFsS5flWVGat5\n" \ "kraHTXxvffz7yGHJiFkiuQENBFOaNYoBCADBPjB83l1O2m/Nr5KDm6/BwKfrRsoJ\n" \ "DmMZ8nNHNUc/zK4RI4EFKkr35PSmgbA8yOlaSDWVz9zuKyOtb8Nohct2/lrac8zI\n" \ "+b4enZ/Z6qehoAdY1t4QYmA2PebKuerBXjIF1RWsPQDpu3GIZw4oBbdu5oUGB4I9\n" \ "yIepindM2b2I9dlY3ct4uhRbBmXPFcslmJ1K4pCurXvr4Po4DCcWqUmsGUQQbI1G\n" \ "UyAzSad7u9y3CRqhHFwzyFRRfl+/mgB2a6XvbGlG5Dkp1g7T/HIVJu+zv58AQkFw\n" \ "+ABuWNKCXa3TB51bkiBQlkRTSAu2tVZ8hVGZE+wUw0o9rLiy6mldFvbLABEBAAGJ\n" \ "AR8EGAEKAAkFAlOaNYoCGwwACgkQwPIhDg8ZPc1uDwf/SGoiZHjUsTWm4gZgZCzA\n" \ "jOpZs7dKjLL8Wm5G3HTFIGX0O8HCzQJARWq05N6EYmI4nPXxu08ba30SubybSeFU\n" \ "+iAPymqm2YNXrE2RwLWko78M0r9enUep6SvbGKnukPG7lz/33PsxIVyATfMmcmzV\n" \ "4chyC7pICTwgHv/zC3S/k7GoS82Z39LO4R4aDa4aubNq6mx4eHUd0MSnYud1IzRx\n" \ "D8cPxh9fCdoW0OpddqKNczAvO4bl5wwDafrEa7HpIX/sMVMZXo2h6TkitdLCdEfk\n" \ "tgEjS0hTsFtfwsXt9TKi1x3HJIbcm8t78ubpWXepB/iNKVzv4punFHhKiz54ZFyN\n" \ "dQ==\n" \ "=lqIH\n" \ "-----END PGP PUBLIC KEY BLOCK-----\n" # load the keypair above sk = PGPKey() sk.parse(sec) pk = PGPKey() pk.parse(pub) sigsubject = bytearray(b"Hello!I'm a test document.I'm going to get signed a bunch of times.KBYE!") sig = PGPSignature.new(SignatureType.BinaryDocument, PubKeyAlgorithm.RSAEncryptOrSign, HashAlgorithm.SHA512, sk.fingerprint.keyid) sig._signature.subpackets['h_CreationTime'][-1].created = datetime(2014, 8, 6, 23, 28, 51) sig._signature.subpackets.update_hlen() hdata = sig.hashdata(sigsubject) sig._signature.hash2 = hashlib.new('sha512', hdata).digest()[:2] # create the signature signer = sk.__key__.__privkey__().signer(padding.PKCS1v15(), hashes.SHA512()) signer.update(hdata) sig._signature.signature.from_signer(signer.finalize()) sig._signature.update_hlen() # check encoding assert sig._signature.signature.md_mod_n.to_mpibytes()[2:3] != b'\x00' # with PGPy assert pk.verify(sigsubject, sig) # with GnuPG with gpg.Context(armor=True, offline=True) as c: c.set_engine_info(gpg.constants.PROTOCOL_OpenPGP, home_dir=gnupghome) # import the key key_data = gpg.Data(string=pub) gpg.core.gpgme.gpgme_op_import(c.wrapped, key_data) _, vres = c.verify(gpg.Data(string=sigsubject.decode('latin-1')), gpg.Data(string=str(sig))) assert vres
def _create_private_key(self, pubkey, subkey=True): """ Create a private (sub)key from public key and firmware key material """ fp = bytes(pubkey.fingerprint) if fp not in self._keys: return None key = self._keys[fp] pubkm = pubkey._key.keymaterial if key.algo == KeyAlgo.RSA2048 or key.algo == KeyAlgo.RSA4096: assert pubkey._key.pkalg in [PubKeyAlgorithm.RSAEncryptOrSign, PubKeyAlgorithm.RSAEncrypt, PubKeyAlgorithm.RSASign] plen = len(key.priv) // 2 km = RSAPriv() km.n = pubkm.n km.e = pubkm.e km.p = MPI(MPIs.bytes_to_int(key.priv[:plen])) km.q = MPI(MPIs.bytes_to_int(key.priv[plen:])) km.d = MPI(rsa.rsa_crt_iqmp((km.p-1) * (km.q-1), km.e)) km.u = MPI(rsa.rsa_crt_iqmp(km.q, km.p)) elif key.algo == KeyAlgo.CURVE25519: km = ECDHPriv() km.oid = EllipticCurveOID.Curve25519 km.p = pubkm.p km.kdf = pubkm.kdf # GnuPG stores the secret value in the wrong order # https://lists.gnupg.org/pipermail/gnupg-devel/2018-February/033437.html km.s = MPI(MPIs.bytes_to_int(key.priv, 'little')) elif key.algo == KeyAlgo.ED25519: # GNUK stores "SHA-512(secret)" # PGP private key stores "secret" logging.warning("Cannot extract Ed25519 key: GNUK format incompatible with PGP format\n" " sha512(secret) = {}".format(hexlify(key.priv).decode('utf-8'))) return None elif key.algo in [KeyAlgo.SECP256K1, KeyAlgo.NISTP256R1]: # Secp256k1 can be used with both ECDH and ECDSA if isinstance(pubkm, ECDSAPub): km = ECDSAPriv() elif isinstance(pubkm, ECDHPub): km = ECDHPriv() km.kdf = pubkm.kdf else: raise Exception("Curve {} cannot be used with algorithm {}".format( str(key.algo), pubkm.__class__.__name__)) if key.algo == KeyAlgo.SECP256K1: km.oid = EllipticCurveOID.SECP256K1 elif key.algo == KeyAlgo.NISTP256R1: km.oid = EllipticCurveOID.NIST_P256 else: raise Exception("Should not happen") km.p = pubkm.p km.s = MPI(MPIs.bytes_to_int(key.priv)) else: # https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-06 raise Exception("Key algo {} not implemented yet".format(str(key.algo))) km._compute_chksum() privkey = PrivSubKeyV4() if subkey else PrivKeyV4() privkey.pkalg = pubkey._key.pkalg privkey.keymaterial = km privkey.created = pubkey._key.created privkey.update_hlen() key = PGPKey() key._key = privkey key._signatures = pubkey._signatures return key
try: rsa_priv = RSAPriv() rsa_priv.e = MPI(int(gpg['e'], 16)) rsa_priv.n = MPI(int(gpg['n'], 16)) rsa_priv.d = MPI(int(gpg['d'], 16)) rsa_priv.p = MPI(int(gpg['p'], 16)) rsa_priv.q = MPI(int(gpg['q'], 16)) rsa_priv.u = MPI(int(gpg['u'], 16)) rsa_priv._compute_chksum() restored_priv_key = PrivKeyV4() restored_priv_key.pkalg = PubKeyAlgorithm.RSAEncryptOrSign restored_priv_key.keymaterial = rsa_priv restored_priv_key.update_hlen() pgp_key = PGPKey() pgp_key._key = restored_priv_key public_key, _ = PGPKey.from_blob(gpg['public']) # fingerprint contains cration date so we need explicit copy this one pgp_key._key.created = public_key._key.created pgp_key.add_uid(public_key.userids[0], usage={ KeyFlags.Sign, KeyFlags.EncryptCommunications, KeyFlags.EncryptStorage }, hashes=[ HashAlgorithm.SHA256, HashAlgorithm.SHA384, HashAlgorithm.SHA512, HashAlgorithm.SHA224