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 クラスライブラリにおける各種乱数生成器の実装とその特性などについてお話いたします。

E77287648aff5484ac7659748e45c936?s=128

KOMIYA Atsushi

November 23, 2019
Tweet

Transcript

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

    Atsushi
  2. @komiya_atsushi

  3. 本日のお話

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

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

  6. 乱数生成器の種類

  7. 乱数生成器の種類: 真の乱数生成器 • 予測不可能な乱数列を生成する乱数生成器 • 専用のハードウェアが用いられたりする ◦ 身近な例で言えば なども真の乱数生成器に該当する •

    Linux の /dev/random は真の乱数生成器と呼べそう? ◦ macOS は擬似乱数生成器 (Yarrow アルゴリズム) による実装らし い
  8. 乱数生成器の種類: 擬似乱数生成器 • 決定論的乱数生成器 (DRBG) とも呼ばれる • アルゴリズムによって乱数列が生成される ◦ アルゴリズムと内部状態が既知なら、生成される乱数列を予測できる

    ◦ 内部状態を特定の値に変更 (シードを再設定) することで乱数列を再現できる • 周期が存在しうる ◦ 周期が一巡すると、過去に生成したのと全く同じ乱数列が生成される • 生成アルゴリズムの一例 ◦ 線形合同法、メルセンヌツイスタ、Xorshift, SplitMix, PCG, ...
  9. 乱数生成器の種類: 暗号論的擬似乱数生成器 • 擬似乱数生成器の一種 • セキュリティに関わる場面での利用に適している ◦ 例: パスワードをハッシュ化する際の salt

    の生成 • 要求仕様がある ◦ (1) next-bit test に合格する ◦ (2) state compromise extension に耐える ◦ 詳細は ja.wikipedia.org/wiki/暗号論的擬似乱数生成器 を参照
  10. 乱数生成器の性能評価

  11. 性能評価のポイント • 乱数の生成速度 • 内部状態のメモリフットプリント • 乱数列の周期の長さ • 生成される乱数列の統計的品質 ◦

    生成される乱数に統計的な偏りがなく、一様に分布している ことが望ましい ◦ ランダムであることを評価するための統計的仮説検定が存在 する
  12. 乱数生成器の統計的な欠陥が引き起こした事例 ja.wikipedia.org/wiki/線形合同法 > 短所 より引用 下位ビットのランダム性が低い。 Mの値によっては、最下位に近いビットはほとんどランダムでなく、完全に 規則性を持っていることすらある。たとえば Mが偶数の場合(コンピュータでの実装が楽であるために、 Mに

    2の冪を採用した場合はこれに当たる)、 最下位ビットは、同じものが出続けるか、 0と1が交互にでるかの どちらかである。 ja.wikipedia.org/wiki/カルドセプト_サーガ > バグ問題 より引用 とくに深刻と言われるバグは、 ダイスの目が偶数・奇数を繰り返す というものであった。疑似乱数の生成な いし利用法に問題があったと推測されている(たとえばある種の線形合同法を不適切に用いると、下位 1 ビットは0と1を繰り返す)。
  13. 統計的な品質評価 • 代表的なテストスイート ◦ Diehard (1995) ◦ TestU01 (2007) ▪

    Small Crush / Crush / Big Crush の三種がある ◦ PractRand (2010?) • いずれのテストスイートも数多くの統計的仮説検定の組み合わせで構 成されている • 最近では、TestU01 の Big Crush がデファクトスタンダードとして利用 されている印象がある ◦ Big Crush の fail が 0 個であることを謳う乱数生成器の実装を見かける
  14. Java における 乱数生成器

  15. Java クラスライブラリが提供する乱数生成器 • 乱数生成器のクラス ◦ java.util.Random ◦ java.security.SecureRandom ◦ java.util.concurrent.ThreadLocalRandom

    ◦ java.util.SplittableRandom ◦ これらクラスの継承関係が少し (次頁参照) • 乱数生成メソッド ◦ Math.random()
  16. None
  17. java.util.Random クラス • 線形合同法を利用した擬似乱数生成器 • 一言でいうなれば「レガシー」 ◦ 統計的品質がよろしくない ▪ TestU01

    の Big Crush で 50 個近く fail する ▪ commons.apache.org/proper/commons-rng/userguide/rng.html#a 5._Quality を参照 ◦ 乱数生成の速度がよろしくない ▪ 内部状態の更新に AtomicLong の操作が必要になるため ▪ 単一スレッドで利用していても遅い ▪ 同一インスタンスを複数スレッドで扱うと悲惨なことになる
  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) メソッドの実装 線形合同法そのものの 処理はとてもシンプル 下位ビットの利用を避 けている
  19. // シードを指定してインスタンスを生成する Random jur = new Random(123); System.out.println(jur.nextInt()); // =>

    -1188957731 // シードを (再) 設定する jur.setSeed(456); System.out.println(jur.nextInt()); // => -1035442920 Random クラスの利用例
  20. SecureRandom クラス • 暗号論的擬似乱数生成器の実装 • 具体的な乱数生成アルゴリズムは複数存在する (後述) ◦ SecureRandom.getInstance(String) でアルゴリズム名を引数に指

    定し、インスタンスを得る • Web アプリケーションのセッション ID やパスワードの salt など、 推測されたくないランダム文字列の生成などで利用するのに適し ている ◦ 実際に、Tomcat のセッション ID (jsessionid) の生成において SecureRandom が使われている
  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 • プラットフォームおよびアルゴリズムの組み合わせ次第で、 乱数生成時にブロッキングが生じることに注意する必要がある
  22. SecureRandom で利用できるアルゴリズム • NativePRNG / NativePRNGBlocking / NativePRNGNonBlocking ◦ /dev/random

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

    NativePRNGNonBlocking /dev/urandom /dev/urandom
  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");
  25. SecureRandom で利用できるアルゴリズム • SHA1PRNG ◦ レガシーだが、すべてのプラットフォームで利用できる ◦ (参考) Android では

    deprecated になっている ▪ android-developers.googleblog.com/2016/06/securit y-crypto-provider-deprecated-in.html 参照 • その他 (詳しく知らないので割愛) ◦ PKCS11 ◦ Windows-PRNG
  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 クラスの利用例
  27. ThreadLocalRandom クラス • マルチスレッドでの利用に適した擬似乱数生成器の実装 ◦ スレッドごとに固有の擬似乱数生成器 (厳密には内部状態) が存在する ◦ 利用する際に明示的なインスタンス生成が不要である

    • SplittableRandom クラスと同じ SplitMix アルゴリズムを利用し ている (Java 8 以降) ◦ Java 7 では、Random クラスと同じ線形合同法を採用していた • Random#setSeed() を呼び出すことができないため、乱数生成の 再現性が求められるケースでの利用には適さない ◦ UnsupportedOperationException が発生する
  28. // インスタンスの生成は不要 System.out.println(ThreadLocalRandom.current().nextInt()); // ThreadLocalRandom.current() で取得したオブジェクトを // 他のスレッドで利用するのはご法度 // シードを設定しようとすると例外が発生する

    try { ThreadLocalRandom.current().setSeed(123); } catch (UnsupportedOperationException e) { e.printStackTrace(); } ThreadLocalRandom クラスの利用例
  29. SplittableRandom クラス • Fork/Join フレームワークを含むマルチスレッドでの利用に適した 擬似乱数生成器 • SplittableRandom#split() により、現在の乱数生成器と完全 に異なる乱数列を生成するインスタンスを生成できる

    ◦ 複数スレッドで並列して乱数生成する際に、まったく同じ乱数列が生成される と困る場合に重宝する ◦ ThreadLocalRandom は異なるスレッドでも同じ乱数列を生成しうる ◦ ForkJoinTask の fork() にあわせて split するなどの使い方がある • Random クラスを継承していないことに注意が必要
  30. // java.util.Random と同様に // コンストラクタでインスタンスを生成する SplittableRandom spr = new SplittableRandom();

    System.out.println(spr.nextInt()); // split された乱数生成器は、異なる乱数列を生成する SplittableRandom another = spr.split(); System.out.println(another.nextInt()); SplittableRandom クラスの利用例
  31. Math.random() メソッド • [0, 1) の区間の double 型の乱数を生成する • 乱数生成器のインスタンスを明示的に生成することなく気軽に使

    える • Math クラス内部で一つの Random オブジェクトを保有している ◦ 複数スレッドからこのメソッドを並列して呼び出すと、速度パフォーマンス 的に悲惨なことになる • これもレガシーである ◦ ThreadLocalRandom.current().nextDouble() で代替できる
  32. Java クラスライブラリ **以外** の手段

  33. Apache Commons RNG • commons.apache.org/proper/commons-rng/ • Apache Commons Math に実装されていた乱数生成関連の

    機能が切り出され、また速度性能面で改良が施されている • 豊富な擬似乱数生成器の実装と、多種多様な確率分布から のサンプリングを提供する ◦ シミュレーション目的で乱数生成したい場合に適している • 提供される API は java.util.Random クラスを継承していないことに注 意
  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 のみ?
  35. 乱数生成器の実装を 使い分ける

  36. 利用目的に適した乱数生成器を選ぶ • セキュリティに関わる場面で利用する? ◦ 暗号論的擬似乱数生成器を選ぶべき • シミュレーション目的で利用する? ◦ 周期が長い擬似乱数生成器 (メルセンヌツイスタ

    など) が適している • Web アプリケーションなど低レイテンシが求められる場面で利 用する? ◦ 乱数生成が高速かつメモリフットプリントの小さい擬似乱数生成器 (SplitMix など) が適している
  37. 乱数生成器の **実装** を選ぶ • セキュリティ用途 ◦ SecureRandom クラス ◦ アルゴリズム選定はプラットフォームに依存する

    ◦ ブロッキングの有無に注意する • シミュレーション ◦ Apache Commons RNG の メルセンヌツイスタ実装など • Web アプリケーション ◦ ThreadLocalRandom クラス ◦ 再現性が求められるなら SplittableRandom なども
  38. まとめ

  39. まとめ • 擬似乱数生成器の特性を理解する ◦ 生成速度、メモリフットプリント、周期、統計的品質 ◦ セキュリティ用途で利用できるか • 乱数の利用用途に従って、適切な乱数生成器とその実 装を選ぶ

    • Java にはレガシーな乱数生成関連の実装があること に留意する
  40. Thanks!