如果想在实现一个Android应用程序的客户端来控制Brillo系统,那么首要问题是解决如何从/privet/v3/auth->@<-中获取access token。当然在此之前,我们还要完成device pairing的操作,在device pairing中的confirm阶段,我们需要从服务器中得到的session id与device commitment中生成client commitment, 而在client commitment创建的时候,我们需要客户端代码能进行p224_spake2 方式的加密、解密操作。网上也找不到关于p224_spake2相关的java代码实现,自己用java实现又不太现实(查看了一下cpp的实现: external/libchrome/crypto/p224_spake.cc,发现自己没那个能力啊)。试试看能不能以JAVA + JNI + native的代码实现。
- 相关的编译环境:
Brillo项目使用的是brillo-m9-dev分支的代码;Android项目使用的android-5.1.1_r15分支的代码。
NOTE: Android项目不要使用6.0(M)的分支,在6.0下编译出来的JNI库6.0版本以下的系统可能无法使用。
- 生成静态链接库:libbrokit.a
需要在Brillo项目上生成一个静态库,这个静态库要求:
- 包含external/libchrome/crypto/p224_spake.cc
- 包含external/libweave/{data_encoding.cc,third_party/{modp_b64/modp_b64.cc,libuweave/sr/crypto_hmac.c},我们在生成client commitment, authorization code的时候,会用到libweave中相关实现。
先看一下如何生成client commitment, authorization code:
bool create_auth_code(std::string password, std::string sessionId, std::string devCmt, std::string &cliCmt, std::string &authCode) { crypto::P224EncryptedKeyExchange spake{ crypto::P224EncryptedKeyExchange::kPeerTypeClient, password}; cliCmt = weave::Base64Encode(spake.GetNextMessage()); std::vector<uint8_t> deviceCommitmentDecoded; weave::Base64Decode(devCmt, &deviceCommitmentDecoded); spake.ProcessMessage(std::string(deviceCommitmentDecoded.begin(), deviceCommitmentDecoded.end())); const std::string &key = spake.GetUnverifiedKey(); std::vector<uint8_t> authCodeRaw{ weave::privet::HmacSha256(std::vector<uint8_t>{key.begin(), key.end()}, std::vector<uint8_t>{sessionId.begin(), sessionId.end()})}; authCode = weave::Base64Encode(authCodeRaw); return true; }
可以看到会使用到libweave中的crypto::P224EncryptedKeyExchange, weave::Base64Encode, weave::privet::HmacSha256这些相关的实现。为了尽可能少地改动源代码,这里通过Android.mk(makefile)来实现:
libbr_libchrome.a包含external/libchrome中的相关文件:
LOCAL_PATH := $(call my-dir) BRPRE := libbro include $(CLEAR_VARS) LIBCHROME_PATH := external/libchrome #LOCAL_RTTI_FLAG := -frtti LOCAL_CPP_EXTENSION := .cc LOCAL_CFLAGS := \ -Wall -Werror \ -Wno-char-subscripts -Wno-missing-field-initializers \ -Wno-unused-function -Wno-unused-parameter -fvisibility=hidden LOCAL_CPPFLAGS := \ -Wno-deprecated-register -Wno-sign-promo \ -Wno-non-virtual-dtor LOCAL_C_INCLUDES := \ external/gmock/include \ external/gtest/include \ external/libchrome LOCAL_MODULE := $(BRPRE)_libchrome LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := STATIC_LIBRARIES libchrome_LOCAL_SRC_FILES := \ base/at_exit.cc \ base/callback_internal.cc \ base/files/file_util_posix.cc \ base/lazy_instance.cc \ base/pickle.cc \ base/rand_util.cc \ base/rand_util_posix.cc \ base/strings/string_number_conversions.cc \ base/strings/string_util.cc \ base/synchronization/lock_impl_posix.cc \ base/threading/platform_thread_posix.cc \ crypto/p224.cc \ crypto/p224_spake.cc \ crypto/random.cc \ crypto/secure_hash_default.cc \ crypto/secure_util.cc \ crypto/sha2.cc \ crypto/third_party/nss/sha512.cc libchrome_LOCAL_SRC_FILES += \ base/command_line.cc \ base/debug/alias.cc \ base/debug/debugger.cc \ base/debug/debugger_posix.cc \ base/files/file_path.cc \ base/files/file_path_constants.cc \ base/logging.cc \ base/strings/string_piece.cc $(call local-intermediates-dir)/$(LIBCHROME_PATH)/%.cc: PRIV_INTERMEDIATES_DIR := $(call local-intermediates-dir) $(call local-intermediates-dir)/$(LIBCHROME_PATH)/%.cc: | \ $(ACP) $(addprefix $(LIBCHROME_PATH)/,$(libchrome_LOCAL_SRC_FILES)) $(hide)mkdir -p $(dir $@) $(hide)$(ACP) $(patsubst $(PRIV_INTERMEDIATES_DIR)/%,%,$@) $@ LOCAL_GENERATED_SOURCES := \ $(addprefix $(call local-intermediates-dir)/$(LIBCHROME_PATH)/,$(libchrome_LOCAL_SRC_FILES)) include $(BUILD_STATIC_LIBRARY)
libbro_libweave.a包含external/libweave中的相关文件:
include $(CLEAR_VARS) LIBWEAVE_PATH := external/libweave #LOCAL_RTTI_FLAG := -frtti LOCAL_CPP_EXTENSION := .cc LOCAL_CFLAGS := \ -Wall -Werror \ -Wno-char-subscripts -Wno-missing-field-initializers \ -Wno-unused-function -Wno-unused-parameter -fvisibility=hidden LOCAL_CPPFLAGS := \ -Wno-deprecated-register \ -Wno-sign-compare \ -Wno-sign-promo \ -Wno-non-virtual-dtor LOCAL_C_INCLUDES := \ external/boringssl/src/include \ external/libchrome \ external/libweave \ external/libweave/third_party/modp_b64/modp_b64 \ external/libweave/third_party/modp_b64 \ external/libweave/third_party/libuweave LOCAL_MODULE := $(BRPRE)_libweave LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := STATIC_LIBRARIES libweave_LOCAL_SRC_FILES := \ src/data_encoding.cc \ src/privet/openssl_utils.cc \ third_party/modp_b64/modp_b64.cc \ third_party/libuweave/src/crypto_hmac.c $(call local-intermediates-dir)/$(LIBWEAVE_PATH)/%.cc: PRIV_INTERMEDIATES_DIR := $(call local-intermediates-dir) $(call local-intermediates-dir)/$(LIBWEAVE_PATH)/%.cc: | \ $(ACP) $(addprefix $(LIBWEAVE_PATH)/,$(libweave_LOCAL_SRC_FILES)) $(hide)mkdir -p $(dir $@) $(hide)$(ACP) $(patsubst $(PRIV_INTERMEDIATES_DIR)/%,%,$@) $@ $(call local-intermediates-dir)/$(LIBWEAVE_PATH)/%.c: PRIV_INTERMEDIATES_DIR := $(call local-intermediates-dir) $(call local-intermediates-dir)/$(LIBWEAVE_PATH)/%.c: | \ $(ACP) $(addprefix $(LIBWEAVE_PATH)/,$(libweave_LOCAL_SRC_FILES)) $(hide)mkdir -p $(dir $@) $(hide)$(ACP) $(patsubst $(PRIV_INTERMEDIATES_DIR)/%,%,$@) $@ LOCAL_GENERATED_SOURCES := \ $(addprefix $(call local-intermediates-dir)/$(LIBWEAVE_PATH)/,$(libweave_LOCAL_SRC_FILES)) include $(BUILD_STATIC_LIBRARY)
而libbrokit.a包含的则是libbro_libchrome.a, libbro_libchrome.a, 以及我自己实现的create_auth_code()方法(在文件kit.cc中):
include $(CLEAR_VARS) #LOCAL_RTTI_FLAG := -frtti LOCAL_CPP_EXTENSION := .cc LOCAL_CFLAGS := \ -Wall -Werror \ -Wno-char-subscripts -Wno-missing-field-initializers \ -Wno-unused-function -Wno-unused-parameter -fvisibility=hidden LOCAL_CPPFLAGS := \ -Wno-deprecated-register -Wno-sign-promo \ -Wno-non-virtual-dtor LOCAL_C_INCLUDES := \ external/gmock/include \ external/gtest/include \ external/libchrome \ external/libweave LOCAL_MODULE := $(BRPRE)kit LOCAL_WHOLE_STATIC_LIBRARIES := \ $(BRPRE)_libchrome $(BRPRE)_libweave \ libcrypto_static LOCAL_SRC_FILES += \ kit.cc include $(BUILD_STATIC_LIBRARY) my_LOCAL_BUILT_MODULE := $(LOCAL_BUILT_MODULE) $(LOCAL_PATH)/$(notdir $(my_LOCAL_BUILT_MODULE)): $(LOCAL_BUILT_MODULE) $(copy-file-to-target)
最后会将libbrokit.a copy到与这个Android.mk同级的目录中。
同时,这里还有一个简单的应用,用于验证生成的libbrokit.a是否存在问题:
include $(CLEAR_VARS) LOCAL_CPP_EXTENSION := .cc LOCAL_CFLAGS := \ -Wall -Werror \ -Wno-char-subscripts -Wno-missing-field-initializers \ -Wno-unused-function -Wno-unused-parameter LOCAL_CPPFLAGS := \ -Wno-deprecated-register -Wno-sign-promo \ -Wno-non-virtual-dtor LOCAL_C_INCLUDES := \ external/gmock/include \ external/gtest/include \ external/libchrome \ external/libweave LOCAL_MODULE := Kit LOCAL_STATIC_LIBRARIES := \ $(BRPRE)kit LOCAL_SHARED_LIBRARIES := \ liblog LOCAL_SRC_FILES += \ main.cc include $(BUILD_EXECUTABLE) # ---------------------------------------------------------------------------- $(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/$(notdir $(my_LOCAL_BUILT_MODULE))
main.cc文件内容如下:
int main(int argc, char *argv[]) { std::string sessionId{argv[1]}, devCmt{argv[2]}; std::string cliCmt, authCode; create_auth_code("hello", sessionId, devCmt, cliCmt, authCode); return 0; }
- 生成动态链接库:libbrodm-jni.so
本来想着已经有完整的静态库了并且它所依赖的动态库只有libc, libc++和liblog,在Brillo项目中随便整一个JNI库应该没有什么问题,考虑到Brillo项目中有libnativehelper文件夹,应该可以在这个项目中编译一个JNI库,于是mma, 报错了!libnativehelper代码编译出错了:
In file included from libnativehelper/AsynchronousCloseMonitor.cpp:19: libnativehelper/include/nativehelper/AsynchronousCloseMonitor.h:20:10: fatal error: 'ScopedPthreadMutexLock.h' file not found #include "ScopedPthreadMutexLock.h" ^ 1 error generated. ninja: build stopped: subcommand failed.
考虑到brillo-m9-dev可能是开发分支,Brillo系统中没有使用到JAVA或者是JNI的代码,编译出错也正常。于是考虑将android-6.0.0_r5分支的代码checkout,再次编译,果然没有问题。
有了JNI库,我们就可以编译一个Android app运行下,看一下动态库能不能正常加载,相关的方法能不能正常运行。很遗憾,在Brillo项目中编译出来的JNI库不能在android-5.1.1的系统上正常运行,从log上看,加载JNI库就有问题:
W/linker (12375): libbrodm-jni.so: unused DT entry: type 0x6000000f arg 0xa490 W/linker (12375): libbrodm-jni.so: unused DT entry: type 0x60000010 arg 0xcb8 W/linker (12375): libbrodm-jni.so: unused DT entry: type 0x6ffffef5 arg 0x9344 W/linker (12375): libbrodm-jni.so: unused DT entry: type 0x6ffffffe arg 0xa440 W/linker (12375): libbrodm-jni.so: unused DT entry: type 0x6fffffff arg 0x2 E/art (12375): dlopen("/data/app/com.brobwind.brodm-2/lib/arm/libbrodm-jni.so", RTLD_LAZY) failed: dlopen failed: empty/missing DT_HASH in "libbrodm-jni.so" (built with --hash-style=gnu?) D/AndroidRuntime(12375): Shutting down VM --------- beginning of crash E/AndroidRuntime(12375): FATAL EXCEPTION: main E/AndroidRuntime(12375): Process: com.brobwind.brodm, PID: 12375 E/AndroidRuntime(12375): java.lang.UnsatisfiedLinkError: dlopen failed: empty/missing DT_HASH in "libbrodm-jni.so" (built with --hash-style=gnu?) E/AndroidRuntime(12375): at java.lang.Runtime.loadLibrary(Runtime.java:371) E/AndroidRuntime(12375): at java.lang.System.loadLibrary(System.java:988) E/
于是只好在android-51.1_r15项目中编译了,可是编译的时候居然出错了:
target SharedLib: libbrodm-jni (out/target/product/mini-emulator-armv7-a-neon/obj/SHARED_LIBRARIES/libbrodm-jni_intermediates/LINKED/libbrodm-jni.so) bionic/libc/include/stdio.h:398: error: undefined reference to '__fwrite_chk' bionic/libc/include/stdio.h:398: error: undefined reference to '__fwrite_chk' bionic/libc/include/stdio.h:398: error: undefined reference to '__fwrite_chk' bionic/libc/include/string.h:166: error: undefined reference to '__memchr_chk' bionic/libc/include/string.h:166: error: undefined reference to '__memchr_chk' collect2: error: ld returned 1 exit status
这两个函数在bionic里面定义,android-5.1.1_15项目中没有定义。
心想android-6.0的代码比较新,应该可以编译通过。果然,再一次编译通过。可是,还是运行不了,同样的问题。
所以 最后才会有了__bionic_patch.cpp这个文件:
extern "C" size_t /*__attribute__((weak))*/ __fwrite_chk(const void * __restrict buf, size_t size, size_t count, FILE * __restrict stream, size_t /*buf_size*/) { return fwrite(buf, size, count, stream); } extern "C" void* /*__attribute__((weak))*/ __memchr_chk(const void* s, int c, size_t n, size_t /*buf_size*/) { return memchr(s, c, n); }
关于完整的代码,请参考:->@<-