@kokuin/crypto 0.0.0-stage → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Paul Le Cam
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,53 @@
1
- # Temporary Holding Version
1
+ # @kokuin/crypto
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Strict asynchronous crypto operations implemented with noble.
4
+ This package imports no React Native modules and requires no Rust toolchain.
5
+
6
+ ```ts
7
+ import { nobleCrypto } from '@kokuin/crypto'
8
+ import type { CryptoProvider } from '@kokuin/crypto'
9
+
10
+ export async function signMessage(seed: Uint8Array, message: Uint8Array) {
11
+ return nobleCrypto.sign({ alg: 'EdDSA', privateKey: seed, message })
12
+ }
13
+ ```
14
+
15
+ `CryptoProvider` supplies `sign`, `signBatch`, `verify`, `verifyBatch`, `blake3` and `blake3Batch`.
16
+ All methods return Promises. Noble computation still runs synchronously on the calling thread.
17
+ Inputs are consumed before the Promise returns, preserving caller mutations after invocation.
18
+
19
+ Ed25519 signing accepts 32-byte seeds. Verification rejects noncanonical encodings and small-order or mixed-order points.
20
+ ES256 verification accepts compact 64-byte signatures and compressed or uncompressed SEC1 keys.
21
+ It hashes raw messages exactly once with SHA-256 and rejects high-S signatures.
22
+ Malformed signatures or keys resolve `false`. Unsupported algorithms reject with `TypeError`.
23
+ Detached buffers and shared buffers reject with `TypeError`.
24
+
25
+ blake3 output lengths must be integers from 1 through 65536. Invalid lengths reject with `RangeError`.
26
+ Empty batches resolve empty arrays. Batch results preserve input order and own independent output storage.
27
+
28
+ Identity, token, controller, records and vault APIs accept this provider through explicit options.
29
+ Applications can use its standalone methods or pass the structural contract to their own code.
30
+ Native route qualification still requires stored measurements and final physical-device retesting.
31
+
32
+ ## Provider adoption
33
+
34
+ Identity, controller, record and vault APIs now accept this provider contract.
35
+ The six methods remain asynchronous. Verification now takes a named object. Strict noble caches public-key validation in a bounded 256-entry cache.
36
+ Every signature point still receives strict validation. Applications must keep provider behaviour immutable throughout its lifetime.
37
+
38
+ See [provider adoption](../../docs/reference/crypto-provider.md) for selection, migration and verification policy.
39
+
40
+ ## Named params migration
41
+
42
+ `CryptoProvider.verify` now accepts one `VerifyItem`: `{ alg, signature, message, publicKey }`.
43
+ `sign`, `signBatch`, `blake3` and `blake3Batch` take flat params objects.
44
+ `verifyBatch` retains its single array argument.
45
+
46
+ These signature and typing changes preserve cryptographic results, error causes, ordering and storage behaviour.
47
+
48
+ | API | Named params type | Previous positional form |
49
+ | --- | --- | --- |
50
+ | `CryptoProvider.sign` | `CryptoProviderSignParams` | `sign(alg, privateKey, message)` |
51
+ | `CryptoProvider.signBatch` | `CryptoProviderSignBatchParams` | `signBatch(alg, items)` |
52
+ | `CryptoProvider.blake3` | `CryptoProviderBlake3Params` | `blake3(input, dkLen)` |
53
+ | `CryptoProvider.blake3Batch` | `CryptoProviderBlake3BatchParams` | `blake3Batch(inputs, dkLen)` |
package/lib/bytes.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ /** Accept byte views across realms while excluding shared and detached buffers. */
2
+ export declare function assertUnsharedBytes(value: Uint8Array): asserts value is Uint8Array<ArrayBuffer>;
package/lib/bytes.js ADDED
@@ -0,0 +1,7 @@
1
+ /** Accept byte views across realms while excluding shared and detached buffers. */ export function assertUnsharedBytes(value) {
2
+ if (!ArrayBuffer.isView(value) || Object.prototype.toString.call(value) !== '[object Uint8Array]' || Object.prototype.toString.call(value.buffer) !== '[object ArrayBuffer]') {
3
+ throw new TypeError('Expected an unshared Uint8Array');
4
+ }
5
+ // Throws TypeError on a detached buffer.
6
+ new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
7
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { assertUnsharedBytes } from './bytes.js';
2
+ export { nobleCrypto } from './noble.js';
3
+ export type { CryptoAlgorithm, CryptoProvider, CryptoProviderBlake3BatchParams, CryptoProviderBlake3Params, CryptoProviderSignBatchParams, CryptoProviderSignParams, SignItem, VerifyItem, } from './types.js';
package/lib/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { assertUnsharedBytes } from './bytes.js';
2
+ export { nobleCrypto } from './noble.js';
package/lib/noble.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import type { CryptoProvider } from './types.js';
2
+ export declare const nobleCrypto: CryptoProvider;
package/lib/noble.js ADDED
@@ -0,0 +1,104 @@
1
+ import { ed25519 } from '@noble/curves/ed25519.js';
2
+ import { p256 } from '@noble/curves/nist.js';
3
+ import { blake3 } from '@noble/hashes/blake3.js';
4
+ import { bytesToHex } from '@noble/hashes/utils.js';
5
+ import { assertUnsharedBytes } from './bytes.js';
6
+ function sign(alg, seed, message) {
7
+ if (alg !== 'EdDSA') throw new TypeError('Unsupported signing algorithm');
8
+ assertUnsharedBytes(seed);
9
+ assertUnsharedBytes(message);
10
+ if (seed.length !== 32) throw new TypeError('Expected a 32-byte Ed25519 seed');
11
+ return ed25519.sign(message, seed);
12
+ }
13
+ function strictPoint(bytes) {
14
+ const point = ed25519.Point.fromBytes(bytes, false);
15
+ return !point.isSmallOrder() && point.isTorsionFree();
16
+ }
17
+ const PUBLIC_KEY_CACHE_LIMIT = 256;
18
+ const publicKeyValidation = new Map();
19
+ function strictPublicKey(bytes) {
20
+ const key = bytesToHex(bytes);
21
+ const cached = publicKeyValidation.get(key);
22
+ if (cached !== undefined) return cached;
23
+ let valid = false;
24
+ try {
25
+ valid = strictPoint(bytes);
26
+ } catch {
27
+ // Invalid encodings are stable refusals too.
28
+ }
29
+ if (publicKeyValidation.size === PUBLIC_KEY_CACHE_LIMIT) {
30
+ const oldest = publicKeyValidation.keys().next().value;
31
+ if (oldest !== undefined) publicKeyValidation.delete(oldest);
32
+ }
33
+ publicKeyValidation.set(key, valid);
34
+ return valid;
35
+ }
36
+ function verify({ alg, signature, message, publicKey }) {
37
+ if (alg !== 'EdDSA' && alg !== 'ES256') throw new TypeError('Unsupported verification algorithm');
38
+ for (const bytes of [
39
+ signature,
40
+ message,
41
+ publicKey
42
+ ])assertUnsharedBytes(bytes);
43
+ if (signature.length !== 64) return false;
44
+ try {
45
+ if (alg === 'EdDSA') {
46
+ return publicKey.length === 32 && strictPublicKey(publicKey) && strictPoint(signature.subarray(0, 32)) && ed25519.verify(signature, message, publicKey, {
47
+ zip215: false
48
+ });
49
+ }
50
+ if (!(publicKey.length === 33 && (publicKey[0] === 2 || publicKey[0] === 3) || publicKey.length === 65 && publicKey[0] === 4)) return false;
51
+ return p256.verify(signature, message, publicKey, {
52
+ lowS: true,
53
+ prehash: true,
54
+ format: 'compact'
55
+ });
56
+ } catch {
57
+ return false;
58
+ }
59
+ }
60
+ function assertOutputLength(dkLen) {
61
+ if (!Number.isInteger(dkLen) || dkLen < 1 || dkLen > 65536) throw new RangeError('Invalid blake3 output length');
62
+ }
63
+ function hash(input, dkLen) {
64
+ assertUnsharedBytes(input);
65
+ assertOutputLength(dkLen);
66
+ return blake3(input, {
67
+ dkLen
68
+ });
69
+ }
70
+ // Inputs are consumed synchronously before the Promise is returned.
71
+ export const nobleCrypto = {
72
+ async sign ({ alg, privateKey: seed, message }) {
73
+ return sign(alg, seed, message);
74
+ },
75
+ async signBatch ({ alg, items }) {
76
+ if (alg !== 'EdDSA') throw new TypeError('Unsupported signing algorithm');
77
+ return items.map((item)=>sign(alg, item.privateKey, item.message));
78
+ },
79
+ async verify ({ alg, signature, message, publicKey }) {
80
+ return verify({
81
+ alg,
82
+ signature,
83
+ message,
84
+ publicKey
85
+ });
86
+ },
87
+ async verifyBatch (items) {
88
+ return items.map((item)=>{
89
+ return verify({
90
+ alg: item.alg,
91
+ signature: item.signature,
92
+ message: item.message,
93
+ publicKey: item.publicKey
94
+ });
95
+ });
96
+ },
97
+ async blake3 ({ input, dkLen }) {
98
+ return hash(input, dkLen);
99
+ },
100
+ async blake3Batch ({ inputs, dkLen }) {
101
+ assertOutputLength(dkLen);
102
+ return inputs.map((input)=>hash(input, dkLen));
103
+ }
104
+ };
package/lib/types.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ export type CryptoAlgorithm = 'EdDSA' | 'ES256';
2
+ export type SignItem = {
3
+ privateKey: Uint8Array;
4
+ message: Uint8Array;
5
+ };
6
+ export type VerifyItem = {
7
+ alg: CryptoAlgorithm;
8
+ signature: Uint8Array;
9
+ message: Uint8Array;
10
+ publicKey: Uint8Array;
11
+ };
12
+ export type CryptoProviderSignParams = {
13
+ alg: 'EdDSA';
14
+ privateKey: Uint8Array;
15
+ message: Uint8Array;
16
+ };
17
+ export type CryptoProviderSignBatchParams = {
18
+ alg: 'EdDSA';
19
+ items: Array<SignItem>;
20
+ };
21
+ export type CryptoProviderBlake3Params = {
22
+ input: Uint8Array;
23
+ dkLen: number;
24
+ };
25
+ export type CryptoProviderBlake3BatchParams = {
26
+ inputs: Array<Uint8Array>;
27
+ dkLen: number;
28
+ };
29
+ export type CryptoProvider = {
30
+ sign(params: CryptoProviderSignParams): Promise<Uint8Array>;
31
+ signBatch(params: CryptoProviderSignBatchParams): Promise<Array<Uint8Array>>;
32
+ verify(params: VerifyItem): Promise<boolean>;
33
+ verifyBatch(items: Array<VerifyItem>): Promise<Array<boolean>>;
34
+ blake3(params: CryptoProviderBlake3Params): Promise<Uint8Array>;
35
+ blake3Batch(params: CryptoProviderBlake3BatchParams): Promise<Array<Uint8Array>>;
36
+ };
package/lib/types.js ADDED
@@ -0,0 +1 @@
1
+ export { };
package/package.json CHANGED
@@ -1,6 +1,38 @@
1
1
  {
2
2
  "name": "@kokuin/crypto",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.7.1",
4
+ "description": "Strict asynchronous crypto providers",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/TairuFramework/kokuin",
8
+ "directory": "packages/crypto"
9
+ },
10
+ "license": "MIT",
11
+ "sideEffects": false,
12
+ "type": "module",
13
+ "exports": {
14
+ ".": "./lib/index.js"
15
+ },
16
+ "main": "lib/index.js",
17
+ "types": "lib/index.d.ts",
18
+ "files": [
19
+ "lib/*",
20
+ "LICENSE"
21
+ ],
22
+ "dependencies": {
23
+ "@noble/curves": "^2.4.0",
24
+ "@noble/hashes": "^2.4.0"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "scripts": {
30
+ "build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
31
+ "build:clean": "del-cli lib",
32
+ "build:js": "del-cli 'lib/**/*.js' && swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
33
+ "build:types": "del-cli 'lib/**/*.d.ts' 'lib/**/*.d.ts.map' && tsc --emitDeclarationOnly --skipLibCheck",
34
+ "test": "pnpm run test:types && pnpm run test:unit",
35
+ "test:types": "tsc --noEmit --skipLibCheck -p tsconfig.test.json",
36
+ "test:unit": "vitest run"
37
+ }
6
38
  }