Skip to content

Commit 1d299cb

Browse files
committed
Merge branch 'master' into v1
2 parents 66dbbf8 + a41026c commit 1d299cb

11 files changed

Lines changed: 123 additions & 30 deletions

.github/workflows/release.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,19 @@ jobs:
5252
- name: Extract release notes from CHANGELOG.md
5353
run: |
5454
awk 'BEGIN{p=0} /^# / {if(p++) exit; next} p' CHANGELOG.md | awk 'NF' > RELEASE_NOTES.md
55-
55+
5656
- name: Append changelog comparison link
5757
run: |
5858
git fetch --tags
5959
CUR_TAG=${GITHUB_REF#refs/tags/v}
6060
PREV_TAG=$(git tag --sort=-v:refname | grep '^v[0-9]' | grep -A1 "^v$CUR_TAG" | tail -n1)
61-
echo -e "\n**Full Changelog**: https://github.com/bitanon/hashlib/compare/${PREV_TAG}...${CUR_TAG}" >> RELEASE_NOTES.md
61+
echo -e "\n**Full Changelog**: https://github.com/bitanon/hashlib/compare/${PREV_TAG}...v${CUR_TAG}" >> RELEASE_NOTES.md
6262
6363
- name: Create GitHub Release
6464
uses: softprops/action-gh-release@v2
6565
with:
6666
tag_name: ${{ github.ref_name }}
67-
name: Version ${{ github.ref_name }}
67+
name: Version ${GITHUB_REF#refs/tags/v}
6868
body_path: RELEASE_NOTES.md
6969
env:
7070
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
.packages
55

66
# Conventional directory for outputs.
7+
dart-*/
78
build/
89
doc/
910
coverage/

.pubignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
/build
66
/scripts
77
/.github
8+
/dart-*

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
# 1.24.0
2+
3+
- generator_vm.dart
4+
- Avoids mixing predictable datetime output with CSPRNG output.
5+
- generator_js.dart
6+
- Avoids mixing predictable datetime output with CSPRNG output.
7+
- Adds `NodeRandom` as an implementation of `Random` for NodeJS.
8+
- `nextInt(max)` draws 32-bit values from NodeJS's internal `crypto` package.
9+
- `secureRandom()` uses `NodeRandom` for secure random numbers.
10+
- `$generateSeed()` delegates to `secureRandom()`.
11+
- generators.dart
12+
- Fixes spelling of `_hashGenerator`
13+
114
# 1.23.0
215

316
- Fixes typo: Alder is renamed to Adler

dart_test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
timeout: 1m
55
concurrency: 8
6-
platforms: [vm, node]
6+
platforms: [vm, node, chrome]
77
allow_duplicate_test_names: false
88

99
tags:

lib/src/random/generator_js.dart

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,38 @@
1-
// Copyright (c) 2024, Sudipto Chandra
1+
// Copyright (c) 2025, Sudipto Chandra
22
// All rights reserved. Check LICENSE file for details.
33

4-
import 'dart:async';
54
import 'dart:math' show Random;
65

6+
import 'generator_js_legacy.dart'
7+
if (dart.library.js_interop) 'generator_js_interop.dart';
8+
79
const int _mask32 = 0xFFFFFFFF;
810

9-
int _seedCounter = Zone.current.hashCode;
11+
/// For Node.js environment + dart2js compiler
12+
class NodeRandom extends CryptoRandom implements Random {
13+
@override
14+
int nextInt(final int max) {
15+
if (max < 1 || max > _mask32 + 1) {
16+
throw RangeError.range(
17+
max, 1, _mask32 + 1, 'max', 'max must be <= (1 << 32)');
18+
}
19+
return cryptoRandomInt(max);
20+
}
1021

11-
/// Returns a secure random generator in JS runtime
12-
Random secureRandom() => Random($generateSeed());
13-
14-
/// Generates a random seed in JS runtime
15-
int $generateSeed() {
16-
int code = DateTime.now().millisecondsSinceEpoch;
17-
code -= _seedCounter++;
18-
if (code.bitLength & 1 == 1) {
19-
code *= ~code;
22+
@override
23+
double nextDouble() {
24+
final int first26Bits = nextInt(1 << 26);
25+
final int next27Bits = nextInt(1 << 27);
26+
final int random53Bits = (first26Bits << 27) + next27Bits; // JS int limit
27+
return random53Bits / (1 << 53);
2028
}
21-
code ^= ~_seedCounter << 5;
22-
_seedCounter += code & 7;
23-
return code & _mask32;
29+
30+
@override
31+
bool nextBool() => nextInt(2) == 1;
2432
}
33+
34+
/// Returns a secure random generator in JS runtime
35+
Random secureRandom() => NodeRandom();
36+
37+
/// Generates a random seed
38+
int $generateSeed() => secureRandom().nextInt(_mask32);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) 2025, Sudipto Chandra
2+
// All rights reserved. Check LICENSE file for details.
3+
4+
import 'dart:js_interop';
5+
6+
@JS()
7+
@staticInterop
8+
class Process {}
9+
10+
@JS()
11+
@staticInterop
12+
class Versions {}
13+
14+
@JS()
15+
@staticInterop
16+
class Crypto {}
17+
18+
extension on Crypto {
19+
external int randomInt(final int max);
20+
}
21+
22+
@JS('require')
23+
external Crypto require(final String id);
24+
25+
/// For Node.js environment + dart2js compiler
26+
abstract class CryptoRandom {
27+
Crypto? _crypto;
28+
29+
int cryptoRandomInt(final int max) {
30+
_crypto ??= require('crypto');
31+
return _crypto!.randomInt(max);
32+
}
33+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) 2025, Sudipto Chandra
2+
// All rights reserved. Check LICENSE file for details.
3+
4+
// Pre `js_interop` compatible version
5+
// Works with dart2js in a Node.js runtime (where `require` exists).
6+
7+
// ignore_for_file: deprecated_member_use, depend_on_referenced_packages
8+
9+
import 'dart:js' as js;
10+
11+
js.JsObject _require(String id) {
12+
final r = js.context['require'];
13+
if (r == null) {
14+
throw StateError("`require` is not available.");
15+
}
16+
return (r as js.JsFunction).apply([id]) as js.JsObject;
17+
}
18+
19+
/// For Node.js environment + dart2js compiler
20+
abstract class CryptoRandom {
21+
js.JsObject? _crypto;
22+
js.JsFunction? _randomInt;
23+
24+
int cryptoRandomInt(final int max) {
25+
_crypto ??= _require('crypto');
26+
_randomInt ??= _crypto!['randomInt'] as js.JsFunction;
27+
return (_randomInt!.apply([max]) as num).toInt();
28+
}
29+
}

lib/src/random/generator_vm.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,4 @@ Random secureRandom() => Random.secure();
1111

1212
/// Generates a random seed
1313
@pragma('vm:prefer-inline')
14-
int $generateSeed() =>
15-
(DateTime.now().microsecondsSinceEpoch & _mask32) ^
16-
Random.secure().nextInt(_mask32);
14+
int $generateSeed() => Random.secure().nextInt(_mask32);

lib/src/random/generators.dart

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ import 'package:hashlib/src/algorithms/sm3.dart';
1111
import 'package:hashlib/src/algorithms/xxh64/xxh64.dart';
1212
import 'package:hashlib/src/core/hash_base.dart';
1313

14-
import 'generator_vm.dart' if (dart.library.js) 'generator_js.dart';
15-
export 'generator_vm.dart' if (dart.library.js) 'generator_js.dart';
14+
import 'generator_vm.dart'
15+
if (dart.library.html) 'generator_vm.dart'
16+
if (dart.library.js) 'generator_js.dart';
17+
export 'generator_vm.dart'
18+
if (dart.library.html) 'generator_vm.dart'
19+
if (dart.library.js) 'generator_js.dart';
1620

1721
const int _mask32 = 0xFFFFFFFF;
1822

@@ -36,13 +40,13 @@ extension RNGBuilder on RNG {
3640
case RNG.keccak:
3741
return _keccakGenerateor(seed);
3842
case RNG.sha256:
39-
return _hashGenerateor(SHA256Hash(), seed);
43+
return _hashGenerator(SHA256Hash(), seed);
4044
case RNG.md5:
41-
return _hashGenerateor(MD4Hash(), seed);
45+
return _hashGenerator(MD4Hash(), seed);
4246
case RNG.xxh64:
43-
return _hashGenerateor(XXHash64Sink(111), seed);
47+
return _hashGenerator(XXHash64Sink(111), seed);
4448
case RNG.sm3:
45-
return _hashGenerateor(SM3Hash(), seed);
49+
return _hashGenerator(SM3Hash(), seed);
4650
case RNG.system:
4751
return _systemGenerator(seed);
4852
case RNG.secure:
@@ -117,7 +121,7 @@ NextIntFunction _keccakGenerateor([int? seed]) {
117121
}
118122

119123
/// Returns a iterable of 32-bit integers generated from the [sink].
120-
NextIntFunction _hashGenerateor(
124+
NextIntFunction _hashGenerator(
121125
HashDigestSink sink, [
122126
int? seed,
123127
]) {

0 commit comments

Comments
 (0)