OEMCrypto v12 Haystack and Adapter

Merge of several CLs from the widevine repo.

Merge from widevine repo of http://go/wvgerrit/22440
Build OEMCrypto v12 Haystacks with cache flush

level3/mips/libwvlevel3.a  Level3 Library 4465 Nov 29 2016 13:34:45
level3/arm/libwvlevel3.a  Level3 Library 4445 Nov 29 2016 14:02:08
level3/x86/libwvlevel3.a  Level3 Library 4464 Nov 29 2016 14:22:21

Merge from widevine repo of http://go/wvgerrit/22403
Pull cache flush out of Haystack

Merge from widevine repo of http://go/wvgerrit/21145
OEMCrypto v12 stubs -- just the header file changes.

Merge from widevine repo of http://go/wvgerrit/21146
Add OEMCrypto v12 functions to profiler

This CL adds the new oemcrypto v12 functions for provision 3.0 to the
list of profiler functions.

Merge from widevine repo of http://go/wvgerrit/21143
OEMCrypto v12 adapter

This CL updates the oemcrypto dynamic and static adpaters to include
oemcrypto v12 funtionality.  It adds the three new Provisioning 3.0
functions.

It also adds code in the initialization routine to null out all of
the function pointers if any of them fail to load.  It is better to
fall back to level 3 than to use an inconsistent level 1.

b/31528025

Change-Id: I3579dc93e00ad7e7c743beecdd8291eac557d4e4
This commit is contained in:
Fred Gylys-Colwell
2016-11-29 14:56:36 -08:00
parent 6d000b5295
commit 4cac936b10
8 changed files with 134 additions and 15 deletions

View File

@@ -35,6 +35,8 @@ OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel level,
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel level, OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel level,
size_t* maximum); size_t* maximum);
uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel level); uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel level);
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(
SecurityLevel level);
} // namespace wvcdm } // namespace wvcdm
#endif // WVCDM_CORE_OEMCRYPTO_ADAPTER_H_ #endif // WVCDM_CORE_OEMCRYPTO_ADAPTER_H_

View File

@@ -188,6 +188,21 @@ typedef OEMCryptoResult (*L1_DeleteUsageEntry_t)(
typedef OEMCryptoResult (*L1_ForceDeleteUsageEntry_t)(const uint8_t* pst, typedef OEMCryptoResult (*L1_ForceDeleteUsageEntry_t)(const uint8_t* pst,
size_t pst_length); size_t pst_length);
typedef OEMCryptoResult (*L1_DeleteUsageTable_t)(); typedef OEMCryptoResult (*L1_DeleteUsageTable_t)();
typedef OEMCrypto_ProvisioningMethod (*L1_GetProvisioningMethod_t)();
typedef OEMCryptoResult (*L1_GetOEMPublicCertificate_t)(
OEMCrypto_SESSION session,
uint8_t *public_cert,
size_t *public_cert_length);
typedef OEMCryptoResult (*L1_RewrapDeviceRSAKey30_t)(
OEMCrypto_SESSION session,
const uint32_t *nonce,
const uint8_t* encrypted_message_key,
size_t encrypted_message_key_length,
const uint8_t* enc_rsa_key,
size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv,
uint8_t* wrapped_rsa_key,
size_t* wrapped_rsa_key_length);
struct FunctionPointers { struct FunctionPointers {
uint32_t version; uint32_t version;
@@ -235,6 +250,9 @@ struct FunctionPointers {
L1_DeleteUsageEntry_t DeleteUsageEntry; L1_DeleteUsageEntry_t DeleteUsageEntry;
L1_ForceDeleteUsageEntry_t ForceDeleteUsageEntry; L1_ForceDeleteUsageEntry_t ForceDeleteUsageEntry;
L1_DeleteUsageTable_t DeleteUsageTable; L1_DeleteUsageTable_t DeleteUsageTable;
L1_GetProvisioningMethod_t GetProvisioningMethod;
L1_GetOEMPublicCertificate_t GetOEMPublicCertificate;
L1_RewrapDeviceRSAKey30_t RewrapDeviceRSAKey30;
L1_LoadKeys_V8_t LoadKeys_V8; L1_LoadKeys_V8_t LoadKeys_V8;
L1_GenerateRSASignature_V8_t GenerateRSASignature_V8; L1_GenerateRSASignature_V8_t GenerateRSASignature_V8;
@@ -245,7 +263,7 @@ struct FunctionPointers {
// The Cache Flush function is very processor dependent, but is needed by the // The Cache Flush function is very processor dependent, but is needed by the
// haystack code. The haystack code is delivered as a static prebuilt library. // haystack code. The haystack code is delivered as a static prebuilt library.
// For that reason, we pass a function pointer for cache_flush into the // For that reason, we pass a function pointer for cache_flush into the
// haystack. The function is combiled outside of the haystack and may use // haystack. The function is compiled outside of the haystack and may use
// target (processor) specific compiler flags. // target (processor) specific compiler flags.
void clear_cache_function(void *page, size_t len) { void clear_cache_function(void *page, size_t len) {
@@ -309,7 +327,7 @@ struct LevelSession {
level1_.Name = (L1_##Name##_t)dlsym(level1_library_, QUOTE(Function)); \ level1_.Name = (L1_##Name##_t)dlsym(level1_library_, QUOTE(Function)); \
if (!level1_.Name) { \ if (!level1_.Name) { \
LOGW("Could not load L1 %s. Falling Back to L3.", \ LOGW("Could not load L1 %s. Falling Back to L3.", \
QUOTE(OEMCrypto_##Name)); \ QUOTE(Function)); \
return false; \ return false; \
} }
@@ -331,7 +349,14 @@ class Adapter {
OEMCryptoResult Initialize() { OEMCryptoResult Initialize() {
LoadLevel3(); LoadLevel3();
std::string base_path;
wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&base_path);
bool is_in_app = Level3_IsInApp(base_path.c_str());
OEMCryptoResult result = Level3_Initialize(clear_cache_function); OEMCryptoResult result = Level3_Initialize(clear_cache_function);
if (is_in_app) {
return result;
}
if (force_level3()) { if (force_level3()) {
LOGW("Test code. User requested falling back to L3"); LOGW("Test code. User requested falling back to L3");
return result; return result;
@@ -350,6 +375,7 @@ class Adapter {
if (LoadLevel1()) { if (LoadLevel1()) {
LOGD("OEMCrypto_Initialize Level 1 success. I will use level 1."); LOGD("OEMCrypto_Initialize Level 1 success. I will use level 1.");
} else { } else {
level1_ = FunctionPointers(); // revert to all null pointers.
dlclose(level1_library_); dlclose(level1_library_);
level1_library_ = NULL; level1_library_ = NULL;
level1_valid_ = false; level1_valid_ = false;
@@ -402,7 +428,7 @@ class Adapter {
if (level1_.version == 8) { if (level1_.version == 8) {
LOOKUP(LoadKeys_V8, OEMCrypto_LoadKeys_V8); LOOKUP(LoadKeys_V8, OEMCrypto_LoadKeys_V8);
LOOKUP(GenerateRSASignature_V8, OEMCrypto_GenerateRSASignature_V8); LOOKUP(GenerateRSASignature_V8, OEMCrypto_GenerateRSASignature_V8);
} else { } else { // version >= 9
LOOKUP(GenerateRSASignature, OEMCrypto_GenerateRSASignature); LOOKUP(GenerateRSASignature, OEMCrypto_GenerateRSASignature);
LOOKUP(SupportsUsageTable, OEMCrypto_SupportsUsageTable); LOOKUP(SupportsUsageTable, OEMCrypto_SupportsUsageTable);
LOOKUP(UpdateUsageTable, OEMCrypto_UpdateUsageTable); LOOKUP(UpdateUsageTable, OEMCrypto_UpdateUsageTable);
@@ -413,7 +439,7 @@ class Adapter {
if (level1_.version == 9) { if (level1_.version == 9) {
LOOKUP(LoadKeys_V9_or_V10, OEMCrypto_LoadKeys_V9_or_V10); LOOKUP(LoadKeys_V9_or_V10, OEMCrypto_LoadKeys_V9_or_V10);
LOOKUP(GetHDCPCapability_V9, OEMCrypto_GetHDCPCapability_V9); LOOKUP(GetHDCPCapability_V9, OEMCrypto_GetHDCPCapability_V9);
} else { } else { // version >= 10.
LOOKUP(LoadTestKeybox, OEMCrypto_LoadTestKeybox); LOOKUP(LoadTestKeybox, OEMCrypto_LoadTestKeybox);
LOOKUP(LoadTestRSAKey, OEMCrypto_LoadTestRSAKey); LOOKUP(LoadTestRSAKey, OEMCrypto_LoadTestRSAKey);
LOOKUP(QueryKeyControl, OEMCrypto_QueryKeyControl); LOOKUP(QueryKeyControl, OEMCrypto_QueryKeyControl);
@@ -426,16 +452,29 @@ class Adapter {
if (level1_.version == 10) { if (level1_.version == 10) {
LOOKUP(LoadKeys_V9_or_V10, OEMCrypto_LoadKeys_V9_or_V10); LOOKUP(LoadKeys_V9_or_V10, OEMCrypto_LoadKeys_V9_or_V10);
LOOKUP(DecryptCTR_V10, OEMCrypto_DecryptCTR_V10); LOOKUP(DecryptCTR_V10, OEMCrypto_DecryptCTR_V10);
} else { // version 11. } else { // version >= 11.
LOOKUP(LoadKeys, OEMCrypto_LoadKeys); LOOKUP(LoadKeys, OEMCrypto_LoadKeys);
LOOKUP(DecryptCENC, OEMCrypto_DecryptCENC); LOOKUP(DecryptCENC, OEMCrypto_DecryptCENC);
LOOKUP(SecurityPatchLevel, OEMCrypto_Security_Patch_Level); LOOKUP(SecurityPatchLevel, OEMCrypto_Security_Patch_Level);
if (level1_.version >= 12) {
LOOKUP(GetProvisioningMethod, OEMCrypto_GetProvisioningMethod);
LOOKUP(GetOEMPublicCertificate, OEMCrypto_GetOEMPublicCertificate);
LOOKUP(RewrapDeviceRSAKey30, OEMCrypto_RewrapDeviceRSAKey30);
}
} }
} }
} }
// If we have a valid keybox, initialization is done. We're good.
if (OEMCrypto_SUCCESS == level1_.IsKeyboxValid()) { if (OEMCrypto_SUCCESS == level1_.IsKeyboxValid()) {
return true; return true;
} }
// If we use provisioning 3.0, initialization is done. We may not
// be good, but there's no reason to try loading a keybox. Any errors
// will have to be caught in the future when provisioning fails.
if (level1_.version > 11 &&
(level1_.GetProvisioningMethod() == OEMCrypto_OEMCertificate)) {
return true;
}
uint8_t buffer[1]; uint8_t buffer[1];
size_t buffer_size = 0; size_t buffer_size = 0;
if (OEMCrypto_ERROR_NOT_IMPLEMENTED == level1_.GetKeyData(buffer, if (OEMCrypto_ERROR_NOT_IMPLEMENTED == level1_.GetKeyData(buffer,
@@ -522,6 +561,9 @@ class Adapter {
level3_.DeleteUsageEntry = Level3_DeleteUsageEntry; level3_.DeleteUsageEntry = Level3_DeleteUsageEntry;
level3_.ForceDeleteUsageEntry = Level3_ForceDeleteUsageEntry; level3_.ForceDeleteUsageEntry = Level3_ForceDeleteUsageEntry;
level3_.DeleteUsageTable = Level3_DeleteUsageTable; level3_.DeleteUsageTable = Level3_DeleteUsageTable;
level3_.GetProvisioningMethod = Level3_GetProvisioningMethod;
level3_.GetOEMPublicCertificate = Level3_GetOEMPublicCertificate;
level3_.RewrapDeviceRSAKey30 = Level3_RewrapDeviceRSAKey30;
level3_.version = Level3_APIVersion(); level3_.version = Level3_APIVersion();
} }
@@ -658,6 +700,17 @@ OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
return fcn->InstallKeybox(keybox, keyBoxLength); return fcn->InstallKeybox(keybox, keyBoxLength);
} }
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(
SecurityLevel level) {
oemprofiler::ProfiledScope ps(
oemprofiler::OEM_FUNCTION_GET_PROVISIONING_METHOD);
if (!kAdapter) return OEMCrypto_ProvisioningError;
const FunctionPointers* fcn = kAdapter->get(level);
if (!fcn) return OEMCrypto_ProvisioningError;
if (fcn->version < 12) return OEMCrypto_Keybox;
return fcn->GetProvisioningMethod();
}
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) { OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) {
oemprofiler::ProfiledScope ps(oemprofiler::OEM_FUNCTION_IS_KEYBOX_VALID); oemprofiler::ProfiledScope ps(oemprofiler::OEM_FUNCTION_IS_KEYBOX_VALID);
@@ -1024,6 +1077,24 @@ extern "C" OEMCryptoResult OEMCrypto_IsKeyboxValid() {
return OEMCrypto_IsKeyboxValid(kLevelDefault); return OEMCrypto_IsKeyboxValid(kLevelDefault);
} }
extern "C" OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod() {
return OEMCrypto_GetProvisioningMethod(kLevelDefault);
}
extern "C" OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(
OEMCrypto_SESSION session,
uint8_t *public_cert,
size_t *public_cert_length) {
wvcdm::oemprofiler::ProfiledScope ps(
wvcdm::oemprofiler::OEM_FUNCTION_GET_OEM_PUBLIC_CERTIFICATE);
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
if (pair.fcn->version < 12) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return pair.fcn->GetOEMPublicCertificate(pair.session, public_cert,
public_cert_length);
}
extern "C" OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, extern "C" OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,
size_t* idLength) { size_t* idLength) {
return OEMCrypto_GetDeviceID(deviceID, idLength, kLevelDefault); return OEMCrypto_GetDeviceID(deviceID, idLength, kLevelDefault);
@@ -1046,6 +1117,30 @@ extern "C" OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData,
return fcn->GetRandom(randomData, dataLength); return fcn->GetRandom(randomData, dataLength);
} }
extern "C" OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
OEMCrypto_SESSION session,
const uint32_t *nonce,
const uint8_t* encrypted_message_key,
size_t encrypted_message_key_length,
const uint8_t* enc_rsa_key,
size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv,
uint8_t* wrapped_rsa_key,
size_t* wrapped_rsa_key_length) {
wvcdm::oemprofiler::ProfiledScope ps(
wvcdm::oemprofiler::OEM_FUNCTION_REWRAP_DEVICE_RSA_KEY_30);
if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
LevelSession pair = kAdapter->get(session);
if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION;
if (pair.fcn->version < 12) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return pair.fcn->RewrapDeviceRSAKey30(session, nonce, encrypted_message_key,
encrypted_message_key_length,
enc_rsa_key, enc_rsa_key_length,
enc_rsa_key_iv, wrapped_rsa_key,
wrapped_rsa_key_length);
}
extern "C" OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey( extern "C" OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length, const uint32_t* nonce, const uint8_t* signature, size_t signature_length, const uint32_t* nonce,

View File

@@ -48,6 +48,9 @@ enum OEM_FUNCTION {
OEM_FUNCTION_DELETE_USAGE_ENTRY, OEM_FUNCTION_DELETE_USAGE_ENTRY,
OEM_FUNCTION_FORCE_DELETE_USAGE_ENTRY, OEM_FUNCTION_FORCE_DELETE_USAGE_ENTRY,
OEM_FUNCTION_DELETE_USAGE_TABLE, OEM_FUNCTION_DELETE_USAGE_TABLE,
OEM_FUNCTION_GET_PROVISIONING_METHOD,
OEM_FUNCTION_GET_OEM_PUBLIC_CERTIFICATE,
OEM_FUNCTION_REWRAP_DEVICE_RSA_KEY_30,
OEM_FUNCTION_TESTING, // dummy value for testing purposes OEM_FUNCTION_TESTING, // dummy value for testing purposes
OEM_FUNCTION_COUNT OEM_FUNCTION_COUNT

View File

@@ -87,7 +87,9 @@ typedef enum OEMCryptoResult {
* 2. Place the decrypted data into protected memory (SecureBuffer). The * 2. Place the decrypted data into protected memory (SecureBuffer). The
* caller uses a platform-specific method to acquire the protected buffer * caller uses a platform-specific method to acquire the protected buffer
* and a user-memory handle that references it. The handle is supplied * and a user-memory handle that references it. The handle is supplied
* to the decrypt call in the descriptor. * to the decrypt call in the descriptor. If the buffer is filled with
* several OEMCrypto calls, the same handle will be used, and the offset
* will be incremented to indicate where the next write should take place.
* 3. Place the decrypted data directly into the audio or video decoder fifo * 3. Place the decrypted data directly into the audio or video decoder fifo
* (Direct). The caller will use platform-specific methods to initialize * (Direct). The caller will use platform-specific methods to initialize
* the fifo and the decoders. The decrypted stream data is not accessible * the fifo and the decoders. The decrypted stream data is not accessible
@@ -101,6 +103,7 @@ typedef enum OEMCryptoResult {
* (type == OEMCrypto_BufferType_Secure) * (type == OEMCrypto_BufferType_Secure)
* buffer - handle to a platform-specific secure buffer. * buffer - handle to a platform-specific secure buffer.
* max_length - Size of platform-specific secure buffer. * max_length - Size of platform-specific secure buffer.
* offset - offset from beginning of buffer to which OEMCrypto should write.
* (type == OEMCrypto_BufferType_Direct) * (type == OEMCrypto_BufferType_Direct)
* is_video - If true, decrypted bytes are routed to the video * is_video - If true, decrypted bytes are routed to the video
* decoder. If false, decrypted bytes are routed to the * decoder. If false, decrypted bytes are routed to the

View File

@@ -16,7 +16,7 @@
namespace wvoec3 { namespace wvoec3 {
#define Level3_PreInitialize _lcc00 #define Level3_IsInApp _lcc00
#define Level3_Initialize _lcc01 #define Level3_Initialize _lcc01
#define Level3_Terminate _lcc02 #define Level3_Terminate _lcc02
#define Level3_InstallKeybox _lcc03 #define Level3_InstallKeybox _lcc03
@@ -60,10 +60,13 @@ namespace wvoec3 {
#define Level3_ForceDeleteUsageEntry _lcc43 #define Level3_ForceDeleteUsageEntry _lcc43
#define Level3_LoadTestRSAKey _lcc45 #define Level3_LoadTestRSAKey _lcc45
#define Level3_SecurityPatchLevel _lcc46 #define Level3_SecurityPatchLevel _lcc46
#define Level3_GetProvisioningMethod _lcc49
#define Level3_GetOEMPublicCertificate _lcc50
#define Level3_RewrapDeviceRSAKey30 _lcc51
extern "C" { extern "C" {
bool Level3_PreInitialize(const char* path); bool Level3_IsInApp(const char* path);
OEMCryptoResult Level3_Initialize(void (*ClearCache)(void *, size_t)); OEMCryptoResult Level3_Initialize(void (*ClearCache)(void *, size_t));
OEMCryptoResult Level3_Terminate(void); OEMCryptoResult Level3_Terminate(void);
OEMCryptoResult Level3_OpenSession(OEMCrypto_SESSION *session); OEMCryptoResult Level3_OpenSession(OEMCrypto_SESSION *session);
@@ -107,14 +110,14 @@ OEMCryptoResult Level3_SelectKey(const OEMCrypto_SESSION session,
const uint8_t* key_id, const uint8_t* key_id,
size_t key_id_length); size_t key_id_length);
OEMCryptoResult Level3_DecryptCENC(OEMCrypto_SESSION session, OEMCryptoResult Level3_DecryptCENC(OEMCrypto_SESSION session,
const uint8_t *data_addr, const uint8_t *data_addr,
size_t data_length, size_t data_length,
bool is_encrypted, bool is_encrypted,
const uint8_t *iv, const uint8_t *iv,
size_t block_offset, size_t block_offset,
const OEMCrypto_DestBufferDesc* out_buffer, const OEMCrypto_DestBufferDesc* out_buffer,
const OEMCrypto_CENCEncryptPatternDesc* pattern, const OEMCrypto_CENCEncryptPatternDesc* pattern,
uint8_t subsample_flags); uint8_t subsample_flags);
OEMCryptoResult Level3_CopyBuffer(const uint8_t *data_addr, OEMCryptoResult Level3_CopyBuffer(const uint8_t *data_addr,
size_t data_length, size_t data_length,
OEMCrypto_DestBufferDesc* out_buffer, OEMCrypto_DestBufferDesc* out_buffer,
@@ -127,6 +130,10 @@ OEMCryptoResult Level3_WrapKeybox(const uint8_t *keybox,
size_t transportKeyLength); size_t transportKeyLength);
OEMCryptoResult Level3_InstallKeybox(const uint8_t *keybox, OEMCryptoResult Level3_InstallKeybox(const uint8_t *keybox,
size_t keyBoxLength); size_t keyBoxLength);
OEMCrypto_ProvisioningMethod Level3_GetProvisioningMethod();
OEMCryptoResult Level3_GetOEMPublicCertificate(OEMCrypto_SESSION session,
uint8_t *public_cert,
size_t *public_cert_length);
OEMCryptoResult Level3_LoadTestKeybox(); OEMCryptoResult Level3_LoadTestKeybox();
OEMCryptoResult Level3_IsKeyboxValid(void); OEMCryptoResult Level3_IsKeyboxValid(void);
OEMCryptoResult Level3_GetDeviceID(uint8_t* deviceID, OEMCryptoResult Level3_GetDeviceID(uint8_t* deviceID,
@@ -135,6 +142,15 @@ OEMCryptoResult Level3_GetKeyData(uint8_t* keyData,
size_t *keyDataLength); size_t *keyDataLength);
OEMCryptoResult Level3_GetRandom(uint8_t* randomData, OEMCryptoResult Level3_GetRandom(uint8_t* randomData,
size_t dataLength); size_t dataLength);
OEMCryptoResult Level3_RewrapDeviceRSAKey30(OEMCrypto_SESSION session,
const uint32_t *nonce,
const uint8_t* encrypted_message_key,
size_t encrypted_message_key_length,
const uint8_t* enc_rsa_key,
size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv,
uint8_t* wrapped_rsa_key,
size_t* wrapped_rsa_key_length);
OEMCryptoResult Level3_RewrapDeviceRSAKey(OEMCrypto_SESSION session, OEMCryptoResult Level3_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
const uint8_t* message, const uint8_t* message,
size_t message_length, size_t message_length,