Скачиваний:
274
Добавлен:
02.05.2014
Размер:
511.49 Кб
Скачать

Функции bCrypt.

Подобно генерации случайных чисел, функции хеширования играют важнейшую роль в обеспечении мер и функций безопасности во множестве задач современной вычислительной техники. Функции хеширования предоставляются в виде объектов в CNG, но, поскольку интерфейс API должен быть доступен из кода режима ядра (написанного в основном на C), это требует некоторой предварительной работы по обратной сборке объекта в единое целое. Как было упомянуто во введении, в CNG поставщики алгоритмов и функции хеширования представляются объектами. Поскольку они являются частью BCrypt, функции BCryptGetPropertyиBCryptSetPropertyможно использовать для запроса и установки именованных свойств конкретного объекта. Соответствующие функции предоставляются для управления свойствами объектов посредством NCrypt.

Функция BCryptCreateHashсоздает новый объект хеширования. Однако прежде чем ее вызвать, необходимо выделить буфер, который функция хеширования будет использовать для обработки. Здесь проявляется один из побочных эффектов поддержки в одном интерфейсе API как пользовательского режима, так и режима ядра — режиму ядра небезразлично, где выделяется память, в частности, является эта память выгружаемой или невыгружаемой. Помимо буфера разработчик отвечает за управление ресурсами таблиц дескрипторов и таблиц хеширования, предназначенными для объекта хеша.

Размер буфера, необходимый для создания объекта хеша, является свойством поставщика алгоритма, и идентификатор BCRYPT_OBJECT_LENGTHпредоставляет имя этого свойства для создания запроса. Нарис. 4показано, как можно было бы загрузить поставщик алгоритма для алгоритма хешированияSHA-256и запросить размер буфера хеша.

Первый параметр BCryptGetProperty указывает на запрашиваемый объект. Второй параметр указывает имя свойства. Третий и четвертый указывают буфер назначения, в котором хранится значение свойства, а также размер буфера. Параметр bytesCopied удобен в тех случаях, когда заранее не известен предполагаемый размер буфера. В текущей ситуации для этой функции не определены никакие флаги, поэтому для параметра флагов следует передать нулевое значение.

Определив размер буфера хеша, необходимо выделить участок памяти для объекта хеша. За исключением случая режима ядра, не имеет большого значения, где размещается этот буфер, и разработчик по своему усмотрению может использовать любое удобное хранилище, если оно находится в стабильном состоянии (другими словами, не допускается использование незафиксированного управляемого массива байтов). На рис. 5предоставляется простой класс Buffer, который можно использовать для простых выделений буфера пользовательского режима. Этот класс будет снова использоваться при создании объектов ключей шифрования, поэтому рекомендуется иметь наготове какой-нибудь класс Buffer.

Выполнив все предварительные условия, теперь можно, наконец, с помощью функции BCryptCreateHash создать объект хеша, как показано в следующем примере:

Buffer hashBuffer;

NT_VERIFY(hashBuffer.Create(hashBufferSize));

BCRYPT_HANDLE hash = 0;

NT_VERIFY(::BCryptCreateHash(algorithmProvider, &hash,

hashBuffer.GetData(),

hashBuffer.GetSize(),

0, // secret

0, // secret size

0)); // flags

С помощью первого параметра BCryptCreateHash определяется поставщик алгоритма, реализующего интерфейс хеширования. Второй параметр принимает дескриптор объекта хэша. Третий и четвертый параметры определяют буфер хеша и его размер. В алгоритмах хеширования с ключами для идентификации секретного ключа используются секретные параметры. В текущей ситуации для этой функции не определены никакие флаги, поэтому для параметра флагов следует передать нулевое значение. Завершив работу с объектом хеша, необходимо уничтожить его с помощью функции BCryptDestroyHash, а затем освободить буфер объекта хеша.

Теперь, ознакомившись с методом создания и уничтожения объектов хеша, сделаем с их помощью что-нибудь действительно полезное. После создания объекта хеша можно вызывать функцию BCryptHashData для выполнения однонаправленного хеширования буфера. Эту функцию можно вызывать многократно для объединения в хеш дополнительных буферов. Следует помнить, что размер значения хеша фиксирован и определяется поставщиком алгоритма, поэтому не имеет значения, сколько раз вызывается функция BCryptHashData — размер результирующего значения хэша всегда будет одним и тем же.

Данный пример демонстрирует способ хеширования данных, прочитанных из потока с помощью интерфейса IStream модели COM:

BYTE buffer[256] = { 0 };

ULONG bytesRead = 0;

while (SUCCEEDED(stream->Read(buffer, _countof(buffer),

&bytesRead)) && 0 < bytesRead)

{

NT_VERIFY(::BCryptHashData(hash, buffer, bytesRead, 0));

}

Первый параметр BCryptHashData является дескриптором, возвращаемым из функции BCryptCreateHash. Второй и третий параметры указывают буфер, содержащий данные для хеширования, а также размер этого буфера. Обратите внимание на то, что мы аккуратно передаем bytesRead в качестве размера буфера, чтобы быть уверенными в том, что включаются только данные, действительно считанные из потока. Наконец, для BCryptHashData пока не определены никакие флаги, поэтому для последнего параметра необходимо передать нулевое значение.

После того, как функции хеширования переданы все данные, результирующее значение можно получить, вызвав функцию BCryptFinishHash. Размер результирующего значения хеша зависит от конкретного используемого алгоритма хеширования. Если этот размер неизвестен, можно запросить объект хеша с идентификатором BCRYPT_HASH_LENGTH. В следующем примере запрашивается размер значения хеша, создается для него буфер, и, наконец, значение хеша копируется в буфер с помощью BCryptFinishHash:

ULONG hashValueSize = 0;

NT_VERIFY(::BCryptGetProperty(hash, BCRYPT_HASH_LENGTH, ...))

Buffer hashValue;

NT_VERIFY(hashValue.Create(hashValueSize));

NT_VERIFY(::BCryptFinishHash(hash,

hashValue.GetData(),

hashValue.GetSize(),

0)); // flags

Итак, мы начинаем узнавать эту схему. Первый параметр BCryptFinishHash является дескриптором объекта хеша. Второй и третий параметры указывают буфер, принимающий значение хеша, а также размер этого буфера. Последний параметр указывает флаги, ни одного из которых здесь нет.

Наконец, объекты хеша можно дублировать. Это удобно в том случае, когда требуется получить два или больше значений хеша, основанных на некоторых общих данных. Можно начать с создания отдельного объекта хеша, чтобы выполнить хеширование общих данных, а затем продублировать его один или несколько раз, добавляя к дубликатам любые приращения. После того, как объект хеша продублирован, два объекта хеша содержат одинаковое состояние, но между ними нет связи, и к каждому из них можно по своему усмотрению добавить уникальные данные, создать значение хеша или уничтожить один из объектов хеша без каких бы то ни было последствий для другого.

Дублирование объекта хеша берет на себя функция BCryptDuplicateHash. Разработчику требуется всего лишь предоставить дескриптор дублируемого объекта хеша, а также новый буфер, который функция будет использовать для обработки.

Buffer newHashBuffer;

NT_VERIFY(newHashBuffer.Create(hashBufferSize));

BCRYPT_HANDLE newHash = 0;

NT_VERIFY(::BCryptDuplicateHash(hash,

&newHash,

newHashBuffer.GetData(),

newHashBuffer.GetSize(),

0));