Upgrade to Pro — share decks privately, control downloads, hide ads and more …

#JJUG Java における乱数生成器とのつき合い方

#JJUG Java における乱数生成器とのつき合い方

JJUG CCC 2019 fall のセッション「Java における乱数生成器とのつき合い方」の発表資料です。発表概要は以下の通りです。

現在の Java (Java 11) には様々な種類の乱数生成器が実装されていますが、Java そのものやの乱数生成器に詳しくない方にとっては、いつどのような状況でどの実装を使えばいいのか迷ってしまうことがあるかと思います。そのような迷いを解消するために、このセッションでは乱数生成器の基礎的な前提知識からはじめ、Java クラスライブラリにおける各種乱数生成器の実装とその特性などについてお話いたします。

KOMIYA Atsushi

November 23, 2019
Tweet

More Decks by KOMIYA Atsushi

Other Decks in Programming

Transcript

  1. Java における
    乱数生成器とのつき合い方
    JJUG CCC 2019 fall / 2019-11-23
    KOMIYA Atsushi

    View Slide

  2. @komiya_atsushi

    View Slide

  3. 本日のお話

    View Slide

  4. Java で
    乱数を生成する
    (乱数生成器を利用する)

    View Slide

  5. 乱数生成器の
    基礎知識

    View Slide

  6. 乱数生成器の種類

    View Slide

  7. 乱数生成器の種類: 真の乱数生成器
    ● 予測不可能な乱数列を生成する乱数生成器
    ● 専用のハードウェアが用いられたりする
    ○ 身近な例で言えば なども真の乱数生成器に該当する
    ● Linux の /dev/random は真の乱数生成器と呼べそう?
    ○ macOS は擬似乱数生成器 (Yarrow アルゴリズム) による実装らし

    View Slide

  8. 乱数生成器の種類: 擬似乱数生成器
    ● 決定論的乱数生成器 (DRBG) とも呼ばれる
    ● アルゴリズムによって乱数列が生成される
    ○ アルゴリズムと内部状態が既知なら、生成される乱数列を予測できる
    ○ 内部状態を特定の値に変更 (シードを再設定) することで乱数列を再現できる
    ● 周期が存在しうる
    ○ 周期が一巡すると、過去に生成したのと全く同じ乱数列が生成される
    ● 生成アルゴリズムの一例
    ○ 線形合同法、メルセンヌツイスタ、Xorshift, SplitMix, PCG, ...

    View Slide

  9. 乱数生成器の種類: 暗号論的擬似乱数生成器
    ● 擬似乱数生成器の一種
    ● セキュリティに関わる場面での利用に適している
    ○ 例: パスワードをハッシュ化する際の salt の生成
    ● 要求仕様がある
    ○ (1) next-bit test に合格する
    ○ (2) state compromise extension に耐える
    ○ 詳細は ja.wikipedia.org/wiki/暗号論的擬似乱数生成器 を参照

    View Slide

  10. 乱数生成器の性能評価

    View Slide

  11. 性能評価のポイント
    ● 乱数の生成速度
    ● 内部状態のメモリフットプリント
    ● 乱数列の周期の長さ
    ● 生成される乱数列の統計的品質
    ○ 生成される乱数に統計的な偏りがなく、一様に分布している
    ことが望ましい
    ○ ランダムであることを評価するための統計的仮説検定が存在
    する

    View Slide

  12. 乱数生成器の統計的な欠陥が引き起こした事例
    ja.wikipedia.org/wiki/線形合同法 > 短所 より引用
    下位ビットのランダム性が低い。 Mの値によっては、最下位に近いビットはほとんどランダムでなく、完全に
    規則性を持っていることすらある。たとえば Mが偶数の場合(コンピュータでの実装が楽であるために、 Mに
    2の冪を採用した場合はこれに当たる)、 最下位ビットは、同じものが出続けるか、 0と1が交互にでるかの
    どちらかである。
    ja.wikipedia.org/wiki/カルドセプト_サーガ > バグ問題 より引用
    とくに深刻と言われるバグは、 ダイスの目が偶数・奇数を繰り返す というものであった。疑似乱数の生成な
    いし利用法に問題があったと推測されている(たとえばある種の線形合同法を不適切に用いると、下位 1
    ビットは0と1を繰り返す)。

    View Slide

  13. 統計的な品質評価
    ● 代表的なテストスイート
    ○ Diehard (1995)
    ○ TestU01 (2007)
    ■ Small Crush / Crush / Big Crush の三種がある
    ○ PractRand (2010?)
    ● いずれのテストスイートも数多くの統計的仮説検定の組み合わせで構
    成されている
    ● 最近では、TestU01 の Big Crush がデファクトスタンダードとして利用
    されている印象がある
    ○ Big Crush の fail が 0 個であることを謳う乱数生成器の実装を見かける

    View Slide

  14. Java における
    乱数生成器

    View Slide

  15. Java クラスライブラリが提供する乱数生成器
    ● 乱数生成器のクラス
    ○ java.util.Random
    ○ java.security.SecureRandom
    ○ java.util.concurrent.ThreadLocalRandom
    ○ java.util.SplittableRandom
    ○ これらクラスの継承関係が少し (次頁参照)
    ● 乱数生成メソッド
    ○ Math.random()

    View Slide

  16. View Slide

  17. java.util.Random クラス
    ● 線形合同法を利用した擬似乱数生成器
    ● 一言でいうなれば「レガシー」
    ○ 統計的品質がよろしくない
    ■ TestU01 の Big Crush で 50 個近く fail する
    ■ commons.apache.org/proper/commons-rng/userguide/rng.html#a
    5._Quality を参照
    ○ 乱数生成の速度がよろしくない
    ■ 内部状態の更新に AtomicLong の操作が必要になるため
    ■ 単一スレッドで利用していても遅い
    ■ 同一インスタンスを複数スレッドで扱うと悲惨なことになる

    View Slide

  18. protected int next(int bits) {
    long oldseed, nextseed;
    AtomicLong seed = this.seed;
    do {
    oldseed = seed.get();
    nextseed = (oldseed * multiplier + addend) & mask;
    } while (!seed.compareAndSet(oldseed, nextseed));
    return (int)(nextseed >>> (48 - bits));
    }
    参考: Random#next(int) メソッドの実装
    線形合同法そのものの
    処理はとてもシンプル
    下位ビットの利用を避
    けている

    View Slide

  19. // シードを指定してインスタンスを生成する
    Random jur = new Random(123);
    System.out.println(jur.nextInt()); // => -1188957731
    // シードを (再) 設定する
    jur.setSeed(456);
    System.out.println(jur.nextInt()); // => -1035442920
    Random クラスの利用例

    View Slide

  20. SecureRandom クラス
    ● 暗号論的擬似乱数生成器の実装
    ● 具体的な乱数生成アルゴリズムは複数存在する (後述)
    ○ SecureRandom.getInstance(String) でアルゴリズム名を引数に指
    定し、インスタンスを得る
    ● Web アプリケーションのセッション ID やパスワードの salt など、
    推測されたくないランダム文字列の生成などで利用するのに適し
    ている
    ○ 実際に、Tomcat のセッション ID (jsessionid) の生成において
    SecureRandom が使われている

    View Slide

  21. SecureRandom で利用できるアルゴリズム
    ● プラットフォーム (OS) によって、標準で利用できるアルゴリズムが
    異なる
    ○ Java 8 時点でのプラットフォーム別アルゴリズム
    ■ docs.oracle.com/javase/jp/8/docs/technotes/guides/security/SunPr
    oviders.html#SecureRandomImp
    ○ Java 11 で利用できるアルゴリズムの一覧
    ■ docs.oracle.com/javase/jp/11/docs/specs/security/standard-names.ht
    ml#securerandom-number-generation-algorithms
    ● プラットフォームおよびアルゴリズムの組み合わせ次第で、
    乱数生成時にブロッキングが生じることに注意する必要がある

    View Slide

  22. SecureRandom で利用できるアルゴリズム
    ● NativePRNG / NativePRNGBlocking /
    NativePRNGNonBlocking
    ○ /dev/random や /dev/urandom を利用して擬似乱数を生
    成するアルゴリズム
    ○ UNIX 系プラットフォームで利用できる
    ○ SecureRandom#setSeed() による、再現性のある乱数生
    成はできない様子
    ■ 少なくとも macOS ではできないことを確認

    View Slide

  23. SecureRandom で利用できるアルゴリズム
    generateSeed() nextBytes()
    NativePRNG /dev/random /dev/urandom
    NativePRNGBlocking /dev/random /dev/random
    NativePRNGNonBlocking /dev/urandom /dev/urandom

    View Slide

  24. SecureRandom で利用できるアルゴリズム
    ● DRBG (Java 9~, JEP 273)
    ○ NIST SP 800-90 Ar1 で規格化されたアルゴリズム
    ■ nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-
    90Ar1.pdf 参照
    ○ DRBG の具体的なアルゴリズムは、システムプロパティ
    securerandom.drbg.config で指定する
    ■ Security.setProperty("securerandom.drbg.config",
    "CTR_DRBG");

    View Slide

  25. SecureRandom で利用できるアルゴリズム
    ● SHA1PRNG
    ○ レガシーだが、すべてのプラットフォームで利用できる
    ○ (参考) Android では deprecated になっている
    ■ android-developers.googleblog.com/2016/06/securit
    y-crypto-provider-deprecated-in.html 参照
    ● その他 (詳しく知らないので割愛)
    ○ PKCS11
    ○ Windows-PRNG

    View Slide

  26. // アルゴリズムを明示的に指定してインスタンスを取得する
    SecureRandom sr = SecureRandom.getInstance("DRBG");
    sr.setSeed(789);
    System.out.println(sr.nextInt()); // => -1542194244
    sr.setSeed(789);
    sr = SecureRandom.getInstance("NativePRNGNonBlocking");
    System.out.println(sr.nextInt()); // 出力される値は不定
    SecureRandom クラスの利用例

    View Slide

  27. ThreadLocalRandom クラス
    ● マルチスレッドでの利用に適した擬似乱数生成器の実装
    ○ スレッドごとに固有の擬似乱数生成器 (厳密には内部状態) が存在する
    ○ 利用する際に明示的なインスタンス生成が不要である
    ● SplittableRandom クラスと同じ SplitMix アルゴリズムを利用し
    ている (Java 8 以降)
    ○ Java 7 では、Random クラスと同じ線形合同法を採用していた
    ● Random#setSeed() を呼び出すことができないため、乱数生成の
    再現性が求められるケースでの利用には適さない
    ○ UnsupportedOperationException が発生する

    View Slide

  28. // インスタンスの生成は不要
    System.out.println(ThreadLocalRandom.current().nextInt());
    // ThreadLocalRandom.current() で取得したオブジェクトを
    // 他のスレッドで利用するのはご法度
    // シードを設定しようとすると例外が発生する
    try {
    ThreadLocalRandom.current().setSeed(123);
    } catch (UnsupportedOperationException e) {
    e.printStackTrace();
    }
    ThreadLocalRandom クラスの利用例

    View Slide

  29. SplittableRandom クラス
    ● Fork/Join フレームワークを含むマルチスレッドでの利用に適した
    擬似乱数生成器
    ● SplittableRandom#split() により、現在の乱数生成器と完全
    に異なる乱数列を生成するインスタンスを生成できる
    ○ 複数スレッドで並列して乱数生成する際に、まったく同じ乱数列が生成される
    と困る場合に重宝する
    ○ ThreadLocalRandom は異なるスレッドでも同じ乱数列を生成しうる
    ○ ForkJoinTask の fork() にあわせて split するなどの使い方がある
    ● Random クラスを継承していないことに注意が必要

    View Slide

  30. // java.util.Random と同様に
    // コンストラクタでインスタンスを生成する
    SplittableRandom spr = new SplittableRandom();
    System.out.println(spr.nextInt());
    // split された乱数生成器は、異なる乱数列を生成する
    SplittableRandom another = spr.split();
    System.out.println(another.nextInt());
    SplittableRandom クラスの利用例

    View Slide

  31. Math.random() メソッド
    ● [0, 1) の区間の double 型の乱数を生成する
    ● 乱数生成器のインスタンスを明示的に生成することなく気軽に使
    える
    ● Math クラス内部で一つの Random オブジェクトを保有している
    ○ 複数スレッドからこのメソッドを並列して呼び出すと、速度パフォーマンス
    的に悲惨なことになる
    ● これもレガシーである
    ○ ThreadLocalRandom.current().nextDouble() で代替できる

    View Slide

  32. Java クラスライブラリ
    **以外** の手段

    View Slide

  33. Apache Commons RNG
    ● commons.apache.org/proper/commons-rng/
    ● Apache Commons Math に実装されていた乱数生成関連の
    機能が切り出され、また速度性能面で改良が施されている
    ● 豊富な擬似乱数生成器の実装と、多種多様な確率分布から
    のサンプリングを提供する
    ○ シミュレーション目的で乱数生成したい場合に適している
    ● 提供される API は java.util.Random クラスを継承していないことに注

    View Slide

  34. Amazon Corretto Crypto Provider (ACCP)
    ● Amazon による Java Cryptography Architecture (JCA) の実装
    ○ aws.amazon.com/jp/blogs/opensource/introducing-amazon-corretto-cry
    pto-provider-accp/ 参照
    ● 暗号化などの処理における速度パフォーマンスの改善が期待でき

    ● SecureRandom のアルゴリズム実装として、DRBG
    (AES-CTR-256) が提供されている
    ○ x86-64 の RDRAND 命令が利用できる必要がある
    ● 利用できるプラットフォームは Linux のみ?

    View Slide

  35. 乱数生成器の実装を
    使い分ける

    View Slide

  36. 利用目的に適した乱数生成器を選ぶ
    ● セキュリティに関わる場面で利用する?
    ○ 暗号論的擬似乱数生成器を選ぶべき
    ● シミュレーション目的で利用する?
    ○ 周期が長い擬似乱数生成器 (メルセンヌツイスタ など) が適している
    ● Web アプリケーションなど低レイテンシが求められる場面で利
    用する?
    ○ 乱数生成が高速かつメモリフットプリントの小さい擬似乱数生成器
    (SplitMix など) が適している

    View Slide

  37. 乱数生成器の **実装** を選ぶ
    ● セキュリティ用途
    ○ SecureRandom クラス
    ○ アルゴリズム選定はプラットフォームに依存する
    ○ ブロッキングの有無に注意する
    ● シミュレーション
    ○ Apache Commons RNG の メルセンヌツイスタ実装など
    ● Web アプリケーション
    ○ ThreadLocalRandom クラス
    ○ 再現性が求められるなら SplittableRandom なども

    View Slide

  38. まとめ

    View Slide

  39. まとめ
    ● 擬似乱数生成器の特性を理解する
    ○ 生成速度、メモリフットプリント、周期、統計的品質
    ○ セキュリティ用途で利用できるか
    ● 乱数の利用用途に従って、適切な乱数生成器とその実
    装を選ぶ
    ● Java にはレガシーな乱数生成関連の実装があること
    に留意する

    View Slide

  40. Thanks!

    View Slide