configs/arch-config/.config/calibre/plugins/DeACSM/modules/oscrypto/oscrypto/asymmetric.py

459 lines
13 KiB
Python

# coding: utf-8
from __future__ import unicode_literals, division, absolute_import, print_function
import hashlib
import binascii
from . import backend
from ._asn1 import (
armor,
Certificate as Asn1Certificate,
DHParameters,
EncryptedPrivateKeyInfo,
Null,
OrderedDict,
Pbkdf2Salt,
PrivateKeyInfo,
PublicKeyInfo,
)
from ._asymmetric import _unwrap_private_key_info
from ._errors import pretty_message
from ._types import type_name, str_cls
from .kdf import pbkdf2, pbkdf2_iteration_calculator
from .symmetric import aes_cbc_pkcs7_encrypt
from .util import rand_bytes
_backend = backend()
if _backend == 'mac':
from ._mac.asymmetric import (
Certificate,
dsa_sign,
dsa_verify,
ecdsa_sign,
ecdsa_verify,
generate_pair,
generate_dh_parameters,
load_certificate,
load_pkcs12,
load_private_key,
load_public_key,
PrivateKey,
PublicKey,
rsa_pkcs1v15_sign,
rsa_pkcs1v15_verify,
rsa_pss_sign,
rsa_pss_verify,
rsa_pkcs1v15_encrypt,
rsa_pkcs1v15_decrypt,
rsa_oaep_encrypt,
rsa_oaep_decrypt,
)
elif _backend == 'win' or _backend == 'winlegacy':
from ._win.asymmetric import (
Certificate,
dsa_sign,
dsa_verify,
ecdsa_sign,
ecdsa_verify,
generate_pair,
generate_dh_parameters,
load_certificate,
load_pkcs12,
load_private_key,
load_public_key,
PrivateKey,
PublicKey,
rsa_pkcs1v15_sign,
rsa_pkcs1v15_verify,
rsa_pss_sign,
rsa_pss_verify,
rsa_pkcs1v15_encrypt,
rsa_pkcs1v15_decrypt,
rsa_oaep_encrypt,
rsa_oaep_decrypt,
)
else:
from ._openssl.asymmetric import (
Certificate,
dsa_sign,
dsa_verify,
ecdsa_sign,
ecdsa_verify,
generate_pair,
generate_dh_parameters,
load_certificate,
load_pkcs12,
load_private_key,
load_public_key,
PrivateKey,
PublicKey,
rsa_pkcs1v15_sign,
rsa_pkcs1v15_verify,
rsa_pss_sign,
rsa_pss_verify,
rsa_pkcs1v15_encrypt,
rsa_pkcs1v15_decrypt,
rsa_oaep_encrypt,
rsa_oaep_decrypt,
)
__all__ = [
'Certificate',
'dsa_sign',
'dsa_verify',
'dump_certificate',
'dump_dh_parameters',
'dump_openssl_private_key',
'dump_private_key',
'dump_public_key',
'ecdsa_sign',
'ecdsa_verify',
'generate_pair',
'generate_dh_parameters',
'load_certificate',
'load_pkcs12',
'load_private_key',
'load_public_key',
'PrivateKey',
'PublicKey',
'rsa_oaep_decrypt',
'rsa_oaep_encrypt',
'rsa_pkcs1v15_decrypt',
'rsa_pkcs1v15_encrypt',
'rsa_pkcs1v15_sign',
'rsa_pkcs1v15_verify',
'rsa_pss_sign',
'rsa_pss_verify',
]
def dump_dh_parameters(dh_parameters, encoding='pem'):
"""
Serializes an asn1crypto.algos.DHParameters object into a byte string
:param dh_parameters:
An asn1crypto.algos.DHParameters object
:param encoding:
A unicode string of "pem" or "der"
:return:
A byte string of the encoded DH parameters
"""
if encoding not in set(['pem', 'der']):
raise ValueError(pretty_message(
'''
encoding must be one of "pem", "der", not %s
''',
repr(encoding)
))
if not isinstance(dh_parameters, DHParameters):
raise TypeError(pretty_message(
'''
dh_parameters must be an instance of asn1crypto.algos.DHParameters,
not %s
''',
type_name(dh_parameters)
))
output = dh_parameters.dump()
if encoding == 'pem':
output = armor('DH PARAMETERS', output)
return output
def dump_public_key(public_key, encoding='pem'):
"""
Serializes a public key object into a byte string
:param public_key:
An oscrypto.asymmetric.PublicKey or asn1crypto.keys.PublicKeyInfo object
:param encoding:
A unicode string of "pem" or "der"
:return:
A byte string of the encoded public key
"""
if encoding not in set(['pem', 'der']):
raise ValueError(pretty_message(
'''
encoding must be one of "pem", "der", not %s
''',
repr(encoding)
))
is_oscrypto = isinstance(public_key, PublicKey)
if not isinstance(public_key, PublicKeyInfo) and not is_oscrypto:
raise TypeError(pretty_message(
'''
public_key must be an instance of oscrypto.asymmetric.PublicKey or
asn1crypto.keys.PublicKeyInfo, not %s
''',
type_name(public_key)
))
if is_oscrypto:
public_key = public_key.asn1
output = public_key.dump()
if encoding == 'pem':
output = armor('PUBLIC KEY', output)
return output
def dump_certificate(certificate, encoding='pem'):
"""
Serializes a certificate object into a byte string
:param certificate:
An oscrypto.asymmetric.Certificate or asn1crypto.x509.Certificate object
:param encoding:
A unicode string of "pem" or "der"
:return:
A byte string of the encoded certificate
"""
if encoding not in set(['pem', 'der']):
raise ValueError(pretty_message(
'''
encoding must be one of "pem", "der", not %s
''',
repr(encoding)
))
is_oscrypto = isinstance(certificate, Certificate)
if not isinstance(certificate, Asn1Certificate) and not is_oscrypto:
raise TypeError(pretty_message(
'''
certificate must be an instance of oscrypto.asymmetric.Certificate
or asn1crypto.x509.Certificate, not %s
''',
type_name(certificate)
))
if is_oscrypto:
certificate = certificate.asn1
output = certificate.dump()
if encoding == 'pem':
output = armor('CERTIFICATE', output)
return output
def dump_private_key(private_key, passphrase, encoding='pem', target_ms=200):
"""
Serializes a private key object into a byte string of the PKCS#8 format
:param private_key:
An oscrypto.asymmetric.PrivateKey or asn1crypto.keys.PrivateKeyInfo
object
:param passphrase:
A unicode string of the passphrase to encrypt the private key with.
A passphrase of None will result in no encryption. A blank string will
result in a ValueError to help ensure that the lack of passphrase is
intentional.
:param encoding:
A unicode string of "pem" or "der"
:param target_ms:
Use PBKDF2 with the number of iterations that takes about this many
milliseconds on the current machine.
:raises:
ValueError - when a blank string is provided for the passphrase
:return:
A byte string of the encoded and encrypted public key
"""
if encoding not in set(['pem', 'der']):
raise ValueError(pretty_message(
'''
encoding must be one of "pem", "der", not %s
''',
repr(encoding)
))
if passphrase is not None:
if not isinstance(passphrase, str_cls):
raise TypeError(pretty_message(
'''
passphrase must be a unicode string, not %s
''',
type_name(passphrase)
))
if passphrase == '':
raise ValueError(pretty_message(
'''
passphrase may not be a blank string - pass None to disable
encryption
'''
))
is_oscrypto = isinstance(private_key, PrivateKey)
if not isinstance(private_key, PrivateKeyInfo) and not is_oscrypto:
raise TypeError(pretty_message(
'''
private_key must be an instance of oscrypto.asymmetric.PrivateKey
or asn1crypto.keys.PrivateKeyInfo, not %s
''',
type_name(private_key)
))
if is_oscrypto:
private_key = private_key.asn1
output = private_key.dump()
if passphrase is not None:
cipher = 'aes256_cbc'
key_length = 32
kdf_hmac = 'sha256'
kdf_salt = rand_bytes(key_length)
iterations = pbkdf2_iteration_calculator(kdf_hmac, key_length, target_ms=target_ms, quiet=True)
# Need a bare minimum of 10,000 iterations for PBKDF2 as of 2015
if iterations < 10000:
iterations = 10000
passphrase_bytes = passphrase.encode('utf-8')
key = pbkdf2(kdf_hmac, passphrase_bytes, kdf_salt, iterations, key_length)
iv, ciphertext = aes_cbc_pkcs7_encrypt(key, output, None)
output = EncryptedPrivateKeyInfo({
'encryption_algorithm': {
'algorithm': 'pbes2',
'parameters': {
'key_derivation_func': {
'algorithm': 'pbkdf2',
'parameters': {
'salt': Pbkdf2Salt(
name='specified',
value=kdf_salt
),
'iteration_count': iterations,
'prf': {
'algorithm': kdf_hmac,
'parameters': Null()
}
}
},
'encryption_scheme': {
'algorithm': cipher,
'parameters': iv
}
}
},
'encrypted_data': ciphertext
}).dump()
if encoding == 'pem':
if passphrase is None:
object_type = 'PRIVATE KEY'
else:
object_type = 'ENCRYPTED PRIVATE KEY'
output = armor(object_type, output)
return output
def dump_openssl_private_key(private_key, passphrase):
"""
Serializes a private key object into a byte string of the PEM formats used
by OpenSSL. The format chosen will depend on the type of private key - RSA,
DSA or EC.
Do not use this method unless you really must interact with a system that
does not support PKCS#8 private keys. The encryption provided by PKCS#8 is
far superior to the OpenSSL formats. This is due to the fact that the
OpenSSL formats don't stretch the passphrase, making it very easy to
brute-force.
:param private_key:
An oscrypto.asymmetric.PrivateKey or asn1crypto.keys.PrivateKeyInfo
object
:param passphrase:
A unicode string of the passphrase to encrypt the private key with.
A passphrase of None will result in no encryption. A blank string will
result in a ValueError to help ensure that the lack of passphrase is
intentional.
:raises:
ValueError - when a blank string is provided for the passphrase
:return:
A byte string of the encoded and encrypted public key
"""
if passphrase is not None:
if not isinstance(passphrase, str_cls):
raise TypeError(pretty_message(
'''
passphrase must be a unicode string, not %s
''',
type_name(passphrase)
))
if passphrase == '':
raise ValueError(pretty_message(
'''
passphrase may not be a blank string - pass None to disable
encryption
'''
))
is_oscrypto = isinstance(private_key, PrivateKey)
if not isinstance(private_key, PrivateKeyInfo) and not is_oscrypto:
raise TypeError(pretty_message(
'''
private_key must be an instance of oscrypto.asymmetric.PrivateKey or
asn1crypto.keys.PrivateKeyInfo, not %s
''',
type_name(private_key)
))
if is_oscrypto:
private_key = private_key.asn1
output = _unwrap_private_key_info(private_key).dump()
headers = None
if passphrase is not None:
iv = rand_bytes(16)
headers = OrderedDict()
headers['Proc-Type'] = '4,ENCRYPTED'
headers['DEK-Info'] = 'AES-128-CBC,%s' % binascii.hexlify(iv).decode('ascii')
key_length = 16
passphrase_bytes = passphrase.encode('utf-8')
key = hashlib.md5(passphrase_bytes + iv[0:8]).digest()
while key_length > len(key):
key += hashlib.md5(key + passphrase_bytes + iv[0:8]).digest()
key = key[0:key_length]
iv, output = aes_cbc_pkcs7_encrypt(key, output, iv)
if private_key.algorithm == 'ec':
object_type = 'EC PRIVATE KEY'
elif private_key.algorithm == 'rsa':
object_type = 'RSA PRIVATE KEY'
elif private_key.algorithm == 'dsa':
object_type = 'DSA PRIVATE KEY'
return armor(object_type, output, headers=headers)