25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

173 lines
6.5 KiB

  1. """passlib.handlers.oracle - Oracle DB Password Hashes"""
  2. #=============================================================================
  3. # imports
  4. #=============================================================================
  5. # core
  6. from binascii import hexlify, unhexlify
  7. from hashlib import sha1
  8. import re
  9. import logging; log = logging.getLogger(__name__)
  10. # site
  11. # pkg
  12. from passlib.utils import to_unicode, xor_bytes
  13. from passlib.utils.compat import irange, u, \
  14. uascii_to_str, unicode, str_to_uascii
  15. from passlib.crypto.des import des_encrypt_block
  16. import passlib.utils.handlers as uh
  17. # local
  18. __all__ = [
  19. "oracle10g",
  20. "oracle11g"
  21. ]
  22. #=============================================================================
  23. # oracle10
  24. #=============================================================================
  25. def des_cbc_encrypt(key, value, iv=b'\x00' * 8, pad=b'\x00'):
  26. """performs des-cbc encryption, returns only last block.
  27. this performs a specific DES-CBC encryption implementation
  28. as needed by the Oracle10 hash. it probably won't be useful for
  29. other purposes as-is.
  30. input value is null-padded to multiple of 8 bytes.
  31. :arg key: des key as bytes
  32. :arg value: value to encrypt, as bytes.
  33. :param iv: optional IV
  34. :param pad: optional pad byte
  35. :returns: last block of DES-CBC encryption of all ``value``'s byte blocks.
  36. """
  37. value += pad * (-len(value) % 8) # null pad to multiple of 8
  38. hash = iv # start things off
  39. for offset in irange(0,len(value),8):
  40. chunk = xor_bytes(hash, value[offset:offset+8])
  41. hash = des_encrypt_block(key, chunk)
  42. return hash
  43. # magic string used as initial des key by oracle10
  44. ORACLE10_MAGIC = b"\x01\x23\x45\x67\x89\xAB\xCD\xEF"
  45. class oracle10(uh.HasUserContext, uh.StaticHandler):
  46. """This class implements the password hash used by Oracle up to version 10g, and follows the :ref:`password-hash-api`.
  47. It does a single round of hashing, and relies on the username as the salt.
  48. The :meth:`~passlib.ifc.PasswordHash.hash`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods all require the
  49. following additional contextual keywords:
  50. :type user: str
  51. :param user: name of oracle user account this password is associated with.
  52. """
  53. #===================================================================
  54. # algorithm information
  55. #===================================================================
  56. name = "oracle10"
  57. checksum_chars = uh.HEX_CHARS
  58. checksum_size = 16
  59. #===================================================================
  60. # methods
  61. #===================================================================
  62. @classmethod
  63. def _norm_hash(cls, hash):
  64. return hash.upper()
  65. def _calc_checksum(self, secret):
  66. # FIXME: not sure how oracle handles unicode.
  67. # online docs about 10g hash indicate it puts ascii chars
  68. # in a 2-byte encoding w/ the high byte set to null.
  69. # they don't say how it handles other chars, or what encoding.
  70. #
  71. # so for now, encoding secret & user to utf-16-be,
  72. # since that fits, and if secret/user is bytes,
  73. # we assume utf-8, and decode first.
  74. #
  75. # this whole mess really needs someone w/ an oracle system,
  76. # and some answers :)
  77. if isinstance(secret, bytes):
  78. secret = secret.decode("utf-8")
  79. user = to_unicode(self.user, "utf-8", param="user")
  80. input = (user+secret).upper().encode("utf-16-be")
  81. hash = des_cbc_encrypt(ORACLE10_MAGIC, input)
  82. hash = des_cbc_encrypt(hash, input)
  83. return hexlify(hash).decode("ascii").upper()
  84. #===================================================================
  85. # eoc
  86. #===================================================================
  87. #=============================================================================
  88. # oracle11
  89. #=============================================================================
  90. class oracle11(uh.HasSalt, uh.GenericHandler):
  91. """This class implements the Oracle11g password hash, and follows the :ref:`password-hash-api`.
  92. It supports a fixed-length salt.
  93. The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords:
  94. :type salt: str
  95. :param salt:
  96. Optional salt string.
  97. If not specified, one will be autogenerated (this is recommended).
  98. If specified, it must be 20 hexadecimal characters.
  99. :type relaxed: bool
  100. :param relaxed:
  101. By default, providing an invalid value for one of the other
  102. keywords will result in a :exc:`ValueError`. If ``relaxed=True``,
  103. and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning`
  104. will be issued instead. Correctable errors include
  105. ``salt`` strings that are too long.
  106. .. versionadded:: 1.6
  107. """
  108. #===================================================================
  109. # class attrs
  110. #===================================================================
  111. #--GenericHandler--
  112. name = "oracle11"
  113. setting_kwds = ("salt",)
  114. checksum_size = 40
  115. checksum_chars = uh.UPPER_HEX_CHARS
  116. #--HasSalt--
  117. min_salt_size = max_salt_size = 20
  118. salt_chars = uh.UPPER_HEX_CHARS
  119. #===================================================================
  120. # methods
  121. #===================================================================
  122. _hash_regex = re.compile(u("^S:(?P<chk>[0-9a-f]{40})(?P<salt>[0-9a-f]{20})$"), re.I)
  123. @classmethod
  124. def from_string(cls, hash):
  125. hash = to_unicode(hash, "ascii", "hash")
  126. m = cls._hash_regex.match(hash)
  127. if not m:
  128. raise uh.exc.InvalidHashError(cls)
  129. salt, chk = m.group("salt", "chk")
  130. return cls(salt=salt, checksum=chk.upper())
  131. def to_string(self):
  132. chk = self.checksum
  133. hash = u("S:%s%s") % (chk.upper(), self.salt.upper())
  134. return uascii_to_str(hash)
  135. def _calc_checksum(self, secret):
  136. if isinstance(secret, unicode):
  137. secret = secret.encode("utf-8")
  138. chk = sha1(secret + unhexlify(self.salt.encode("ascii"))).hexdigest()
  139. return str_to_uascii(chk).upper()
  140. #===================================================================
  141. # eoc
  142. #===================================================================
  143. #=============================================================================
  144. # eof
  145. #=============================================================================