cos_common.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. # -*- coding: utf-8 -*-
  2. from __future__ import print_function
  3. import struct
  4. import io
  5. try:
  6. range = xrange
  7. except NameError:
  8. pass
  9. def _left_rotate(n, b):
  10. """Left rotate a 32-bit integer n by b bits."""
  11. return ((n << b) | (n >> (32 - b))) & 0xffffffff
  12. def _process_chunk(chunk, h0, h1, h2, h3, h4):
  13. """Process a chunk of data and return the new digest variables."""
  14. assert len(chunk) == 64
  15. w = [0] * 80
  16. # Break chunk into sixteen 4-byte big-endian words w[i]
  17. for i in range(16):
  18. w[i] = struct.unpack(b'>I', chunk[i * 4:i * 4 + 4])[0]
  19. # Extend the sixteen 4-byte words into eighty 4-byte words
  20. for i in range(16, 80):
  21. w[i] = _left_rotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1)
  22. # Initialize hash value for this chunk
  23. a = h0
  24. b = h1
  25. c = h2
  26. d = h3
  27. e = h4
  28. for i in range(80):
  29. if 0 <= i <= 19:
  30. # Use alternative 1 for f from FIPS PB 180-1 to avoid bitwise not
  31. f = d ^ (b & (c ^ d))
  32. k = 0x5A827999
  33. elif 20 <= i <= 39:
  34. f = b ^ c ^ d
  35. k = 0x6ED9EBA1
  36. elif 40 <= i <= 59:
  37. f = (b & c) | (b & d) | (c & d)
  38. k = 0x8F1BBCDC
  39. elif 60 <= i <= 79:
  40. f = b ^ c ^ d
  41. k = 0xCA62C1D6
  42. a, b, c, d, e = ((_left_rotate(a, 5) + f + e + k + w[i]) & 0xffffffff,
  43. a, _left_rotate(b, 30), c, d)
  44. # Add this chunk's hash to result so far
  45. h0 = (h0 + a) & 0xffffffff
  46. h1 = (h1 + b) & 0xffffffff
  47. h2 = (h2 + c) & 0xffffffff
  48. h3 = (h3 + d) & 0xffffffff
  49. h4 = (h4 + e) & 0xffffffff
  50. return h0, h1, h2, h3, h4
  51. class Sha1Hash(object):
  52. """A class that mimics that hashlib api and implements the SHA-1 algorithm."""
  53. name = 'python-sha1'
  54. digest_size = 20
  55. block_size = 64
  56. def __init__(self):
  57. # Initial digest variables
  58. self._h = (
  59. 0x67452301,
  60. 0xEFCDAB89,
  61. 0x98BADCFE,
  62. 0x10325476,
  63. 0xC3D2E1F0,
  64. )
  65. # bytes object with 0 <= len < 64 used to store the end of the message
  66. # if the message length is not congruent to 64
  67. self._unprocessed = b''
  68. # Length in bytes of all data that has been processed so far
  69. self._message_byte_length = 0
  70. def update(self, arg):
  71. """Update the current digest.
  72. This may be called repeatedly, even after calling digest or hexdigest.
  73. Arguments:
  74. arg: bytes, bytearray, or BytesIO object to read from.
  75. """
  76. if isinstance(arg, (bytes, bytearray)):
  77. arg = io.BytesIO(arg)
  78. # Try to build a chunk out of the unprocessed data, if any
  79. chunk = self._unprocessed + arg.read(64 - len(self._unprocessed))
  80. # Read the rest of the data, 64 bytes at a time
  81. while len(chunk) == 64:
  82. self._h = _process_chunk(chunk, *self._h)
  83. self._message_byte_length += 64
  84. chunk = arg.read(64)
  85. self._unprocessed = chunk
  86. return self
  87. def digest(self):
  88. """Produce the final hash value (big-endian) as a bytes object"""
  89. return b''.join(struct.pack(b'>I', h) for h in self._produce_digest())
  90. def hexdigest(self):
  91. """Produce the final hash value (big-endian) as a hex string"""
  92. return '%08x%08x%08x%08x%08x' % self._produce_digest()
  93. def inner_digest(self):
  94. tmp = struct.unpack(">5I", struct.pack("<5I", *self._h))
  95. return '%08x%08x%08x%08x%08x' % tmp
  96. def _produce_digest(self):
  97. """Return finalized digest variables for the data processed so far."""
  98. # Pre-processing:
  99. message = self._unprocessed
  100. message_byte_length = self._message_byte_length + len(message)
  101. # append the bit '1' to the message
  102. message += b'\x80'
  103. # append 0 <= k < 512 bits '0', so that the resulting message length (in bytes)
  104. # is congruent to 56 (mod 64)
  105. message += b'\x00' * ((56 - (message_byte_length + 1) % 64) % 64)
  106. # append length of message (before pre-processing), in bits, as 64-bit big-endian integer
  107. message_bit_length = message_byte_length * 8
  108. message += struct.pack(b'>Q', message_bit_length)
  109. # Process the final chunk
  110. # At this point, the length of the message is either 64 or 128 bytes.
  111. h = _process_chunk(message[:64], *self._h)
  112. if len(message) == 64:
  113. return h
  114. return _process_chunk(message[64:], *h)
  115. def sha1(data):
  116. """SHA-1 Hashing Function
  117. A custom SHA-1 hashing function implemented entirely in Python.
  118. Arguments:
  119. data: A bytes or BytesIO object containing the input message to hash.
  120. Returns:
  121. A hex SHA-1 digest of the input message.
  122. """
  123. return Sha1Hash().update(data).hexdigest()
  124. class Sha1Util(object):
  125. @staticmethod
  126. def get_sha1_by_slice(file_name, slice_size):
  127. """ Get SHA array based on Qcloud Slice Upload Interface
  128. :param file_name: local file path
  129. :param slice_size: slice size in bit
  130. :return: sha array like [{“offset”:0, “datalen”:1024,”datasha”:”aaa”}, {}, {}]
  131. """
  132. from os import path
  133. with open(file_name, 'rb') as f:
  134. result = []
  135. file_size = path.getsize(file_name)
  136. sha1_obj = Sha1Hash()
  137. for current_offset in range(0, file_size, slice_size):
  138. data_length = min(slice_size, file_size - current_offset)
  139. sha1_obj.update(f.read(data_length))
  140. sha1_val = sha1_obj.inner_digest()
  141. result.append({"offset": current_offset, "datalen": data_length, "datasha": sha1_val})
  142. result[-1]['datasha'] = sha1_obj.hexdigest()
  143. return result
  144. if __name__ == '__main__':
  145. # Imports required for command line parsing. No need for these elsewhere
  146. import argparse
  147. import sys
  148. import os
  149. # Parse the incoming arguments
  150. parser = argparse.ArgumentParser()
  151. parser.add_argument('input', nargs='?',
  152. help='input file or message to hash')
  153. args = parser.parse_args()
  154. data = None
  155. if args.input is None:
  156. # No argument given, assume message comes from standard input
  157. try:
  158. # sys.stdin is opened in text mode, which can change line endings,
  159. # leading to incorrect results. Detach fixes this issue, but it's
  160. # new in Python 3.1
  161. data = sys.stdin.detach()
  162. except AttributeError:
  163. # Linux ans OSX both use \n line endings, so only windows is a
  164. # problem.
  165. if sys.platform == "win32":
  166. import msvcrt
  167. msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
  168. data = sys.stdin
  169. elif os.path.isfile(args.input):
  170. # An argument is given and it's a valid file. Read it
  171. data = open(args.input, 'rb')
  172. else:
  173. data = args.input
  174. # Show the final digest
  175. print('sha1-digest:', sha1(data))