PageRenderTime 44ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/module/_hashlib/interp_hashlib.py

https://bitbucket.org/pypy/pypy/
Python | 179 lines | 145 code | 25 blank | 9 comment | 17 complexity | 7206fc742334fa27b68aeb036d3f3930 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from __future__ import with_statement
  2. from rpython.rlib import rgc, ropenssl
  3. from rpython.rlib.objectmodel import we_are_translated
  4. from rpython.rlib.rstring import StringBuilder
  5. from rpython.rtyper.lltypesystem import lltype, rffi
  6. from rpython.tool.sourcetools import func_renamer
  7. from pypy.interpreter.baseobjspace import W_Root
  8. from pypy.interpreter.error import OperationError, oefmt
  9. from pypy.interpreter.gateway import unwrap_spec, interp2app
  10. from pypy.interpreter.typedef import TypeDef, GetSetProperty
  11. from pypy.module.thread.os_lock import Lock
  12. algorithms = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')
  13. def hash_name_mapper_callback(obj_name, userdata):
  14. if not obj_name:
  15. return
  16. # Ignore aliased names, they pollute the list and OpenSSL appears
  17. # to have a its own definition of alias as the resulting list
  18. # still contains duplicate and alternate names for several
  19. # algorithms.
  20. if rffi.cast(lltype.Signed, obj_name[0].c_alias):
  21. return
  22. try:
  23. space = global_name_fetcher.space
  24. w_name = space.wrap(rffi.charp2str(obj_name[0].c_name))
  25. global_name_fetcher.meth_names.append(w_name)
  26. except OperationError as e:
  27. global_name_fetcher.w_error = e
  28. class NameFetcher:
  29. def setup(self, space):
  30. self.space = space
  31. self.meth_names = []
  32. self.w_error = None
  33. def _cleanup_(self):
  34. self.__dict__.clear()
  35. global_name_fetcher = NameFetcher()
  36. def fetch_names(space):
  37. global_name_fetcher.setup(space)
  38. ropenssl.init_digests()
  39. ropenssl.OBJ_NAME_do_all(ropenssl.OBJ_NAME_TYPE_MD_METH,
  40. hash_name_mapper_callback, None)
  41. if global_name_fetcher.w_error:
  42. raise global_name_fetcher.w_error
  43. meth_names = global_name_fetcher.meth_names
  44. global_name_fetcher.meth_names = None
  45. return space.call_function(space.w_frozenset, space.newlist(meth_names))
  46. class W_Hash(W_Root):
  47. NULL_CTX = lltype.nullptr(ropenssl.EVP_MD_CTX.TO)
  48. ctx = NULL_CTX
  49. def __init__(self, space, name, copy_from=NULL_CTX):
  50. self.name = name
  51. digest_type = self.digest_type_by_name(space)
  52. self.digest_size = rffi.getintfield(digest_type, 'c_md_size')
  53. # Allocate a lock for each HASH object.
  54. # An optimization would be to not release the GIL on small requests,
  55. # and use a custom lock only when needed.
  56. self.lock = Lock(space)
  57. ctx = lltype.malloc(ropenssl.EVP_MD_CTX.TO, flavor='raw')
  58. rgc.add_memory_pressure(ropenssl.HASH_MALLOC_SIZE + self.digest_size)
  59. try:
  60. if copy_from:
  61. ropenssl.EVP_MD_CTX_copy(ctx, copy_from)
  62. else:
  63. ropenssl.EVP_DigestInit(ctx, digest_type)
  64. self.ctx = ctx
  65. except:
  66. lltype.free(ctx, flavor='raw')
  67. raise
  68. self.register_finalizer(space)
  69. def _finalize_(self):
  70. ctx = self.ctx
  71. if ctx:
  72. self.ctx = lltype.nullptr(ropenssl.EVP_MD_CTX.TO)
  73. ropenssl.EVP_MD_CTX_cleanup(ctx)
  74. lltype.free(ctx, flavor='raw')
  75. def digest_type_by_name(self, space):
  76. digest_type = ropenssl.EVP_get_digestbyname(self.name)
  77. if not digest_type:
  78. raise oefmt(space.w_ValueError, "unknown hash function")
  79. return digest_type
  80. def descr_repr(self, space):
  81. addrstring = self.getaddrstring(space)
  82. return space.wrap("<%s HASH object at 0x%s>" % (
  83. self.name, addrstring))
  84. @unwrap_spec(string='bufferstr')
  85. def update(self, space, string):
  86. with rffi.scoped_nonmovingbuffer(string) as buf:
  87. with self.lock:
  88. # XXX try to not release the GIL for small requests
  89. ropenssl.EVP_DigestUpdate(self.ctx, buf, len(string))
  90. def copy(self, space):
  91. "Return a copy of the hash object."
  92. with self.lock:
  93. return W_Hash(space, self.name, copy_from=self.ctx)
  94. def digest(self, space):
  95. "Return the digest value as a string of binary data."
  96. digest = self._digest(space)
  97. return space.newbytes(digest)
  98. def hexdigest(self, space):
  99. "Return the digest value as a string of hexadecimal digits."
  100. digest = self._digest(space)
  101. hexdigits = '0123456789abcdef'
  102. result = StringBuilder(self.digest_size * 2)
  103. for c in digest:
  104. result.append(hexdigits[(ord(c) >> 4) & 0xf])
  105. result.append(hexdigits[ ord(c) & 0xf])
  106. return space.wrap(result.build())
  107. def get_digest_size(self, space):
  108. return space.wrap(self.digest_size)
  109. def get_block_size(self, space):
  110. digest_type = self.digest_type_by_name(space)
  111. block_size = rffi.getintfield(digest_type, 'c_block_size')
  112. return space.wrap(block_size)
  113. def get_name(self, space):
  114. return space.wrap(self.name)
  115. def _digest(self, space):
  116. with lltype.scoped_alloc(ropenssl.EVP_MD_CTX.TO) as ctx:
  117. with self.lock:
  118. ropenssl.EVP_MD_CTX_copy(ctx, self.ctx)
  119. digest_size = self.digest_size
  120. with rffi.scoped_alloc_buffer(digest_size) as buf:
  121. ropenssl.EVP_DigestFinal(ctx, buf.raw, None)
  122. ropenssl.EVP_MD_CTX_cleanup(ctx)
  123. return buf.str(digest_size)
  124. W_Hash.typedef = TypeDef(
  125. 'HASH',
  126. __repr__=interp2app(W_Hash.descr_repr),
  127. update=interp2app(W_Hash.update),
  128. copy=interp2app(W_Hash.copy),
  129. digest=interp2app(W_Hash.digest),
  130. hexdigest=interp2app(W_Hash.hexdigest),
  131. #
  132. digest_size=GetSetProperty(W_Hash.get_digest_size),
  133. digestsize=GetSetProperty(W_Hash.get_digest_size),
  134. block_size=GetSetProperty(W_Hash.get_block_size),
  135. name=GetSetProperty(W_Hash.get_name),
  136. )
  137. W_Hash.typedef.acceptable_as_base_class = False
  138. @unwrap_spec(name=str, string='bufferstr')
  139. def new(space, name, string=''):
  140. w_hash = W_Hash(space, name)
  141. w_hash.update(space, string)
  142. return space.wrap(w_hash)
  143. # shortcut functions
  144. def make_new_hash(name, funcname):
  145. @func_renamer(funcname)
  146. @unwrap_spec(string='bufferstr')
  147. def new_hash(space, string=''):
  148. return new(space, name, string)
  149. return new_hash
  150. for _name in algorithms:
  151. _newname = 'new_%s' % (_name,)
  152. globals()[_newname] = make_new_hash(_name, _newname)