mac

2014年08月06日

「パスワードの管理を避ける」という考え方

はてなブックマークに登録

手持ちのアカウントが増えてくるとパスワードの管理方法が考えどころとなります。このところメジャーなネットサービスへの不正ログインやアカウントの悪用が相次いで報じられていることもあり パスワード管理の重要性は「古くて新しい問題」としてあらためて広い層に浸透しつつあります。

その一方でパスワードをきっちり管理することは必ずしも簡単ではありません。平易な内容だと第三者によって類推・導出されるリスクが大きいものの複雑にすると覚えにくい。だからと言って複数のサービスで同じものを使いまわすとそれが漏洩した場合に一斉に攻撃を受ける危険がある。結局、安全度の高いパスワードをサービスごとに使い分けることは人間の記憶だけでは困難なので何らかの外部記憶を利用することになります。ただし、それが何らかのデバイスであれコンピュータデータの形式であれ、情報としてそこへ保存した時点で盗難・流出の可能性はゼロではなくなります。多くの場合、そういった諸々の事情を含んだ上で手元の実用上の便宜とのバランスを判断して管理方法を決めることになりますね。

社内でそういう話をしていた時にふと思いました。一般に秘密文字列の内容は文字の並びとして意味をなさないものであるほど効果的なわけで、そのような情報はあまり「人間向き」とは言えないでしょう。もともと人間向きではない上に内緒にしておきたい情報を人間が丸ごと管理しようとすること自体に無理があるのではないか?その情報を直接管理せずに済ませることはできないか?生々しいパスワードをどこにも保管せずにすむのならそれにこしたことはありません。

ソースコードと実行形式の関係を連想しました。プログラムのソースコードとそれをビルドする環境があればビルドずみの実行形式そのものを保持し続けることは必須ではありません。そのことと同様に所定の情報もまた必要な時に正しく用いることができれば十分であり、それを再現可能とする材料をあらかじめ揃えておけば情報そのものを恒常的に保持することを免れるはずです。
その切り口で方法のひとつを考えてみました。一方向関数の応用です。

  • 利用者が任意のフレーズ (A) を灰色の変換器へ投入すると文字列 (D) が出力される
  • 処理 (B) の変換規則は決め打ちではなく、ランダムな内容で生成ずみの所定のフィルタ (C) を参照して決定される
  • (C) には (D) へ含める文字のバリエーションと (D) の文字列長を定義可能
  • 処理に際し利用者は任意の (C) を指定する
  • 固有の (A) (B) (C) の組合せは固有の (D) を生成する
  • (A) から (D) への変換は不可逆
出力文字列 (D) は意味を持つ内容ではないためパスワード等の秘密文字列として使うことができます。一般的なパスワード生成器等で生成したパスワードは同じものを再発行できないためそれ自体を慎重に保管しておかなければなりませんが、この変換器では固有の (A) (B) (C) が揃っていれば常に固有の (D) を得ることが可能であるためその必要はありません。また、(C) のデータが単体で漏れたとしても正しい (A) (B) がなければ (D) を再現することはできません。さらに、この変換器は機能の性質上基本的に個人用であるため、処理 (B) の内容へ利用者が本人固有の要素を加味することを可能とするといった工夫も考えられそうです。

このようにいたってシンプルな内容ですが、同様のアイディアが形になったものを今のところ知らずこうした話題には興味を感じます。前述のようにパスワードの管理に外部記憶を使わざるを得ない最大の理由は「それがパスワードとして適切な内容であればあるほど人の記憶に馴染まないから」という皮肉な事情にありますが、たとえばこういった方法なら人間の記憶というとても貴重なリソースを最大限に活かせるのではないか?という気がします。

実装例

試みに上のアイディアを Mac OS X 用 / Android 用アプリとして実装してみました。より便利に使うための機能を付加する余地が随所にあるものの この内容でも相応の実用性はありそうです。

2018年6月追記:DropBox API の仕様変更に伴い Android 版アプリは一旦公開停止としています。現時点では改訂の目処が立っておらず、本アプリを利用して下さった皆様には大変ご迷惑をおかけしますが宜しくご了承下さい。

  • Mac OS X 版: NoPass100.dmg
    (535,655 bytes md5sum: 95bf7543509f4752b2da204fc545106b)
  • Android 版: NoPass - Google Play ストア
  • ソースコード: NoPass - GitHub

  • アプリでは前述 (C) のフィルタデータを「マップデータ」の呼称で統一しています
  • Max OS X 版は "~/Library/Application Support/NoPass/NoPass.dat" へマップデータを出力します
  • Dropbox 経由で複数の環境から同じマップデータを利用できます
  • Mac OS X 版はマップデータの 使用/ 作成/ 編集/ 削除/ アップロード/ ダウンロード/ 同期 が可能です
  • Android 版はマップデータの 使用/ ダウンロード のみが可能であり Mac OS X 版の併用と Dropbox アカウントが必要です

  • 動作の様子(動画 1分19秒 無音)
  • アプリ画面

(tanabe)
klab_gijutsu2 at 18:01|この記事のURLComments(0)
2014年03月25日

「stone for iOS」について

はてなブックマークに登録

オープンソースの多機能パケットリピータ「stone」は数多くの OS プラットフォームで利用されています。スマートフォンまわりでは先年手元で Android 環境向けのポーティングを行いましたが、このところ iPhone を使う機会が増えていることもあり iOS 環境でも動かしたいと考えました。iOS 用 GUI アプリとしてひと通り動作するようになったため次の場所で公開しています。興味のある方はお試し下さい。

iTunes App Store - stone for iOS
ソースコード: GitHub - stone for iOS
2018年6月追記:諸般の事情により App Store 上の本アプリの更新は今後当面見送らせて頂きます。本アプリをご利用下さった皆様に謹んで御礼申し上げます。

使い方

アプリの使い方を簡単に説明します。

メイン画面


画面最上部のコマンドフィールドに stone パラメータを記述します。パラメータの記述方法は「stone の README」を参照して下さい。

各ボタンの機能

  • [Run] - stone の処理を開始します
  • [Stop] - 本アプリケーションを終了します
  • [Clear] - 表示中のログを消去します
  • [History] - コマンド履歴を表示します
  • ツールボタン - ツールメニューを表示します

"-dd www.gcd.org:80 1234"
初期状態でコマンドフィールドに表示されている上記のパラメータは stone の効果を確認するためのサンプルです。[Run] で stone の処理を開始し、ブラウザから
http://[この端末の IP アドレス]:1234/
へアクセスすると http://www.gcd.org/ (stone 公式サイト)へ繋がります。

コマンド履歴画面


stone で実行したコマンドパラメータの履歴です。タップするとメイン画面のコマンドフィールドへペーストされます。「編集」ボタンからエントリの削除・並べ替えができます。

ツールメニュー


各種設定やヘルプを表示します。詳細は各画面をご覧下さい。

ローカルファイルの保存

".stone" の拡張子が付与された任意のファイルをメーラ等のアプリケーション上でタップすると、そのファイルを本アプリケーションのローカルファイルとしてコピーすることができます。保存ずみのローカルファイルは stone パラメータへの指定が可能です。
(例)"-C stone.cnf"

※現バージョンの stone for iOS は、iOS キーチェーンへの電子証明書のインポートおよび参照には未対応です。証明書はローカルファイルとして保存した上で使用して下さい。

謝辞

本ソフトウェアは以下のオープンソースソフトウェア資産を使用しています。素晴らしいソフトウェアを公開されている各氏に深謝いたします。

stone

Copyright (c) 1995-2014 by Hiroaki Sengoku

「この stone に関する全ての著作権は、原著作者である仙石浩明が所有します。この stone は、GNU General Public License (GPL) に準ずるフリーソフトウェアです。個人的に使用する場合は、改変・複製に制限はありません。配布する場合は GPL に従って下さい。また、openssl とリンクして使用することを許可します。」
「この stone は無保証です。この stone を使って生じたいかなる損害に対しても、原著作者は責任を負いません。詳しくは GPL を参照して下さい。」

stone 公式サイト

OpenSSL

Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved.

"This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
"THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
"This product includes cryptographic software written by Eric Young (eay@cryptsoft.com). This product includes software written by Tim Hudson (tjh@cryptsoft.com)."

OpenSSL 公式サイト

(tanabe)
* Cryptographic Software Notice *

This software includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted. See http://www.wassenaar.org/ for more information.

The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has classified this software as Export Commodity Control Number (ECCN) 5D002.C.1, which includes information security software using or performing cryptographic functions with asymmetric algorithms. The form and manner of this distribution makes it eligible for export under the License Exception Technology Software Unrestricted (TSU) for both object code and source code. (see the BIS Export Administration Regulations, Section 740.13)

klab_gijutsu2 at 15:02|この記事のURLComments(3)
2013年12月03日

Mac OSX で vmnet が BIOCSETIF できなくてハマった話し

はてなブックマークに登録

1年くらいブログ記事書くのをサボっていたので、ちょっとマニアックなネタを投下します。ISUCON3 は予選で敗退してしまった @pandax381 です。

vmnet が BIOCSETIF できない!

Mac OSX(marvericks)で VMware Fusion 6 を使っているのですが、ホスト側の仮想インタフェース(vmnetX)のパケットがキャプチャできないという問題に遭遇しました。具体的には、パケットをキャプチャするためにBPFデバイスをオープンして、vmnetX を ioctl(BIOCSETIF) でアタッチするの処理で失敗してしまうのです。一見普通のEthernetデバイスに見えるのに、どうしてBPFでパケットをキャプチャできないのか調べてみました。

tcpdump で試してみる

僕の作っていたプログラムがイケてないのかもしれないので、まずは tcpdump を使ってキャプチャできるか試してみます。そして、tcpdump先生でもダメならあきらめましょう。

tcpdump に -i オプションで vmnet8 を指定して実行してみます。

$ sudo tcpdump -i vmnet8
tcpdump: vmnet8: No such device exists
(BIOCSETIF failed: Device not configured)

あれ、うまくいかない。。そういえば、OSX の tcpdump はインタフェース指定しなければ全てのインタフェースからキャプチャできたはずなので、-i オプションなしで実行してみます。

$ sudo tcpdump
tcpdump: data link type PKTAP
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on pktap, link-type PKTAP (Packet Tap), capture size 65535 bytes
18:18:51.715943 IP 192.168.2.128 > 192.168.2.1: ICMP echo request, id 54865, seq 1, length 64
18:18:51.715969 IP 192.168.2.1 > 192.168.2.128: ICMP echo reply, id 54865, seq 1, length 64
18:18:52.715822 IP 192.168.2.128 > 192.168.2.1: ICMP echo request, id 54865, seq 2, length 64
18:18:52.715862 IP 192.168.2.1 > 192.168.2.128: ICMP echo reply, id 54865, seq 2, length 64
18:18:53.716669 IP 192.168.2.128 > 192.168.2.1: ICMP echo request, id 54865, seq 3, length 64
18:18:53.716705 IP 192.168.2.1 > 192.168.2.128: ICMP echo reply, id 54865, seq 3, length 64

ちゃんと vmnet8 に接続されている仮想マシンとのパケットが拾えています。tcpdump は何かしらの方法で vmnet8 を覗けているようです。

なにかオプションを指定する必要があるかもしれないので、マニュアルを見てみると・・・

On Darwin systems version 13 or later, when the interface is unspecified, tcpdump will use a pseudo interface to capture packets on a set of interfaces determined by the kernel (excludes by default loopback and tunnel interfaces).

Alternatively, to capture on more than one interface at a time, one may use "pktap" as the interface parameter followed by an optional list of comma separated interface names to include. For example, to capture on the loopback and en0 interface:

       tcpdump -i pktap,lo0,en0

-i オプションの項目に、ずばり答えが書いてありました...^^;

・OSX(Darwin)の場合には、-i オプションを指定しないと「擬似インタフェース」を使用して全インタフェースからキャプチャする
・任意の複数のインタフェースでキャプチャする場合には -i pktap,lo0,en0 と指定する

どうやら pktap というのが「擬似インタフェース」で、これを使用すれば vmnet8 のパケットがキャプチャできそうなので、早速試してみます。

$ sudo tcpdump -i pktap,vmnet8
tcpdump: data link type PKTAP
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on pktap,vmnet8, link-type PKTAP (Packet Tap), capture size 65535 bytes
18:48:35.799610 IP 192.168.2.128 > 192.168.2.1: ICMP echo request, id 56686, seq 13, length 64
18:48:35.799679 IP 192.168.2.1 > 192.168.2.128: ICMP echo reply, id 56686, seq 13, length 64
18:48:36.802213 IP 192.168.2.128 > 192.168.2.1: ICMP echo request, id 56686, seq 14, length 64
18:48:36.802252 IP 192.168.2.1 > 192.168.2.128: ICMP echo reply, id 56686, seq 14, length 64
18:48:37.803839 IP 192.168.2.128 > 192.168.2.1: ICMP echo request, id 56686, seq 15, length 64
18:48:37.803876 IP 192.168.2.1 > 192.168.2.128: ICMP echo reply, id 56686, seq 15, length 64

ばっちりキャプチャできました!

tcpdump はどんな裏技を使っているのか

tcpdump がどんな処理をしているのかソースを解析してみます。

とりあえず、カレントバージョンの tcpdump と libpcap のソースを grep みるも「pktap」や「PKTAP」というキーワードが見当たらない。。。

仕方ないので、tcpdumpのバージョンを確認してみると

$ tcpdump -h
tcpdump version 4.3.0 -- Apple version 56
libpcap version 1.3.0 - Apple version 41
Usage: tcpdump [-aAbdDefhHgIJkKlLnNOpPqQ:RStuUvxX] [ -B size ] [ -c count ]
		[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]
		[ -i interface ] [ -j tstamptype ] [ -M secret ]
		[ -Q metadata-filter-expression ]
		[ -r file ] [ -s snaplen ] [ -T type ] [ -w file ]
		[ -W filecount ] [ -y datalinktype ] [ -z command ]
		[ -Z user ] [ expression ]

ほほう、Apple version ですとな。カスタムバージョンのようなので、http://www.opensource.apple.com/source/ から tcpdump と libpcap を探します。

この中をひたすら漁っていると、libpcap の中に「pcap-darwin.c」という、いかにもそれっぽいソースファイルが見つかりました。

pcap_setup_pktap_interface() や pcap_cleanup_pktap_interface() などの関数があるので、これで当たりのようです。

ざっとソースを眺めて、以下のことが分かりました。

  • ioctl(SIOCIFCREATE) :PKTAPインタフェースを作成
  • ioctl(SIOCSDRVSPEC) :PKTAPインタフェースのフィルタを設定(キャプチャするデバイスと除外するデバイスを設定できる)
  • ioctl(SIOCIFDESTROY):PKTAPインタフェースを削除

そして、一番の知りたかった BPF で BIOCSETIF する際の処理では、実デバイスではなくPKTAPインタフェースを指定していました。

なるほど、謎は全て解けた!

自力でやってみる

なにをすれば良いか分かったので、早速自分でコードを書いてみました。

https://github.com/pandax381/pktap_demo/

  • PKTAPインタフェースを作成
  • 引数で指定されたデバイスをキャプチャ対象としてフィルタを設定(複数可、指定がなければ全てのデバイスが対象)
  • BPFデバイスをオープンしてPKTAPインタフェース(pktapX)を指定
  • 明示的にプロミスキャスモードにはしない(BIOCPROMISC が失敗する)
  • BPFからパケットをキャプチャしてデバッグ出力

PKTAPインタフェースから取得したパケットには、struct pktap_header型のリンクヘッダが付いているので、詳細出力します。

※ /usr/include/ 配下に net/pktap.h が見当たらなかったので、XNU のソースツリーから拝借するようにしています

実行結果はこんな感じ

$ sudo ./pktap_demo vmnet8
### pktap_debug_print ###
pth_length: 108
pth_type_next: 1
pth_dlt: 1
pth_ifname: vmnet8
pth_flags: 1
pth_protocol_family: 2
pth_frame_pre_length: 14
pth_frame_post_length: 0
pth_pid: -1
pth_comm: 
pth_svc: 0
pth_iftype: 6
pth_ifunit: 8
pth_epid: -1
pth_ecomm: 
+------+-------------------------------------------------+------------------+
| 0000 | 00 50 56 c0 00 08 00 0c 29 6b b6 e9 08 00 45 00 | .PV.....)k....E. |
| 0010 | 00 54 00 00 40 00 40 01 b4 d7 c0 a8 02 80 c0 a8 | .T..@.@......... |
| 0020 | 02 01 08 00 ac fe ec 31 00 01 1e 14 9b 52 00 00 | .......1.....R.. |
| 0030 | 00 00 e5 94 01 00 00 00 00 00 10 11 12 13 14 15 | ................ |
| 0040 | 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 | .......... !"#$% |
| 0050 | 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 | &'()*+,-./012345 |
| 0060 | 36 37                                           | 67               |
+------+-------------------------------------------------+------------------+
### pktap_debug_print ###
pth_length: 108
pth_type_next: 1
pth_dlt: 1
pth_ifname: vmnet8
pth_flags: 2
pth_protocol_family: 2
pth_frame_pre_length: 14
pth_frame_post_length: 0
pth_pid: -1
pth_comm: 
pth_svc: 0
pth_iftype: 6
pth_ifunit: 8
pth_epid: -1
pth_ecomm: 
+------+-------------------------------------------------+------------------+
| 0000 | 00 0c 29 6b b6 e9 00 50 56 c0 00 08 08 00 45 00 | ..)k...PV.....E. |
| 0010 | 00 54 b5 a8 40 00 40 01 ff 2e c0 a8 02 01 c0 a8 | .T..@.@......... |
| 0020 | 02 80 00 00 b4 fe ec 31 00 01 1e 14 9b 52 00 00 | .......1.....R.. |
| 0030 | 00 00 e5 94 01 00 00 00 00 00 10 11 12 13 14 15 | ................ |
| 0040 | 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 | .......... !"#$% |
| 0050 | 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 | &'()*+,-./012345 |
| 0060 | 36 37                                           | 67               |
+------+-------------------------------------------------+------------------+

余談

Google先生に聞いても、唯一ヒットする情報は Apple先生のソースコードだけだったので、意地になって動くコードを書く所までやってみました。社内でドヤってみたけど、レイヤが低すぎたのか誰も反応してくれないのでブログを書いてみたという落ちです。後悔はしていない。

なお、PKTAP は ifconfig でも作成できます。pktap_demo のコード書き終わってから ifconfig のコードを読んでいて発見しました...orz

$ sudo ifconfig pktap create
pktap0
$ sudo ifconfig pktap destroy

コマンドで PKTAP にフィルタを設定するには、pktapctl を自分でコンパイルすればできます。

http://opensource.apple.com/source/network_cmds/network_cmds-433/pktapctl/pktapctl.c

あと、そもそもの目的は vmnet に対して出力もしたかったのですが、PKTAPだとキャプチャ専用で出力はできないっぽいです。


@pandax381

klab_gijutsu2 at 20:47|この記事のURLComments(0)TrackBack(0)
2013年08月30日

Exif データにアクセスするコードを自作してみる

はてなブックマークに登録

Exif (Exchangeable image file format) は現在出回っているほとんどのデジタルカメラや携帯電話・スマートフォン等での撮影画像のファイル形式として利用されています。画像データそのものに加え機器本体や撮影時の設定や環境など多くの情報が記録されることを活かしたソフトウェアやサービスも増えていますね。

先日ちょっとしたアプリを書いていた折に JPEG ファイル内の Exif データを参照する処理が必要になりました。初めての機会だったのでやり方を調べたところ 開発環境向けに用意されたこれこれのライブラリを使うのが常道とのことで、リファレンスを読みながらそういうコードを書きました。プログラムは期待通りに動作しその時はそれで特にどうと言うこともなかったのですが、後日別のプラットフォームで Exif データを利用するアイディアを思いつき、その環境でまた別のライブラリの使い方など調べているうちに何だか面倒になりました。

ここでのターゲットはあくまでも一介のデータ形式です。既存のソフトウェア資産を利用することは生産性の上でも効率の面でも間違った選択ではないものの、「普通のファイルに記録されたデータ」にアクセスするための道具立てにいろいろ左右されることが煩わしく感じられたのです。Exif の規格は公開されているため仕様を確認して必要なコードを自作すれば今後この形式を既存のライブラリごしに「ブラックボックス」として扱う必要はなくなるはずで、また、自作のコードであれば拡張や応用の融通が利きやすく必要なら別のプログラム言語への移植も楽でしょう。

デジタルカメラという今日ではごく身近な道具を通じてその存在を知りながら中身をずっと知らずにいた Exif のしくみに触れることはそれ自体も興味ぶかい経験でした。その一端をご紹介します。C 言語で試作したソースコード一式を記事の最後に掲載しています。

Exif の仕様

まず Exif のデータ形式の仕様をできるだけコンパクトに整理してみます。もっとも詳しく正確なのはもちろん公式の規格書ですから不明な点があればそこでの記述を参照して下さい。

規格書

電子情報技術産業協会 (JEITA) ホームページ上の「JEITA 規格総合検索」フォームから、「規格名」に "Exif" を指定して検索を実行すると「JEITA CP-3451C デジタルスチルカメラ用 画像ファイルフォーマット規格 Exif 2.3」を電子文書として閲覧できます。 ※ 2013年8月31日現在

一般社団法人 電子情報技術産業協会(JEITA) 公式サイト

全体の構造

下の図は規格書をもとに JPEG ファイル中の Exif データの構造をまとめてみたものです。要点をピックアップしてみます。

  • Exif データは JPEG ファイル内に配置可能な「アプリケーションセグメント」のひとつである APP1 セグメントに格納される
  • 当該 APP1 セグメントが Exif のセグメントであるか否かは当該セグメントのヘッダ内容で識別可能
  • Exif セグメント内に格納された数値データのバイトオーダーは big-endian, little-endian の両方があり得る
  • Exif セグメントには一件以上の IFD (Image File Directory) が含まれる
    • 0th IFD:主画像に関する付属情報を保持(Exifセグメントに必ず存在)
    • 1st IFD:主画像のサムネイルに関する情報およびサムネイル画像データを保持
    • Exif IFD:Exif 固有の付属情報を保持
    • GPS IFD:GPS 等の位置情報を保持
    • Interoperability IFD:互換性を保証するための情報を保持
  • 各 IFD は任意の数のタグフィールドを保持する
  • タグの種類は規格で定義されており各タグには識別用の番号が割り当てられている
  • 各タグの保持する値には複数の種類がありそのタイプとデータ長によって値の格納方法が規定されている

タグフィールドの扱い方

どこにどのような情報が配置されているかは上の図を追えば把握できるでしょう。構造を順に辿って最後に到着するのは具体的なデータの格納された末端のタグフィールドです。もちろんこれらが最も重要な要素であり、ここではタグの値にアクセスする方法を説明します。

フィールドの構成
一件のタグフィールドには以下の内容が含まれます
  • タグ 番号    2 バイト    所定のタグを識別するための番号
  • タイプ    2 バイト    タグの値のデータタイプを示す
  • カウント    4 バイト    値の個数
  • オフセット    4 バイト    値の格納された位置
タグ番号とそのタイプ・カウントの定義および各タグの詳細は規格書に掲載されており、所定のタグ番号とその意味との紐づけにはここでの情報を使用します。以下に記事の例を示します。
タグ名称         Field Name   タグ番号(Hex)   タイプ    カウント
Exif バージョン  ExifVersion     9000 	    UNDEFINED 	   4
色空間情報       ColorSpace      A001         SHORT        1
  :               :           :            :         :

■ Exif バージョン ExifVersion
 本規格での対応バージョンを示す。このフィールドが存在しなければ,本規格に準拠していない
 と判断される。本規格に準拠する場合には,4Byte の ASCII "0230" を記録しなければならない。
 Type が UNDEFINED のため最後に NULL は記録してはならない。
    Tag     =  36864 (9000.H)
    Type    =  UNDEFINED
    Count   =  4
    Default =  "0230"

■      :
    :
タイプについて

タグの値には以下のタイプがあります。値の取得方法は後述のカウント・オフセットのルールとの組合せで決まります。

  • 1 = BYTE      8 ビット符号なし整数
  • 2 = ASCII     NULL 文字で終端する ASCII 文字列。ASCII のカウントは NULL 文字分を含む
  • 3 = SHORT     16 ビット符号なし整数
  • 4 = LONG     32 ビット符号なし整数
  • 5 = RATIONAL     LONG 2 個で表現する値。ひとつめの LONG は分子、ふたつめは分母を表す
  • 7 = UNDEFINED     任意のバイト列
  • 9 = SLONG     32 ビット符号つき整数
  • 10 = SRATIONAL     SLONG 2 個で表現する値。ひとつめの LONG は分子、ふたつめは分母を表す
カウントについて

カウントは「値の個数」を示します。「バイト数の合計」を表すものではありません。以下に例を示します。

  • ASCII タイプの値 "Apple\0" のカウントは 6
  • SHORT タイプの値 10 のカウントは 1
  • LONG タイプの値 10 のカウントは 1
  • LONG タイプの値 10, 11 のカウントは 2
  • RATIONAL タイプの値 3/5 のカウントは 1 (※ 2 ではない)
  • SRATIONAL タイプの値 3/5, 1/2 のカウントは 2
  • UNDEFINED タイプの値 0x03, 0x21, 0x00, 0xFF のカウントは 4
オフセットについて

4 バイトのオフセット領域にはタグの値が記録されている位置(上図中の★が起点)が保持されています。ただし、値が 4 バイト以内で表現できる場合はその値がオフセット領域に直書きされます。4 バイト以内の値の例を以下に示します。

  • BYTE タイプの値 1    -> 1 バイト
  • BYTE タイプの値 1, 3, 5, 7    -> 4 バイト
  • ASCII タイプの値 "abc\0"    -> 4 バイト
  • SHORT タイプの値 10    -> 2 バイト
  • SHORT タイプの値 10, 11    -> 4 バイト
  • LONG タイプの値 10    -> 4 バイト
  • UNDEFINED タイプの値 0x03, 0x21, 0xFF    -> 3 バイト
さらに、値の長さが 4 バイト未満の場合、値はオフセット領域に「左詰め」で格納されます。以下の要領です。

■ BYTE タイプの値 1    

  0   1   2   3
┌─┬─┬─┬─┐
│01│00│00│00│ データのバイトオーダーがビッグエンディアンの場合
└─┴─┴─┴─┘
┌─┬─┬─┬─┐
│01│00│00│00│ データのバイトオーダーがリトルエンディアンの場合
└─┴─┴─┴─┘

■ SHORT タイプの値 1, 3     

  0   1   2   3
┌─┬─┬─┬─┐
│00│01│00│03│ データのバイトオーダーがビッグエンディアンの場合
└─┴─┴─┴─┘
┌─┬─┬─┬─┐
│01│00│03│00│ データのバイトオーダーがリトルエンディアンの場合
└─┴─┴─┴─┘

データの実例

図は Exif セグメントを含むある JPEG ファイル冒頭部分の HEX ダンプイメージです。上述のデータ構造とデータ形式を踏まえてここから読み取られる 0th IFD の内容を併記しています。

試作したコード

C 言語で書いたソースコード一式を以下で公開しています。

ソースコード: GitHub - exif

Windows 用実行形式 (x86)


  • 現在のバージョンには以下の機能があります
    • 所定の JPEG ファイルの Exif セグメント中の一部またはすべての IFD の内容をダンプ表示
    • 指定された IFD タイプ+タグ番号に該当するタグフィールドの情報を取得
    • 所定の JPEG ファイルから Exif セグメントを除去した新しい JPEG ファイルを出力

  • exif.c, exif.h の二本が実体で sample_main.c は関数呼び出しのサンプルです。
    サンプルプログラムのビルドは以下の要領です
    • gcc の場合:  gcc -o exif sample_main.c exif.c
    • Microsoft Visual C++ の場合:  cl.exe /o exif sample_main.c exif.c

  • 標準ライブラリ関数のみを使っているため多くの環境での利用が可能です。手元では以下の組合せでの動作を確認しました
    • Windows XP 32bit + Microsoft Visual C++ でビルドした 32bit バイナリ
    • Windows 7 64bit + Microsoft Visual C++ でビルドした 64bit バイナリ
    • Redhat Linux 32bit + 32bit 版 gcc でビルドしたバイナリ
    • Mac OS X 64bit + 64bit 版 gcc でビルドしたバイナリ
  • 動作確認用の test.jpg をサンプルプログラムで処理した結果を以下に引用します
$ ./exif test.jpg

[test.jpg] createIfdTableArray: result=4

{0TH IFD}
 - Make: [Apple]
 - Model: [iPod touch]
 - Orientation: 1
 - XResolution: 72/1
 - YResolution: 72/1
 - ResolutionUnit: 2
 - Software: [6.1.4]
 - DateTime: [2013:09:01 09:49:00]
 - YCbCrPositioning: 1
 - ExifIFDPointer: 206
 - GPSInfoIFDPointer: 576

{EXIF IFD}
 - ExposureTime: 1/30
 - FNumber: 12/5
 - ExposureProgram: 2
 - PhotographicSensitivity: 400
 - ExifVersion: 0 2 2 1
 - DateTimeOriginal: [2013:09:01 09:49:00]
 - DateTimeDigitized: [2013:09:01 09:49:00]
 - ComponentsConfiguration: 0x01 0x02 0x03 0x00
 - ShutterSpeedValue: 4035/821
 - ApertureValue: 4845/1918
 - BrightnessValue: 2234/1113
 - MeteringMode: 5
 - Flash: 32
 - FocalLength: 77/20
 - FlashPixVersion: 0 1 0 0
 - ColorSpace: 1
 - PixelXDimension: 960
 - PixelYDimension: 720
 - SensingMethod: 2
 - ExposureMode: 0
 - WhiteBalance: 0
 - FocalLengthIn35mmFormat: 32
 - SceneCaptureType: 0

{GPS IFD} -- GPS 経由で得られた撮影地点の座標情報
 - GPSLatitudeRef: [S]
 - GPSLatitude: 69/1 17/100 0/1
 - GPSLongitudeRef: [E]
 - GPSLongitude: 39/1 35/100 0/1
 - GPSAltitudeRef: 0
 - GPSAltitude: 6151/470
 - GPSTimeStamp: 0/1 48/1 3921/100

{1ST IFD} -- 埋め込みサムネイルの情報 〜 サムネイル画像データへ続く
 - Compression: 6
 - XResolution: 72/1
 - YResolution: 72/1
 - ResolutionUnit: 2
 - JPEGInterchangeFormat: 840
 - JPEGInterchangeFormatLength: 8648

0th IFD : Model = [iPod touch]
Exif IFD : DateTimeOriginal = [2013:09:01 09:49:00]
GPS IFD : GPSLatitude = 69/1 17/100 0/1
removeExifSegmentFromJPEGFile: result=1

$ ./exif _noexif.jpg
[_noexif.jpg] does not seem to contain the Exif segment.


(tanabe)
klab_gijutsu2 at 14:55|この記事のURLComments(2)TrackBack(1)
2013年01月18日

親指サイズの USB 赤外線リモコンが面白い

はてなブックマークに登録

2013.01.29 追記    Mac OS X 用の試作コードを掲載しました

昨年末、調べごとをしていた時にちょっと気になる商品が目に留まりました。 株式会社ビット・トレード・ワン 様の「USB 接続 赤外線リモコン KIT」という製品です。

特徴をざっくりまとめてみるとこんな感じです。

  • [パソコンから家庭用機器をリモコン操作]、[リモコンでパソコンを操作] の2つの機能を持つ
  • 赤外線送信用のライブラリやツール・ファームウェアのソースコードが公開されている
  • 家電協/ NEC/ SONY の各リモコンコードフォーマットに対応
  • 某清涼菓子のケースにぴったり収まるサイズ
  • キットは 1,680 円、組立ずみ製品でも 2,480 円と低価格
  • PC 側対応 OS はWindows 7, Vista, XP
PC から制御可能な学習型赤外線リモコンといえば 2006 年の発売以来ロングセラーを続ける PC-OP-RS1 がとても有名ですが、このキットは昨年(2012年)発売された製品とのことで、ソフトウェア要素の多くがオープンであることに魅力を感じました。一方で 公式フォーラムを覗いてみるとエアコンまわりをはじめいろいろ制約もあるようで、それをどう考えるかは価格とのバランスの解釈次第でしょう。そういった話題も含めて好奇心をそそられ、年明け早々に入手しました。画像は「フリスク」と並べてみた様子です。

PC とは A−miniB タイプの USB ケーブルで接続。Windows 上でヒューマンインターフェイスデバイスとして認識され標準の HID ドライバが使用されます。

とりあえず使ってみると・・

さっそく専用の 送信用設定ツール に手元のいくつかの機器のリモコンコードを学習させ本キットから信号を出力し、それを元の機器が認識できるか否かを試してみると下の図の結果となりました。 続きを読む

klab_gijutsu2 at 11:09|この記事のURLComments(26)
2011年02月24日

ソーシャル携帯電波マップを作ろう

はてなブックマークに登録

先週出張で東京に来ました。いつものように東北新幹線です。

道中2時間半いつも特に何をすると決めている訳ではなく、まあ寝るか、本を読むか、iPhoneを片手にメールやサイトチェックをするか、うーん、大体は寝ているんじゃないでしょうか私。眠いもの。

ただ、今回いつもとは違いめずらしく眠気がなかったので、おもむろにMacBook Airを開いてcodingなんぞやろうかという気になりました。しかしそれがいけなかった。まだ購入してから間もない状態だったので、環境が全く整ってない。それどころか書こうとしていたソースコードパッケージすら入れてないときたもんだ。…いや、まあ、でもこういうときに備えてMobileMeのストレージにスナップショットを置いてあったりしただろ落ち着け俺、とやおらPocketWifiを取り出してダウンロードを始めた。が、落ちて来ない。

そうだった。東京から宇都宮〜那須塩原あたりまでのだだっ広い関東平野goes onならいざ知らず、上り列車乗り込んですぐの盛岡あたりでは移動の途中で圏外になることも多く、なかなか使い物にならないのである。市街地はそこそこにしてすぐに田園と山林風景のど真ん中を行くことになるわけだから、ビジネス用途の強いイーモバイルの電波は届かなくても仕方が無いのだ。こいつはぎゃふんだ。

というわけで、行きの電車内で大いに苦しめられたこの電波状況、キャリアのサービスマップだけでは分からない、「ほんとにほんとのところどうなのよ?」というあたりを調べてみようと思った。言ってみればユーザ実測によるリアルサービスマップ。ソーシャル時代の今ならこれをみんなで寄せ集めて日本全国くまなく調査することだって夢じゃない。そうそう、時代はソーシャル…、と、そんなことを妄想しながらその後ほどなく私がふて寝に入ったのは言うまでもない。

概要はこうだ。新幹線で移動する間中、ずっとGPSで現在位置を把握する。そして並行してPINGを打ち続け、GPSでの位置・PING送信・応答これら3つのイベントを時間軸に沿って記録する。後でこれを地図上にプロットしてみようというわけだ。

手元にあるのはiPhoneとMacBook Air。iPhoneにはGPSがついているので問題ない。PINGの実装はちょっと悩んだが、Cocoaのサンプルコードにちょうど"SimplePing"というそのものズバリなものがあるのを見つけた。Mac用となってはいるが、iOS用にも移植できるんじゃないかと見立て、これを持って来てみた。

http://developer.apple.com/library/mac/#samplecode/SimplePing/Listings/SimplePing_m.html
SimplePing: SimplePing.m

幸いADC(Apple Developer Connection)に登録しているので、iPhoneにアプリケーションを転送して実機でアプリを動かすことはできる。ここまで調べたところで出張が終わり、さあ帰りの新幹線の便となった。

帰りの新幹線。シートに座ってMacBook Airを開いて作業を開始。列車も東京駅を出発。途中小山駅を過ぎたあたりで空腹が勝りすぎて弁当を食さずにはいられなくなるなどアクシデントに遭いつつも、白石蔵王を通り過ぎ仙台駅にさしかかったあたりまでで一通りのものが出来た。

やった内容は大まかにこう。

  • まず、Xcode上でFile > New > New Projectのメニューを選び、iOS Applicationから新規アプリケーションを作成する。"Navigation-based"でも、"Tab bar"でも何でも良い。名前も何でも良い。
  • SimplePingから、SimplePing.{h,m}ファイルを持ってくる。また、SimplePingがPING応答の際にコールバックするdelegateクラスがmain.m中にあるので、それを持ってくる。
  • 上記のdelegateクラスを今回新規作成したアプリケーションにおけるNSApplicationのdelegateクラスに対するカテゴリ(Mix-in)として実装に追加する。
  • アプリケーション起動時に呼び出されるコールバックメソッド"application:didFinishLaunchingWithOptions:"において、GPSおよびPING送信を開始するためのコードを追加。
  • さらに、GPS出力およびPING送受信の結果を記録するデバッグログを追加。
  • ↑の情報は、作業工程圧縮のためiPhone上でのデータ書き込み機能を実装せず、とりあえずXcodeデバッガの出力に任せる。iPhoneはMacにUSB接続したまま動かす。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    // Add the navigation controller's view to the window and display.
    self.window.rootViewController = self.navigationController;
    [self.window makeKeyAndVisible];
    // ↑ここまでテンプレート

    // ここからGPS準備
    _locationManager = [[CLLocationManager alloc] init];    
    _locationManager.delegate = self;
    [_locationManager startUpdatingLocation];

    // ここからPING準備    
    self.pinger = [SimplePing simplePingWithHostName:@"www.klab.jp"];
    self.pinger.delegate = self;
    [self.pinger start];
       
    return YES;
}

ざっとこのくらい。これで一応デバッグログにPING送受信とGPSの結果がタイムスタンプとともに吐き出されるので、最低限のデータが取れる。本来ならMapKitと組み合わせてリアルタイムに地図表示まで持っていきたかったところだが、敢えなく時間切れ。

さて仙台を抜け、多くの客が降りだいぶ客席にも余裕ができた頃、最初の試行を開始。しかしそのときちょうどトンネルに入ったらしく、あっけなく問題が発生。上記コード例から分かるように"www.klab.jp"へのPING送信のため名前解決をしているのだが、起動時にしくじるとリトライをしない実装になっているため、トンネルを抜けるまで始められないというバグにぶちあたった。

そうなのだ。仙台から北はまさに行きで悩まされた難受信地域。一足遅かったか…と一瞬天を仰ぐも、気をとりなおしてトンネルを抜けてアンテナが立つと共にリトライ。その後も途中何度も圏外になり、細かいデバッグや調整を繰り返しながら、何とかデータを取ることができた。

データを時間軸で残すようにしたのは大正解だった。作業時間の圧縮のため、デバッグログNSLog()関数での出力にせざるを得なかったが、デフォルトでタイムスタンプ込みの出力をしてくれるため大変助かった。

何しろ、PINGの送信時点と応答受信時点のタイムスタンプを計算すればRound Trip Timeが計れるが、そのくらいアプリケーション内で計算できれば本当はよいものの、デバッグログに逃がすことでその計算も後回しにすることができたのだから。お尻の時間が決まっている場合は、「イカにして必要な作業に集中して手間と時間を投入するか」が重要になってくるということじゃなイカ?

さて、この次にデータをいよいよ地図上に表示する作業に移るわけだが、ここまできてはたと気がついた。Macでネットワークがつながらず往生したのはPocketWifiでだったが、今回iPhoneはそのまま3G回線、すなわちソフトバンク経由でPING送っていたということに…。

まあ、そんなこともありますよね。えへ。

(こうら ひろのぶ)

klab_gijutsu2 at 20:49|この記事のURLComments(0)TrackBack(0)
Blog内検索
Archives
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
最新コメント