環境
- node
- v4.4.4
- v6.0.0
概要
同じ日本語を crypto
でハッシュ化する時にencodingを指定しないと、 v4.4.4
と v6.0.0
で結果に違いが出るから、有無を言わず指定しておかないと後で怖い思いするかも
正しいHash値
echo -n "ほげえ" | sha1sum
05ac5a4890e77393c279714ea453a00bec0cd994 -
何も考えず node で実行すると...
$ node -e "console.log(require('crypto').createHash('sha1').update('ほげえ').digest('hex'))"
12009e955051b6f293e873ef48ce3786af673cfb
05ac5a4890e77393c279714ea453a00bec0cd994
!= 12009e955051b6f293e873ef48ce3786af673cfb
なぜ違うのだ...
解決方法
update
関数の引数でencodingを明示的に指定する
$ node -e "console.log(require('crypto').createHash('sha1').update('ほげえ', 'utf8').digest('hex'))"
05ac5a4890e77393c279714ea453a00bec0cd994
原因
v4.4.4 において update
関数の実装は↓
$ node -e "console.log(require('crypto').createHash('sha1').update.toString())"
function (data, encoding) {
encoding = encoding || exports.DEFAULT_ENCODING;
if (encoding === 'buffer' && typeof data === 'string')
encoding = 'binary';
this._binding.update(data, encoding);
return this;
}
encodingが未指定だと、DEFAULT_ENCODING が使われている。
DEFAULT_ENCODINGには buffer
が入っている。
$ node -e "console.log(require('crypto').DEFAULT_ENCODING.toString())"
buffer
なので結局 ↓ を通り
if (encoding === 'buffer' && typeof data === 'string')
encoding = 'binary';
binary
エンコードとして渡ってしまう。
binaryを指定するとどうなるのか公式ドキュメント1にあたってみると、
'binary' - A way of encoding the buffer into a one-byte (latin-1) encoded string. The string 'latin-1' is not supported. Instead, pass 'binary' to use 'latin-1' encoding.
latin-1
エンコードに変換されるという。
つまりは、↓のようにバイトコードが変わってしまう。
$ node -e "console.log(new Buffer('ほげえ', 'utf8'))"
<Buffer e3 81 bb e3 81 92 e3 81 88>
$ node -e "console.log(new Buffer('ほげえ', 'binary'))"
<Buffer 7b 52 48>
なので、全く別の文字列をハッシュ化したことになる。
v6.0.0 の場合
ちなみに node.js v6.0.0 だと utf8
を明示的にしていしなくても良いようになっている。
$ node -e "console.log(require('crypto').createHash('sha1').update('ほげえ').digest('hex'))"
05ac5a4890e77393c279714ea453a00bec0cd994
v6.0.0 では実装が変わっていて、 encodingが未定義だと exports.DEFAULT_ENCODING
がそのまま渡されるようになっている。
$ node -e "console.log(require('crypto').createHash('sha1').update.toString())"
function (data, encoding) {
encoding = encoding || exports.DEFAULT_ENCODING;
this._handle.update(data, encoding);
return this;
}
DEFAULT_ENCODING
は buffer
のままで変わらず。
$ node -e "console.log(require('crypto').DEFAULT_ENCODING)"
buffer
new Buffer('ほげえ', 'buffer')
はエラーになるので、 デフォルトのencodingを使う、という意味で、new Buffer('ほげえ')
が渡されていると思う(想像)
デフォルトのencodingはutf8なので、v6.0.0では何も考えず日本語をハッシュ化できる。