//+----------------------------------------------------------------------- // // File: RC4CWRAP.C // // Contents: CryptoSystem wrapper functions for RC4 // // // History: 25 Feb 92 RichardW Created // //------------------------------------------------------------------------ #ifndef KERNEL_MODE #include #include #include #include #else #include #endif #include #include #include #include #include #include #include #include //#define DONT_SUPPORT_OLD_ETYPES 1 typedef struct RC4_KEYSTRUCT RC4KEY; #define RC4_LEGAL_KEYSIZE 8 #define RC4_CONFOUNDER_LEN 8 typedef struct _RC4_MDx_HEADER { UCHAR Confounder[RC4_CONFOUNDER_LEN]; UCHAR Checksum[MD4_LEN]; } RC4_MDx_HEADER, *PRC4_MDx_HEADER; typedef struct _RC4_STATE_BUFFER { PCHECKSUM_FUNCTION ChecksumFunction; PCHECKSUM_BUFFER ChecksumBuffer; RC4KEY Key; } RC4_STATE_BUFFER, *PRC4_STATE_BUFFER; typedef struct _RC4_HMAC_STATE_BUFFER { UCHAR Key[MD5_LEN]; BOOLEAN IncludeHmac; } RC4_HMAC_STATE_BUFFER, *PRC4_HMAC_STATE_BUFFER; NTSTATUS NTAPI rc4Md4Initialize(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *); NTSTATUS NTAPI rc4LmInitialize(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *); #ifndef DONT_SUPPORT_OLD_ETYPES NTSTATUS NTAPI rc4Plain2Initialize(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *); NTSTATUS NTAPI rc4LmHashPassword(PSECURITY_STRING, PUCHAR); #endif NTSTATUS NTAPI rc4Encrypt(PCRYPT_STATE_BUFFER, PUCHAR, ULONG, PUCHAR, PULONG); NTSTATUS NTAPI rc4Decrypt(PCRYPT_STATE_BUFFER, PUCHAR, ULONG, PUCHAR, PULONG); NTSTATUS NTAPI rc4Finish(PCRYPT_STATE_BUFFER *); NTSTATUS NTAPI rc4Md4HashPassword(PSECURITY_STRING, PUCHAR); NTSTATUS NTAPI rc4Md4RandomKey(PUCHAR, ULONG, PUCHAR); NTSTATUS NTAPI rc4Control(ULONG, PCRYPT_STATE_BUFFER, PUCHAR, ULONG); NTSTATUS NTAPI rc4PlainInitializeOld(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *); NTSTATUS NTAPI rc4PlainExpInitializeOld(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *); NTSTATUS NTAPI rc4HmacInitializeOld(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *); NTSTATUS NTAPI rc4HmacExpInitializeOld(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *); NTSTATUS NTAPI rc4HmacEncryptOld(PCRYPT_STATE_BUFFER, PUCHAR, ULONG, PUCHAR, PULONG); NTSTATUS NTAPI rc4HmacDecryptOld(PCRYPT_STATE_BUFFER, PUCHAR, ULONG, PUCHAR, PULONG); NTSTATUS NTAPI rc4HmacFinishOld(PCRYPT_STATE_BUFFER *); NTSTATUS NTAPI rc4HmacControlOld(ULONG, PCRYPT_STATE_BUFFER, PUCHAR, ULONG); NTSTATUS NTAPI rc4HmacRandomKeyOld(PUCHAR, ULONG, PUCHAR); #ifdef KERNEL_MODE #pragma alloc_text( PAGEMSG, rc4Md4Initialize ) #pragma alloc_text( PAGEMSG, rc4LmInitialize ) #pragma alloc_text( PAGEMSG, rc4Encrypt ) #pragma alloc_text( PAGEMSG, rc4Decrypt ) #pragma alloc_text( PAGEMSG, rc4Finish ) #pragma alloc_text( PAGEMSG, rc4Md4HashPassword ) #pragma alloc_text( PAGEMSG, rc4Md4RandomKey ) #pragma alloc_text( PAGEMSG, rc4Control ) #pragma alloc_text( PAGEMSG, rc4PlainInitializeOld ) #pragma alloc_text( PAGEMSG, rc4PlainExpInitializeOld ) #pragma alloc_text( PAGEMSG, rc4HmacInitializeOld ) #pragma alloc_text( PAGEMSG, rc4HmacExpInitializeOld ) #pragma alloc_text( PAGEMSG, rc4HmacDecryptOld ) #pragma alloc_text( PAGEMSG, rc4HmacEncryptOld ) #pragma alloc_text( PAGEMSG, rc4HmacFinishOld ) #pragma alloc_text( PAGEMSG, rc4HmacControlOld ) #pragma alloc_text( PAGEMSG, rc4HmacRandomKeyOld ) #endif CRYPTO_SYSTEM csRC4_MD4 = { KERB_ETYPE_RC4_MD4, // Etype 1, // Blocksize (stream) 0, // no exportable version MD4_LEN, // Key size, in bytes sizeof(RC4_MDx_HEADER), // header size KERB_CHECKSUM_MD4, // Preferred Checksum CSYSTEM_INTEGRITY_PROTECTED, // attributes L"RSADSI RC4-MD4", // Text name rc4Md4Initialize, rc4Encrypt, rc4Decrypt, rc4Finish, rc4Md4HashPassword, rc4Md4RandomKey, rc4Control }; #ifndef DONT_SUPPORT_OLD_ETYPES CRYPTO_SYSTEM csRC4_LM = { KERB_ETYPE_RC4_LM, // Etype 1, // Blocksize (stream) 0, // no exportable version MD4_LEN, // State buffer size sizeof(RC4_MDx_HEADER), // header size KERB_CHECKSUM_LM, // Preferred Checksum CSYSTEM_INTEGRITY_PROTECTED, // attributes L"RSADSI RC4-LM", // Text name rc4LmInitialize, rc4Encrypt, rc4Decrypt, rc4Finish, rc4LmHashPassword, rc4Md4RandomKey, rc4Control }; CRYPTO_SYSTEM csRC4_PLAIN2 = { KERB_ETYPE_RC4_PLAIN2, // Etype 1, // Blocksize (stream) 0, // no exportable version MD4_LEN, // Key size, in bytes 0, // header size KERB_CHECKSUM_MD4, // Preferred Checksum 0, // no attributes L"RSADSI RC4-PLAIN", // Text name rc4Plain2Initialize, rc4Encrypt, rc4Decrypt, rc4Finish, rc4Md4HashPassword, rc4Md4RandomKey, rc4Control }; #endif CRYPTO_SYSTEM csRC4_HMAC_OLD = { KERB_ETYPE_RC4_HMAC_OLD, // Etype 1, // Blocksize (stream) KERB_ETYPE_RC4_HMAC_OLD_EXP,// Exportable version MD4_LEN, // Key size, in bytes sizeof(RC4_MDx_HEADER), // header size KERB_CHECKSUM_MD4, // Preferred Checksum CSYSTEM_INTEGRITY_PROTECTED, // attributes L"RSADSI RC4-HMAC", // Text name rc4HmacInitializeOld, rc4HmacEncryptOld, rc4HmacDecryptOld, rc4HmacFinishOld, rc4Md4HashPassword, rc4HmacRandomKeyOld, rc4HmacControlOld }; CRYPTO_SYSTEM csRC4_HMAC_OLD_EXP = { KERB_ETYPE_RC4_HMAC_OLD_EXP, // Etype 1, // Blocksize (stream) KERB_ETYPE_RC4_HMAC_OLD_EXP, // Exportable version MD4_LEN, // Key size, in bytes sizeof(RC4_MDx_HEADER), // header size KERB_CHECKSUM_MD4, // Preferred Checksum CSYSTEM_INTEGRITY_PROTECTED | CSYSTEM_EXPORT_STRENGTH, // attributes L"RSADSI RC4-HMAC", // Text name rc4HmacInitializeOld, rc4HmacEncryptOld, rc4HmacDecryptOld, rc4HmacFinishOld, rc4Md4HashPassword, rc4HmacRandomKeyOld, rc4HmacControlOld }; CRYPTO_SYSTEM csRC4_PLAIN_OLD = { KERB_ETYPE_RC4_PLAIN_OLD, // Etype 1, // Blocksize (stream) KERB_ETYPE_RC4_PLAIN_OLD_EXP, // exportable version MD4_LEN, // Key size, in bytes 0, // header size KERB_CHECKSUM_MD4, // Preferred Checksum 0, // no attributes L"RSADSI RC4", // Text name rc4PlainInitializeOld, rc4HmacEncryptOld, rc4HmacDecryptOld, rc4HmacFinishOld, rc4Md4HashPassword, rc4HmacRandomKeyOld, rc4HmacControlOld }; CRYPTO_SYSTEM csRC4_PLAIN_OLD_EXP = { KERB_ETYPE_RC4_PLAIN_OLD_EXP, // Etype 1, // Blocksize (stream) KERB_ETYPE_RC4_PLAIN_OLD_EXP, // exportable version MD4_LEN, // Key size, in bytes 0, // header size KERB_CHECKSUM_MD4, // Preferred Checksum CSYSTEM_EXPORT_STRENGTH, // no attributes L"RSADSI RC4-EXP", // Text name rc4PlainExpInitializeOld, rc4HmacEncryptOld, rc4HmacDecryptOld, rc4HmacFinishOld, rc4Md4HashPassword, rc4HmacRandomKeyOld, rc4HmacControlOld }; NTSTATUS NTAPI rc4Initialize( PUCHAR pbKey, ULONG KeySize, ULONG MessageType, ULONG ChecksumFunction, PCRYPT_STATE_BUFFER * psbBuffer) { NTSTATUS Status; PRC4_STATE_BUFFER pRC4Key; PCHECKSUM_FUNCTION Checksum = NULL; // // Get the appropriate checksum here. // if (ChecksumFunction != 0) { Status = CDLocateCheckSum( ChecksumFunction, &Checksum ); if (!NT_SUCCESS(Status)) { return(Status); } } // // if the key is too short, fail here. // if (KeySize < RC4_LEGAL_KEYSIZE) { return(SEC_E_ETYPE_NOT_SUPP); } #ifdef KERNEL_MODE pRC4Key = ExAllocatePool(NonPagedPool, sizeof(RC4_STATE_BUFFER)); #else pRC4Key = LocalAlloc(0, sizeof(RC4_STATE_BUFFER)); #endif if (pRC4Key == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } rc4_key(&pRC4Key->Key, RC4_LEGAL_KEYSIZE, pbKey); // // Initialize the checksum function, if we have one. // pRC4Key->ChecksumFunction = Checksum; if (Checksum != NULL) { Status = Checksum->Initialize( 0, &pRC4Key->ChecksumBuffer ); if (!NT_SUCCESS(Status)) { #ifdef KERNEL_MODE ExFreePool(pRC4Key); #else LocalFree(pRC4Key); #endif return(Status); } } *psbBuffer = (PCRYPT_STATE_BUFFER) pRC4Key; return(STATUS_SUCCESS); } NTSTATUS NTAPI rc4Md4Initialize( IN PUCHAR pbKey, IN ULONG KeySize, IN ULONG MessageType, OUT PCRYPT_STATE_BUFFER * psbBuffer ) { return(rc4Initialize( pbKey, KeySize, MessageType, KERB_CHECKSUM_MD4, psbBuffer )); } NTSTATUS NTAPI rc4LmInitialize( IN PUCHAR pbKey, IN ULONG KeySize, IN ULONG MessageType, OUT PCRYPT_STATE_BUFFER * psbBuffer ) { return(rc4Initialize( pbKey, KeySize, MessageType, KERB_CHECKSUM_LM, psbBuffer )); } #ifndef DONT_SUPPORT_OLD_ETYPES NTSTATUS NTAPI rc4Plain2Initialize( IN PUCHAR pbKey, IN ULONG KeySize, IN ULONG MessageType, OUT PCRYPT_STATE_BUFFER * psbBuffer ) { return(rc4Initialize( pbKey, KeySize, MessageType, 0, // no checksum psbBuffer )); } #endif NTSTATUS NTAPI rc4Encrypt( IN PCRYPT_STATE_BUFFER psbBuffer, IN PUCHAR pbInput, IN ULONG cbInput, OUT PUCHAR pbOutput, OUT PULONG cbOutput ) { PRC4_STATE_BUFFER StateBuffer = (PRC4_STATE_BUFFER) psbBuffer; PRC4_MDx_HEADER CryptHeader = (PRC4_MDx_HEADER) pbOutput; ULONG Offset = 0; if (StateBuffer->ChecksumFunction != NULL) { Offset = sizeof(RC4_MDx_HEADER); } RtlMoveMemory( pbOutput + Offset, pbInput, cbInput ); *cbOutput = cbInput + Offset; RtlZeroMemory( CryptHeader, Offset ); rc4(&StateBuffer->Key, *cbOutput, pbOutput); return( STATUS_SUCCESS ); } NTSTATUS NTAPI rc4Decrypt( PCRYPT_STATE_BUFFER psbBuffer, PUCHAR pbInput, ULONG cbInput, PUCHAR pbOutput, PULONG cbOutput) { PRC4_STATE_BUFFER StateBuffer = (PRC4_STATE_BUFFER) psbBuffer; RC4_MDx_HEADER TempHeader; ULONG Offset = 0; if (*cbOutput < cbInput) { *cbOutput = cbInput; return(STATUS_BUFFER_TOO_SMALL); } RtlCopyMemory( pbOutput, pbInput, cbInput ); rc4(&StateBuffer->Key, cbInput, pbOutput); if (StateBuffer->ChecksumFunction != NULL) { Offset = sizeof(RC4_MDx_HEADER); } RtlZeroMemory( &TempHeader, Offset ); if (RtlEqualMemory( &TempHeader, pbOutput, Offset ) != TRUE) { return(STATUS_UNSUCCESSFUL); } *cbOutput = cbInput - Offset; RtlMoveMemory( pbOutput, pbOutput + Offset, *cbOutput ); return( STATUS_SUCCESS ); } NTSTATUS NTAPI rc4Finish( PCRYPT_STATE_BUFFER * psbBuffer) { PRC4_STATE_BUFFER StateBuffer = (PRC4_STATE_BUFFER) *psbBuffer; if (StateBuffer->ChecksumFunction != NULL) { StateBuffer->ChecksumFunction->Finish(&StateBuffer->ChecksumBuffer); } #ifdef KERNEL_MODE ExFreePool(*psbBuffer); #else LocalFree(*psbBuffer); #endif *psbBuffer = NULL; return(STATUS_SUCCESS); } NTSTATUS NTAPI rc4HashPassword( IN PSECURITY_STRING Password, IN ULONG Checksum, OUT PUCHAR Key ) { PCHECKSUM_FUNCTION SumFunction; PCHECKSUM_BUFFER Buffer; NTSTATUS Status; Status = CDLocateCheckSum(Checksum, &SumFunction); if (!NT_SUCCESS(Status)) { return(SEC_E_CHECKSUM_NOT_SUPP); } Status = SumFunction->Initialize(0, &Buffer); if (!NT_SUCCESS(Status)) { return(Status); } (void) SumFunction->Sum(Buffer, Password->Length, (PUCHAR) Password->Buffer); (void) SumFunction->Finalize(Buffer, Key); (void) SumFunction->Finish(&Buffer); return(STATUS_SUCCESS); } NTSTATUS NTAPI rc4Md4HashPassword( PSECURITY_STRING pbPassword, PUCHAR pbKey) { return(rc4HashPassword( pbPassword, KERB_CHECKSUM_MD4, pbKey)); } NTSTATUS NTAPI rc4LmHashPassword( PSECURITY_STRING pbPassword, PUCHAR pbKey) { return(rc4HashPassword( pbPassword, KERB_CHECKSUM_LM, pbKey)); } NTSTATUS NTAPI rc4RandomKey( IN ULONG KeyLength, OUT PUCHAR pbKey ) { CDGenerateRandomBits(pbKey,KeyLength); return(STATUS_SUCCESS); } NTSTATUS NTAPI rc4Md4RandomKey( IN OPTIONAL PUCHAR Seed, IN ULONG SeedLength, OUT PUCHAR pbKey ) { memset( pbKey, 0xab, MD4_LEN ); return(rc4RandomKey(5,pbKey)); } NTSTATUS NTAPI rc4Control( IN ULONG Function, IN PCRYPT_STATE_BUFFER StateBuffer, IN PUCHAR InputBuffer, IN ULONG InputBufferSize ) { UCHAR TempBuffer[128]; PRC4_STATE_BUFFER Rc4StateBuffer = (PRC4_STATE_BUFFER) StateBuffer; if (Function != CRYPT_CONTROL_SET_INIT_VECT) { return(STATUS_INVALID_PARAMETER); } if (InputBufferSize > sizeof(TempBuffer)) { return(STATUS_INVALID_PARAMETER); } // // We set the IV by encrypting the supplied buffer and leaving the // keystate changed. // memcpy( TempBuffer, InputBuffer, InputBufferSize ); rc4(&Rc4StateBuffer->Key, InputBufferSize, TempBuffer ); return(STATUS_SUCCESS); } ////////////////////////////////////////////////////////////////////////// // // RC4 HMAC crypt type // ////////////////////////////////////////////////////////////////////////// BOOLEAN md5Hmac( IN PUCHAR pbKeyMaterial, IN ULONG cbKeyMaterial, IN PUCHAR pbData, IN ULONG cbData, IN PUCHAR pbData2, IN ULONG cbData2, OUT PUCHAR HmacData ) { BOOLEAN fRet = FALSE; #define HMAC_K_PADSIZE 64 UCHAR Kipad[HMAC_K_PADSIZE]; UCHAR Kopad[HMAC_K_PADSIZE]; UCHAR HMACTmp[HMAC_K_PADSIZE+MD5_LEN]; ULONG dwBlock; MD5_CTX Md5Hash; // truncate if (cbKeyMaterial > HMAC_K_PADSIZE) cbKeyMaterial = HMAC_K_PADSIZE; RtlZeroMemory(Kipad, HMAC_K_PADSIZE); RtlCopyMemory(Kipad, pbKeyMaterial, cbKeyMaterial); RtlZeroMemory(Kopad, HMAC_K_PADSIZE); RtlCopyMemory(Kopad, pbKeyMaterial, cbKeyMaterial); // // Kipad, Kopad are padded sMacKey. Now XOR across... // for(dwBlock=0; dwBlockKey ); LocalKeySize = MD5_LEN; } else { md5Hmac( pbKey, KeySize, "fortybits", sizeof("fortybits"), (PUCHAR) &MessageType, sizeof(ULONG), StateBuffer->Key ); LocalKeySize = 5; // 40 bits } // // Pad exportable keys with 0xababab // memset( StateBuffer->Key+LocalKeySize, 0xab, MD5_LEN-LocalKeySize ); StateBuffer->IncludeHmac = IncludeHmac; *psbBuffer = StateBuffer; return(STATUS_SUCCESS); } NTSTATUS NTAPI rc4HmacInitializeOld( IN PUCHAR pbKey, IN ULONG KeySize, IN ULONG MessageType, OUT PCRYPT_STATE_BUFFER * psbBuffer ) { return(rc4HmacBaseInitializeOld( pbKey, KeySize, MessageType, TRUE, // include hmac FALSE, // not exportable psbBuffer )); } NTSTATUS NTAPI rc4PlainInitializeOld( IN PUCHAR pbKey, IN ULONG KeySize, IN ULONG MessageType, OUT PCRYPT_STATE_BUFFER * psbBuffer ) { return(rc4HmacBaseInitializeOld( pbKey, KeySize, MessageType, FALSE, // no hmac FALSE, // not exportable psbBuffer )); } NTSTATUS NTAPI rc4PlainExpInitializeOld( IN PUCHAR pbKey, IN ULONG KeySize, IN ULONG MessageType, OUT PCRYPT_STATE_BUFFER * psbBuffer ) { return(rc4HmacBaseInitializeOld( pbKey, KeySize, // only use 40 bites MessageType, FALSE, // no hmac TRUE, // exportable psbBuffer )); } NTSTATUS NTAPI rc4HmacControlOld( IN ULONG Function, IN PCRYPT_STATE_BUFFER StateBuffer, IN PUCHAR InputBuffer, IN ULONG InputBufferSize ) { PRC4_HMAC_STATE_BUFFER HmacStateBuffer = (PRC4_HMAC_STATE_BUFFER) StateBuffer; if (Function == CRYPT_CONTROL_SET_INIT_VECT) { md5Hmac( HmacStateBuffer->Key, MD5_LEN, InputBuffer, InputBufferSize, NULL, 0, HmacStateBuffer->Key ); } else { return(STATUS_INVALID_PARAMETER); } return(STATUS_SUCCESS); } NTSTATUS NTAPI rc4HmacEncryptOld( IN PCRYPT_STATE_BUFFER psbBuffer, IN PUCHAR pbInput, IN ULONG cbInput, OUT PUCHAR pbOutput, OUT PULONG cbOutput ) { PRC4_HMAC_STATE_BUFFER StateBuffer = (PRC4_HMAC_STATE_BUFFER) psbBuffer; PRC4_MDx_HEADER CryptHeader = (PRC4_MDx_HEADER) pbOutput; ULONG Offset = 0; RC4KEY Rc4Key; if (StateBuffer->IncludeHmac) { Offset = sizeof(RC4_MDx_HEADER); } else { Offset = 0; } RtlMoveMemory( pbOutput + Offset, pbInput, cbInput ); *cbOutput = cbInput + Offset; // // Create the header - the confounder & checksum // if (Offset != 0) { RtlZeroMemory( CryptHeader->Checksum, MD4_LEN ); CDGenerateRandomBits( CryptHeader->Confounder, RC4_CONFOUNDER_LEN ); md5Hmac( StateBuffer->Key, MD5_LEN, pbOutput, *cbOutput, NULL, 0, CryptHeader->Checksum ); } rc4_key( &Rc4Key, MD5_LEN, StateBuffer->Key ); rc4(&Rc4Key, *cbOutput, pbOutput); return(STATUS_SUCCESS); } NTSTATUS NTAPI rc4HmacDecryptOld( PCRYPT_STATE_BUFFER psbBuffer, PUCHAR pbInput, ULONG cbInput, PUCHAR pbOutput, PULONG cbOutput) { PRC4_HMAC_STATE_BUFFER StateBuffer = (PRC4_HMAC_STATE_BUFFER) psbBuffer; RC4_MDx_HEADER TempHeader; UCHAR TempChecksum[MD5_LEN]; ULONG Offset = sizeof(RC4_MDx_HEADER); RC4KEY Rc4Key; if (!StateBuffer->IncludeHmac) { Offset = 0; } if (cbInput < Offset) { return(STATUS_INVALID_PARAMETER); } rc4_key( &Rc4Key, MD5_LEN, StateBuffer->Key ); // // Copy the input to the output before decrypting // RtlCopyMemory( &TempHeader, pbInput, Offset ); *cbOutput = cbInput - Offset; RtlMoveMemory( pbOutput, pbInput + Offset, *cbOutput ); // // Now decrypt the two buffers // if (Offset != 0) { rc4( &Rc4Key, Offset, (PUCHAR) &TempHeader ); } rc4( &Rc4Key, *cbOutput, pbOutput ); // // Now verify the checksum. First copy it out of the way, zero the // header // if (Offset != 0) { RtlCopyMemory( TempChecksum, TempHeader.Checksum, MD5_LEN ); RtlZeroMemory( TempHeader.Checksum, MD5_LEN ); md5Hmac( StateBuffer->Key, MD5_LEN, (PUCHAR) &TempHeader, Offset, pbOutput, *cbOutput, TempHeader.Checksum ); if (RtlEqualMemory( TempHeader.Checksum, TempChecksum, MD5_LEN ) != TRUE) { return(STATUS_UNSUCCESSFUL); } } return(STATUS_SUCCESS); } NTSTATUS NTAPI rc4HmacFinishOld( PCRYPT_STATE_BUFFER * psbBuffer) { #ifdef KERNEL_MODE ExFreePool(*psbBuffer); #else LocalFree(*psbBuffer); #endif *psbBuffer = NULL; return(STATUS_SUCCESS); } NTSTATUS NTAPI rc4HmacRandomKeyOld( IN OPTIONAL PUCHAR Seed, IN ULONG SeedLength, OUT PUCHAR pbKey ) { return(rc4RandomKey(MD5_LEN,pbKey)); }