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 をぶっ飛ばして無理やり両方作らせる
  • 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を比較するとわかりやすい。

……おわかりいただけただろうか? (ホラー番組風)

多くの煩雑なフラグ指定を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
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 をする

このブログの人気の投稿

風花雪月

pemって言ったら改行も仕様に含むんだよもん

LibreOfficeで表紙、目次、本体でフッターのページ番号のスタイルを変える