information from ASN.1 structures into unicode.
-
Digest calculation
------------------
Additionally there is **DigestType** class which may be needed to
construct CMS SignedData objects or add signatures to them.
+
+MAC calculation
+---------------
+
+Mac is Message Authentication Code - it is like keyed digest, which
+depends not only on message, but also on key, which should be used both
+when initially computing MAC and when verifying it. MACs can be viewed
+as "digital signatures with symmetric keys".
+
+Most common type of MAC is HMAC (i.e. hash-based MAC), described in
+[RFC 2104](https://tools.ietf.org/html/rfc2104), but there are other,
+for instance [GOST 28147-89](https://tools.ietf.org/html/rfc5830) defines MAC based on symmetric cipher.
+Also GSM 0348 uses DES symmetric cipher as MAC. OpenSSL supports
+GOST mac via loadable engine module, but doesn't seem to support any
+non-HMAC MAC in the core. So, MAC is only test in the test suite which
+requires loadable engine.
+
Symmetric ciphers
-----------------
3. OCSP ([RFC 6960](http://tools.ietf.org/html/rfc6960))request creation and response parsing
4. Timestamping ([RFC 3161](http://tools.ietf.org/html/rfc3161))
support.
-6. MAC support. Few people know that there is more MACs than just HMAC,
-and even fewer, that OpenSSL supports them.
vim: spelllang=en tw=72
__all__ = ['LibCryptoError','clear_err_stack']
+def _check_null(s):
+ """
+ Handle transparently NULL returned from error reporting functions
+ instead of strings
+ """
+ if s is None:
+ return ""
+ return s
+
class LibCryptoError(Exception):
"""
Exception for libcrypto errors. Adds all the info, which can be
e=libcrypto.ERR_get_error()
m = msg
while e != 0:
- m+="\n\t"+libcrypto.ERR_lib_error_string(e)+":"+\
- libcrypto.ERR_func_error_string(e)+":"+\
- libcrypto.ERR_reason_error_string(e)
+ m+="\n\t"+_check_null(libcrypto.ERR_lib_error_string(e))+":"+\
+ _check_null(libcrypto.ERR_func_error_string(e))+":"+\
+ _check_null(libcrypto.ERR_reason_error_string(e))
e=libcrypto.ERR_get_error()
self.args=(m,)
--- /dev/null
+# -*- encoding: utf-8 -*-
+"""
+This module provides interface to OpenSSL MAC functions.
+
+It has not only HMAC support, but can support other types of MAC.
+
+"""
+
+from ctypescrypto.digest import Digest,DigestType,DigestError
+from ctypescrypto.oid import Oid
+from ctypescrypto import libcrypto
+from ctypes import c_int,c_char_p, c_void_p, c_size_t,POINTER,create_string_buffer,pointer
+
+__all__ = ['MAC','DigestError']
+class MAC(Digest):
+ """
+ This object represents MAC context. It is quite simular
+ to digest algorithm. It is simular to hmac objects provided
+ by standard library
+ """
+ def __init__(self,algorithm,key,digest=None,**kwargs):
+ """
+ Constructor has to obligatory arguments:
+
+ @param algorithm - which is name of MAC algorithm i.e 'hmac' or
+ 'gost-mac' or equivalent Oid object
+ @param key - byte buffer with key.
+
+ Optional parameters are:
+ digest - Oid or name of the digest algorithm to use. If none
+ specified, OpenSSL will try to derive one from the MAC
+ algorithm (or if algorithm is hmac, we'll substititute md5
+ for compatibility with standard hmac module
+
+ any other keyword argument is passed to EVP_PKEY_CTX as string
+ option.
+
+ """
+ if isinstance(algorithm,str):
+ self.algorithm=Oid(algorithm)
+ elif isinstance(algorithm,Oid):
+ self.algorithm=algorithm
+ else:
+ raise TypeError("Algorthm must be string or Oid")
+ if self.algorithm==Oid('hmac') and digest is None:
+ digest='md5'
+ self.name=self.algorithm.shortname().lower()
+ if digest is not None:
+ self.digest_type=DigestType(digest)
+ self.name+='-'+self.digest_type.digest_name
+ d=self.digest_type.digest
+ else:
+ self.digest_type=None
+ d=None
+ self.key=libcrypto.EVP_PKEY_new_mac_key(self.algorithm.nid,None,key,len(key))
+ if self.key is None:
+ raise DigestError("EVP_PKEY_new_mac_key")
+ pctx=c_void_p()
+ self.ctx = libcrypto.EVP_MD_CTX_create()
+ if self.ctx == 0:
+ raise DigestError("Unable to create digest context")
+ if libcrypto.EVP_DigestSignInit(self.ctx,pointer(pctx),d,None,self.key) <= 0:
+ raise DigestError("Unable to intialize digest context")
+ self.digest_finalized=False
+ if self.digest_type is None:
+ self.digest_type=DigestType(Oid(libcrypto.EVP_MD_type(libcrypto.EVP_MD_CTX_md(self.ctx))))
+ for (name,val) in kwargs.items():
+ if EVP_PKEY_CTX_ctrl_str(ctx,name,val)<=0:
+ raise DigestError("Unable to set mac parameter")
+ self.digest_size = self.digest_type.digest_size()
+ self.block_size = self.digest_type.block_size()
+ def digest(self,data=None):
+ """
+ Method digest is redefined to return keyed MAC value instead of
+ just digest.
+ """
+ if data is not None:
+ self.update(data)
+ b=create_string_buffer(256)
+ size=c_size_t(256)
+ if libcrypto.EVP_DigestSignFinal(self.ctx,b,pointer(size))<=0:
+ raise DigestError('SignFinal')
+ self.digest_finalized=True
+ return b.raw[:size.value]
+
+libcrypto.EVP_DigestSignFinal.argtypes=(c_void_p,c_char_p,POINTER(c_size_t))
+libcrypto.EVP_DigestSignFinal.restype=c_int
+libcrypto.EVP_DigestSignInit.argtypes=(c_void_p,POINTER(c_void_p),c_void_p,c_void_p,c_void_p)
+libcrypto.EVP_DigestSignInit.restype=c_int
+libcrypto.EVP_PKEY_CTX_ctrl_str.argtypes=(c_void_p,c_char_p,c_char_p)
+libcrypto.EVP_PKEY_CTX_ctrl_str.restype=c_int
+libcrypto.EVP_PKEY_new_mac_key.argtypes=(c_int,c_void_p,c_char_p,c_int)
+libcrypto.EVP_PKEY_new_mac_key.restype=c_void_p
+libcrypto.EVP_MD_CTX_md.argtypes=(c_void_p,)
+libcrypto.EVP_MD_CTX_md.restype=c_void_p
clear_err_stack()
pkey_id=c_int(0)
libcrypto.EVP_PKEY_asn1_get0_info(byref(pkey_id),None,None,None,None,ameth)
- libcrypto.ENGINE_finish(tmpeng)
+ #libcrypto.ENGINE_finish(tmpeng)
if "paramsfrom" in kwargs:
ctx=libcrypto.EVP_PKEY_CTX_new(kwargs["paramsfrom"].key,None)
else:
libcrypto.PEM_write_bio_PUBKEY.argtypes=(c_void_p,c_void_p)
libcrypto.i2d_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
libcrypto.i2d_PrivateKey_bio.argtypes=(c_void_p,c_void_p)
+libcrypto.ENGINE_finish.argtypes=(c_void_p,)
--- /dev/null
+# -*- encoding: utf-8 -*-
+from ctypescrypto.oid import Oid
+from base64 import b16decode,b16encode
+from ctypescrypto.mac import *
+from ctypescrypto.engine import set_default
+import unittest
+
+class TestMac(unittest.TestCase):
+ def test_hmac_default(self):
+ d=MAC('hmac',key='1234'*4)
+ d.update('The Quick brown fox jumps over the lazy dog\n')
+ self.assertEqual(d.name,'hmac-md5')
+ self.assertEqual(d.hexdigest(),'A9C16D91CDF2A99273B72336D0D16B56')
+ def test_hmac_digestdataa(self):
+ d=MAC('hmac',key='1234'*4)
+ h=d.hexdigest('The Quick brown fox jumps over the lazy dog\n')
+ self.assertEqual(d.name,'hmac-md5')
+ self.assertEqual(h,'A9C16D91CDF2A99273B72336D0D16B56')
+ def test_hmac_byoid(self):
+ d=MAC(Oid('hmac'),key='1234'*4)
+ d.update('The Quick brown fox jumps over the lazy dog\n')
+ self.assertEqual(d.name,'hmac-md5')
+ self.assertEqual(d.hexdigest(),'A9C16D91CDF2A99273B72336D0D16B56')
+ def test_mac_wrongtype(self):
+ with self.assertRaises(TypeError):
+ d=MAC(Oid('hmac').nid,key='1234'*4)
+ def test_hmac_sha256(self):
+ d=MAC('hmac',key='1234'*16,digest='sha256')
+ d.update('The Quick brown fox jumps over the lazy dog\n')
+ self.assertEqual(d.name,'hmac-sha256')
+ self.assertEqual(d.hexdigest(),'BEBA086E1C67200664DCDEEC697D99DB1A8DAA72933A36B708FC5FD568173095')
+ def test_gostmac(self):
+ set_default('gost')
+ d=MAC('gost-mac',key='1234'*8)
+ d.update('The Quick brown fox jumps over the lazy dog\n')
+ self.assertEqual(d.name,'gost-mac')
+ self.assertEqual(d.digest_size,4)
+ self.assertEqual(d.hexdigest(),'76F25AE3')
+ with self.assertRaisesRegexp(DigestError,"invalid mac key length"):
+ d=MAC('gost-mac',key='1234'*4)
+
+if __name__ == "__main__":
+ unittest.main()