windows-nt/Source/XPSP1/NT/ds/win32/ntcrypto/randlib/rc4safe.c
2020-09-26 16:20:57 +08:00

555 lines
10 KiB
C

/*++
Copyright (c) 1998-1999 Microsoft Corporation
Module Name:
rc4safe.c
Abstract:
access rc4 key material in thread safe manner, without adversly affecting
performance of multi-thread users.
Author:
Scott Field (sfield) 02-Jul-99
--*/
#ifndef KMODE_RNG
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <zwapi.h>
#else
#include <ntosp.h>
#include <windef.h>
#endif // KMODE_RNG
#include <rc4.h>
#include <randlib.h>
#include "umkm.h"
typedef struct {
unsigned int BytesUsed; // bytes processed for key associated with this entry
__LOCK_TYPE lock; // lock for serializing key entry
RC4_KEYSTRUCT rc4key; // key material associated with this entry
} RC4_SAFE, *PRC4_SAFE, *LPRC4_SAFE;
typedef enum _MemoryMode {
Paged = 1,
NonPaged
} MemoryMode;
typedef struct {
unsigned int Entries; // count of array entries.
MemoryMode Mode; // allocation & behavior mode of SAFE_ARRAY
RC4_SAFE *Array[]; // array of pointers to RC4_SAFE entries.
} RC4_SAFE_ARRAY, *PRC4_SAFE_ARRAY, *LPRC4_SAFE_ARRAY;
//
// !!! RC4_SAFE_ENTRIES must be even multiple of 4 !!!
//
#ifndef KMODE_RNG
#define RC4_SAFE_ENTRIES (8)
#else
//
// we don't expect as much traffic in kernel mode, so use less resources.
//
#define RC4_SAFE_ENTRIES (4)
#endif
#ifdef KMODE_RNG
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, rc4_safe_select)
#pragma alloc_text(PAGE, rc4_safe)
#pragma alloc_text(PAGE, rc4_safe_key)
#pragma alloc_text(PAGE, rc4_safe_select)
#pragma alloc_text(PAGE, rc4_safe_startup)
#pragma alloc_text(PAGE, rc4_safe_shutdown)
#endif // ALLOC_PRAGMA
#endif // KMODE_RNG
void
rc4_safe_select(
IN void *pContext,
OUT unsigned int *pEntry,
OUT unsigned int *pBytesUsed
)
/*++
Routine Description:
key selector: choose a thread safe key.
outputs key identifier and bytes used with key.
--*/
{
RC4_SAFE_ARRAY *pRC4SafeArray;
RC4_SAFE *pRC4Safe;
static unsigned int circular;
unsigned int local;
#ifdef KMODE_RNG
PAGED_CODE();
#endif // KMODE_RNG
pRC4SafeArray = (RC4_SAFE_ARRAY*)pContext;
//
// there are 2 ways to increment the array selector:
// 1. Just increment the pointer. On a multi-processor system,
// with multiple threads calling rc4_safe_select simultaneously,
// this can lead to several threads selecting the same array element.
// This will lead to lock contention on the array element lock.
// Currently, we've decided this scenario will be fairly rare, so the
// penalty associated with option 2 is avoided.
// 2. Use InterlockedIncrement to determine the array element. This would
// yield no collision on an array element, hence no lock contention
// on the embedded array lock. This introduces additional bus traffic
// on SMP machines due to the lock primitive.
//
//
#ifdef KMODE_RNG
local = (unsigned int)InterlockedIncrement( (PLONG)&circular );
#else
circular++;
local = circular;
#endif // KMODE_RNG
//
// array index will not wrap.
//
local &= ( pRC4SafeArray->Entries-1 );
pRC4Safe = pRC4SafeArray->Array[local];
*pEntry = local;
*pBytesUsed = pRC4Safe->BytesUsed;
}
#ifdef KMODE_RNG
void
rc4_safe_select_np(
IN void *pContext,
OUT unsigned int *pEntry,
OUT unsigned int *pBytesUsed
)
/*++
Routine Description:
Non-paged, high-IRQL version of rc4_safe_select()
--*/
{
RC4_SAFE_ARRAY *pRC4SafeArray;
RC4_SAFE *pRC4Safe;
unsigned int local;
pRC4SafeArray = (RC4_SAFE_ARRAY*)pContext;
if( *pEntry == 0 ) {
if( pRC4SafeArray->Entries == 1 ) {
local = 0;
} else {
static unsigned int circular;
local = (unsigned int)InterlockedIncrement( (PLONG)&circular );
//
// array index will not wrap.
//
local = (local % pRC4SafeArray->Entries);
}
} else {
local = KeGetCurrentProcessorNumber();
}
pRC4Safe = pRC4SafeArray->Array[local];
*pEntry = local;
*pBytesUsed = pRC4Safe->BytesUsed;
}
#endif // KMODE_RNG
void
rc4_safe(
IN void *pContext,
IN unsigned int Entry,
IN unsigned int cb,
IN void *pv
)
/*++
Routine Description:
Initializes the rc4 key identified by the Entry index.
Input key material is pv, of size cb.
--*/
{
RC4_SAFE_ARRAY *pRC4SafeArray;
RC4_SAFE *pRC4Safe;
#ifdef KMODE_RNG
KIRQL OldIrql;
PAGED_CODE();
#endif // KMODE_RNG
pRC4SafeArray = (RC4_SAFE_ARRAY*)pContext;
Entry &= ( pRC4SafeArray->Entries - 1 );
pRC4Safe = pRC4SafeArray->Array[ Entry ];
ENTER_LOCK( &(pRC4Safe->lock) );
rc4( &(pRC4Safe->rc4key), cb, (unsigned char*) pv );
pRC4Safe->BytesUsed += cb;
LEAVE_LOCK( &(pRC4Safe->lock) );
}
#ifdef KMODE_RNG
void
rc4_safe_np(
IN void *pContext,
IN unsigned int Entry,
IN unsigned int cb,
IN void *pv
)
/*++
Routine Description:
Non-paged, high IRQL version of rc4_safe()
--*/
{
RC4_SAFE_ARRAY *pRC4SafeArray;
RC4_SAFE *pRC4Safe;
pRC4SafeArray = (RC4_SAFE_ARRAY*)pContext;
// NOTE:
// we ignore the Entry parameter.
// future consideration may be ignore only when Entry == 0xffffffff
// but, this would only be perf penalty currently.
//
Entry = KeGetCurrentProcessorNumber();
pRC4Safe = pRC4SafeArray->Array[ Entry ];
rc4( &(pRC4Safe->rc4key), cb, (unsigned char*) pv );
pRC4Safe->BytesUsed += cb;
}
#endif // KMODE_RNG
void
rc4_safe_key(
IN void *pContext,
IN unsigned int Entry,
IN unsigned int cb,
IN const void *pv
)
/*++
Routine Description:
Initializes the rc4 key identified by the Entry index.
Input key material is pv, of size cb.
--*/
{
RC4_SAFE_ARRAY *pRC4SafeArray;
RC4_SAFE *pRC4Safe;
#ifdef KMODE_RNG
KIRQL OldIrql;
PAGED_CODE();
#endif // KMODE_RNG
pRC4SafeArray = (RC4_SAFE_ARRAY*)pContext;
Entry &= ( pRC4SafeArray->Entries - 1 );
pRC4Safe = pRC4SafeArray->Array[ Entry ];
ENTER_LOCK( &(pRC4Safe->lock) );
rc4_key( &(pRC4Safe->rc4key), cb, (unsigned char*) pv );
pRC4Safe->BytesUsed = 0;
LEAVE_LOCK( &(pRC4Safe->lock) );
}
#ifdef KMODE_RNG
void
rc4_safe_key_np(
IN void *pContext,
IN unsigned int Entry,
IN unsigned int cb,
IN const void *pv
)
/*++
Routine Description:
Non-paged, high-IRQL version of rc4_safe_key()
--*/
{
RC4_SAFE_ARRAY *pRC4SafeArray;
RC4_SAFE *pRC4Safe;
pRC4SafeArray = (RC4_SAFE_ARRAY*)pContext;
if( Entry == 0xffffffff ) {
Entry = KeGetCurrentProcessorNumber();
}
pRC4Safe = pRC4SafeArray->Array[ Entry ];
rc4_key( &pRC4Safe->rc4key, cb, (unsigned char*) pv );
pRC4Safe->BytesUsed = 0;
}
#endif // KMODE_RNG
unsigned int
rc4_safe_startup(
IN OUT void **ppContext
)
/*++
Routine Description:
key selector: choose a thread safe key.
outputs key identifier and bytes used with key.
--*/
{
RC4_SAFE_ARRAY *pRC4SafeArray;
RC4_SAFE *pRC4Safe;
unsigned int Entries;
unsigned int i;
#ifdef KMODE_RNG
PAGED_CODE();
#endif // KMODE_RNG
Entries = RC4_SAFE_ENTRIES;
pRC4SafeArray = (RC4_SAFE_ARRAY *)ALLOC_NP(
sizeof(RC4_SAFE_ARRAY) +
(Entries * sizeof(RC4_SAFE *)) +
(Entries * sizeof(RC4_SAFE))
);
if( pRC4SafeArray == NULL ) {
return FALSE;
}
pRC4SafeArray->Entries = Entries;
pRC4SafeArray->Mode = Paged;
pRC4Safe = (RC4_SAFE*) ((unsigned char*)pRC4SafeArray +
sizeof(RC4_SAFE_ARRAY) +
(Entries * sizeof(RC4_SAFE*))
);
for ( i = 0 ; i < Entries ; i++, pRC4Safe++ ) {
pRC4SafeArray->Array[i] = pRC4Safe;
INIT_LOCK( &(pRC4Safe->lock) );
//
// cause client to re-key for each initialized array entry.
//
pRC4Safe->BytesUsed = 0xffffffff;
}
*ppContext = pRC4SafeArray;
return TRUE;
}
#ifdef KMODE_RNG
unsigned int
rc4_safe_startup_np(
IN OUT void **ppContext
)
/*++
Routine Description:
Non-Paged, high IRQL version of rc4_safe_startup()
--*/
{
RC4_SAFE_ARRAY *pRC4SafeArray;
RC4_SAFE *pRC4Safe;
unsigned int Entries;
unsigned int i;
//
// get installed processor count.
//
Entries = KeNumberProcessors;
pRC4SafeArray = (RC4_SAFE_ARRAY *)ALLOC_NP(
sizeof(RC4_SAFE_ARRAY) +
(Entries * sizeof(RC4_SAFE *)) +
(Entries * sizeof(RC4_SAFE))
);
if( pRC4SafeArray == NULL ) {
return FALSE;
}
pRC4SafeArray->Entries = Entries;
pRC4SafeArray->Mode = NonPaged;
pRC4Safe = (RC4_SAFE*) ((unsigned char*)pRC4SafeArray +
sizeof(RC4_SAFE_ARRAY) +
(Entries * sizeof(RC4_SAFE*))
);
for ( i = 0 ; i < Entries ; i++, pRC4Safe++ ) {
pRC4SafeArray->Array[i] = pRC4Safe;
//
// cause client to re-key for each initialized array entry.
//
pRC4Safe->BytesUsed = 0xffffffff;
}
*ppContext = pRC4SafeArray;
return TRUE;
}
#endif
void
rc4_safe_shutdown(
IN void *pContext
)
/*++
Routine Description:
rc4_safe_shutdown called to free resources associated with internal structures.
typically called during DLL_PROCESS_DETACH type shutdown code.
--*/
{
RC4_SAFE_ARRAY *SafeArray;
unsigned int SafeEntries;
unsigned int i;
#ifdef KMODE_RNG
PAGED_CODE();
#endif // KMODE_RNG
SafeArray = (RC4_SAFE_ARRAY*)pContext;
SafeEntries = SafeArray->Entries;
for ( i = 0 ; i < SafeEntries ; i++ ) {
RC4_SAFE *pRC4Safe = SafeArray->Array[i];
DELETE_LOCK( &(pRC4Safe->lock) );
}
FREE( pContext );
}
#ifdef KMODE_RNG
void
rc4_safe_shutdown_np(
IN void *pContext
)
/*++
Routine Description:
Non-paged, high-IRQL version of rc4_safe_shutdown()
--*/
{
FREE( pContext );
}
#endif