Хэш-целое число в Python, чтобы соответствовать Oracle STANDARD_HASH - PullRequest
14 голосов
/ 04 апреля 2019

В Oracle мои данные были хэшированы путем передачи целого числа в STANDARD_HASH следующим образом.Как я могу получить то же значение хеша, используя Python?

Результат в Oracle, когда целое число передано в STANDARD_HASH:

SELECT STANDARD_HASH(123, 'SHA256') FROM DUAL;
# A0740C0829EC3314E5318E1F060266479AA31F8BBBC1868DA42B9E608F52A09F

Результат в Python, когда строка передана в:

import hashlib

hashlib.sha256(str.encode(str(123))).hexdigest().upper()
# A665A45920422F9D417E4867EFDC4FB8A04A1F3FFF1FA07E998E86F7F7A27AE3
# I want to modify this function to get the hash value above.

Возможно эта информация тоже поможет.Я не могу ничего изменить на стороне Oracle, но если бы я мог, я бы преобразовал столбец в CHAR, и он дал бы то же значение, что и моя текущая реализация Python.Ниже приведен пример.

Результат в Oracle, когда строка, переданная STANDARD_HASH:

SELECT STANDARD_HASH('123', 'SHA256') FROM DUAL;
# A665A45920422F9D417E4867EFDC4FB8A04A1F3FFF1FA07E998E86F7F7A27AE3 (matches Python result)

Я сделал несколько попыток, например, просто передал целое число в Python, но это приводит кошибка, что требуется строкаЯ также искал способ кодировать целое число, но не достиг прогресса.

Ответы [ 2 ]

10 голосов
/ 04 апреля 2019

Oracle представляет числа в своем собственном внутреннем формате, который можно увидеть с помощью функции dump() в Oracle.Например,

SELECT dump(123) FROM dual;
Typ=2 Len=3: 194,2,24

Итак, чтобы хэшировать число в Python и получить тот же результат, что и в Oracle, вам нужно преобразовать число Python в набор байтов:так же, как Oracle делает это во внутренних органах.

Хороший анализ внутренней логики, используемой Oracle, можно найти здесь .Это верно с одним незначительным упущением, связанным с завершающими отрицательными числами.Кроме того, он написан с точки зрения декодирования номера Oracle из его байтов.В нашем случае нам нужно кодировать номер Oracle в его внутренний байтовый формат.Тем не менее, я широко использовал его при формировании этого ответа.

В приведенном ниже коде показана функция Python, to_oracle_number(), которая будет возвращать массив целых чисел, имеющий такое же байтовое представление числа, которое рассчитывает база данных Oracle.,Он должен обрабатывать любое число (положительное, отрицательное, дробное, ноль и т. Д.).

Код в самом низу также показывает, как вызывать эту функцию и хэшировать ее результаты, чтобы получить то же значение хеш-функции, которое было вычислено вбаза данных Oracle, которая, как мне кажется, является основой вашего вопроса.

ПРИМЕЧАНИЕ. Функция ожидает, что число, которое вы хотите преобразовать, будет передано в виде строки, чтобы избежать потери точности.

import math
import decimal
import hashlib

def to_oracle_number( nstr ):
  # define number n that we want to convert
  n = decimal.Decimal(nstr)

  # compute exponent (base 100) and convert to Oracle byte along with sign
  #print (abs(n))
  l_exp = 0
  l_len = 0

  l_abs_n = abs(n)


  if l_abs_n != 0:
    l_exp = math.floor(math.log(l_abs_n,100))
    # Oracle adds 1 to all bytes when encoding
    l_exp = l_exp + 1
    # Oracle adds 64 to exponent whe encoding
    l_exp = l_exp + 64

  if n < 0:
    # take 1's complement of exponent so far (bitwise xor)
    l_exp = (l_exp ^ 127)

  if n >= 0:
    # add sign bit.  zero is considered positive.
    l_exp = l_exp + 128

  l_bytes = []
  l_bytes.append(l_exp)

  l_len = l_len + 1   # exponent and sign take 1 byte

  l_whole_part = str(int(l_abs_n))
  # make sure there is an even number of digits in the whole part
  if len(l_whole_part) % 2 == 1:
    l_whole_part = '0' + l_whole_part

  # get the fractional digits, so if 0.01234, just 01234
  l_frac_part = str(l_abs_n - int(l_abs_n))[2:]
  # make sure there is an even number of digits in the fractional part
  if len(l_frac_part) % 2 == 1:
    l_frac_part = l_frac_part + '0'

  l_mantissa = l_whole_part + l_frac_part

  # chop off leading 00 pairs
  while l_mantissa[0:2] == '00':
    l_mantissa = l_mantissa[2:]

  # chop off trailing 00 pairs
  while l_mantissa[-2:] == '00':
    l_mantissa = l_mantissa[:-2]

  # compute number of 2-character chunks
  l_chunk_count = int(len(l_mantissa) / 2)

  l_chunks = '';

  for i in range(0, l_chunk_count):
    l_chunk = int(l_mantissa[i*2:i*2+2])
    if n < 0:
      # for negative numbers, we subtract from 100
      l_chunk = 100-l_chunk

    # Oracle adds 1 to all bytes
    l_chunk = l_chunk + 1

    # Add the chunk to our answer
    l_chunks = l_chunks + ',' + str(l_chunk)
    l_bytes.append(l_chunk)
    l_len = l_len + 1   # we have computed one more byte
    #print (str(i) + ':' + str(l_chunk))

  if n < 0 and l_len < 21:
    # terminating negative numbers always end in byte 102 (do not know why)
    l_chunks = l_chunks + ',102'
    l_bytes.append(102)
    l_len = l_len + 1

  l_computed_dump = 'Typ=2 Len=' + str(l_len) + ': ' + str(l_exp) + l_chunks
  print  (l_computed_dump)
  print  (l_bytes)

  return l_bytes


# test it

m = hashlib.sha256()
b = bytes(to_oracle_number('123'))  # pass a string so no precision errors
m.update(b)
print(m.hexdigest().upper())

ВЫХОД

Typ=2 Len=3: 194,2,24
[194, 2, 24]
A0740C0829EC3314E5318E1F060266479AA31F8BBBC1868DA42B9E608F52A09F
2 голосов
/ 14 апреля 2019

ПРЕДУПРЕЖДЕНИЕ: Оригинальное решение для потока пришло от @Matthew McPeak's, и этот ответ должен быть вознагражден, ниже вы найдете слегка измененную версию, где я добавил немного рефакторинга к его алгоритм хотя:

import math
import decimal
import hashlib


def to_oracle_number(nstr):
    n = decimal.Decimal(nstr)

    # compute exponent (base 100) and convert to Oracle byte along with sign
    l_exp, l_len, l_abs_n = 0, 0, abs(n)

    if l_abs_n != 0:
        l_exp = math.floor(math.log(l_abs_n, 100)) + 65

    l_exp = (l_exp ^ 127) if n < 0 else l_exp + 128
    l_bytes = [l_exp]
    l_len += 1   # exponent and sign take 1 byte
    l_whole_part = str(int(l_abs_n))

    # make sure there is an even number of digits in the whole part
    if len(l_whole_part) % 2 == 1:
        l_whole_part = '0' + l_whole_part

    # get the fractional digits, so if 0.01234, just 01234
    l_frac_part = str(l_abs_n - int(l_abs_n))[2:]

    # make sure there is an even number of digits in the fractional part
    if len(l_frac_part) % 2 == 1:
        l_frac_part += '0'

    l_mantissa = l_whole_part + l_frac_part

    # chop off leading 00 pairs
    while l_mantissa[0:2] == '00':
        l_mantissa = l_mantissa[2:]

    # chop off trailing 00 pairs
    while l_mantissa[-2:] == '00':
        l_mantissa = l_mantissa[:-2]

    # compute number of 2-character chunks
    l_chunks = ''

    for i in range(0, int(len(l_mantissa) / 2)):
        l_chunk = int(l_mantissa[i * 2:i * 2 + 2])
        if n < 0:
            l_chunk = 100 - l_chunk

        l_chunk += 1
        l_chunks = f"{l_chunks},l_chunk"
        l_bytes.append(l_chunk)
        l_len += 1

    if n < 0 and l_len < 21:
        # terminating negative numbers always end in byte 102 (do not know why)
        l_chunks += ',102'
        l_bytes.append(102)
        l_len += 1

    # bytes(l_bytes)l_computed_dump = f"Typ=2 Len={l_len}: {l_exp}{l_chunks}"
    m = hashlib.sha256()
    m.update(bytes(l_bytes))
    return m.hexdigest().upper()


if __name__ == '__main__':
    assert to_oracle_number('123') == "A0740C0829EC3314E5318E1F060266479AA31F8BBBC1868DA42B9E608F52A09F"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...