UUID は分散システムで一意な Identifier として使えること(= 衝突しない前提で使える)を目的にした ID で、その version 4 はランダムに作るやつ。
この前ふと、uuid v4 をパスワードのような使い方をしていいのか?が気になって調べてみた。
Secure - Cryptographically-strong random values
Node.js を良く触るが uuid モジュールでは、暗号学的に安全なランダムを使ってるよう。
ちなみに、version 7未満では Math.random() が使われることがあり、暗号的に安全じゃないかもしれないらしい(Math.random() はここで少し触れてる 疑似乱数は本当に予測できるのか試したい - Memo:)
uuid モジュールは内部的には、crypto.randomUUID()
を呼んでいる。crypto は標準的な暗号機能のための API で、node.js の実装は、OpenSSL のラッパーみたいなものらしい。
Crypto | Node.js v21.1.0 Documentation
const crypto = require("crypto"); console.log(crypto.randomUUID());
randomFill()
のここで乱数を作ってそう。
const job = new RandomBytesJob( kCryptoJobAsync, buf, offset, size);
node/lib/internal/crypto/random.js at v16.x · nodejs/node · GitHub
更にたどっていくと...
多分ここっぽいです。OpenSSL の RAND_bytes()
を呼んでますね。
bool RandomBytesTraits::DeriveBits( Environment* env, const RandomBytesConfig& params, ByteSource* unused) { CheckEntropy(); // Ensure that OpenSSL's PRNG is properly seeded. return RAND_bytes(params.buffer, params.size) != 0; }
node/src/crypto/crypto_random.cc at v16.x · nodejs/node · GitHub
OpenSSL の wiki を眺めると RAND_bytes には software 的な乱数と、hardware 的に作られる物があるみたい。
どっちを使っているのかは分からないが、エントロピーが溜まるまで待ってるということは、hardware 的な乱数を使ってるような気はする(自信ない)。
どこから呼んでるのかは全く分からなかったが、 SetEngine
から engine を設定することで RAND_bytes がハードウェアを使ってくれるようになるみたいなので、恐らく使えるときはハードウェアを使おうとするとかなんじゃないかと想像してる。 C++ 追うの辛すぎひ。
node/src/crypto/crypto_util.cc at v16.x · nodejs/node · GitHub
とりあえず、node.js の uuid モジュールの v4 はパスワード的な使い方をしても問題なさそう。(※ すべての実装に暗号的強度があるとは言えないので、uuid の実装は必ず調べる必要がある)
なぜ UUID v4 の生成に暗号的乱数を使ってるのか?身長・本名や年齢・誕生日、出身地や大学、年収は?
UUID は用途上、暗号に使うわけではなく分散システムでの ID が目的なので、衝突しにくさがあれば別にセキュアである必要はない気がするけど、どうしてわざわざ暗号的に強い乱数を使って作ってるのだろう....。
調べた結果........
わかりませんでした!!
予想
「暗号論的擬似乱数生成器 の wikipedia 」の要求仕様の項目では、2つの仕様が満たす必要があるとあった。 1を満たすということは、単純に良質な乱数であるともいえる。2を満たすということは乱数に周期性や規則性が無いといってもいいと思う。
- 統計的無作為試験に合格するほど、無作為な乱数になってる
- 初期状態や途中の状態が攻撃者に明らかになっても破られないこと
この2つを満たした乱数は、「予測」し辛いだけではなく「衝突」もしにくいからという気がしてる。また、乱数に周期性があると2つの生成器の周期が重なってしまった場合、ほぼ100% 衝突するようになってしまう。2を満たしていれば周期性が無いはずなのでそのようなことも起きない。
ということは、衝突耐性を持たせるために暗号論的擬似乱数生成器を選んでいる....
ってことじゃないかと半ば無理やり納得しておわります。
ところで、UUID といえば何故か奇妙なハイフンで区切られているが、なぜそんな形式になったんだろう。それが気になって仕方なかったので以下で考えてみた。よかったら見てね。