Chrome extension から NaCl 経由でOpenSSLを使ってみた
うぐぅめんどくさい。まる一日で終わったのが奇跡だった。偶然動いてるだけかもしんない。
(追記: 以下、2013-05-16時点で少しだけ修正を加えた。変更点はstableがpepper_26/となった点くらいだと思う)
○ nacl_sdk で pepper_25/ 下に hello_tutorial を入れて遊ぶところまでまずやる
現在のチュートリアルをやる (https://developers.google.com/native-client/devguide/tutorial)
これによって、動く例を作るのであった
○ naclports で OpenSSL を取ってくる
naclportsにすでにOpenSSL 1.0.0eを取ってくる仕組みがある。これを上手く使う。
naclportsのインストールは大体ここを参考にする。depot_toolが必要、とあるのでそちらも: https://code.google.com/p/naclports/wiki/HowTo_Checkout
naclportsをインストールしたディレクトリ上で (NACL_SDK_ROOT を設定した状態で) 、"NACL_PACKAGES_BITSIZE=32 make openssl" と "NACL_PACKAGES_BITSIZE=64 make openssl" を naclports/src 下で実行する。32bitsと64bitsの両方のライブラリをゲットする
……のだが、現状だと片方をビルドするともう片方のビルドを拒否される。多分Makefileが naclports/src/out/sentinel で32bitsと64bitsのファイルを区別してないから。しょうが無いので out/sentinel をぶっ飛ばして無理やり両方作らせる
の両方出来る。
○ hello_tutorial のMakefileを大改造だ
苦労の証をそのまんまぶっこんでみる
# Copyright (c) 2012 The Native Client Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # GNU Make based build file. For details on GNU Make see: # http://www.gnu.org/software/make/manual/make.html # # # Project information # # These variables store project specific settings for the project name # build flags, files to copy or install. In the examples it is typically # only the list of sources and project name that will actually change and # the rest of the makefile is boilerplate for defining build rules. # PROJECT:=hello_tutorial LDFLAGS:=-lppapi_cpp -lppapi -lssl -lcrypto -lglibc-compat -lnosys # LDFLAGS:=-lppapi_cpp -lppapi -lnosys CXX_SOURCES:=$(PROJECT).cc NACLPORTS_ROOT = /home/dmiyakawa/utils/naclports/src # INCLUDES_32 = -I$(NACL_SDK_ROOT)/toolchain/linux_x86_newlib/i686-nacl/usr/include # INCLUDES_64 = -I$(NACL_SDK_ROOT)/toolchain/linux_x86_newlib/x86_64-nacl/usr/include # LIBSSL_32 = $(NACLPORTS_ROOT)/out/repository-i686/openssl-1.0.0e/libssl.a $(NACLPORTS_ROOT)/out/repository-i686/openssl-1.0.0e/libcrypto.a $(NACLPORTS_ROOT)/out/repository-i686/glibc-compat-0.1/out/libglibc-compat.a # LIBSSL_64 = $(NACLPORTS_ROOT)/out/repository-x86_64/openssl-1.0.0e/libssl.a $(NACLPORTS_ROOT)/out/repository-x86_64/openssl-1.0.0e/libcrypto.a $(NACLPORTS_ROOT)/out/repository-x86_64/glibc-compat-0.1/out/libglibc-compat.a LIBSSL_32 = LIBSSL_64 = # # Get pepper directory for toolchain and includes. # # If NACL_SDK_ROOT is not set, then assume it can be found a two directories up, # from the default example directory location. # THIS_MAKEFILE:=$(abspath $(lastword $(MAKEFILE_LIST))) NACL_SDK_ROOT?=$(abspath $(dir $(THIS_MAKEFILE))../..) # Project Build flags WARNINGS:=-Wno-long-long -Wall -Wswitch-enum -pedantic -Werror CXXFLAGS:=-pthread -std=gnu++98 $(WARNINGS) # # Compute tool paths # # OSNAME:=$(shell python $(NACL_SDK_ROOT)/tools/getos.py) TC_PATH:=$(abspath $(NACL_SDK_ROOT)/toolchain/$(OSNAME)_x86_newlib) CXX:=$(TC_PATH)/bin/i686-nacl-g++ # # Disable DOS PATH warning when using Cygwin based tools Windows # CYGWIN ?= nodosfilewarning export CYGWIN # Declare the ALL target first, to make the 'all' target the default build all: $(PROJECT)_x86_32.nexe $(PROJECT)_x86_64.nexe # Define 32 bit compile and link rules for main application x86_32_OBJS:=$(patsubst %.cc,%_32.o,$(CXX_SOURCES)) $(x86_32_OBJS) : %_32.o : %.cc $(THIS_MAKE) $(CXX) -o $@ -c $< -m32 -O0 -g $(CXXFLAGS) $(INCLUDES_32) $(PROJECT)_x86_32.nexe : $(x86_32_OBJS) $(CXX) -o $@ $^ $(LIBSSL_32) -m32 -O0 -g $(CXXFLAGS) $(LDFLAGS) # Define 64 bit compile and link rules for C++ sources x86_64_OBJS:=$(patsubst %.cc,%_64.o,$(CXX_SOURCES)) $(x86_64_OBJS) : %_64.o : %.cc $(THIS_MAKE) $(CXX) -o $@ -c $< -m64 -O0 -g $(CXXFLAGS) $(INCLUDES_64) $(PROJECT)_x86_64.nexe : $(x86_64_OBJS) $(CXX) -o $@ $^ $(LIBSSL_64) -m64 -O0 -g $(CXXFLAGS) $(LDFLAGS) # Define a phony rule so it always runs, to build nexe and start up server. .PHONY: RUN RUN: all python ../httpd.py .PHONY: clean clean: rm *.o *.nexe
ポイントは -lssl, -lcrypto の後に -lglibc-compat と -lnosys を入れるところだった。
○ hello_tutorial.cc で無理やり使う
まぁ、こういう感じに
これをalertで使うようにする。だいたいこういう感じ。
ちなみに、これと似たような感じでDiffie-Hellman関係の関数を呼ぼうとすると軒並みNULLが返ってきて心が折れそうになる。なんでなの……
○ 以上の実装をextension にする。
https://developer.chrome.com/extensions/getstarted.html
で作られる getstarted/ に
<!doctype html> <html> <head> <title>Getting Started Extension's Popup</title> <style> body { min-width:357px; overflow-x:hidden; } img { margin:5px; border:2px solid black; vertical-align:middle; width:75px; height:75px; } </style> <!-- JavaScript and HTML must be in separate files for security. --> <script src="nacl.js"></script> </head> <body> <h1>Native Client Module HelloTutorial</h1> <p> <div id="listener"> <script type="text/javascript" src="nacl2.js"></script> <embed name="nacl_module" id="hello_tutorial" width=0 height=0 src="hello_tutorial.nmf" type="application/x-nacl" /> </div> </p> <h2>Status</h2> <div id="status_field">NO-STATUS</div> </body> </html>
○ pepper_25からpepper_26に変わったら上のがコンパイル通らないよ? (2013-05-16)
pepper_25からpepper_26に変わる際に(多分内部のファイル構造と)Makefileの流儀が大幅に変わったようだ。
hello_worldプロジェクトのMakefileを比較するとわかりやすい。
多くの煩雑なフラグ指定をNaCl SDKのtools/common.mk 内部で処理してくれるようになった、ということ。今後書くMakefileに関しては設定が圧倒的に楽になった(んだろう)が、pepper_25の方法で書かれたMakefileはオワコン。
今確認した限り、pepper_27とpepper_canary (現時点) のhello_worldではpepper_26の流儀と同じ風味で書かれているので、pepper_25までの書き方が筋悪だったということで一つ。
ただ、この過程でpepper_26ではNaCl Portsのssl関係がNACL_ARCH=arm 指定時にコンパイルが通らなくなっており (https://code.google.com/p/naclports/issues/detail?id=65)、現在betaであるpepper_27 まで待つ必要があるらしい。ウゼェ……!
後方互換的に現状ではpepper_25で上記のサンプルを動作させるしかない。ググってたどり着いた人、残念でしたー><
○ pepper_25と(最新のnaclports)の組み合わせは微妙? (2013-05-17)
ヘッダとライブラリのパスで不整合があるような……
○ Macでなんとかする
openssl は (pepper_27とかの時点でも) Macでのビルドをサポートしていない
非公式には以下のような方法があるようだ:
https://groups.google.com/forum/#!msg/native-client-discuss/_2wULoEliSQ/CyprWLOyMm4J
以下の方が若干だが興味深い
https://groups.google.com/forum/?fromgroups#!topic/native-client-discuss/J8XiyzdPZo8
(仕組み上バイナリは同一になる、らしい)
○ pepper_29 だとLinuxでもnaclportsのビルドがこける (2013-09-12)
NACL_GLIBC=1 をする
(追記: 以下、2013-05-16時点で少しだけ修正を加えた。変更点はstableがpepper_26/となった点くらいだと思う)
○ nacl_sdk で pepper_25/ 下に hello_tutorial を入れて遊ぶところまでまずやる
現在のチュートリアルをやる (https://developers.google.com/native-client/devguide/tutorial)
これによって、動く例を作るのであった
○ naclports で OpenSSL を取ってくる
naclportsにすでにOpenSSL 1.0.0eを取ってくる仕組みがある。これを上手く使う。
naclportsのインストールは大体ここを参考にする。depot_toolが必要、とあるのでそちらも: https://code.google.com/p/naclports/wiki/HowTo_Checkout
naclportsをインストールしたディレクトリ上で (NACL_SDK_ROOT を設定した状態で) 、"NACL_PACKAGES_BITSIZE=32 make openssl" と "NACL_PACKAGES_BITSIZE=64 make openssl" を naclports/src 下で実行する。32bitsと64bitsの両方のライブラリをゲットする
……のだが、現状だと片方をビルドするともう片方のビルドを拒否される。多分Makefileが naclports/src/out/sentinel で32bitsと64bitsのファイルを区別してないから。しょうが無いので out/sentinel をぶっ飛ばして無理やり両方作らせる
- naclports/src/out/repository-x86_64/openssl-1.0.0e/libssl.a
- naclports/src/out/repository-i686/openssl-1.0.0e/libssl.a
の両方出来る。
○ hello_tutorial のMakefileを大改造だ
苦労の証をそのまんまぶっこんでみる
# Copyright (c) 2012 The Native Client Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # GNU Make based build file. For details on GNU Make see: # http://www.gnu.org/software/make/manual/make.html # # # Project information # # These variables store project specific settings for the project name # build flags, files to copy or install. In the examples it is typically # only the list of sources and project name that will actually change and # the rest of the makefile is boilerplate for defining build rules. # PROJECT:=hello_tutorial LDFLAGS:=-lppapi_cpp -lppapi -lssl -lcrypto -lglibc-compat -lnosys # LDFLAGS:=-lppapi_cpp -lppapi -lnosys CXX_SOURCES:=$(PROJECT).cc NACLPORTS_ROOT = /home/dmiyakawa/utils/naclports/src # INCLUDES_32 = -I$(NACL_SDK_ROOT)/toolchain/linux_x86_newlib/i686-nacl/usr/include # INCLUDES_64 = -I$(NACL_SDK_ROOT)/toolchain/linux_x86_newlib/x86_64-nacl/usr/include # LIBSSL_32 = $(NACLPORTS_ROOT)/out/repository-i686/openssl-1.0.0e/libssl.a $(NACLPORTS_ROOT)/out/repository-i686/openssl-1.0.0e/libcrypto.a $(NACLPORTS_ROOT)/out/repository-i686/glibc-compat-0.1/out/libglibc-compat.a # LIBSSL_64 = $(NACLPORTS_ROOT)/out/repository-x86_64/openssl-1.0.0e/libssl.a $(NACLPORTS_ROOT)/out/repository-x86_64/openssl-1.0.0e/libcrypto.a $(NACLPORTS_ROOT)/out/repository-x86_64/glibc-compat-0.1/out/libglibc-compat.a LIBSSL_32 = LIBSSL_64 = # # Get pepper directory for toolchain and includes. # # If NACL_SDK_ROOT is not set, then assume it can be found a two directories up, # from the default example directory location. # THIS_MAKEFILE:=$(abspath $(lastword $(MAKEFILE_LIST))) NACL_SDK_ROOT?=$(abspath $(dir $(THIS_MAKEFILE))../..) # Project Build flags WARNINGS:=-Wno-long-long -Wall -Wswitch-enum -pedantic -Werror CXXFLAGS:=-pthread -std=gnu++98 $(WARNINGS) # # Compute tool paths # # OSNAME:=$(shell python $(NACL_SDK_ROOT)/tools/getos.py) TC_PATH:=$(abspath $(NACL_SDK_ROOT)/toolchain/$(OSNAME)_x86_newlib) CXX:=$(TC_PATH)/bin/i686-nacl-g++ # # Disable DOS PATH warning when using Cygwin based tools Windows # CYGWIN ?= nodosfilewarning export CYGWIN # Declare the ALL target first, to make the 'all' target the default build all: $(PROJECT)_x86_32.nexe $(PROJECT)_x86_64.nexe # Define 32 bit compile and link rules for main application x86_32_OBJS:=$(patsubst %.cc,%_32.o,$(CXX_SOURCES)) $(x86_32_OBJS) : %_32.o : %.cc $(THIS_MAKE) $(CXX) -o $@ -c $< -m32 -O0 -g $(CXXFLAGS) $(INCLUDES_32) $(PROJECT)_x86_32.nexe : $(x86_32_OBJS) $(CXX) -o $@ $^ $(LIBSSL_32) -m32 -O0 -g $(CXXFLAGS) $(LDFLAGS) # Define 64 bit compile and link rules for C++ sources x86_64_OBJS:=$(patsubst %.cc,%_64.o,$(CXX_SOURCES)) $(x86_64_OBJS) : %_64.o : %.cc $(THIS_MAKE) $(CXX) -o $@ -c $< -m64 -O0 -g $(CXXFLAGS) $(INCLUDES_64) $(PROJECT)_x86_64.nexe : $(x86_64_OBJS) $(CXX) -o $@ $^ $(LIBSSL_64) -m64 -O0 -g $(CXXFLAGS) $(LDFLAGS) # Define a phony rule so it always runs, to build nexe and start up server. .PHONY: RUN RUN: all python ../httpd.py .PHONY: clean clean: rm *.o *.nexe
ポイントは -lssl, -lcrypto の後に -lglibc-compat と -lnosys を入れるところだった。
○ hello_tutorial.cc で無理やり使う
まぁ、こういう感じに
char* generate_key_pair() {
RSA* rsa = NULL;
BIO* bio_priv = NULL;
BIO* bio_pub = NULL;
BUF_MEM *bptr_priv = NULL;
BUF_MEM *bptr_pub = NULL;
char* ret = NULL;
rsa = RSA_generate_key(1024, RSA_3, NULL, NULL);
if (!RSA_check_key(rsa)) {
goto free;
}
bio_priv = BIO_new(BIO_s_mem());
bio_pub = BIO_new(BIO_s_mem());
if (!PEM_write_bio_RSAPrivateKey(bio_priv, rsa, NULL, NULL, 0, 0, NULL)) {
goto free;
}
if (!PEM_write_bio_RSAPublicKey(bio_pub, rsa)) {
goto free;
}
BIO_get_mem_ptr(bio_priv, &bptr_priv);
BIO_get_mem_ptr(bio_pub, &bptr_pub);
ret = (char *)malloc(bptr_priv->length + bptr_pub->length + 1);
memcpy(ret, bptr_priv->data, bptr_priv->length);
memcpy(ret + bptr_priv->length, bptr_pub->data, bptr_pub->length);
ret[bptr_priv->length + bptr_pub->length] = '\0';
free:
if (!bio_pub) {
BIO_free(bio_pub);
}
if (!bio_priv) {
BIO_free(bio_priv);
}
if (!rsa) {
RSA_free(rsa);
}
return ret;
}
これをalertで使うようにする。だいたいこういう感じ。
virtual void HandleMessage(const pp::Var& var_message) {
if (!var_message.is_string()) {
return;
}
std::string message = var_message.AsString();
pp::Var var_reply;
if (message == kHelloString) {
SSL_library_init();
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();
OpenSSL_add_all_algorithms();
char* data = generate_key_pair();
if (data) {
var_reply = pp::Var(data);
} else {
var_reply = pp::Var("Failed");
}
PostMessage(var_reply);
if (data != NULL) {
free(data);
}
}
}
ちなみに、これと似たような感じでDiffie-Hellman関係の関数を呼ぼうとすると軒並みNULLが返ってきて心が折れそうになる。なんでなの……
○ 以上の実装をextension にする。
https://developer.chrome.com/extensions/getstarted.html
で作られる getstarted/ に
- hello_tutorial.nmf
- hello_tutorial_x86_64.nexe
- hello_tutorial_x86_32.nexe
を入れる。
次に、popup.html を割と無理やり hello_tutorial.html 的にする
<!doctype html> <html> <head> <title>Getting Started Extension's Popup</title> <style> body { min-width:357px; overflow-x:hidden; } img { margin:5px; border:2px solid black; vertical-align:middle; width:75px; height:75px; } </style> <!-- JavaScript and HTML must be in separate files for security. --> <script src="nacl.js"></script> </head> <body> <h1>Native Client Module HelloTutorial</h1> <p> <div id="listener"> <script type="text/javascript" src="nacl2.js"></script> <embed name="nacl_module" id="hello_tutorial" width=0 height=0 src="hello_tutorial.nmf" type="application/x-nacl" /> </div> </p> <h2>Status</h2> <div id="status_field">NO-STATUS</div> </body> </html>
nacl.jsとnacl2.js はhello_tutorial.htmlの中に埋め込まれたjsファイルのコンテンツをそのまま入れる。どーも、extensionにおいてはhtml内に埋め込まれたJavaScriptはご法度の模様
うまくいけば、getstarted/ のボタンを押した時にflickerの画像が出てくる代わりにこの無愛想なポップアップが出てくる。○ pepper_25からpepper_26に変わったら上のがコンパイル通らないよ? (2013-05-16)
pepper_25からpepper_26に変わる際に(多分内部のファイル構造と)Makefileの流儀が大幅に変わったようだ。
hello_worldプロジェクトのMakefileを比較するとわかりやすい。
- pepper_25 https://gist.github.com/dmiyakawa/5590428
- pepper_26 https://gist.github.com/dmiyakawa/5590436
……おわかりいただけただろうか? (ホラー番組風)
今確認した限り、pepper_27とpepper_canary (現時点) のhello_worldではpepper_26の流儀と同じ風味で書かれているので、pepper_25までの書き方が筋悪だったということで一つ。
ただ、この過程でpepper_26ではNaCl Portsのssl関係がNACL_ARCH=arm 指定時にコンパイルが通らなくなっており (https://code.google.com/p/naclports/issues/detail?id=65)、現在betaであるpepper_27 まで待つ必要があるらしい。ウゼェ……!
後方互換的に現状ではpepper_25で上記のサンプルを動作させるしかない。ググってたどり着いた人、残念でしたー><
○ pepper_25と(最新のnaclports)の組み合わせは微妙? (2013-05-17)
ヘッダとライブラリのパスで不整合があるような……
○ Macでなんとかする
openssl は (pepper_27とかの時点でも) Macでのビルドをサポートしていない
非公式には以下のような方法があるようだ:
https://groups.google.com/forum/#!msg/native-client-discuss/_2wULoEliSQ/CyprWLOyMm4J
以下の方が若干だが興味深い
https://groups.google.com/forum/?fromgroups#!topic/native-client-discuss/J8XiyzdPZo8
Luckily the library that's created on Linux will be usable on all x86 platforms.pepper_25にて x86_64 archの Debian wheezy 上でビルドしたopenssl関係のライブラリはMacでも確かに動作した。ワロタ
(仕組み上バイナリは同一になる、らしい)
○ pepper_29 だとLinuxでもnaclportsのビルドがこける (2013-09-12)
NACL_GLIBC=1 をする