refactor(core,keys): BIP-39's English list moves into halkyon_core

The list a recovery phrase's words are of moves from halkyon_keys into
halkyon_core, exported as bip39English, so that the server module can
take a waiting account's matching code from it (Halkyon Learn's ADR
0021, D33) without depending on halkyon_keys and libsodium. Its test
(2048 words, sorted, the published file's SHA-256, unique by their first
four letters) moves along; its MIT licence too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-25 15:04:09 +01:00
commit b2aa7667c4
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
10 changed files with 41 additions and 20 deletions

View file

@ -12,8 +12,8 @@ The repository's packages carry different licences, each package's full text in
| `packages/halkyon_base_server`, the Serverpod module every Halkyon server includes | Server Side Public License, version 1 | `SSPL-1.0` |
| `packages/halkyon_base_client`, generated from the module's models, with SRP's client half | GNU Affero General Public License, version 3 or any later version | `AGPL-3.0-or-later` |
| `packages/halkyon_app`, `packages/halkyon_ui` | GNU Affero General Public License, version 3 or any later version | `AGPL-3.0-or-later` |
| `packages/halkyon_keys`, an account's keys on the device; BIP-39's English word list in it under the MIT License (`BIP39-LICENSE`) | GNU Affero General Public License, version 3 or any later version | `AGPL-3.0-or-later` |
| `packages/halkyon_core`, `packages/halkyon_interop` | GNU Affero General Public License, version 3 or any later version, as halkyon_shared, where they began, licensed them | `AGPL-3.0-or-later` |
| `packages/halkyon_keys`, an account's keys on the device | GNU Affero General Public License, version 3 or any later version | `AGPL-3.0-or-later` |
| `packages/halkyon_core`, `packages/halkyon_interop`; BIP-39's English word list in halkyon_core under the MIT License (`BIP39-LICENSE`) | GNU Affero General Public License, version 3 or any later version, as halkyon_shared, where they began, licensed them | `AGPL-3.0-or-later` |
| `apps/todo/halkyon_todo_flutter`, Todo's app | GNU Affero General Public License, version 3 or any later version | `AGPL-3.0-or-later` |
| `apps/todo/halkyon_todo_client`, generated from Todo's models | GNU Affero General Public License, version 3 or any later version | `AGPL-3.0-or-later` |
| `apps/todo/halkyon_todo_server` | Server Side Public License, version 1 (decided 2026-10-19, as Learn's) | `SSPL-1.0` |

View file

@ -0,0 +1,26 @@
The English word list in lib/src/bip39_english.dart is BIP-39's
(https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt), by Marek
Palatinus, Pavol Rusnak, Aaron Voisine and Sean Bowe, 2013. BIP-39 falls under
the MIT License:
MIT License
Copyright (c) 2013 Marek Palatinus, Pavol Rusnak, Aaron Voisine, Sean Bowe
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -6,6 +6,8 @@
/// - A `Logger` interface and basic `ConsoleLogger` implementation.
/// - The optional `HalkyonException` marker interface.
/// - `SerialQueue` and `CoalescingRunner`, which run work one at a time.
/// - BIP-39's English word list (`bip39English`), which a recovery phrase's
/// words and an account's matching code are of.
library;
// Re-export the clock package directly. Halkyon apps depend on `Clock` for
@ -15,6 +17,7 @@ export 'package:clock/clock.dart' show Clock, clock;
export 'src/async/coalescing_runner.dart';
export 'src/async/serial_queue.dart';
export 'src/bip39_english.dart';
export 'src/exceptions.dart';
export 'src/logger.dart';
export 'src/uuid.dart';

File diff suppressed because it is too large Load diff

View file

@ -12,4 +12,6 @@ dependencies:
uuid: ^4.5.0
dev_dependencies:
# SHA-256, which the BIP-39 list is checked against.
crypto: ^3.0.7
test: ^1.25.0

View file

@ -0,0 +1,26 @@
import 'package:crypto/crypto.dart';
import 'package:halkyon_core/halkyon_core.dart';
import 'package:test/test.dart';
void main() {
test("the word list is BIP-39's English list: 2,048 words, sorted, each "
'once', () {
expect(bip39English, hasLength(2048));
expect(bip39English.toSet(), hasLength(2048));
expect([...bip39English]..sort(), bip39English);
// The SHA-256 of english.txt, a word a line.
expect(
sha256.convert('${bip39English.join('\n')}\n'.codeUnits).toString(),
'2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda',
);
});
test('no two words begin with the same four letters', () {
expect(
bip39English
.map((word) => word.substring(0, word.length.clamp(0, 4)))
.toSet(),
hasLength(2048),
);
});
}

View file

@ -57,10 +57,11 @@ phrase.
`dart test`: libsodium's own Argon2id known answers (its `pwhash_argon2id` test, so the
compiled libsodium is the one these keys were made with), BIP-39's English vectors
(trezor/python-mnemonic's `vectors.json`, MIT), the word list's SHA-256, round trips,
and every refusal.
(trezor/python-mnemonic's `vectors.json`, MIT), round trips, and every refusal; the
word list's SHA-256 is checked in halkyon_core.
## Licence
GNU Affero General Public License, version 3 or any later version (`LICENSE`). The
English word list is BIP-39's, under the MIT License (`BIP39-LICENSE`).
English word list is BIP-39's, in halkyon_core, under the MIT License (its
`BIP39-LICENSE`).

View file

@ -1,7 +1,7 @@
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:halkyon_keys/src/bip39_english.dart';
import 'package:halkyon_core/halkyon_core.dart' show bip39English;
import 'package:halkyon_keys/src/key_refused.dart';
import 'package:sodium/sodium_sumo.dart';

View file

@ -14,6 +14,9 @@ environment:
dependencies:
# SHA-256, which BIP-39's checksum is made with.
crypto: ^3.0.7
# BIP-39's English list, which the recovery phrase's words are of.
halkyon_core:
path: ../halkyon_core
# libsodium's API (Argon2id, X25519, Ed25519, XChaCha20-Poly1305, sealed
# boxes, the KDF), pinned exactly: what keys an account's data must never
# move by accident. Its build hook compiles libsodium 1.0.22 from the source

View file

@ -1,8 +1,6 @@
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:halkyon_keys/halkyon_keys.dart';
import 'package:halkyon_keys/src/bip39_english.dart';
import 'package:sodium/sodium_sumo.dart';
import 'package:test/test.dart';
@ -21,18 +19,6 @@ Future<void> main() async {
isA<KeyRefused>().having((refused) => refused.reason, 'reason', reason),
);
test("the word list is BIP-39's English list: 2,048 words, sorted, each "
'once', () {
expect(bip39English, hasLength(2048));
expect(bip39English.toSet(), hasLength(2048));
expect([...bip39English]..sort(), bip39English);
// The SHA-256 of english.txt, a word a line.
expect(
sha256.convert('${bip39English.join('\n')}\n'.codeUnits).toString(),
'2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda',
);
});
group("BIP-39's English test vectors (trezor/python-mnemonic)", () {
for (final (entropy, mnemonic) in bip39Vectors) {
test(mnemonic.split(' ').take(3).join(' '), () {