iPX社員によるブログ

iPX社員が"社の動向"から"自身の知見や趣味"、"セミナーなどのおすすめ情報"に至るまで幅広い話題を投下していくブログ。社の雰囲気を感じ取っていただけたら幸いです。

ストリーム暗号ChaCha20よる擬似乱数生成のRust実装について

近況

お久しぶりです。iPXのTCDの砂子です。 社員が増えてきたためかブログの記 事当番がなかなかまわってこないようなきがします。 以前に書いたのが 2018/07/29になるので半年ぶりになるのですね。

前回の記事を書いた時点では栃木在住だったのですが、本社勤務が決まった関 係で現在は東京都に引越しをしました。都内だとIT関連の勉強会なども通いや くすなり有益なことがおおいです。

さて、前回に引続きRust言語の話題となります。書籍なども一通り読み終え たのでかねてより目標にしていた疑似乱数生成の実装を行なってみました。

ChaCha20 stream cipher

どの疑似乱数生成器の実装しようかと考えているうちにGoogleの暗号化規格 「Andiantum」の記事が目にとまりました。

記事を読んでいるとその規格の一部にストリーム暗号ChaCha20が使用されて いることが分かります。ChaCha20はTLSの暗号方式にも採用されているようです。

ストリーム暗号とは音声通信や動画配信などを暗号化するために用 いられます。逐次発生するデータをその都度暗号化して流すので\"ストリーム\" が冠されていると覚えれば分かりやすいかと。

ちなみにブロック暗号はファイルなど大きさの定まったまとまりを暗号化す る暗号方式です。AESなどがこれにあたります。

疑似乱数にストリーム暗号が関係あるのか?となりますが、ストリーム暗号 はその実装で疑似乱数を発生するものがあります。そのためWikipediaのペー ジにもありますが疑似乱数を作るさいにストリーム暗号を利用ることがあり ます。

RFC8439 実装

開発者のサイトなどでC言語でかかれた参考ソースコードなどがありますが、 それをそのまま移植しては少々おもしろくありません。

そこでChaCha20の仕様はRFC8439 を参考にしました。途中までObsoleteされた RFC7539を参照していましたが、別記事などをみて間違いに気付きました。

ちなみにRFC8439の全部を実装するのではなく下記2章の途中までの実装しま す。

 1.  Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4
     1.1. Conventions Used in This Document . . . . . . . . . . . . 5
 2.  The Algorithms . . . . . . . . . . . . . . . . . . . . . . . 5
     2.1. The ChaCha Quarter Round . . . . . . . . . . . . . . . . 5
     2.1.1. Test Vector for the ChaCha Quarter Round . . . . . . 6 
     2.2. A Quarter Round on the ChaCha State . . . . . . . . . . . 6 
     2.2.1. Test Vector for the Quarter Round on the ChaCha State 7 
     2.3. The ChaCha20 Block Function . . . . . . . . . . . . . . . 7 
     2.3.1. The ChaCha20 Block Function in Pseudocode . . . . . . 9 
     2.3.2. Test Vector for the ChaCha20 Block Function . . . . . 10 
     2.4. The ChaCha20 Encryption Algorithm . . . . . . . . . . . . 11 
     2.4.1. The ChaCha20 Encryption Algorithm in Pseudocode . . . 12 
     2.4.2. Example and Test Vector for the ChaCha20 Cipher . . . 12

今回はrustで開発するにあたりひとつのファイルでテストまで済ませていま す。再利用可能にするにはもうすこしモジュール構成などをしっかりしたほ うがよいのですが次の課題とします。

2.1. The ChaCha Quarter Round

これがこの暗号方式のキモになるラウンド関数です。ビット演算で情報を でたらめな状態にする関数です。

使われているビット演算は加算、排他的論理和(XOR)、回転(rotate)になり ます。

加算と回転はC言語での表現と異なるので注意が必要です。 加算は overflow_add を使います。戻値は加算した値とくりあがりのタプルになっ ているので0番目を指定します。因みに + をつかって実装すると 実行時にオーバーフローによるエラーとなります。循環シフトは符号無し 32bitにメソッドが予め存在するため特に問題ありません。

// 2.1.  The ChaCha Quarter Round

//    The basic operation of the ChaCha algorithm is the quarter round.  It
//    operates on four 32-bit unsigned integers, denoted a, b, c, and d.
//    The operation is as follows (in C-like notation):

//    1.  a += b; d ^= a; d <<<= 16;
//    2.  c += d; b ^= c; b <<<= 12;
//    3.  a += b; d ^= a; d <<<= 8;
//    4.  c += d; b ^= c; b <<<= 7;

fn quarter_round(mut a: u32, mut b: u32, mut c: u32, mut d: u32) -> (u32, u32, u32, u32) {
    a = a.overflowing_add(b).0;
    d ^= a;
    d = d.rotate_left(16);
    c = c.overflowing_add(d).0;
    b ^= c;
    b = b.rotate_left(12);
    a = a.overflowing_add(b).0;
    d ^= a;
    d = d.rotate_left(8);
    c = c.overflowing_add(d).0;
    b ^= c;
    b = b.rotate_left(7);
    (a, b, c, d)
}

2.2. A Quarter Round on the ChaCha State

こちらは2.1で実装したラウンド関数を16ワードの配列に適用する実装です。 chacha20のラウンド関数は4x4=16ワードの入力にたいして複数回適用します。こ の実装は16ワードのうち4ワードを指定してラウンド関数を実行します。

2.1でも関数で実装しましたが、関数呼出や配列ののコストなどが問題にな るためマクロ機能で実装したいのですが今回は対応しませんでした。

// 2.2.  A Quarter Round on the ChaCha State

//    The ChaCha state does not have four integer numbers: it has 16.  So
//    the quarter-round operation works on only four of them -- hence the
//    name.  Each quarter round operates on four predetermined numbers in
//    the ChaCha state.  We will denote by QUARTERROUND(x,y,z,w) a quarter-
//    round operation on the numbers at indices x, y, z, and w of the
//    ChaCha state when viewed as a vector.  For example, if we apply
//    QUARTERROUND(1,5,9,13) to a state, this means running the quarter-
//    round operation on the elements marked with an asterisk, while
//    leaving the others alone:

//       0  *a   2   3
//       4  *b   6   7
//       8  *c  10  11
//      12  *d  14  15

fn apply_quarter_round(x: usize, y: usize, z: usize, w: usize, words_16: Vec<u32>) -> Vec<u32> {
    let old_a = words_16[x];
    let old_b = words_16[y];
    let old_c = words_16[z];
    let old_d = words_16[w];

    let (new_a, new_b, new_c, new_d) = quarter_round(old_a, old_b, old_c, old_d);

    let mut ret = vec![0; 16];
    ret[..16].clone_from_slice(&words_16[..16]);
    ret[x] = new_a;
    ret[y] = new_b;
    ret[z] = new_c;
    ret[w] = new_d;

    ret
}

2.3. The ChaCha20 Block Function

このセクションは3つに関数を分割しました。

  • setup~key~
  • block~function~
  • serialized

setup~keyは鍵~、カウンタ、Nonceから内部で使用する16ワードを鍵を作り ます。実装ではビット演算にmemクレートのtrasmuteを使用している箇所が あります。これは組み込みデータ型間でビット単位での変換を実現してい ます。4つの符号無し8bitのデータ型u8を1つの符号無し32bitに変換してい ます。これはpremitiveな操作になるためunsafeブロックで囲みます。

fn setup_key(key: Vec<u8>, counter: u32, nonce: Vec<u8>) -> Vec<u32> {
    // The ChaCha20 state is initialized as follows:

    let mut state: Vec<u32> = vec![0; 16];

    // o  The first four words (0-3) are constants: 0x61707865, 0x3320646e,
    //    0x79622d32, 0x6b206574.
    state[0] = 0x6170_7865;
    state[1] = 0x3320_646e;
    state[2] = 0x7962_2d32;
    state[3] = 0x6b20_6574;

    // o  The next eight words (4-11) are taken from the 256-bit key by
    //    reading the bytes in little-endian order, in 4-byte chunks.
    for i in 0..8 {
        let idx_state = i + 4;
        let idx_key = 4 * i;

        unsafe {
            state[idx_state] = mem::transmute::<[u8; 4], u32>([
                key[idx_key],
                key[idx_key + 1],
                key[idx_key + 2],
                key[idx_key + 3],
            ]);
        }
    }

    // o  Word 12 is a block counter.  Since each block is 64-byte, a 32-bit
    //    word is enough for 256 gigabytes of data.
    state[12] = counter;

    // o  Words 13-15 are a nonce, which should not be repeated for the same
    //    key.  The 13th word is the first 32 bits of the input nonce taken
    //    as a little-endian integer, while the 15th word is the last 32
    //    bits.
    for i in 0..3 {
        let idx_state = 13 + i;
        let idx_nonce = 4 * i;

        unsafe {
            state[idx_state] = mem::transmute::<[u8; 4], u32>([
                nonce[idx_nonce],
                nonce[idx_nonce + 1],
                nonce[idx_nonce + 2],
                nonce[idx_nonce + 3],
            ]);
        }
    }

    state
}

block~functionはラウンド関数をsetupkeyで作成された16ワードに8回適~ 用します。そして、適用後の値と適用前の値を排他的論理和を取得します。

fn block_function(key: Vec<u8>, counter: u32, nonce: Vec<u8>) -> Vec<u32> {
    // The ChaCha20 state is initialized as follows:

    let mut state = setup_key(key, counter, nonce);

    let mut working_state = state.clone();
    for _ in 1..=10 {
        working_state = apply_quarter_round(0, 4, 8, 12, working_state);
        working_state = apply_quarter_round(1, 5, 9, 13, working_state);
        working_state = apply_quarter_round(2, 6, 10, 14, working_state);
        working_state = apply_quarter_round(3, 7, 11, 15, working_state);
        working_state = apply_quarter_round(0, 5, 10, 15, working_state);
        working_state = apply_quarter_round(1, 6, 11, 12, working_state);
        working_state = apply_quarter_round(2, 7, 8, 13, working_state);
        working_state = apply_quarter_round(3, 4, 9, 14, working_state);
    }

    for i in 0..16 {
        state[i] = state[i].overflowing_add(working_state[i]).0;
    }

    state
}

最後にserializedを行ないます。

fn serialized(arr32: Vec<u32>) -> Vec<u8> {
    let mut serialized: Vec<u8> = vec![0; arr32.len() * 4];
    for i in 0..16 {
        unsafe {
            let arr8 = mem::transmute::<u32, [u8; 4]>(arr32[i]);
            serialized[i * 4] = arr8[0];
            serialized[i * 4 + 1] = arr8[1];
            serialized[i * 4 + 2] = arr8[2];
            serialized[i * 4 + 3] = arr8[3];
        }
    }

    serialized
}

2.4. The ChaCha20 Encryption Algorithm

前述の3つの節の関数を使用して暗号化を実装します。

平文を64ワード毎に8ワードの鍵、4ワードのカウンタと4ワードのナンスで 生成した疑似乱数をXOR演算で暗号化してゆきます。

fn chacha20_encrypt(key: Vec<u8>, counter: u32, nonce: Vec<u8>, plaintext: Vec<u8>) -> Vec<u8> {
    let mut encrypted_message = vec![0; plaintext.len()];

    for j in 0..(plaintext.len() / 64) {
        let key_stream = serialized(block_function(
            key.clone(),
            counter + j as u32,
            nonce.clone(),
        ));
        let block = &plaintext[j * 64..=(j * 64 + 63)];

        for k in 0..64 {
            encrypted_message[j * 64 + k] = block[k] ^ key_stream[k];
        }
    }
    if plaintext.len() % 64 != 1 {
        let j = plaintext.len() / 64;
        let key_stream = serialized(block_function(
            key.clone(),
            counter + j as u32,
            nonce.clone(),
        ));
        let block = &plaintext[j * 64..plaintext.len()];

        for k in 0..plaintext.len() % 64 {
            encrypted_message[j * 64 + k] = block[k] ^ key_stream[k];
        }
    }   
}

疑似乱数生成

さて、実装も一通りすんだところで疑似乱数の生成に取り掛かります。上記 のRFC8439では暗号化の仕様を定めています。疑似乱数生成の仕様ではあり ません。

疑似乱数を生成する方法はrustのrandクレートを参考にすることにしました。 また、この機能で生成した乱数を実装が正しくできたかの確認につかいます。

  • impl BlockRngCore for ChaChaCore
  • impl SeedableRng for ChaChaCore

ChaCha20の暗号処理で使用する下記3つの変数をそれぞれ下記のように割り 当てます。

  1. Key == Seed
  2. Nonce == 0
  3. conter == 乱数を生成でインクリメント

先ず、randクレートで正解の乱数を作成します。

use rand::{ChaChaRng, Rng, SeedableRng};

fn main() {
    //let seed: &[_] = &[1, 2, 3, 4, 5, 6 , 7 ,8, 9, 10, 11, 12,13 ,14,15,16];
    let seed: [u8; 32] = [
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
        0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
        0x1e, 0x1f,
    ];

    let mut rng: ChaChaRng = SeedableRng::from_seed(seed);

    for _ in 0..40 {
        println!("{}", rng.gen::<u32>());
    }
}

結果は下記になります。

2100034873
1780073945
1996733837
1229642936
1876440458
3429555900
1283312818
2451892952
3888915243
2871222434
1777274431
1686095930
3929375269
765720497
2690787266
205609800
826456088
3517376173
1633444115
659440559
4126388728
1549512161
318568684
1551185194
1829242994
1564274385
609780125
1006636644
1593221275
3461963230
2135566861
3445265713
3693998658
3583134375
4018841452
997363241
914301792
3082742343
815587571
3806560462

同一のseedを使用して自作のChaCha20の実装で乱数を発生させます。

#[test]
fn test_generate_rng() {
    let seed: Vec<u8> = vec![
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
        0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
        0x1e, 0x1f,
    ];

    let nonce: Vec<u8> = vec![
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    ];

    let expected: Vec<u32> = vec![
        2100034873, 1780073945, 1996733837, 1229642936, 1876440458, 3429555900, 1283312818,
        2451892952, 3888915243, 2871222434, 1777274431, 1686095930, 3929375269, 765720497,
        2690787266, 205609800, 826456088, 3517376173, 1633444115, 659440559, 4126388728,
        1549512161, 318568684, 1551185194, 1829242994, 1564274385, 609780125, 1006636644,
        1593221275, 3461963230, 2135566861, 3445265713, 3693998658, 3583134375, 4018841452,
        997363241, 914301792, 3082742343, 815587571, 3806560462,
    ];

    let r = block_function(seed.clone(), 0, nonce.clone());
    for i in 0..16 {
        assert_eq!(expected[i], r[i]);
    }

    let r = block_function(seed.clone(), 1, nonce.clone());
    for i in 0..16 {
        assert_eq!(expected[i + 16], r[i]);
    }
}

結果が一致しました。

まとめ

なんとかRustでまとまったコーディングをすることができました。しかし、 RFCのセクション毎の実装をクリアすることに集中したため引数などのリス トをクローンしたりと及第点とは到底いえない状態です。

先ず、次の点を課題として認識しもう少し実装を磨こうと思います。

  • マクロ機能
  • ビット演算
  • モジュール構成

クレートとして公開するようなことはないでしょうが、公開の方法などを調 べて整えるまでできれば言語の学習としては一段落でしょうか?

大学の卒業研究で暗号学的ハッシュ関数MD5の後継であるMD6の高階差分解析 がテーマでした。卒業から4年程たったときに当時の研究室に連絡を入れ、 自分が作成した卒業論文を入手して続きを研究しようと考えました。その時 は転職や転居などがあり結局それは有耶無耶になりました。

現在も暗号とはそれほど関係ない業務ですが、FPGA等を触る機会があります。 そのような技術で当時の卒業研究のテーマをやったらどの程度のことができ たのかと考えることがあります。その度にあの時から勉強を続けていたら よかったなと後悔することもあります。

長期に渡る知見を蓄えた場合にどのような世界が見えるのか?いまから始め るのは遅い気もしますが、一つのテーマに腰を据え知見を蓄えその状態に到 達したいとそう思います。

この世界をサバイブするには

こんにちは、ヤマダです。
非常にご無沙汰、ご無沙汰でございますね…。
最近は全く東京にいません、展示会以降はお陰様で色々なお客様の元に訪問をしているため、専ら色々な土地を渡り歩いているため、東京にいると逆にそわそわしちゃいます。

少し前の話ですが、国内景気が後退局面傾向にあるニュースが流れていました。
この業界に限らず景気に左右されてしまう社会なので、こんな報道があると薄氷を歩くような気分です。
(個人的には、以前から徐々に景気が悪くなっていたように感ずるのですが…。)

そんな状況の中でもこと自動車という領域においては、「足を止めると息が止まる」、いくら企業として息切れをしていても決して手が止められない、手を止めたら最後数日であっという間に他社に追い抜かれてしまうという息切れしてしまうような切羽詰まった状況ですね。
一夜にして新鋭プレイヤーに追い抜かれていたり、かと思ったら一夜にして有名なプレイヤーが失落したりと業界全体がまさに激動、浦島太郎の心持ちを味わう人も多いかと思います。

社会においては雨降る悪条件の中、必ず決めなければいけない試合というのはままあるかと思うのですが、そんなぬかるみの中でも、むしろそのぬかるみを利用して鮮やかなシュートを決めちゃう人や企業もいますよね。
特にここで言う良いプレイを行う人間は、やはり海外勢が多いような気がしています。
日本企業は黙したまま泥に埋まっている、もしくは「泥から抜け出すにはどうすればよいか」という事を必死で考えており、その先の点を取る行動にまで考えが及んでいない所が多いんじゃないかなと思っています。

こういった中でも生き抜くには…とここ最近考えている生存戦略の話をしたいと思います。

iPXは半数以上が未経験者で構成されています、と言うと伝えた方全員にかなり驚かれます。
特に会社紹介なんかをすると、事業領域の広さに対して社員数の少なさと未経験者の多さに素直に感嘆される方が多いです。
しかも出身がかなりバラエティーに富んでいます、最近入社した人間だと作業療法士や科捜研の男、土木現場の監督していたと様々です。
ちなみに私もiPXに入社する前は工芸品を売っていました。

採用に関しては正直な所この売り手市場の現在経験者を取っている余裕すらないという実情もあるのですが、同じ遺伝子ばかりで構成されていると、突然環境が変異した場合対応できなくなるため、環境変化に順応できる組織体になるべく、異なる背景を持った人間を積極採用していると一個人としてはそう捉えています。
ただ同時に企業活動である以上、同じミッションを理解して協調していかないと、地力の高い組織になっていかないというのも事実です。
いかに個々人の能力の差別化を図りながら一体となっていくか、かなり実行の塩梅が難しいと感じるテーマですが、実はそういった命題は巷では溢れているのではないでしょうか。

「自分の軸はぶれず、かつ身につけた価値観(この文脈だと、バイアスと言った方が良いかもしれません)には固執せず、いかにキッパリと捨てられるか」
「いかに自分が変われるか、変わりながらいかに自分の変えない部分を作るか」
「いかに相手を尊重しながら自分の信念を伝えるか」
「どの観点で起こった事象を良しとするのか、どの観点で起こった事象を悪しとするのか」
「ルールに従いながら、いかに法則を塗り替えるか」
「いかに自分の利益を出しつつ、関わった全員が満足するか」

こういった一見矛盾をはらむ命題は、社会生活を送る上では避けて通れない壁として、大なり小なり立ちはだかっています。
それはフレキシブルな対応力なんていう平々凡々な言葉では語れないように思います。
恐らく正しい言葉として無いんじゃないかなと感じているのですが、あえて呼ぶとしたら矛盾を成立させる力、矛盾力をいかに付けるかが肝なのではないかと、最近は常々考えています。
矛盾を成立させることによる力は、片方向を成立させた推力よりもかなり大きく、かつ巡り巡って様々な物事に影響をもたらします。

でもそういった矛盾をはらむ力は、一度極端にまみれないと生まれません。
ということで、次回はその極端にフォーカスを当てた小話をしたいと思います…。

女性の海外一人旅のTIPS

はじめまして。入社2か月の木村です!
前職は、宿泊・レジャー系の接客業をしておりました。
業界外からの転職のため、まだ技術的なことは語れないので、趣味の話をしたいと思います。

私の趣味は釣り、読書、旅行等なのですが、今回は海外旅行、特に女性が一人で行く場合に役立つ小ネタを中心に書きます。

できれば公共交通機関の発達している都市を選ぶ。

バリのリゾートホテルから一歩も出ないんだ!食事もホテル内で済ますんだ!のんびりするんだ!
みたいな目的の旅行なら、公共交通機関がなくてもよいと思います。
しかし、観光したい、買い物したい、外食したい、みたいな人は、
公共交通機関が発達していないとタクシー(アジアだとトゥクトゥクとかバイクタクシー?)しか移動手段がありません。
一人旅でタクシーに乗るというのは、どこかに連れていかれてしまうかもしれない、ぼられるかもしれない、という余計な心配がつきまといます。
できれば地下鉄や路面電車が発達していると安心です。

女性専用ドミトリーに泊まる。

こちらは、高級ホテルに泊まれる経済力がある人や、もしくは超神経質な人には該当しないので読み飛ばしてもらってもよいと思います。
一人でホテルに泊まるとき、セキュリティが一番気になるかと思います。
安いホテルだと、フロントも夜は無人だったりするので特に心配です。
夜寝ている間に知らない人がカギを開けて入ってきたら・・・?!と思うと自分が女性じゃなくても寝られません。
その心配は、ドミトリー形式で同じような一人旅の人達と一緒の部屋に泊まることで解決できます。
ドミトリーとは相部屋のことで、二段ベッドがいくつか置かれていて、部屋の定員が4名・6名・8名・10名等、人数によって値段が異なります。
多くの場合、男女混合ドミトリーと女性専用ドミトリーがあります。
私は洗濯や着替えが気楽なので女性ドミトリーにしか泊まったことがないです。
少しだけ勇気を出して話しかければ、そこでいろいろな国籍の友達ができて一緒にご飯を食べたり観光をしたりという経験もできます。
ただし、他人と同室で寝泊まりするということなので、神経質な人は疲れてしまうかもしれません。

夜出かけるときは、暗くなってから女性の一人歩きが可能な雰囲気か確認してから出かけるようにする。

日本では繁華街での女性の夜間の一人歩きは平気ですが、危険な国・都市はもちろんあります。
昼間は平気でも夜となると別物なので、夜に女性が一人で歩いているか注意して観察してみたほうが良いです。
もし一人歩きが全然見つからなかった場合、さっさとホテルに戻り夜はのんびり過ごすか、ホテル内で友達を作って複数人で一緒に出掛けるといいと思います。

Google MapのオフラインマップをWifiがつながっているときにダウンロードする。

Google Mapって、オフラインでも使用できるようにダウンロードできるって、知っていましたか?
日本国内の地図データは残念ながら対応していないのですが、海外は多くの国が対応しています。
国際ローミングサービスを利用したり、ポケットWifiを持っていく人も多いと思いますが、
私は値段が高かったのと、あまり必要性を感じなかったので利用しませんでした。
GPS機能は機内モード等にしていなければローミングをオフにしていても有効です。(ただし精度は落ちますが)
ホテルや最寄り駅周辺の地図をダウンロードしておくと、救われることがあるかもしれません。
方法の詳細は今後変わるかもしれないのでここには書きませんが、気になる方はググってください。簡単です。
一切お金もかからないのでやっておいて損はないはずです。

日本円は最小限に、海外キャッシング可能なクレジットカードを持っていく。

クレカがほとんどの場所で使える現代で、たくさん両替をする必要はないと思います。
まず、現金よりクレカのほうがレートが良いので、できるだけクレカで払うようにします。
ただしどうしても現金が必要なときのために、少しだけ現地通貨も持っておかないといけません。
そんなときのために、PLUSマーク(気になったらググってください)の付いたクレカを2枚程持っていくと安心です。
PLUSマークとは、海外のどこのATMでも現地通貨を必要な分だけ引き出せる、という印です。
キャッシングなので、手数料や利子はかかってしまいますが、多くの場合両替よりはお得です。
契約時にキャッシング機能を付けていない場合もあるかもしれないので、日本にいるうちに契約書等を確認したほうが良いと思います。
あと、カードの相性で使えなかったり、上限に達してしまったりするので、VISAとDC等カード会社の違うクレカを複数枚持っていたほうが安心です。
私は大体現地の空港のATMで現地通貨を手に入れます。
ちなみにATMがなかったのはタイのかなり辺鄙な離島くらいでした。そこもクレカは使えました。

鞄はファスナー付、リュックはNG。

説明するまでもないですが、意外とそのような鞄は持っていなかったりします。
人が多いところはスリも多いところなので、私はファスナーを閉めたうえでその金具を握りしめています。
特にヨーロッパで、気づかないうちにスリに鞄のファスナーを開けられたという話をよく聞きます。
ポケットに財布・スマホを入れるのもNGです。
あと、男性に多いのがボディバッグや斜め掛けバッグを背中に回しているパターンです。
これもリュック同様、真っ先にターゲットです!

ガイドブックは地球の歩き方一択。

食べ物やお土産の写真がたくさん載っているガイドブックは、魅力的に見える上に読んでいて楽しいのですが現地では情報不足なことが多いです。。。
到着してから実際に必要なのは、グルメ情報やお土産の写真よりも、各場所へのアクセスや、交通機関のタイムスケジュール・金額、都市間の移動方法、等でした。
地球の歩き方は情報量が多くて読むにはなかなかしんどいのですが、現地では安心感が段違いです!(まわしものではない)

持っていったほうがいいもの。

たくさんのおしゃれ着は正直不要です。一人だと自分の写真を撮る機会も限られています。
そんな中必要なものは・・・
・鏡(お化粧用。ドミトリーでは洗面所の取り合いが起こりやすい)
・サンダル(室内・お風呂用。)
・延長コード(必須!ドミトリーではコンセントの取り合いが起こりやすい)
・変換プラグ(最近は100均でも買える。延長コードさえあればこれは1つあれば十分)
・洗濯洗剤・洗濯ひも・洗濯ばさみ(5日以上の旅行なら必要。ただし物価の安い国はランドリーショップが町中にあり便利なのでいらないかも)

以上です。

ついつい長くなってしまいました。
これから一人旅にチャレンジしたい人がたまたまこのブログを見る、もしくはかねてからの当ブログの読者さんがこの記事を読んで旅に出たくなる、そんなことがもしあれば、私はとてもうれしいです。

次はどこに行こうか、考えただけでワクワクしますね!

それでは、この辺で・・・

自己紹介+趣味の話。。。

初めまして。
昨年8月後半に入社いたしました、タルモトです。
たるちゃん、たるるなど呼ばれております。
入社前からの知り合いには下の名前+くんで呼ばれてたりします。
※その方の紹介で入社しました。
以後お見知りおきを。

前職ではセキュリティ製品メーカーの事務・営業アシスタント・広報・WEBなど割と何でも屋でした。

【セキュリティ製品って?】
いわゆる「鍵」です。
マンション・アパート・戸建てに使われる玄関ドアの鍵をメインに扱っており
オフィスなどにあるロッカー・キャビネットの鍵や宅配ボックスなどなどのメーカーでした。
賃貸の管理会社、建築系のお客様がメインで個人のお客様まで。

事務職では注文データの受発注入力、雑務。
営業アシスタントでは提案資料の作成。
広報・WEBではWEBサイトの更新、新商品の撮影(静画・動画)⇒編集まで。

色々やりました。

iPXに入社後の現在は栃木にてお客様のもとで仕事をさせていただいております。
異業種からの転職組のためわからないことだらけではありますが、尽力させていただいております。

10月の弊社全社会議(慰労会)にてGMへの任命があり、4月からは本格的にGM(GameMaker)として活動します。
いわゆる営業マン、こちらも未経験。

日々勉強となりますがよろしくお願いいたします。
全力で頑張ります。

ここからは趣味の話です。

続きを読む

元外資IT企業PMが英文メールでよく使った、とりあえず通じる表現2つ【ほぼそのまま使える例文付き】

初めまして、「iPXの山ちゃん」ことやまもとです。(ちなみに名古屋育ちです!)

入社半年が経ちました。
この記事ではタイトルの通り、私の軽い経歴とそれにまつわる豆知識をお伝えいたします。
当ブログはゆるめです。ご了承ください。

前職では何をしていたかというと、
主に外資ハードウェアメーカーで機器導入プロジェクトのプロジェクトマネージャ(以下PMと書きます)をやってました。

外資なので、社内外問わず国境を跨いだコミュニーケーションは日常的に行われてました。
基本的に文法、用法を細かく言う人はネイティブでさえいません。

日本人が使う英語についての議論はたくさんされていますが、私は

通じさえすれば、仕事ができさえすればいい

と考えてます。

正しい使い方ができることに越したことはありませんが、我々日本語ネイティブだってよく日本語間違うじゃないですか。そんなもんです。

とはいえ、入った当初は本当に大変でした。
PM始めたときのスペックは以下の通りです。
・TOEIC600点台
・留学経験なし
SNSとかテキストメールで英語話者の友達と軽いやりとりしたことはある
・エンジニア未経験
・そもそも新卒入社から半年で外資PMとかいう無理ゲー(ここでは触れません)
・英語自体を勉強するのはわりと好きな方

そんな状態で入社1週間未満で社内システムにあるファイル編集権限をもらうようインドのチームに電話しろとか言われるわけです。
もちろん英語で。
(結局社員にやってもらいましたが)

そんな中、毎日英文メールを送受信するごとにググりまくったり受信メールを見て身に付けた英語のフレーズを少しお伝えします。

※文法、用法が絶対に正しいという保証はできませんが、とりあえずその現場で仕事はできてましたので通じるはずです。使う場合は自己責任でお願いします。

Could you please〜?
〜していただけないでしょうか?

個人的にはめっちゃ重要表現です。
お客様に対してほぼ毎回使ってました。
メールするということは、こちら側から質問なり依頼なり
メールの相手に何かをしてほしいからメールしますよね。

e.g. Could you please provide your phone number?
お電話番号を教えていただけないでしょうか?

この表現にまつわるエピソードをかるーく。

当時の私は、英語では物事をストレートに伝えなきゃダメだ!という通説を曲解した結果
重要顧客である某外資系銀行相手へとある依頼をするときにぞんざいな言葉遣いをしてしまったらしく、チームのメンバーにしこたま怒られました…。。
そのときどんな言葉遣いをしたかはもはや覚えてません。

それ以来お客様相手に対して依頼をするときはほぼ必ずこれを使ってました。
これを使って怒られた記憶はありません。

As for〜 / Regarding〜
〜について、関して

使い分けは特にしてませんでしたが、同じ表現が続くのは良くないとされているので
2回以上この表現をしたい場合は交互に使っていました。

e.g. As for installation we would like to have three business days to prepare.
導入作業について、準備が必要なため3営業日頂きたく存じます。

e.g. Regarding your question, we are currently checking. We will get back to you soon.
お問い合わせに関しましては現在確認中でございます。確認でき次第ご連絡差し上げます。


書いていくうちに前職の思い出が止まらなくなってきたので、今回はこれくらいにしておきます。

以上、過去のブログも合わせて読んでいただけるとわかると思いますが、社員のバックグラウンドが様々なところも弊社の魅力のひとつではないかと思います。

それでは
Viszlát :)

【OpenCV】GrabCutの使い方【セグメンテーション】

入社して半年が経ちました。最年少社員の安藤です。



つい一月半ほど前まで高校時代の同級生と同居していたのですが、最近のぼくのこころのなかにはコンピュータビジョン(CV)が住み着いています。
そんな彼はふとした時、ぼくに問いかけてきます。

「CVは楽しいかね?」

仕事は楽しいかね? (きこ書房)

仕事は楽しいかね? (きこ書房)



楽しいです。



そんなわけで今回はOpenCVの便利なメソッドのひとつを紹介したいと思います。



GrabCutとは?

GrabCutとはセグメンテーションに利用されるアルゴリズムの名称です。

グラフ理論を応用したコスト最小化手法GraphCutを用いたくりかえし処理を行っているそうです。
アルゴリズム本体の詳細については他にまとめが存在しているので、下記をご覧ください。

http://visitlab.jp/pdf/GrabCut.pdf
http://zellij.hatenablog.com/entry/20131004/p1


むずかしそうな数式がたくさん並んでいますね。

こんな時もぼくの精神的CVはいつもそばにいて、助言をあたえてくれるのです。



OpenCVを使え」




OpenCVを使いましょう。

解説

import cv2 as cv
import numpy as np


BLUE = [255,0,0]        # rectangle color
RED = [0,0,255]         # possible BG
GREEN = [0,255,0]       # possible FG
BLACK = [0,0,0]         # obvious BG
WHITE = [255,255,255]   # obvious FG

DRAW_BG = {'color' : BLACK, 'val' : 0}
DRAW_FG = {'color' : WHITE, 'val' : 1}
DRAW_PR_FG = {'color' : GREEN, 'val' : 3}
DRAW_PR_BG = {'color' : RED, 'val' : 2}


# 現在の描画する線の色
value = DRAW_FG


# 描画する線の太さ
thickness = 3


# ウィンドウ名
win_name = "WINDOW"


# grabcut結果表示の際、alphaが大きいほど黒背景が濃ゆくなる
alpha = 0.7
beta = 1 - alpha


# grabcutイテレーション回数
itr = 1


# 描画フラグ
drawing = False


# 出力イメージ番号
pic_num = 1



def mouse_event(event, x, y, flags, param):
    global display, mask, thickness, value, drawing, bgdModel, fgdModel

    if event == cv.EVENT_LBUTTONDOWN:
        drawing = True
        cv.circle(display, (x, y), thickness, value['color'], -1)
        cv.circle(mask, (x, y), thickness, value['val'], -1)

        
    elif event == cv.EVENT_MOUSEMOVE:
        if drawing == True:
            cv.circle(display, (x, y), thickness, value['color'], -1)
            cv.circle(mask, (x, y), thickness, value['val'], -1)
        
        
    elif event == cv.EVENT_LBUTTONUP:
        if drawing == True:
            drawing = False
            cv.circle(display, (x, y), thickness, value['color'], -1)
            cv.circle(mask, (x, y), thickness, value['val'], -1)
            

if __name__ == '__main__':


    img = cv.imread("lena.png")
    display = img.copy()

    mask = np.zeros(img.shape[:2], dtype=np.uint8) 
    bgdModel = np.zeros((1,65), dtype=np.float64)
    fgdModel = np.zeros((1,65), dtype=np.float64)

    cv.namedWindow(win_name, cv.WINDOW_NORMAL)

    roi = cv.selectROI(win_name, img)

    cv.grabCut(img, mask, roi, bgdModel, fgdModel, itr, cv.GC_INIT_WITH_RECT)
    
    mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
    display = img * mask2[:,:,np.newaxis]
    display = cv.addWeighted(display, alpha, img, beta, 0)

    cv.setMouseCallback(win_name, mouse_event)
    
    while (True):

        cv.imshow(win_name, display)
        key = cv.waitKey(1)

        if key == 27:
            cv.destroyAllWindows()
            break


        elif key == ord("n"):

            drawing = False

            cv.grabCut(img, mask, roi, bgdModel, fgdModel, itr, cv.GC_INIT_WITH_MASK)
            mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
            display = img * mask2[:,:,np.newaxis]
            display = cv.addWeighted(display, alpha, img, beta, 0)


        elif key == ord("s"):
            cv.imwrite("processed" + str(pic_num) + ".png", display)
            pic_num += 1


        elif key == ord('0'): # BG drawing
            print(" mark background regions with left mouse button \n")
            value = DRAW_BG
            print("Obvious BGD")


        elif key == ord('1'): # FG drawing
            print(" mark foreground regions with left mouse button \n")
            value = DRAW_FG
            print("Obvious FGD")


        elif key == ord('2'): # PR BG drawing
            value = DRAW_PR_BG
            print("Possible BGD")


        elif key == ord('3'): # PR FG drawing
            value = DRAW_PR_FG
            print("Possible FGD")


このコードをつかってLena氏の顔を抽出してみましょう。

f:id:ipx-writer:20190215154053p:plain

初期矩形情報だけでここまでとれました。

f:id:ipx-writer:20190215154103p:plain

これを修正すると…

f:id:ipx-writer:20190215154126p:plain

f:id:ipx-writer:20190215154130p:plain



きれいに顔だけ抽出できましたね!

大阪旅行

こんにちは。昨年入社いたしましたkuboです。
まだ入社してから1年たっていないので、今は日々、技術について、ビジネスについて勉強しております。
初めての記事で何を書こうか迷ったのですが、まだ技術的なことを書くことは難しいので年末にプライベートで行ってきた大阪旅行について書きたいと思います。

旅行前日

東京から大阪まで夜行バスで移動したのですが、バスがド派手でびっくりしました。
f:id:ipx-writer:20190204205128j:plain
f:id:ipx-writer:20190204205209j:plain
久しぶりの夜行バスで、朝5時ごろにサービスエリアで休憩があったのですが、足がむくみすぎて靴がはけなかったです。

初日

大阪に到着後は最初に海遊館へ。
f:id:ipx-writer:20190204205402j:plain
f:id:ipx-writer:20190204205448j:plain
魚たちを見た後は夜行バスの疲れを取るためにホテルで昼寝。
夜はお好み焼きを食べに行きました。

帰りに綺麗なイルミネーションも見ることができました。
f:id:ipx-writer:20190204205515j:plain

2日目

とりあえずこれ!
f:id:ipx-writer:20190204205542j:plain
お昼はたこ焼きを食べました。
f:id:ipx-writer:20190204205621j:plain
夜は今回の旅行のメインのライブ。大阪城ホールへ向かいます。
f:id:ipx-writer:20190204205715j:plain
f:id:ipx-writer:20190204205736j:plain
ライブの後は疲れ果てて、すぐにホテルで寝ました。

3日目

3日目は通天閣を観光しました。
f:id:ipx-writer:20190204205757j:plain
最後はふたたび明石焼きを食べて旅行終了!
f:id:ipx-writer:20190204205817j:plain
やはり大阪は天下の台所と言われるだけあってご飯はおいしかったです。