Open Media Library Platform

This commit is contained in:
j 2013-10-11 19:28:32 +02:00
commit 411ad5b16f
5849 changed files with 1778641 additions and 0 deletions

View file

@ -0,0 +1,14 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function

View file

@ -0,0 +1,14 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function

View file

@ -0,0 +1,183 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
import six
from cryptography import utils
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.backends.interfaces import DSABackend
from cryptography.hazmat.primitives import interfaces
def _check_dsa_parameters(modulus, subgroup_order, generator):
if (
not isinstance(modulus, six.integer_types) or
not isinstance(subgroup_order, six.integer_types) or
not isinstance(generator, six.integer_types)
):
raise TypeError("DSA parameters must be integers")
if (utils.bit_length(modulus),
utils.bit_length(subgroup_order)) not in (
(1024, 160),
(2048, 256),
(3072, 256)):
raise ValueError("modulus and subgroup_order lengths must be "
"one of these pairs (1024, 160) or (2048, 256) "
"or (3072, 256)")
if generator <= 1 or generator >= modulus:
raise ValueError("generator must be > 1 and < modulus")
@utils.register_interface(interfaces.DSAParameters)
class DSAParameters(object):
def __init__(self, modulus, subgroup_order, generator):
_check_dsa_parameters(modulus, subgroup_order, generator)
self._modulus = modulus
self._subgroup_order = subgroup_order
self._generator = generator
@classmethod
def generate(cls, key_size, backend):
if not isinstance(backend, DSABackend):
raise UnsupportedAlgorithm(
"Backend object does not implement DSABackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
return backend.generate_dsa_parameters(key_size)
@property
def modulus(self):
return self._modulus
@property
def subgroup_order(self):
return self._subgroup_order
@property
def generator(self):
return self._generator
@property
def p(self):
return self.modulus
@property
def q(self):
return self.subgroup_order
@property
def g(self):
return self.generator
@utils.register_interface(interfaces.DSAPrivateKey)
class DSAPrivateKey(object):
def __init__(self, modulus, subgroup_order, generator, x, y):
_check_dsa_parameters(modulus, subgroup_order, generator)
if (
not isinstance(x, six.integer_types) or
not isinstance(y, six.integer_types)
):
raise TypeError("DSAPrivateKey arguments must be integers")
if x <= 0 or x >= subgroup_order:
raise ValueError("x must be > 0 and < subgroup_order")
if y != pow(generator, x, modulus):
raise ValueError("y must be equal to (generator ** x % modulus)")
self._modulus = modulus
self._subgroup_order = subgroup_order
self._generator = generator
self._x = x
self._y = y
@classmethod
def generate(cls, parameters, backend):
if not isinstance(backend, DSABackend):
raise UnsupportedAlgorithm(
"Backend object does not implement DSABackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
return backend.generate_dsa_private_key(parameters)
def signer(self, algorithm, backend):
if not isinstance(backend, DSABackend):
raise UnsupportedAlgorithm(
"Backend object does not implement DSABackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
return backend.create_dsa_signature_ctx(self, algorithm)
@property
def key_size(self):
return utils.bit_length(self._modulus)
def public_key(self):
return DSAPublicKey(self._modulus, self._subgroup_order,
self._generator, self.y)
@property
def x(self):
return self._x
@property
def y(self):
return self._y
def parameters(self):
return DSAParameters(self._modulus, self._subgroup_order,
self._generator)
@utils.register_interface(interfaces.DSAPublicKey)
class DSAPublicKey(object):
def __init__(self, modulus, subgroup_order, generator, y):
_check_dsa_parameters(modulus, subgroup_order, generator)
if not isinstance(y, six.integer_types):
raise TypeError("y must be an integer")
self._modulus = modulus
self._subgroup_order = subgroup_order
self._generator = generator
self._y = y
def verifier(self, signature, algorithm, backend):
if not isinstance(backend, DSABackend):
raise UnsupportedAlgorithm(
"Backend object does not implement DSABackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
return backend.create_dsa_verification_ctx(self, signature,
algorithm)
@property
def key_size(self):
return utils.bit_length(self._modulus)
@property
def y(self):
return self._y
def parameters(self):
return DSAParameters(self._modulus, self._subgroup_order,
self._generator)

View file

@ -0,0 +1,92 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
import warnings
import six
from cryptography import utils
from cryptography.hazmat.primitives import interfaces
@utils.register_interface(interfaces.AsymmetricPadding)
class PKCS1v15(object):
name = "EMSA-PKCS1-v1_5"
@utils.register_interface(interfaces.AsymmetricPadding)
class PSS(object):
MAX_LENGTH = object()
name = "EMSA-PSS"
def __init__(self, mgf, salt_length=None):
self._mgf = mgf
if salt_length is None:
warnings.warn(
"salt_length is deprecated on MGF1 and should be added via the"
" PSS constructor.",
utils.DeprecatedIn04
)
else:
if (not isinstance(salt_length, six.integer_types) and
salt_length is not self.MAX_LENGTH):
raise TypeError("salt_length must be an integer")
if salt_length is not self.MAX_LENGTH and salt_length < 0:
raise ValueError("salt_length must be zero or greater")
if salt_length is None and self._mgf._salt_length is None:
raise ValueError("You must supply salt_length")
self._salt_length = salt_length
@utils.register_interface(interfaces.AsymmetricPadding)
class OAEP(object):
name = "EME-OAEP"
def __init__(self, mgf, algorithm, label):
if not isinstance(algorithm, interfaces.HashAlgorithm):
raise TypeError("Expected instance of interfaces.HashAlgorithm.")
self._mgf = mgf
self._algorithm = algorithm
self._label = label
class MGF1(object):
MAX_LENGTH = object()
def __init__(self, algorithm, salt_length=None):
if not isinstance(algorithm, interfaces.HashAlgorithm):
raise TypeError("Expected instance of interfaces.HashAlgorithm.")
self._algorithm = algorithm
if salt_length is not None:
warnings.warn(
"salt_length is deprecated on MGF1 and should be passed to "
"the PSS constructor instead.",
utils.DeprecatedIn04
)
if (not isinstance(salt_length, six.integer_types) and
salt_length is not self.MAX_LENGTH):
raise TypeError("salt_length must be an integer")
if salt_length is not self.MAX_LENGTH and salt_length < 0:
raise ValueError("salt_length must be zero or greater")
self._salt_length = salt_length

View file

@ -0,0 +1,259 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
import six
from cryptography import utils
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.backends.interfaces import RSABackend
from cryptography.hazmat.primitives import interfaces
@utils.register_interface(interfaces.RSAPublicKey)
class RSAPublicKey(object):
def __init__(self, public_exponent, modulus):
if (
not isinstance(public_exponent, six.integer_types) or
not isinstance(modulus, six.integer_types)
):
raise TypeError("RSAPublicKey arguments must be integers")
if modulus < 3:
raise ValueError("modulus must be >= 3")
if public_exponent < 3 or public_exponent >= modulus:
raise ValueError("public_exponent must be >= 3 and < modulus")
if public_exponent & 1 == 0:
raise ValueError("public_exponent must be odd")
self._public_exponent = public_exponent
self._modulus = modulus
def verifier(self, signature, padding, algorithm, backend):
if not isinstance(backend, RSABackend):
raise UnsupportedAlgorithm(
"Backend object does not implement RSABackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
return backend.create_rsa_verification_ctx(self, signature, padding,
algorithm)
def encrypt(self, plaintext, padding, backend):
if not isinstance(backend, RSABackend):
raise UnsupportedAlgorithm(
"Backend object does not implement RSABackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
return backend.encrypt_rsa(self, plaintext, padding)
@property
def key_size(self):
return utils.bit_length(self.modulus)
@property
def public_exponent(self):
return self._public_exponent
@property
def modulus(self):
return self._modulus
@property
def e(self):
return self.public_exponent
@property
def n(self):
return self.modulus
def _modinv(e, m):
"""
Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1
"""
x1, y1, x2, y2 = 1, 0, 0, 1
a, b = e, m
while b > 0:
q, r = divmod(a, b)
xn, yn = x1 - q * x2, y1 - q * y2
a, b, x1, y1, x2, y2 = b, r, x2, y2, xn, yn
return x1 % m
def rsa_crt_iqmp(p, q):
"""
Compute the CRT (q ** -1) % p value from RSA primes p and q.
"""
return _modinv(q, p)
def rsa_crt_dmp1(private_exponent, p):
"""
Compute the CRT private_exponent % (p - 1) value from the RSA
private_exponent and p.
"""
return private_exponent % (p - 1)
def rsa_crt_dmq1(private_exponent, q):
"""
Compute the CRT private_exponent % (q - 1) value from the RSA
private_exponent and q.
"""
return private_exponent % (q - 1)
@utils.register_interface(interfaces.RSAPrivateKey)
class RSAPrivateKey(object):
def __init__(self, p, q, private_exponent, dmp1, dmq1, iqmp,
public_exponent, modulus):
if (
not isinstance(p, six.integer_types) or
not isinstance(q, six.integer_types) or
not isinstance(dmp1, six.integer_types) or
not isinstance(dmq1, six.integer_types) or
not isinstance(iqmp, six.integer_types) or
not isinstance(private_exponent, six.integer_types) or
not isinstance(public_exponent, six.integer_types) or
not isinstance(modulus, six.integer_types)
):
raise TypeError("RSAPrivateKey arguments must be integers")
if modulus < 3:
raise ValueError("modulus must be >= 3")
if p >= modulus:
raise ValueError("p must be < modulus")
if q >= modulus:
raise ValueError("q must be < modulus")
if dmp1 >= modulus:
raise ValueError("dmp1 must be < modulus")
if dmq1 >= modulus:
raise ValueError("dmq1 must be < modulus")
if iqmp >= modulus:
raise ValueError("iqmp must be < modulus")
if private_exponent >= modulus:
raise ValueError("private_exponent must be < modulus")
if public_exponent < 3 or public_exponent >= modulus:
raise ValueError("public_exponent must be >= 3 and < modulus")
if public_exponent & 1 == 0:
raise ValueError("public_exponent must be odd")
if dmp1 & 1 == 0:
raise ValueError("dmp1 must be odd")
if dmq1 & 1 == 0:
raise ValueError("dmq1 must be odd")
if p * q != modulus:
raise ValueError("p*q must equal modulus")
self._p = p
self._q = q
self._dmp1 = dmp1
self._dmq1 = dmq1
self._iqmp = iqmp
self._private_exponent = private_exponent
self._public_exponent = public_exponent
self._modulus = modulus
@classmethod
def generate(cls, public_exponent, key_size, backend):
if not isinstance(backend, RSABackend):
raise UnsupportedAlgorithm(
"Backend object does not implement RSABackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
return backend.generate_rsa_private_key(public_exponent, key_size)
def signer(self, padding, algorithm, backend):
if not isinstance(backend, RSABackend):
raise UnsupportedAlgorithm(
"Backend object does not implement RSABackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
return backend.create_rsa_signature_ctx(self, padding, algorithm)
def decrypt(self, ciphertext, padding, backend):
if not isinstance(backend, RSABackend):
raise UnsupportedAlgorithm(
"Backend object does not implement RSABackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
return backend.decrypt_rsa(self, ciphertext, padding)
@property
def key_size(self):
return utils.bit_length(self.modulus)
def public_key(self):
return RSAPublicKey(self.public_exponent, self.modulus)
@property
def p(self):
return self._p
@property
def q(self):
return self._q
@property
def private_exponent(self):
return self._private_exponent
@property
def public_exponent(self):
return self._public_exponent
@property
def modulus(self):
return self._modulus
@property
def d(self):
return self.private_exponent
@property
def dmp1(self):
return self._dmp1
@property
def dmq1(self):
return self._dmq1
@property
def iqmp(self):
return self._iqmp
@property
def e(self):
return self.public_exponent
@property
def n(self):
return self.modulus

View file

@ -0,0 +1,21 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
from cryptography.hazmat.primitives.ciphers.base import Cipher
__all__ = [
"Cipher",
]

View file

@ -0,0 +1,147 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
from cryptography import utils
from cryptography.hazmat.primitives import interfaces
def _verify_key_size(algorithm, key):
# Verify that the key size matches the expected key size
if len(key) * 8 not in algorithm.key_sizes:
raise ValueError("Invalid key size ({0}) for {1}".format(
len(key) * 8, algorithm.name
))
return key
@utils.register_interface(interfaces.BlockCipherAlgorithm)
@utils.register_interface(interfaces.CipherAlgorithm)
class AES(object):
name = "AES"
block_size = 128
key_sizes = frozenset([128, 192, 256])
def __init__(self, key):
self.key = _verify_key_size(self, key)
@property
def key_size(self):
return len(self.key) * 8
@utils.register_interface(interfaces.BlockCipherAlgorithm)
@utils.register_interface(interfaces.CipherAlgorithm)
class Camellia(object):
name = "camellia"
block_size = 128
key_sizes = frozenset([128, 192, 256])
def __init__(self, key):
self.key = _verify_key_size(self, key)
@property
def key_size(self):
return len(self.key) * 8
@utils.register_interface(interfaces.BlockCipherAlgorithm)
@utils.register_interface(interfaces.CipherAlgorithm)
class TripleDES(object):
name = "3DES"
block_size = 64
key_sizes = frozenset([64, 128, 192])
def __init__(self, key):
if len(key) == 8:
key += key + key
elif len(key) == 16:
key += key[:8]
self.key = _verify_key_size(self, key)
@property
def key_size(self):
return len(self.key) * 8
@utils.register_interface(interfaces.BlockCipherAlgorithm)
@utils.register_interface(interfaces.CipherAlgorithm)
class Blowfish(object):
name = "Blowfish"
block_size = 64
key_sizes = frozenset(range(32, 449, 8))
def __init__(self, key):
self.key = _verify_key_size(self, key)
@property
def key_size(self):
return len(self.key) * 8
@utils.register_interface(interfaces.BlockCipherAlgorithm)
@utils.register_interface(interfaces.CipherAlgorithm)
class CAST5(object):
name = "CAST5"
block_size = 64
key_sizes = frozenset(range(40, 129, 8))
def __init__(self, key):
self.key = _verify_key_size(self, key)
@property
def key_size(self):
return len(self.key) * 8
@utils.register_interface(interfaces.CipherAlgorithm)
class ARC4(object):
name = "RC4"
key_sizes = frozenset([40, 56, 64, 80, 128, 192, 256])
def __init__(self, key):
self.key = _verify_key_size(self, key)
@property
def key_size(self):
return len(self.key) * 8
@utils.register_interface(interfaces.CipherAlgorithm)
class IDEA(object):
name = "IDEA"
block_size = 64
key_sizes = frozenset([128])
def __init__(self, key):
self.key = _verify_key_size(self, key)
@property
def key_size(self):
return len(self.key) * 8
@utils.register_interface(interfaces.BlockCipherAlgorithm)
@utils.register_interface(interfaces.CipherAlgorithm)
class SEED(object):
name = "SEED"
block_size = 128
key_sizes = frozenset([128])
def __init__(self, key):
self.key = _verify_key_size(self, key)
@property
def key_size(self):
return len(self.key) * 8

View file

@ -0,0 +1,130 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, AlreadyUpdated, NotYetFinalized, UnsupportedAlgorithm,
_Reasons
)
from cryptography.hazmat.backends.interfaces import CipherBackend
from cryptography.hazmat.primitives import interfaces
class Cipher(object):
def __init__(self, algorithm, mode, backend):
if not isinstance(backend, CipherBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement CipherBackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
if not isinstance(algorithm, interfaces.CipherAlgorithm):
raise TypeError("Expected interface of interfaces.CipherAlgorithm")
if mode is not None:
mode.validate_for_algorithm(algorithm)
self.algorithm = algorithm
self.mode = mode
self._backend = backend
def encryptor(self):
if isinstance(self.mode, interfaces.ModeWithAuthenticationTag):
if self.mode.tag is not None:
raise ValueError(
"Authentication tag must be None when encrypting"
)
ctx = self._backend.create_symmetric_encryption_ctx(
self.algorithm, self.mode
)
return self._wrap_ctx(ctx, encrypt=True)
def decryptor(self):
if isinstance(self.mode, interfaces.ModeWithAuthenticationTag):
if self.mode.tag is None:
raise ValueError(
"Authentication tag must be provided when decrypting"
)
ctx = self._backend.create_symmetric_decryption_ctx(
self.algorithm, self.mode
)
return self._wrap_ctx(ctx, encrypt=False)
def _wrap_ctx(self, ctx, encrypt):
if isinstance(self.mode, interfaces.ModeWithAuthenticationTag):
if encrypt:
return _AEADEncryptionContext(ctx)
else:
return _AEADCipherContext(ctx)
else:
return _CipherContext(ctx)
@utils.register_interface(interfaces.CipherContext)
class _CipherContext(object):
def __init__(self, ctx):
self._ctx = ctx
def update(self, data):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized")
return self._ctx.update(data)
def finalize(self):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized")
data = self._ctx.finalize()
self._ctx = None
return data
@utils.register_interface(interfaces.AEADCipherContext)
@utils.register_interface(interfaces.CipherContext)
class _AEADCipherContext(object):
def __init__(self, ctx):
self._ctx = ctx
self._tag = None
self._updated = False
def update(self, data):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized")
self._updated = True
return self._ctx.update(data)
def finalize(self):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized")
data = self._ctx.finalize()
self._tag = self._ctx.tag
self._ctx = None
return data
def authenticate_additional_data(self, data):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized")
if self._updated:
raise AlreadyUpdated("Update has been called on this context")
self._ctx.authenticate_additional_data(data)
@utils.register_interface(interfaces.AEADEncryptionContext)
class _AEADEncryptionContext(_AEADCipherContext):
@property
def tag(self):
if self._ctx is not None:
raise NotYetFinalized("You must finalize encryption before "
"getting the tag")
return self._tag

View file

@ -0,0 +1,107 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
from cryptography import utils
from cryptography.hazmat.primitives import interfaces
@utils.register_interface(interfaces.Mode)
@utils.register_interface(interfaces.ModeWithInitializationVector)
class CBC(object):
name = "CBC"
def __init__(self, initialization_vector):
self.initialization_vector = initialization_vector
def validate_for_algorithm(self, algorithm):
if len(self.initialization_vector) * 8 != algorithm.block_size:
raise ValueError("Invalid iv size ({0}) for {1}".format(
len(self.initialization_vector), self.name
))
@utils.register_interface(interfaces.Mode)
class ECB(object):
name = "ECB"
def validate_for_algorithm(self, algorithm):
pass
@utils.register_interface(interfaces.Mode)
@utils.register_interface(interfaces.ModeWithInitializationVector)
class OFB(object):
name = "OFB"
def __init__(self, initialization_vector):
self.initialization_vector = initialization_vector
def validate_for_algorithm(self, algorithm):
if len(self.initialization_vector) * 8 != algorithm.block_size:
raise ValueError("Invalid iv size ({0}) for {1}".format(
len(self.initialization_vector), self.name
))
@utils.register_interface(interfaces.Mode)
@utils.register_interface(interfaces.ModeWithInitializationVector)
class CFB(object):
name = "CFB"
def __init__(self, initialization_vector):
self.initialization_vector = initialization_vector
def validate_for_algorithm(self, algorithm):
if len(self.initialization_vector) * 8 != algorithm.block_size:
raise ValueError("Invalid iv size ({0}) for {1}".format(
len(self.initialization_vector), self.name
))
@utils.register_interface(interfaces.Mode)
@utils.register_interface(interfaces.ModeWithNonce)
class CTR(object):
name = "CTR"
def __init__(self, nonce):
self.nonce = nonce
def validate_for_algorithm(self, algorithm):
if len(self.nonce) * 8 != algorithm.block_size:
raise ValueError("Invalid nonce size ({0}) for {1}".format(
len(self.nonce), self.name
))
@utils.register_interface(interfaces.Mode)
@utils.register_interface(interfaces.ModeWithInitializationVector)
@utils.register_interface(interfaces.ModeWithAuthenticationTag)
class GCM(object):
name = "GCM"
def __init__(self, initialization_vector, tag=None):
# len(initialization_vector) must in [1, 2 ** 64), but it's impossible
# to actually construct a bytes object that large, so we don't check
# for it
if tag is not None and len(tag) < 4:
raise ValueError(
"Authentication tag must be 4 bytes or longer"
)
self.initialization_vector = initialization_vector
self.tag = tag
def validate_for_algorithm(self, algorithm):
pass

View file

@ -0,0 +1,75 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
import six
from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, InvalidSignature, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import CMACBackend
from cryptography.hazmat.primitives import constant_time, interfaces
@utils.register_interface(interfaces.CMACContext)
class CMAC(object):
def __init__(self, algorithm, backend, ctx=None):
if not isinstance(backend, CMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement CMACBackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
if not isinstance(algorithm, interfaces.BlockCipherAlgorithm):
raise TypeError(
"Expected instance of interfaces.BlockCipherAlgorithm"
)
self._algorithm = algorithm
self._backend = backend
if ctx is None:
self._ctx = self._backend.create_cmac_ctx(self._algorithm)
else:
self._ctx = ctx
def update(self, data):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized")
if isinstance(data, six.text_type):
raise TypeError("Unicode-objects must be encoded before hashing")
self._ctx.update(data)
def finalize(self):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized")
digest = self._ctx.finalize()
self._ctx = None
return digest
def verify(self, signature):
if isinstance(signature, six.text_type):
raise TypeError("Unicode-objects must be encoded before verifying")
digest = self.finalize()
if not constant_time.bytes_eq(digest, signature):
raise InvalidSignature("Signature did not match digest.")
def copy(self):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized")
return CMAC(
self._algorithm,
backend=self._backend,
ctx=self._ctx.copy()
)

View file

@ -0,0 +1,63 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
import sys
import cffi
import six
from cryptography.hazmat.bindings.utils import _create_modulename
TYPES = """
uint8_t Cryptography_constant_time_bytes_eq(uint8_t *, size_t, uint8_t *,
size_t);
"""
FUNCTIONS = """
uint8_t Cryptography_constant_time_bytes_eq(uint8_t *a, size_t len_a,
uint8_t *b, size_t len_b) {
size_t i = 0;
uint8_t mismatch = 0;
if (len_a != len_b) {
return 0;
}
for (i = 0; i < len_a; i++) {
mismatch |= a[i] ^ b[i];
}
/* Make sure any bits set are copied to the lowest bit */
mismatch |= mismatch >> 4;
mismatch |= mismatch >> 2;
mismatch |= mismatch >> 1;
/* Now check the low bit to see if it's set */
return (mismatch & 1) == 0;
}
"""
_ffi = cffi.FFI()
_ffi.cdef(TYPES)
_lib = _ffi.verify(
source=FUNCTIONS,
modulename=_create_modulename([TYPES], FUNCTIONS, sys.version),
ext_package="cryptography",
)
def bytes_eq(a, b):
if isinstance(a, six.text_type) or isinstance(b, six.text_type):
raise TypeError("Unicode-objects must be encoded before comparing")
return _lib.Cryptography_constant_time_bytes_eq(a, len(a), b, len(b)) == 1

View file

@ -0,0 +1,121 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
import six
from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import HashBackend
from cryptography.hazmat.primitives import interfaces
@utils.register_interface(interfaces.HashContext)
class Hash(object):
def __init__(self, algorithm, backend, ctx=None):
if not isinstance(backend, HashBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HashBackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
if not isinstance(algorithm, interfaces.HashAlgorithm):
raise TypeError("Expected instance of interfaces.HashAlgorithm.")
self.algorithm = algorithm
self._backend = backend
if ctx is None:
self._ctx = self._backend.create_hash_ctx(self.algorithm)
else:
self._ctx = ctx
def update(self, data):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized")
if isinstance(data, six.text_type):
raise TypeError("Unicode-objects must be encoded before hashing")
self._ctx.update(data)
def copy(self):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized")
return Hash(
self.algorithm, backend=self._backend, ctx=self._ctx.copy()
)
def finalize(self):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized")
digest = self._ctx.finalize()
self._ctx = None
return digest
@utils.register_interface(interfaces.HashAlgorithm)
class SHA1(object):
name = "sha1"
digest_size = 20
block_size = 64
@utils.register_interface(interfaces.HashAlgorithm)
class SHA224(object):
name = "sha224"
digest_size = 28
block_size = 64
@utils.register_interface(interfaces.HashAlgorithm)
class SHA256(object):
name = "sha256"
digest_size = 32
block_size = 64
@utils.register_interface(interfaces.HashAlgorithm)
class SHA384(object):
name = "sha384"
digest_size = 48
block_size = 128
@utils.register_interface(interfaces.HashAlgorithm)
class SHA512(object):
name = "sha512"
digest_size = 64
block_size = 128
@utils.register_interface(interfaces.HashAlgorithm)
class RIPEMD160(object):
name = "ripemd160"
digest_size = 20
block_size = 64
@utils.register_interface(interfaces.HashAlgorithm)
class Whirlpool(object):
name = "whirlpool"
digest_size = 64
block_size = 64
@utils.register_interface(interfaces.HashAlgorithm)
class MD5(object):
name = "md5"
digest_size = 16
block_size = 64

View file

@ -0,0 +1,75 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
import six
from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, InvalidSignature, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.primitives import constant_time, interfaces
@utils.register_interface(interfaces.HashContext)
class HMAC(object):
def __init__(self, key, algorithm, backend, ctx=None):
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HMACBackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
if not isinstance(algorithm, interfaces.HashAlgorithm):
raise TypeError("Expected instance of interfaces.HashAlgorithm.")
self.algorithm = algorithm
self._backend = backend
self._key = key
if ctx is None:
self._ctx = self._backend.create_hmac_ctx(key, self.algorithm)
else:
self._ctx = ctx
def update(self, msg):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized")
if isinstance(msg, six.text_type):
raise TypeError("Unicode-objects must be encoded before hashing")
self._ctx.update(msg)
def copy(self):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized")
return HMAC(
self._key,
self.algorithm,
backend=self._backend,
ctx=self._ctx.copy()
)
def finalize(self):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized")
digest = self._ctx.finalize()
self._ctx = None
return digest
def verify(self, signature):
if isinstance(signature, six.text_type):
raise TypeError("Unicode-objects must be encoded before verifying")
digest = self.finalize()
if not constant_time.bytes_eq(digest, signature):
raise InvalidSignature("Signature did not match digest.")

View file

@ -0,0 +1,491 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class CipherAlgorithm(object):
@abc.abstractproperty
def name(self):
"""
A string naming this mode (e.g. "AES", "Camellia").
"""
@abc.abstractproperty
def key_size(self):
"""
The size of the key being used as an integer in bits (e.g. 128, 256).
"""
@six.add_metaclass(abc.ABCMeta)
class BlockCipherAlgorithm(object):
@abc.abstractproperty
def block_size(self):
"""
The size of a block as an integer in bits (e.g. 64, 128).
"""
@six.add_metaclass(abc.ABCMeta)
class Mode(object):
@abc.abstractproperty
def name(self):
"""
A string naming this mode (e.g. "ECB", "CBC").
"""
@abc.abstractmethod
def validate_for_algorithm(self, algorithm):
"""
Checks that all the necessary invariants of this (mode, algorithm)
combination are met.
"""
@six.add_metaclass(abc.ABCMeta)
class ModeWithInitializationVector(object):
@abc.abstractproperty
def initialization_vector(self):
"""
The value of the initialization vector for this mode as bytes.
"""
@six.add_metaclass(abc.ABCMeta)
class ModeWithNonce(object):
@abc.abstractproperty
def nonce(self):
"""
The value of the nonce for this mode as bytes.
"""
@six.add_metaclass(abc.ABCMeta)
class ModeWithAuthenticationTag(object):
@abc.abstractproperty
def tag(self):
"""
The value of the tag supplied to the constructor of this mode.
"""
@six.add_metaclass(abc.ABCMeta)
class CipherContext(object):
@abc.abstractmethod
def update(self, data):
"""
Processes the provided bytes through the cipher and returns the results
as bytes.
"""
@abc.abstractmethod
def finalize(self):
"""
Returns the results of processing the final block as bytes.
"""
@six.add_metaclass(abc.ABCMeta)
class AEADCipherContext(object):
@abc.abstractmethod
def authenticate_additional_data(self, data):
"""
Authenticates the provided bytes.
"""
@six.add_metaclass(abc.ABCMeta)
class AEADEncryptionContext(object):
@abc.abstractproperty
def tag(self):
"""
Returns tag bytes. This is only available after encryption is
finalized.
"""
@six.add_metaclass(abc.ABCMeta)
class PaddingContext(object):
@abc.abstractmethod
def update(self, data):
"""
Pads the provided bytes and returns any available data as bytes.
"""
@abc.abstractmethod
def finalize(self):
"""
Finalize the padding, returns bytes.
"""
@six.add_metaclass(abc.ABCMeta)
class HashAlgorithm(object):
@abc.abstractproperty
def name(self):
"""
A string naming this algorithm (e.g. "sha256", "md5").
"""
@abc.abstractproperty
def digest_size(self):
"""
The size of the resulting digest in bytes.
"""
@abc.abstractproperty
def block_size(self):
"""
The internal block size of the hash algorithm in bytes.
"""
@six.add_metaclass(abc.ABCMeta)
class HashContext(object):
@abc.abstractproperty
def algorithm(self):
"""
A HashAlgorithm that will be used by this context.
"""
@abc.abstractmethod
def update(self, data):
"""
Processes the provided bytes through the hash.
"""
@abc.abstractmethod
def finalize(self):
"""
Finalizes the hash context and returns the hash digest as bytes.
"""
@abc.abstractmethod
def copy(self):
"""
Return a HashContext that is a copy of the current context.
"""
@six.add_metaclass(abc.ABCMeta)
class RSAPrivateKey(object):
@abc.abstractmethod
def signer(self, padding, algorithm, backend):
"""
Returns an AsymmetricSignatureContext used for signing data.
"""
@abc.abstractproperty
def modulus(self):
"""
The public modulus of the RSA key.
"""
@abc.abstractproperty
def public_exponent(self):
"""
The public exponent of the RSA key.
"""
@abc.abstractproperty
def private_exponent(self):
"""
The private exponent of the RSA key.
"""
@abc.abstractproperty
def key_size(self):
"""
The bit length of the public modulus.
"""
@abc.abstractmethod
def public_key(self):
"""
The RSAPublicKey associated with this private key.
"""
@abc.abstractproperty
def n(self):
"""
The public modulus of the RSA key. Alias for modulus.
"""
@abc.abstractproperty
def p(self):
"""
One of the two primes used to generate d.
"""
@abc.abstractproperty
def q(self):
"""
One of the two primes used to generate d.
"""
@abc.abstractproperty
def d(self):
"""
The private exponent. This can be calculated using p and q. Alias for
private_exponent.
"""
@abc.abstractproperty
def dmp1(self):
"""
A Chinese remainder theorem coefficient used to speed up RSA
calculations. Calculated as: d mod (p-1)
"""
@abc.abstractproperty
def dmq1(self):
"""
A Chinese remainder theorem coefficient used to speed up RSA
calculations. Calculated as: d mod (q-1)
"""
@abc.abstractproperty
def iqmp(self):
"""
A Chinese remainder theorem coefficient used to speed up RSA
calculations. The modular inverse of q modulo p
"""
@abc.abstractproperty
def e(self):
"""
The public exponent of the RSA key. Alias for public_exponent.
"""
@six.add_metaclass(abc.ABCMeta)
class RSAPublicKey(object):
@abc.abstractmethod
def verifier(self, signature, padding, algorithm, backend):
"""
Returns an AsymmetricVerificationContext used for verifying signatures.
"""
@abc.abstractproperty
def modulus(self):
"""
The public modulus of the RSA key.
"""
@abc.abstractproperty
def public_exponent(self):
"""
The public exponent of the RSA key.
"""
@abc.abstractproperty
def key_size(self):
"""
The bit length of the public modulus.
"""
@abc.abstractproperty
def n(self):
"""
The public modulus of the RSA key. Alias for modulus.
"""
@abc.abstractproperty
def e(self):
"""
The public exponent of the RSA key. Alias for public_exponent.
"""
@six.add_metaclass(abc.ABCMeta)
class DSAParameters(object):
@abc.abstractproperty
def modulus(self):
"""
The prime modulus that's used in generating the DSA keypair and used
in the DSA signing and verification processes.
"""
@abc.abstractproperty
def subgroup_order(self):
"""
The subgroup order that's used in generating the DSA keypair
by the generator and used in the DSA signing and verification
processes.
"""
@abc.abstractproperty
def generator(self):
"""
The generator that is used in generating the DSA keypair and used
in the DSA signing and verification processes.
"""
@abc.abstractproperty
def p(self):
"""
The prime modulus that's used in generating the DSA keypair and used
in the DSA signing and verification processes. Alias for modulus.
"""
@abc.abstractproperty
def q(self):
"""
The subgroup order that's used in generating the DSA keypair
by the generator and used in the DSA signing and verification
processes. Alias for subgroup_order.
"""
@abc.abstractproperty
def g(self):
"""
The generator that is used in generating the DSA keypair and used
in the DSA signing and verification processes. Alias for generator.
"""
@six.add_metaclass(abc.ABCMeta)
class DSAPrivateKey(object):
@abc.abstractproperty
def key_size(self):
"""
The bit length of the prime modulus.
"""
@abc.abstractmethod
def public_key(self):
"""
The DSAPublicKey associated with this private key.
"""
@abc.abstractproperty
def x(self):
"""
The private key "x" in the DSA structure.
"""
@abc.abstractproperty
def y(self):
"""
The public key.
"""
@abc.abstractmethod
def parameters(self):
"""
The DSAParameters object associated with this private key.
"""
@six.add_metaclass(abc.ABCMeta)
class DSAPublicKey(object):
@abc.abstractproperty
def key_size(self):
"""
The bit length of the prime modulus.
"""
@abc.abstractproperty
def y(self):
"""
The public key.
"""
@abc.abstractmethod
def parameters(self):
"""
The DSAParameters object associated with this public key.
"""
@six.add_metaclass(abc.ABCMeta)
class AsymmetricSignatureContext(object):
@abc.abstractmethod
def update(self, data):
"""
Processes the provided bytes and returns nothing.
"""
@abc.abstractmethod
def finalize(self):
"""
Returns the signature as bytes.
"""
@six.add_metaclass(abc.ABCMeta)
class AsymmetricVerificationContext(object):
@abc.abstractmethod
def update(self, data):
"""
Processes the provided bytes and returns nothing.
"""
@abc.abstractmethod
def verify(self):
"""
Raises an exception if the bytes provided to update do not match the
signature or the signature does not match the public key.
"""
@six.add_metaclass(abc.ABCMeta)
class AsymmetricPadding(object):
@abc.abstractproperty
def name(self):
"""
A string naming this padding (e.g. "PSS", "PKCS1").
"""
@six.add_metaclass(abc.ABCMeta)
class KeyDerivationFunction(object):
@abc.abstractmethod
def derive(self, key_material):
"""
Deterministically generates and returns a new key based on the existing
key material.
"""
@abc.abstractmethod
def verify(self, key_material, expected_key):
"""
Checks whether the key generated by the key material matches the
expected derived key. Raises an exception if they do not match.
"""
@six.add_metaclass(abc.ABCMeta)
class CMACContext(object):
@abc.abstractmethod
def update(self, data):
"""
Processes the provided bytes.
"""
def finalize(self):
"""
Returns the message authentication code as bytes.
"""
@abc.abstractmethod
def copy(self):
"""
Return a CMACContext that is a copy of the current context.
"""

View file

@ -0,0 +1,14 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function

View file

@ -0,0 +1,102 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
import six
from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.primitives import constant_time, hmac, interfaces
@utils.register_interface(interfaces.KeyDerivationFunction)
class HKDF(object):
def __init__(self, algorithm, length, salt, info, backend):
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HMACBackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
self._algorithm = algorithm
max_length = 255 * (algorithm.digest_size // 8)
if length > max_length:
raise ValueError(
"Can not derive keys larger than {0} octets.".format(
max_length
))
self._length = length
if isinstance(salt, six.text_type):
raise TypeError(
"Unicode-objects must be encoded before using them as a salt.")
if salt is None:
salt = b"\x00" * (self._algorithm.digest_size // 8)
self._salt = salt
if isinstance(info, six.text_type):
raise TypeError(
"Unicode-objects must be encoded before using them as info.")
if info is None:
info = b""
self._info = info
self._backend = backend
self._used = False
def _extract(self, key_material):
h = hmac.HMAC(self._salt, self._algorithm, backend=self._backend)
h.update(key_material)
return h.finalize()
def _expand(self, key_material):
output = [b""]
counter = 1
while (self._algorithm.digest_size // 8) * len(output) < self._length:
h = hmac.HMAC(key_material, self._algorithm, backend=self._backend)
h.update(output[-1])
h.update(self._info)
h.update(six.int2byte(counter))
output.append(h.finalize())
counter += 1
return b"".join(output)[:self._length]
def derive(self, key_material):
if isinstance(key_material, six.text_type):
raise TypeError(
"Unicode-objects must be encoded before using them as key "
"material."
)
if self._used:
raise AlreadyFinalized
self._used = True
return self._expand(self._extract(key_material))
def verify(self, key_material, expected_key):
if not constant_time.bytes_eq(self.derive(key_material), expected_key):
raise InvalidKey

View file

@ -0,0 +1,74 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
import six
from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import PBKDF2HMACBackend
from cryptography.hazmat.primitives import constant_time, interfaces
@utils.register_interface(interfaces.KeyDerivationFunction)
class PBKDF2HMAC(object):
def __init__(self, algorithm, length, salt, iterations, backend):
if not isinstance(backend, PBKDF2HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement PBKDF2HMACBackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
if not backend.pbkdf2_hmac_supported(algorithm):
raise UnsupportedAlgorithm(
"{0} is not supported for PBKDF2 by this backend".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
self._used = False
self._algorithm = algorithm
self._length = length
if isinstance(salt, six.text_type):
raise TypeError(
"Unicode-objects must be encoded before using them as key "
"material."
)
self._salt = salt
self._iterations = iterations
self._backend = backend
def derive(self, key_material):
if self._used:
raise AlreadyFinalized("PBKDF2 instances can only be used once")
self._used = True
if isinstance(key_material, six.text_type):
raise TypeError(
"Unicode-objects must be encoded before using them as key "
"material."
)
return self._backend.derive_pbkdf2_hmac(
self._algorithm,
self._length,
self._salt,
self._iterations,
key_material
)
def verify(self, key_material, expected_key):
derived_key = self.derive(key_material)
if not constant_time.bytes_eq(derived_key, expected_key):
raise InvalidKey("Keys do not match.")

View file

@ -0,0 +1,172 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
import sys
import cffi
import six
from cryptography import utils
from cryptography.exceptions import AlreadyFinalized
from cryptography.hazmat.bindings.utils import _create_modulename
from cryptography.hazmat.primitives import interfaces
TYPES = """
uint8_t Cryptography_check_pkcs7_padding(const uint8_t *, uint8_t);
"""
FUNCTIONS = """
/* Returns the value of the input with the most-significant-bit copied to all
of the bits. */
static uint8_t Cryptography_DUPLICATE_MSB_TO_ALL(uint8_t a) {
return (1 - (a >> (sizeof(uint8_t) * 8 - 1))) - 1;
}
/* This returns 0xFF if a < b else 0x00, but does so in a constant time
fashion */
static uint8_t Cryptography_constant_time_lt(uint8_t a, uint8_t b) {
a -= b;
return Cryptography_DUPLICATE_MSB_TO_ALL(a);
}
uint8_t Cryptography_check_pkcs7_padding(const uint8_t *data,
uint8_t block_len) {
uint8_t i;
uint8_t pad_size = data[block_len - 1];
uint8_t mismatch = 0;
for (i = 0; i < block_len; i++) {
unsigned int mask = Cryptography_constant_time_lt(i, pad_size);
uint8_t b = data[block_len - 1 - i];
mismatch |= (mask & (pad_size ^ b));
}
/* Check to make sure the pad_size was within the valid range. */
mismatch |= ~Cryptography_constant_time_lt(0, pad_size);
mismatch |= Cryptography_constant_time_lt(block_len, pad_size);
/* Make sure any bits set are copied to the lowest bit */
mismatch |= mismatch >> 4;
mismatch |= mismatch >> 2;
mismatch |= mismatch >> 1;
/* Now check the low bit to see if it's set */
return (mismatch & 1) == 0;
}
"""
_ffi = cffi.FFI()
_ffi.cdef(TYPES)
_lib = _ffi.verify(
source=FUNCTIONS,
modulename=_create_modulename([TYPES], FUNCTIONS, sys.version),
ext_package="cryptography",
)
class PKCS7(object):
def __init__(self, block_size):
if not (0 <= block_size < 256):
raise ValueError("block_size must be in range(0, 256)")
if block_size % 8 != 0:
raise ValueError("block_size must be a multiple of 8")
self.block_size = block_size
def padder(self):
return _PKCS7PaddingContext(self.block_size)
def unpadder(self):
return _PKCS7UnpaddingContext(self.block_size)
@utils.register_interface(interfaces.PaddingContext)
class _PKCS7PaddingContext(object):
def __init__(self, block_size):
self.block_size = block_size
# TODO: more copies than necessary, we should use zero-buffer (#193)
self._buffer = b""
def update(self, data):
if self._buffer is None:
raise AlreadyFinalized("Context was already finalized")
if isinstance(data, six.text_type):
raise TypeError("Unicode-objects must be encoded before padding")
self._buffer += data
finished_blocks = len(self._buffer) // (self.block_size // 8)
result = self._buffer[:finished_blocks * (self.block_size // 8)]
self._buffer = self._buffer[finished_blocks * (self.block_size // 8):]
return result
def finalize(self):
if self._buffer is None:
raise AlreadyFinalized("Context was already finalized")
pad_size = self.block_size // 8 - len(self._buffer)
result = self._buffer + six.int2byte(pad_size) * pad_size
self._buffer = None
return result
@utils.register_interface(interfaces.PaddingContext)
class _PKCS7UnpaddingContext(object):
def __init__(self, block_size):
self.block_size = block_size
# TODO: more copies than necessary, we should use zero-buffer (#193)
self._buffer = b""
def update(self, data):
if self._buffer is None:
raise AlreadyFinalized("Context was already finalized")
if isinstance(data, six.text_type):
raise TypeError("Unicode-objects must be encoded before unpadding")
self._buffer += data
finished_blocks = max(
len(self._buffer) // (self.block_size // 8) - 1,
0
)
result = self._buffer[:finished_blocks * (self.block_size // 8)]
self._buffer = self._buffer[finished_blocks * (self.block_size // 8):]
return result
def finalize(self):
if self._buffer is None:
raise AlreadyFinalized("Context was already finalized")
if len(self._buffer) != self.block_size // 8:
raise ValueError("Invalid padding bytes")
valid = _lib.Cryptography_check_pkcs7_padding(
self._buffer, self.block_size // 8
)
if not valid:
raise ValueError("Invalid padding bytes")
pad_size = six.indexbytes(self._buffer, -1)
res = self._buffer[:-pad_size]
self._buffer = None
return res

View file

@ -0,0 +1,14 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function

View file

@ -0,0 +1,71 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
import struct
import six
from cryptography.exceptions import (
InvalidToken, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.primitives import constant_time, hmac
from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512
class HOTP(object):
def __init__(self, key, length, algorithm, backend):
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HMACBackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
if len(key) < 16:
raise ValueError("Key length has to be at least 128 bits.")
if not isinstance(length, six.integer_types):
raise TypeError("Length parameter must be an integer type")
if length < 6 or length > 8:
raise ValueError("Length of HOTP has to be between 6 to 8.")
if not isinstance(algorithm, (SHA1, SHA256, SHA512)):
raise TypeError("Algorithm must be SHA1, SHA256 or SHA512")
self._key = key
self._length = length
self._algorithm = algorithm
self._backend = backend
def generate(self, counter):
truncated_value = self._dynamic_truncate(counter)
hotp = truncated_value % (10 ** self._length)
return "{0:0{1}}".format(hotp, self._length).encode()
def verify(self, hotp, counter):
if not constant_time.bytes_eq(self.generate(counter), hotp):
raise InvalidToken("Supplied HOTP value does not match")
def _dynamic_truncate(self, counter):
ctx = hmac.HMAC(self._key, self._algorithm, self._backend)
ctx.update(struct.pack(">Q", counter))
hmac_value = ctx.finalize()
offset_bits = six.indexbytes(hmac_value, len(hmac_value) - 1) & 0b1111
offset = int(offset_bits)
p = hmac_value[offset:offset + 4]
return struct.unpack(">I", p)[0] & 0x7fffffff

View file

@ -0,0 +1,41 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
from cryptography.exceptions import (
InvalidToken, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.primitives import constant_time
from cryptography.hazmat.primitives.twofactor.hotp import HOTP
class TOTP(object):
def __init__(self, key, length, algorithm, time_step, backend):
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HMACBackend",
_Reasons.BACKEND_MISSING_INTERFACE
)
self._time_step = time_step
self._hotp = HOTP(key, length, algorithm, backend)
def generate(self, time):
counter = int(time / self._time_step)
return self._hotp.generate(counter)
def verify(self, totp, time):
if not constant_time.bytes_eq(self.generate(time), totp):
raise InvalidToken("Supplied TOTP value does not match")