Loading gatekeeperd/Android.mk +3 −0 Original line number Diff line number Diff line Loading @@ -25,9 +25,12 @@ LOCAL_SHARED_LIBRARIES := \ libgatekeeper \ liblog \ libhardware \ libbase \ libutils \ libcrypto \ libkeystore_binder LOCAL_STATIC_LIBRARIES := libscrypt_static LOCAL_C_INCLUDES := external/scrypt/lib/crypto include $(BUILD_EXECUTABLE) include $(call first-makefiles-under,$(LOCAL_PATH)) gatekeeperd/IGateKeeperService.cpp +40 −12 Original line number Diff line number Diff line Loading @@ -50,18 +50,25 @@ status_t BnGateKeeperService::onTransact( uint8_t *out = NULL; uint32_t outSize = 0; status_t ret = enroll(uid, currentPasswordHandle, currentPasswordHandleSize, int ret = enroll(uid, currentPasswordHandle, currentPasswordHandleSize, currentPassword, currentPasswordSize, desiredPassword, desiredPasswordSize, &out, &outSize); reply->writeNoException(); if (ret == NO_ERROR && outSize > 0 && out != NULL) { reply->writeInt32(1); if (ret == 0 && outSize > 0 && out != NULL) { reply->writeInt32(GATEKEEPER_RESPONSE_OK); reply->writeInt32(0); reply->writeInt32(outSize); reply->writeInt32(outSize); void *buf = reply->writeInplace(outSize); memcpy(buf, out, outSize); free(out); delete[] out; } else if (ret > 0) { reply->writeInt32(GATEKEEPER_RESPONSE_RETRY); reply->writeInt32(ret); } else { reply->writeInt32(-1); reply->writeInt32(GATEKEEPER_RESPONSE_ERROR); } return NO_ERROR; } Loading @@ -78,10 +85,23 @@ status_t BnGateKeeperService::onTransact( static_cast<const uint8_t *>(data.readInplace(currentPasswordSize)); if (!currentPassword) currentPasswordSize = 0; status_t ret = verify(uid, (uint8_t *) currentPasswordHandle, currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize); bool request_reenroll = false; int ret = verify(uid, (uint8_t *) currentPasswordHandle, currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize, &request_reenroll); reply->writeNoException(); reply->writeInt32(ret == NO_ERROR ? 1 : 0); reply->writeInt32(1); if (ret == 0) { reply->writeInt32(GATEKEEPER_RESPONSE_OK); reply->writeInt32(request_reenroll ? 1 : 0); reply->writeInt32(0); // no payload returned from this call } else if (ret > 0) { reply->writeInt32(GATEKEEPER_RESPONSE_RETRY); reply->writeInt32(ret); } else { reply->writeInt32(GATEKEEPER_RESPONSE_ERROR); } return NO_ERROR; } case VERIFY_CHALLENGE: { Loading @@ -101,17 +121,25 @@ status_t BnGateKeeperService::onTransact( uint8_t *out = NULL; uint32_t outSize = 0; status_t ret = verifyChallenge(uid, challenge, (uint8_t *) currentPasswordHandle, bool request_reenroll = false; int ret = verifyChallenge(uid, challenge, (uint8_t *) currentPasswordHandle, currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize, &out, &outSize); &out, &outSize, &request_reenroll); reply->writeNoException(); if (ret == NO_ERROR && outSize > 0 && out != NULL) { reply->writeInt32(1); if (ret == 0 && outSize > 0 && out != NULL) { reply->writeInt32(GATEKEEPER_RESPONSE_OK); reply->writeInt32(request_reenroll ? 1 : 0); reply->writeInt32(outSize); reply->writeInt32(outSize); void *buf = reply->writeInplace(outSize); memcpy(buf, out, outSize); free(out); delete[] out; } else if (ret > 0) { reply->writeInt32(GATEKEEPER_RESPONSE_RETRY); reply->writeInt32(ret); } else { reply->writeInt32(-1); reply->writeInt32(GATEKEEPER_RESPONSE_ERROR); } return NO_ERROR; } Loading gatekeeperd/IGateKeeperService.h +27 −8 Original line number Diff line number Diff line Loading @@ -35,6 +35,12 @@ public: CLEAR_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 4, }; enum { GATEKEEPER_RESPONSE_OK = 0, GATEKEEPER_RESPONSE_RETRY = 1, GATEKEEPER_RESPONSE_ERROR = -1, }; // DECLARE_META_INTERFACE - C++ client interface not needed static const android::String16 descriptor; virtual const android::String16& getInterfaceDescriptor() const; Loading @@ -43,8 +49,13 @@ public: /** * Enrolls a password with the GateKeeper. Returns 0 on success, negative on failure. * Returns: * - 0 on success * - A timestamp T > 0 if the call has failed due to throttling and should not * be reattempted until T milliseconds have elapsed * - -1 on failure */ virtual status_t enroll(uint32_t uid, virtual int enroll(uint32_t uid, const uint8_t *current_password_handle, uint32_t current_password_handle_length, const uint8_t *current_password, uint32_t current_password_length, const uint8_t *desired_password, uint32_t desired_password_length, Loading @@ -52,21 +63,29 @@ public: /** * Verifies a password previously enrolled with the GateKeeper. * Returns 0 on success, negative on failure. * Returns: * - 0 on success * - A timestamp T > 0 if the call has failed due to throttling and should not * be reattempted until T milliseconds have elapsed * - -1 on failure */ virtual status_t verify(uint32_t uid, const uint8_t *enrolled_password_handle, virtual int verify(uint32_t uid, const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, const uint8_t *provided_password, uint32_t provided_password_length) = 0; const uint8_t *provided_password, uint32_t provided_password_length, bool *request_reenroll) = 0; /** * Verifies a password previously enrolled with the GateKeeper. * Returns 0 on success, negative on failure. * Returns: * - 0 on success * - A timestamp T > 0 if the call has failed due to throttling and should not * be reattempted until T milliseconds have elapsed * - -1 on failure */ virtual status_t verifyChallenge(uint32_t uid, uint64_t challenge, virtual int verifyChallenge(uint32_t uid, uint64_t challenge, const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, const uint8_t *provided_password, uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length) = 0; uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) = 0; /** * Returns the secure user ID for the provided android user */ Loading gatekeeperd/SoftGateKeeper.h 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * Copyright 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #ifndef SOFT_GATEKEEPER_H_ #define SOFT_GATEKEEPER_H_ extern "C" { #include <openssl/rand.h> #include <crypto_scrypt.h> } #include <UniquePtr.h> #include <gatekeeper/gatekeeper.h> #include <iostream> #include <unordered_map> namespace gatekeeper { class SoftGateKeeper : public GateKeeper { public: static const uint32_t SIGNATURE_LENGTH_BYTES = 32; // scrypt params static const uint64_t N = 16384; static const uint32_t r = 8; static const uint32_t p = 1; static const int MAX_UINT_32_CHARS = 11; SoftGateKeeper() { key_.reset(new uint8_t[SIGNATURE_LENGTH_BYTES]); memset(key_.get(), 0, SIGNATURE_LENGTH_BYTES); } virtual ~SoftGateKeeper() { } virtual bool GetAuthTokenKey(const uint8_t **auth_token_key, uint32_t *length) const { if (auth_token_key == NULL || length == NULL) return false; *auth_token_key = const_cast<const uint8_t *>(key_.get()); *length = SIGNATURE_LENGTH_BYTES; return true; } virtual void GetPasswordKey(const uint8_t **password_key, uint32_t *length) { if (password_key == NULL || length == NULL) return; *password_key = const_cast<const uint8_t *>(key_.get()); *length = SIGNATURE_LENGTH_BYTES; } virtual void ComputePasswordSignature(uint8_t *signature, uint32_t signature_length, const uint8_t *, uint32_t, const uint8_t *password, uint32_t password_length, salt_t salt) const { if (signature == NULL) return; crypto_scrypt(password, password_length, reinterpret_cast<uint8_t *>(&salt), sizeof(salt), N, r, p, signature, signature_length); } virtual void GetRandom(void *random, uint32_t requested_length) const { if (random == NULL) return; RAND_pseudo_bytes((uint8_t *) random, requested_length); } virtual void ComputeSignature(uint8_t *signature, uint32_t signature_length, const uint8_t *, uint32_t, const uint8_t *, const uint32_t) const { if (signature == NULL) return; memset(signature, 0, signature_length); } virtual uint64_t GetMillisecondsSinceBoot() const { struct timespec time; int res = clock_gettime(CLOCK_BOOTTIME, &time); if (res < 0) return 0; return (time.tv_sec * 1000) + (time.tv_nsec / 1000 / 1000); } virtual bool IsHardwareBacked() const { return false; } virtual bool GetFailureRecord(uint32_t uid, secure_id_t user_id, failure_record_t *record) { failure_record_t *stored = &failure_map_[uid]; if (user_id != stored->secure_user_id) { stored->secure_user_id = user_id; stored->last_checked_timestamp = 0; stored->failure_counter = 0; } memcpy(record, stored, sizeof(*record)); return true; } virtual void ClearFailureRecord(uint32_t uid, secure_id_t user_id) { failure_record_t *stored = &failure_map_[uid]; stored->secure_user_id = user_id; stored->last_checked_timestamp = 0; stored->failure_counter = 0; } virtual bool WriteFailureRecord(uint32_t uid, failure_record_t *record) { failure_map_[uid] = *record; return true; } private: UniquePtr<uint8_t> key_; std::unordered_map<uint32_t, failure_record_t> failure_map_; }; } #endif // SOFT_GATEKEEPER_H_ gatekeeperd/SoftGateKeeperDevice.cpp +16 −6 Original line number Diff line number Diff line Loading @@ -13,8 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ #include <gatekeeper/soft_gatekeeper.h> #include "SoftGateKeeper.h" #include "SoftGateKeeperDevice.h" namespace android { Loading Loading @@ -58,8 +57,11 @@ int SoftGateKeeperDevice::enroll(uint32_t uid, impl_->Enroll(request, &response); if (response.error != ERROR_NONE) if (response.error == ERROR_RETRY) { return response.retry_timeout; } else if (response.error != ERROR_NONE) { return -EINVAL; } *enrolled_password_handle = response.enrolled_password_handle.buffer.release(); *enrolled_password_handle_length = response.enrolled_password_handle.length; Loading @@ -69,7 +71,8 @@ int SoftGateKeeperDevice::enroll(uint32_t uid, int SoftGateKeeperDevice::verify(uint32_t uid, uint64_t challenge, const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, const uint8_t *provided_password, uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length) { uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) { if (enrolled_password_handle == NULL || provided_password == NULL) { Loading @@ -87,14 +90,21 @@ int SoftGateKeeperDevice::verify(uint32_t uid, impl_->Verify(request, &response); if (response.error != ERROR_NONE) if (response.error == ERROR_RETRY) { return response.retry_timeout; } else if (response.error != ERROR_NONE) { return -EINVAL; } if (auth_token != NULL && auth_token_length != NULL) { *auth_token = response.auth_token.buffer.release(); *auth_token_length = response.auth_token.length; } if (request_reenroll != NULL) { *request_reenroll = response.request_reenroll; } return 0; } } // namespace android Loading
gatekeeperd/Android.mk +3 −0 Original line number Diff line number Diff line Loading @@ -25,9 +25,12 @@ LOCAL_SHARED_LIBRARIES := \ libgatekeeper \ liblog \ libhardware \ libbase \ libutils \ libcrypto \ libkeystore_binder LOCAL_STATIC_LIBRARIES := libscrypt_static LOCAL_C_INCLUDES := external/scrypt/lib/crypto include $(BUILD_EXECUTABLE) include $(call first-makefiles-under,$(LOCAL_PATH))
gatekeeperd/IGateKeeperService.cpp +40 −12 Original line number Diff line number Diff line Loading @@ -50,18 +50,25 @@ status_t BnGateKeeperService::onTransact( uint8_t *out = NULL; uint32_t outSize = 0; status_t ret = enroll(uid, currentPasswordHandle, currentPasswordHandleSize, int ret = enroll(uid, currentPasswordHandle, currentPasswordHandleSize, currentPassword, currentPasswordSize, desiredPassword, desiredPasswordSize, &out, &outSize); reply->writeNoException(); if (ret == NO_ERROR && outSize > 0 && out != NULL) { reply->writeInt32(1); if (ret == 0 && outSize > 0 && out != NULL) { reply->writeInt32(GATEKEEPER_RESPONSE_OK); reply->writeInt32(0); reply->writeInt32(outSize); reply->writeInt32(outSize); void *buf = reply->writeInplace(outSize); memcpy(buf, out, outSize); free(out); delete[] out; } else if (ret > 0) { reply->writeInt32(GATEKEEPER_RESPONSE_RETRY); reply->writeInt32(ret); } else { reply->writeInt32(-1); reply->writeInt32(GATEKEEPER_RESPONSE_ERROR); } return NO_ERROR; } Loading @@ -78,10 +85,23 @@ status_t BnGateKeeperService::onTransact( static_cast<const uint8_t *>(data.readInplace(currentPasswordSize)); if (!currentPassword) currentPasswordSize = 0; status_t ret = verify(uid, (uint8_t *) currentPasswordHandle, currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize); bool request_reenroll = false; int ret = verify(uid, (uint8_t *) currentPasswordHandle, currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize, &request_reenroll); reply->writeNoException(); reply->writeInt32(ret == NO_ERROR ? 1 : 0); reply->writeInt32(1); if (ret == 0) { reply->writeInt32(GATEKEEPER_RESPONSE_OK); reply->writeInt32(request_reenroll ? 1 : 0); reply->writeInt32(0); // no payload returned from this call } else if (ret > 0) { reply->writeInt32(GATEKEEPER_RESPONSE_RETRY); reply->writeInt32(ret); } else { reply->writeInt32(GATEKEEPER_RESPONSE_ERROR); } return NO_ERROR; } case VERIFY_CHALLENGE: { Loading @@ -101,17 +121,25 @@ status_t BnGateKeeperService::onTransact( uint8_t *out = NULL; uint32_t outSize = 0; status_t ret = verifyChallenge(uid, challenge, (uint8_t *) currentPasswordHandle, bool request_reenroll = false; int ret = verifyChallenge(uid, challenge, (uint8_t *) currentPasswordHandle, currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize, &out, &outSize); &out, &outSize, &request_reenroll); reply->writeNoException(); if (ret == NO_ERROR && outSize > 0 && out != NULL) { reply->writeInt32(1); if (ret == 0 && outSize > 0 && out != NULL) { reply->writeInt32(GATEKEEPER_RESPONSE_OK); reply->writeInt32(request_reenroll ? 1 : 0); reply->writeInt32(outSize); reply->writeInt32(outSize); void *buf = reply->writeInplace(outSize); memcpy(buf, out, outSize); free(out); delete[] out; } else if (ret > 0) { reply->writeInt32(GATEKEEPER_RESPONSE_RETRY); reply->writeInt32(ret); } else { reply->writeInt32(-1); reply->writeInt32(GATEKEEPER_RESPONSE_ERROR); } return NO_ERROR; } Loading
gatekeeperd/IGateKeeperService.h +27 −8 Original line number Diff line number Diff line Loading @@ -35,6 +35,12 @@ public: CLEAR_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 4, }; enum { GATEKEEPER_RESPONSE_OK = 0, GATEKEEPER_RESPONSE_RETRY = 1, GATEKEEPER_RESPONSE_ERROR = -1, }; // DECLARE_META_INTERFACE - C++ client interface not needed static const android::String16 descriptor; virtual const android::String16& getInterfaceDescriptor() const; Loading @@ -43,8 +49,13 @@ public: /** * Enrolls a password with the GateKeeper. Returns 0 on success, negative on failure. * Returns: * - 0 on success * - A timestamp T > 0 if the call has failed due to throttling and should not * be reattempted until T milliseconds have elapsed * - -1 on failure */ virtual status_t enroll(uint32_t uid, virtual int enroll(uint32_t uid, const uint8_t *current_password_handle, uint32_t current_password_handle_length, const uint8_t *current_password, uint32_t current_password_length, const uint8_t *desired_password, uint32_t desired_password_length, Loading @@ -52,21 +63,29 @@ public: /** * Verifies a password previously enrolled with the GateKeeper. * Returns 0 on success, negative on failure. * Returns: * - 0 on success * - A timestamp T > 0 if the call has failed due to throttling and should not * be reattempted until T milliseconds have elapsed * - -1 on failure */ virtual status_t verify(uint32_t uid, const uint8_t *enrolled_password_handle, virtual int verify(uint32_t uid, const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, const uint8_t *provided_password, uint32_t provided_password_length) = 0; const uint8_t *provided_password, uint32_t provided_password_length, bool *request_reenroll) = 0; /** * Verifies a password previously enrolled with the GateKeeper. * Returns 0 on success, negative on failure. * Returns: * - 0 on success * - A timestamp T > 0 if the call has failed due to throttling and should not * be reattempted until T milliseconds have elapsed * - -1 on failure */ virtual status_t verifyChallenge(uint32_t uid, uint64_t challenge, virtual int verifyChallenge(uint32_t uid, uint64_t challenge, const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, const uint8_t *provided_password, uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length) = 0; uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) = 0; /** * Returns the secure user ID for the provided android user */ Loading
gatekeeperd/SoftGateKeeper.h 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * Copyright 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #ifndef SOFT_GATEKEEPER_H_ #define SOFT_GATEKEEPER_H_ extern "C" { #include <openssl/rand.h> #include <crypto_scrypt.h> } #include <UniquePtr.h> #include <gatekeeper/gatekeeper.h> #include <iostream> #include <unordered_map> namespace gatekeeper { class SoftGateKeeper : public GateKeeper { public: static const uint32_t SIGNATURE_LENGTH_BYTES = 32; // scrypt params static const uint64_t N = 16384; static const uint32_t r = 8; static const uint32_t p = 1; static const int MAX_UINT_32_CHARS = 11; SoftGateKeeper() { key_.reset(new uint8_t[SIGNATURE_LENGTH_BYTES]); memset(key_.get(), 0, SIGNATURE_LENGTH_BYTES); } virtual ~SoftGateKeeper() { } virtual bool GetAuthTokenKey(const uint8_t **auth_token_key, uint32_t *length) const { if (auth_token_key == NULL || length == NULL) return false; *auth_token_key = const_cast<const uint8_t *>(key_.get()); *length = SIGNATURE_LENGTH_BYTES; return true; } virtual void GetPasswordKey(const uint8_t **password_key, uint32_t *length) { if (password_key == NULL || length == NULL) return; *password_key = const_cast<const uint8_t *>(key_.get()); *length = SIGNATURE_LENGTH_BYTES; } virtual void ComputePasswordSignature(uint8_t *signature, uint32_t signature_length, const uint8_t *, uint32_t, const uint8_t *password, uint32_t password_length, salt_t salt) const { if (signature == NULL) return; crypto_scrypt(password, password_length, reinterpret_cast<uint8_t *>(&salt), sizeof(salt), N, r, p, signature, signature_length); } virtual void GetRandom(void *random, uint32_t requested_length) const { if (random == NULL) return; RAND_pseudo_bytes((uint8_t *) random, requested_length); } virtual void ComputeSignature(uint8_t *signature, uint32_t signature_length, const uint8_t *, uint32_t, const uint8_t *, const uint32_t) const { if (signature == NULL) return; memset(signature, 0, signature_length); } virtual uint64_t GetMillisecondsSinceBoot() const { struct timespec time; int res = clock_gettime(CLOCK_BOOTTIME, &time); if (res < 0) return 0; return (time.tv_sec * 1000) + (time.tv_nsec / 1000 / 1000); } virtual bool IsHardwareBacked() const { return false; } virtual bool GetFailureRecord(uint32_t uid, secure_id_t user_id, failure_record_t *record) { failure_record_t *stored = &failure_map_[uid]; if (user_id != stored->secure_user_id) { stored->secure_user_id = user_id; stored->last_checked_timestamp = 0; stored->failure_counter = 0; } memcpy(record, stored, sizeof(*record)); return true; } virtual void ClearFailureRecord(uint32_t uid, secure_id_t user_id) { failure_record_t *stored = &failure_map_[uid]; stored->secure_user_id = user_id; stored->last_checked_timestamp = 0; stored->failure_counter = 0; } virtual bool WriteFailureRecord(uint32_t uid, failure_record_t *record) { failure_map_[uid] = *record; return true; } private: UniquePtr<uint8_t> key_; std::unordered_map<uint32_t, failure_record_t> failure_map_; }; } #endif // SOFT_GATEKEEPER_H_
gatekeeperd/SoftGateKeeperDevice.cpp +16 −6 Original line number Diff line number Diff line Loading @@ -13,8 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ #include <gatekeeper/soft_gatekeeper.h> #include "SoftGateKeeper.h" #include "SoftGateKeeperDevice.h" namespace android { Loading Loading @@ -58,8 +57,11 @@ int SoftGateKeeperDevice::enroll(uint32_t uid, impl_->Enroll(request, &response); if (response.error != ERROR_NONE) if (response.error == ERROR_RETRY) { return response.retry_timeout; } else if (response.error != ERROR_NONE) { return -EINVAL; } *enrolled_password_handle = response.enrolled_password_handle.buffer.release(); *enrolled_password_handle_length = response.enrolled_password_handle.length; Loading @@ -69,7 +71,8 @@ int SoftGateKeeperDevice::enroll(uint32_t uid, int SoftGateKeeperDevice::verify(uint32_t uid, uint64_t challenge, const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length, const uint8_t *provided_password, uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length) { uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) { if (enrolled_password_handle == NULL || provided_password == NULL) { Loading @@ -87,14 +90,21 @@ int SoftGateKeeperDevice::verify(uint32_t uid, impl_->Verify(request, &response); if (response.error != ERROR_NONE) if (response.error == ERROR_RETRY) { return response.retry_timeout; } else if (response.error != ERROR_NONE) { return -EINVAL; } if (auth_token != NULL && auth_token_length != NULL) { *auth_token = response.auth_token.buffer.release(); *auth_token_length = response.auth_token.length; } if (request_reenroll != NULL) { *request_reenroll = response.request_reenroll; } return 0; } } // namespace android