예제 #1
0
    def from_primes(cls, p, q):
        n = p * q
        t = (p - 1) * (q - 1)
        e = 65537

        # Handle the (normally very, very low probability) special
        # case that n is a small number - this may the case for
        # some test code using small primes
        if e >= n:
            e = n / 2 + n % 2

        while t % e == 0:
            e += 1
            # The value 56 is hardcoded for a high probability test -
            # however - see versile.crypto.math._SMALL_PRIMES doc -
            # with that variable set properly, the test is expected to
            # nearly always be resolved based on deterministic Euler
            # sieve tests
            while not is_prime(e, 56):  # HARDCODED
                e += 1
        d = mod_inv(e, t)
        if d is None:
            raise VCryptoException('Could not generate key')
        if (d * e) % t == 1:
            keydata = (n, e, d, p, q)
            return _VLocalRSAKey(keydata)
        else:
            raise VCryptoException('Could not generate key')
예제 #2
0
    def __init__(self, num_transform, in_size, out_size, iv, mode, encrypt,
                 rand):
        self.__num_transform = num_transform
        self.__in_size = in_size
        self.__out_size = out_size
        self.__encrypt = encrypt
        self.__rand = rand

        super(_VNumBlockTransform, self).__init__(in_size)

        if encrypt:
            self.__plainsize = in_size
            self.__ciphersize = out_size
        else:
            self.__plainsize = out_size
            self.__ciphersize = in_size

        # iv always uses plaintext blocksize
        if not isinstance(iv, bytes) or len(iv) != self.__plainsize:
            raise VCryptoException('Invalid initialization vector')
        self.__iv = iv

        if mode == 'cbc':
            self.__transform = self.__transform_cbc
        else:
            raise VCryptoException('Mode not supported')
예제 #3
0
    def __init__(self, keydata, encrypt):
        def _convert(num):
            if num is not None:
                return long(num)
            else:
                return None

        self.__keydata = [_convert(item) for item in keydata]
        n, e, d = self.__keydata[:3]

        if encrypt:
            if e is None:
                raise VCryptoException('Encrypt requires public key')
            py_key = RSA.construct((n, e))
            self.__transform = lambda num: py_key.encrypt(num, None)[0]
        else:
            if d is None:
                raise VCryptoException('Decrypt requires private key')
            if e is not None:
                py_key = RSA.construct((n, e, d))
                self.__transform = lambda num: py_key.decrypt(num)
            else:
                # Rephrase as 'encrypt', PyCrypto does not have private-only
                py_key = RSA.construct((n, d))
                self.__transform = lambda num: py_key.encrypt(num, None)[0]
예제 #4
0
 def load(self, keydata):
     if not isinstance(keydata, bytes):
         raise VCryptoException('Keydata must be in bytes format')
     min_l, max_l, size_inc = self.constraints()
     if not min_l <= len(keydata) <= max_l or len(keydata) % size_inc:
         raise VCryptoException('Invalid key length')
     return self._gen_key(keydata)
예제 #5
0
    def export_spki_public_key(cls, key, fmt=VX509Format.PEM_BLOCK):
        """Exports a X.509 SPKI public key.

        :param key: key to export
        :type  key: :class:`versile.crypto.VAsymmetricKey`
        :param fmt: format to export to
        :type  fmt: :class:`VX509Format` constant
        :returns:   exported key data

        Exports an encoding of the key's :term:`ASN.1` representation
        defined by X.509 SubjectPublicKeyInfo encoding format,
        associated with the PEM header 'BEGIN PUBLIC KEY'.

        .. note::

            For the :term:`PKCS#1`\ encoding format (PEM header 'BEGIN
            RSA PUBLIC KEY'), see :meth:`export_public_key`\ .

        """
        if not isinstance(key, VAsymmetricKey):
            raise TypeError('Invalid key type')
        if key.cipher_name != 'rsa':
            raise VCryptoException('Encoding not supported')
        if key.has_public:
            n, e = key.keydata[:2]

            # Create spki structure
            from versile.crypto.x509.asn1def.cert import SubjectPublicKeyInfo
            asn1 = SubjectPublicKeyInfo().create()
            # - algorithm
            alg = AlgorithmIdentifier().create()
            _alg_id = VObjectIdentifier(1, 2, 840, 113549, 1, 1, 1)
            alg.append(_alg_id, name='algorithm')
            alg.append(VASN1Null(), name='parameters')
            asn1.append(alg, name='algorithm')
            # - keydata
            spk = cls.export_public_key(key, fmt=VX509Format.DER)
            spk = VBitfield.from_octets(spk)
            asn1.append(spk, name='subjectPublicKey')

            if not asn1.validate():
                raise VCryptoException('ASN.1 structure validation error')

            if fmt == VX509Format.ASN1:
                return asn1
            der = asn1.encode_der()
            if fmt == VX509Format.DER:
                return der
            if fmt == VX509Format.PEM:
                if _pyver == 2:
                    return _s2b(base64.encodestring(_b2s(der)))
                else:
                    return base64.encodebytes(der)
            elif fmt == VX509Format.PEM_BLOCK:
                return encode_pem_block(b'PUBLIC KEY', der)
            else:
                raise VCryptoException('Invalid encoding')
예제 #6
0
    def __init__(self, keydata, iv, mode, encrypt, blocksize):

        super(_PyBlockTransform, self).__init__(blocksize=blocksize)

        if not isinstance(keydata, bytes) or not 1 <= len(keydata) <= 56:
            raise VCryptoException('Invalid key data')
        elif not isinstance(iv, bytes) or len(iv) != self.blocksize:
            raise VCryptoException('Invalid initialization vector')

        self.__encrypt = bool(encrypt)
        self.__cipher = self._pycrypto_cipher(keydata, iv, mode)
예제 #7
0
 def __block_transform(self, block, in_size, out_size):
     if len(block) != in_size:
         raise VCryptoException('Invalid input block size')
     num = bytes_to_posint(block)
     trans_num = self.__num_transform(num)
     trans_block = posint_to_bytes(trans_num)
     trans_block_len = len(trans_block)
     if trans_block_len > out_size:
         raise VCryptoException('Invalid output block size')
     elif trans_block_len < out_size:
         pad_num = out_size - trans_block_len
         trans_block = pad_num * b'\x00' + trans_block
     return trans_block
예제 #8
0
    def __init__(self, keydata, iv, mode, encrypt):
        super(_VLocalBlowfishTransform, self).__init__(blocksize=8)

        self.__cipher = Blowfish(keydata)
        if not isinstance(iv, bytes) or len(iv) != self.blocksize:
            raise VCryptoException('Invalid initialization vector')
        self.__iv = iv
        if mode == 'cbc':
            self.__transform = self.__transform_cbc
        elif mode == 'ofb':
            self.__transform = self.__transform_ofb
        else:
            raise VCryptoException('Mode not supported')
        self.__encrypt = bool(encrypt)
예제 #9
0
    def raw_generate(self, bits, sec_id_data):
        if bits < 512 or bits % 8:
            raise VCryptoException('Invalid number of bits')
        try:
            hmac_input = sec_id_data.encode('utf8')
        except:
            raise VCryptoException('Could not UTF-8 encode sec_id_data')
        hmac_input += b':Scheme:dia' + _val2b(bits)

        crypto = VLocalCrypto()
        p_rand = VPseudoRandomHMAC(hash_cls=crypto.sha256,
                                   secret=b'',
                                   seed=hmac_input)
        return crypto.rsa.key_factory.generate(p_rand, bits // 8)
예제 #10
0
    def export_public_key(cls, key, fmt=VX509Format.PEM_BLOCK):
        """Exports a public key in PKCS#1 format.

        :param key: key to export
        :type  key: :class:`versile.crypto.VAsymmetricKey`
        :param fmt: format to export to
        :type  fmt: :class:`VX509Format` constant
        :returns:   exported key data

        Exports an encoding of the key's :term:`ASN.1` representation
        defined by :term:`PKCS#1`\, associated with the PEM header
        'BEGIN RSA PUBLIC KEY'.

        .. note::

            For the X.509 SubjectPublicKeyInfo encoding format (PEM
            header 'BEGIN PUBLIC KEY'), see
            :meth:`export_spki_public_key`\ .

        """
        if not isinstance(key, VAsymmetricKey):
            raise TypeError('Invalid key type')
        if key.cipher_name == 'rsa':
            if key.has_public:
                n, e = key.keydata[:2]

                asn1 = RSAPublicKey().create()
                asn1.append(n, name='modulus')
                asn1.append(e, name='publicExponent')
                if not asn1.validate():
                    raise VCryptoException('ASN.1 structure validation error')

                if fmt == VX509Format.ASN1:
                    return asn1
                der = asn1.encode_der()
                if fmt == VX509Format.DER:
                    return der
                if fmt == VX509Format.PEM:
                    if _pyver == 2:
                        return _s2b(base64.encodestring(_b2s(der)))
                    else:
                        return base64.encodebytes(der)
                elif fmt == VX509Format.PEM_BLOCK:
                    return encode_pem_block(b'RSA PUBLIC KEY', der)
                else:
                    raise VCryptoException('Invalid encoding')
        else:
            raise VCryptoException('Encoding not supported')
예제 #11
0
 def __transform_cbc(self, data):
     len_data = len(data)
     if len_data % self.__in_size:
         raise VCryptoException('Data not block aligned')
     result = []
     start = 0
     while start < len_data:
         end = start + self.__in_size
         block = data[start:end]
         if self.__encrypt:
             if _pyver == 2:
                 indata = b''.join([
                     _s2b(_b_chr(_b_ord(a) ^ _b_ord(b)))
                     for a, b in zip(block, self.__iv)
                 ])
             else:
                 indata = bytes([a ^ b for a, b in zip(block, self.__iv)])
             # Appending random data, similar to RSAES-PKCS1-V1_5-ENCRYPT
             _rand_data = self.__rand(8)
             indata = b''.join((b'\x02', indata, b'\x00', _rand_data))
             cipher = self.__block_transform(indata, self.__in_size + 10,
                                             self.__out_size)
             # Can only take plaintext size bytes as the carry-on mask
             self.__iv = cipher[:(self.__plainsize)]
             result.append(cipher)
         else:
             deciphered = self.__block_transform(block, self.__in_size,
                                                 self.__out_size + 10)
             if _pyver == 2:
                 if deciphered[0] != b'\x02' or deciphered[-9] != b'\x00':
                     raise VCryptoException('Invalid RSA ciphertext')
             else:
                 if deciphered[0] != 0x02 or deciphered[-9] != 0x00:
                     raise VCryptoException('Invalid RSA ciphertext')
             deciphered = deciphered[1:-8]
             if _pyver == 2:
                 plaintext = b''.join([
                     _s2b(_b_chr(_b_ord(a) ^ _b_ord(b)))
                     for a, b in zip(deciphered, self.__iv)
                 ])
             else:
                 plaintext = bytes(
                     [a ^ b for a, b in zip(deciphered, self.__iv)])
             # Can only take plaintext size bytes as the carry-on mask
             self.__iv = block[:(self.__plainsize)]
             result.append(plaintext)
         start += self.__in_size
     return b''.join(result)
예제 #12
0
    def public_ca(self, **kargs):
        """Returns the :term:`Public CA` keypair and certificate.

        :param unsafe: must be set to True
        :type  unsafe: bool
        :returns:      (CA keypair, CA certificate)
        :rtype:        (\ :class:`versile.crypto.VAsymmetricKey`\ ,
                          :class:`versile.crypto.x509.cert.VX509Certificate`\ )
        :raises:       :exc:`versile.crypto.VCryptoException`

        The keyword argument *unsafe* must be set to True when calling
        this method, otherwise an exception is raised. This is a
        safety mechanism to help prevent accidental use of
        :term:`Public CA` crededentials. Using the key for encryption
        or certificate signing is inherently unsafe as the full
        :term:`Public CA` keypair is publicly available.

        .. warning::

            The :term:`Public CA` keypair should not be used for
            anything except signing non-certified keys for protocols
            which require a CA signature due to technical constraints

        """
        if 'unsafe' not in kargs or not kargs['unsafe']:
            raise VCryptoException('The "unsafe" keyword must be set.')
        _keyloader = VX509Crypto.import_private_key
        ca_key = _keyloader(_STD_CA_KEYDATA, fmt=VX509Format.PEM_BLOCK)
        _certloader = VX509Certificate.import_cert
        ca_cert = _certloader(_STD_CA_CERTDATA, fmt=VX509Format.PEM_BLOCK)
        return ca_key, ca_cert
예제 #13
0
 def __init__(self, keylen):
     if keylen not in (16, 24, 32):
         raise VCryptoException('Not a supported AES key length')
     else:
         self.__keylen = keylen
     super_init = super(_PyAES, self).__init__
     super_init('aes%s' % self.__keylen, ('cbc', 'ofb'), True)
예제 #14
0
 def _coeff(self):
     if self.__coeff is None:
         p, q = self.__keydata[3], self.__keydata[4]
         if p is None or q is None:
             raise VCryptoException()
         self.__coeff = mod_inv(q, p)
     return self.__coeff
예제 #15
0
 def _exp2(self):
     if self.__exp2 is None:
         d, q = self.__keydata[2], self.__keydata[4]
         if d is None or q is None:
             raise VCryptoException()
         self.__exp2 = d % (q - 1)
     return self.__exp2
예제 #16
0
 def _exp1(self):
     if self.__exp1 is None:
         d, p = self.__keydata[2], self.__keydata[3]
         if d is None or p is None:
             raise VCryptoException()
         self.__exp1 = d % (p - 1)
     return self.__exp1
예제 #17
0
    def encipher(self, data):
        """Encipher plaintext and return result.

        :param data:  plaintext to encipher (8 bytes)
        :type  data:  bytes
        :returns:     enciphered data (8 bytes)
        :rtype:       bytes

        The data must align with 8-byte blocksize.

        .. note::

            Enciphering is performed without any kind of chaining, and
            the same plaintext will always return the same enciphered
            block of data. In order to use securely as a cipher, it is
            normally required that the cipher is combined with
            chaining techniques.

        """
        len_data = len(data)
        if len_data % 8:
            raise VCryptoException('Data not aligned with 8-byte blocksize')
        if len_data == 8:
            return self._encipher_block(data)
        else:
            result = []
            start = 0
            while start < len_data:
                end = start + 8
                block = data[start:end]
                result.append(self._encipher_block(block))
                start += 8
            return b''.join(result)
예제 #18
0
    def decipher(self, data):
        """Decipher encrypted data and return deciphered plaintext.

        :param data:  enciphered data (8 bytes)
        :type  data:  bytes
        :returns:     deciphered plaintext (8 bytes)
        :rtype:       bytes

        The block of enciphered data must be a multiple of 8 bytes.

        """
        len_data = len(data)
        if len_data % 8:
            raise VCryptoException('Data not aligned with 8-byte blocksize')
        if len_data == 8:
            return self._decipher_block(data)
        else:
            result = []
            start = 0
            while start < len_data:
                end = start + 8
                block = data[start:end]
                result.append(self._decipher_block(block))
                start += 8
            return b''.join(result)
예제 #19
0
 def __transform_cbc(self, data):
     len_data = len(data)
     if len_data % 8:
         raise VCryptoException('Input not aligned to blocksize')
     result = []
     start = 0
     while start < len_data:
         end = start + 8
         block = data[start:end]
         if self.__encrypt:
             if _pyver == 2:
                 indata = b''.join([
                     _s2b(_b_chr(_b_ord(a) ^ _b_ord(b)))
                     for a, b in zip(block, self.__iv)
                 ])
             else:
                 indata = bytes([a ^ b for a, b in zip(block, self.__iv)])
             cipher = self.__cipher.encipher(indata)
             self.__iv = cipher
             result.append(cipher)
         else:
             deciphered = self.__cipher.decipher(block)
             if _pyver == 2:
                 plaintext = b''.join([
                     _s2b(_b_chr(_b_ord(a) ^ _b_ord(b)))
                     for a, b in zip(deciphered, self.__iv)
                 ])
             else:
                 plaintext = bytes(
                     [a ^ b for a, b in zip(deciphered, self.__iv)])
             self.__iv = block
             result.append(plaintext)
         start += 8
     return b''.join(result)
예제 #20
0
 def _keydata(self, key):
     if isinstance(key, VKey):
         if key.cipher_name == self.name:
             return key.keydata
         else:
             raise VCryptoException('Key ciphername mismatch')
     raise TypeError('Key must be bytes or a Key object')
예제 #21
0
    def message(self, plaintext):
        """Returns an encrypted message for a provided plaintext.

        :param plaintext: the plaintext to encode and encrypt
        :type  plaintext: bytes
        :returns:         encrypted message-protected plaintext
        :rtype:           bytes
        :raises:          :exc:`versile.crypto.VCryptoException`

        Raises an exception if provided plaintext is longer than
        :attr:`max_plaintext_len`\ , meaning the plaintext is larger
        than what is allowed inside a single message. Empty plaintext
        is also not allowed.

        """
        plaintext_len = len(plaintext)
        if plaintext_len > self._max_plaintext_len:
            raise VCryptoException('Plaintext too long')
        elif not plaintext:
            raise VCryptoException('Empty plaintext not allowed')

        # Generate padding
        msg_len = 2 + plaintext_len + self._hash_len
        _bsize = self._plaintext_blocksize
        pad_len = msg_len % _bsize
        if pad_len:
            pad_len = _bsize - pad_len

        # Create message content
        encode_len = plaintext_len - 1
        if _pyver == 2:
            plain_len = (_s2b(_b_chr((encode_len & 0xff00) >> 8)) +
                         _s2b(_b_chr(encode_len & 0xff)))
        else:
            plain_len = bytes(
                (((encode_len & 0xff00) >> 8), (encode_len & 0xff)))
        padding = self._pad_provider(pad_len)
        _mac_msg = b''.join(
            (posint_to_bytes(self._msg_num), plain_len, plaintext, padding))
        msg_hash = self._hash_cls.hmac(self._mac_secret, _mac_msg)
        msg = b''.join((plain_len, plaintext, padding, msg_hash))

        # Create encrypted message
        enc_msg = self._encrypter(msg)
        self._msg_num += 1
        return enc_msg
예제 #22
0
 def _pycrypto_cipher(self, keydata, iv, mode):
     if mode == 'cbc':
         _mode = AES.MODE_CBC
     elif mode == 'ofb':
         _mode = AES.MODE_OFB
     else:
         raise VCryptoException('Mode not supported')
     return AES.new(keydata, _mode, iv)
예제 #23
0
 def __max_num(self, key):
     try:
         max_num = self.__num_cipher.encrypter(key).max_number
     except:
         try:
             max_num = self.__num_cipher.decrypter(key).max_number
         except:
             raise VCryptoException('Could not determine max number')
     return max_num
예제 #24
0
 def block_cipher(self, cipher_name):
     if cipher_name == 'blowfish':
         return _VLocalBlowfish()
     elif cipher_name == 'blowfish128':
         return _VLocalBlowfish128()
     elif cipher_name == 'rsa':
         num_cipher = self.num_cipher('rsa')
         return num_cipher.block_cipher()
     else:
         raise VCryptoException('Cipher not supported by this provider')
예제 #25
0
    def emsa_pkcs1_v1_5_encode(cls, msg, enc_len, hash_cls):
        """Encodes an EMSA-PKCS1-v1_5-ENCODE message digest.

        :param msg:      binary message to encode
        :type  msg:      bytes
        :param enc_len:  length of encoded message
        :type  enc_len:  int
        :param hash_cls: hash class for message hashing
        :type  hash_cls: :class:`versile.crypto.VHash`
        :returns:        encoded message of length *enc_len*
        :rtype:          bytes
        :raises:         :exc:`versile.crypto.VCryptoException`

        See :term:`PKCS#1` for details. When a strong hash function is
        used, this method produces an encoded representation of *msg*
        which is suitable for digital signatures.

        An exception is raised if the encoding cannot be made
        (typically because the message does not fit inside a bytes
        object of length *enc_len*\ due to the length of *hash_cls*
        hash method digests).

        """
        hasher = hash_cls(msg)
        der = DigestInfo().create()
        alg_id = hash_cls.oid()
        if not alg_id:
            raise VCryptoException('Hash algorithm not supported')
        seq = AlgorithmIdentifier().create()
        seq.append(alg_id, name='algorithm')
        seq.append(VASN1Null(), name='parameters')
        der.append(seq, name='digestAlgorithm')
        der.append(VASN1OctetString(hasher.digest()), name='digest')
        if not der.validate():
            raise VCryptoException('Internal ASN.1 validation error')
        param_T = der.encode_der()
        pad_len = enc_len - len(param_T) - 3
        if pad_len >= 0:
            param_PS = pad_len * b'\xff'
        else:
            raise VCryptoException('Encoding length too small')
        return b''.join((b'\x00', b'\x01', param_PS, b'\x00', param_T))
예제 #26
0
    def result(self):
        """Returns plaintext of a decrypted and decoded message.

        :returns: decoded plaintext
        :rtype:   bytes
        :raises:  :exc:`versile.crypto.VCryptoException`

        Should only be called if :meth:`done` indicates message
        parsing was completed, otherwise an exception may be raised
        due to incomplete message.

        Raises an exception if the message failed to verify against
        the message hash, meaning the message cannot be trusted and
        may have been tampered with.

        """
        if self._invalid:
            raise VCryptoException('Message failed to verify')
        elif self._result is None:
            raise VCryptoException('Message not yet fully decoded')
        return self._result
예제 #27
0
 def import_ascii(self, keydata):
     name, num_data = self._decode_ascii(keydata)
     if not name.startswith(b'VERSILE RSA '):
         raise VCryptoException()
     name = name[12:]
     numbers = self._decode_numbers(num_data)
     if name == b'KEY PAIR':
         if len(numbers) != 5:
             raise VCryptoException()
         keydata = tuple(numbers)
     elif name == b'PUBLIC KEY':
         if len(numbers) != 2:
             raise VCryptoException()
         keydata = (numbers[0], numbers[1], None, None)
     elif name == b'PRIVATE KEY':
         if len(numbers) != 2:
             raise VCryptoException()
         keydata = (numbers[0], None, numbers[1], None, None)
     else:
         raise VCryptoException()
     return _VLocalRSAKey(keydata)
예제 #28
0
 def merge_with(self, key):
     if self.name != key.name:
         raise VCryptoException('Key cipher types do not match')
     if self.keydata[0] != key.keydata[0]:
         raise VCryptoException('Key modulos do not match')
     if self.has_public and key.has_public:
         if self.keydata[1] != key.keydata[1]:
             raise VCryptoException('Key public component mismatch')
     if self.has_private and key.has_private:
         if self.keydata[2] != key.keydata[2]:
             raise VCryptoException('Key private component mismatch')
     if self.has_private:
         n = self.keydata[0]
         e = key.keydata[1]
         d = self.keydata[2]
     else:
         n = self.keydata[0]
         e = self.keydata[1]
         d = key.keydata[2]
     if None in (n, e, d):
         raise VCryptoException('Incomplete key data, cannot merge')
     # Try to recover p, q factors
     p, q = self.keydata[3:]
     if key.keydata[3] is not None:
         p = key.keydata[3]
     if key.keydata[4] is not None:
         q = key.keydata[4]
     if p is None or q is None:
         p = q = None
     elif p * q != n:
         raise VCryptoException('Got p,q, however p*q != n')
     return _VLocalRSAKey((n, e, d, p, q))
예제 #29
0
 def decrypter(self, key, iv=None, mode='cbc'):
     if key is None:
         raise VCryptoException('Requires key')
     if iv is None:
         iv = self.blocksize(key) * b'\x00'
     num_decrypter = self.__num_cipher.decrypter(key)
     return _VNumBlockTransform(num_decrypter,
                                self.c_blocksize(key),
                                self.blocksize(key),
                                iv,
                                mode,
                                encrypt=False,
                                rand=self.__rand)
예제 #30
0
    def hash_cls(self, hash_name):
        hash_oid = None
        if hash_name == 'sha1':
            _hash = hashlib.sha1
            hash_oid = VObjectIdentifier(1, 3, 14, 3, 2, 26)
        elif hash_name == 'sha224':
            _hash = hashlib.sha224
        elif hash_name == 'sha256':
            _hash = hashlib.sha256
            hash_oid = VObjectIdentifier(2, 16, 840, 1, 101, 3, 4, 2, 1)
        elif hash_name == 'sha384':
            _hash = hashlib.sha384
            hash_oid = VObjectIdentifier(2, 16, 840, 1, 101, 3, 4, 2, 2)
        elif hash_name == 'sha512':
            _hash = hashlib.sha512
            hash_oid = VObjectIdentifier(2, 16, 840, 1, 101, 3, 4, 2, 3)
        elif hash_name == 'md5':
            _hash = hashlib.md5
        else:
            raise VCryptoException('Hash method not implemented')

        class HashCls(VHash):
            @classmethod
            def name(cls):
                return hash_name

            @classmethod
            def oid(cls):
                return hash_oid

            @classmethod
            def digest_size(cls):
                return _hash().digest_size

            def __init__(self, data=None):
                self.__hash = _hash()
                super(HashCls, self).__init__(data=data)

            def update(self, data):
                if _pyver == 2:
                    self.__hash.update(_b2s(data))
                else:
                    self.__hash.update(data)

            def digest(self):
                if _pyver == 2:
                    return _s2b(self.__hash.digest())
                else:
                    return self.__hash.digest()

        return HashCls