@steve02081504/fount-p2p 0.0.21 → 0.0.23

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.
@@ -1,7 +1,6 @@
1
- import { Buffer } from 'node:buffer'
2
-
3
1
  import { pubKeyHash } from '../crypto/crypto.mjs'
4
2
 
3
+ import { hexToBytes } from './bytes_codec.mjs'
5
4
  import {
6
5
  encodeEntityHash,
7
6
  ENTITY_HASH_RE,
@@ -26,9 +25,10 @@ export {
26
25
  */
27
26
  export function hashFromPubKeyHex(pubKeyHex) {
28
27
  const hex = normalizeHex64(pubKeyHex)
29
- if (!isHex64(hex) || Buffer.from(hex, 'hex').length !== 32)
30
- throw new Error('invalid pubKeyHex')
31
- return pubKeyHash(Buffer.from(hex, 'hex'))
28
+ if (!isHex64(hex)) throw new Error('invalid pubKeyHex')
29
+ const bytes = hexToBytes(hex)
30
+ if (bytes.length !== 32) throw new Error('invalid pubKeyHex')
31
+ return pubKeyHash(bytes)
32
32
  }
33
33
 
34
34
  /**
@@ -1,6 +1,6 @@
1
1
  import { sha256TextHex } from '../crypto/crypto.mjs'
2
2
 
3
- import { encodeEntityHash, parseEntityHash } from './entity_id.mjs'
3
+ import { encodeEntityHash, parseEntityHash } from './entity_id_parse.mjs'
4
4
 
5
5
  /** @type {string} 逻辑实体 sentinel nodeHash(非物理节点绑定) */
6
6
  export const LOGICAL_ENTITY_SENTINEL_NODE_HASH = '0'.repeat(64)
@@ -1,9 +1,11 @@
1
- import { randomUUID } from 'node:crypto'
1
+ /**
2
+ * 跨端随机 ID(Web Crypto / Node globalThis.crypto)。
3
+ */
2
4
 
3
5
  /**
4
6
  * @param {string} prefix ID 前缀(如 channel_)
5
7
  * @returns {string} 带前缀的随机 ID
6
8
  */
7
9
  export function prefixedRandomId(prefix) {
8
- return `${prefix}${randomUUID().replace(/-/g, '')}`
10
+ return `${prefix}${globalThis.crypto.randomUUID().replace(/-/g, '')}`
9
11
  }
package/crypto/crypto.mjs CHANGED
@@ -1,105 +1,65 @@
1
- import { Buffer } from 'node:buffer'
2
- import {
3
- createHash,
4
- createPrivateKey,
5
- createPublicKey,
6
- randomBytes,
7
- sign as nodeSign,
8
- verify as nodeVerify,
9
- } from 'node:crypto'
10
-
11
- /** 固定 DER 头(非秘密),让 Node 把 32 字节种子当成私钥 */
12
- const PKCS8_RAW_SEED_PREFIX = Buffer.from('302e020100300506032b657004220420', 'hex')
13
- /** 固定 DER 头(非秘密),让 Node 把 32 字节当成公钥 */
14
- const SPKI_RAW_PUB_PREFIX = Buffer.from('302a300506032b6570032100', 'hex')
15
-
16
1
  /**
17
- * @param {Uint8Array|Buffer} seed32 32 字节私钥种子
18
- * @returns {import('node:crypto').KeyObject} Node 私钥对象
2
+ * 跨端 Ed25519 / SHA-256(Node 与浏览器共用;不依赖 node:crypto)。
19
3
  */
20
- function privateKeyFromSeed(seed32) {
21
- return createPrivateKey({
22
- key: Buffer.concat([PKCS8_RAW_SEED_PREFIX, Buffer.from(seed32).subarray(0, 32)]),
23
- format: 'der',
24
- type: 'pkcs8',
25
- })
26
- }
4
+ import { ed25519 } from '@noble/curves/ed25519.js'
5
+ import { sha256 } from '@noble/hashes/sha2.js'
27
6
 
28
- /**
29
- * @param {Uint8Array|Buffer} rawPublicKey 32 字节公钥
30
- * @returns {import('node:crypto').KeyObject} Node 公钥对象
31
- */
32
- function publicKeyFromRaw(rawPublicKey) {
33
- return createPublicKey({
34
- key: Buffer.concat([SPKI_RAW_PUB_PREFIX, Buffer.from(rawPublicKey).subarray(0, 32)]),
35
- format: 'der',
36
- type: 'spki',
37
- })
38
- }
7
+ import { bytesToHex, toBytes } from '../core/bytes_codec.mjs'
39
8
 
40
9
  /**
41
- * @param {Uint8Array|Buffer} seed32 32 字节私钥种子
42
- * @returns {Uint8Array} 32 字节公钥
10
+ * @param {Uint8Array|ArrayBufferView|ArrayBuffer|string} data 字节或 UTF-8 文本
11
+ * @returns {Uint8Array} 规范化字节
43
12
  */
44
- function rawPublicKeyFromSeed(seed32) {
45
- const der = createPublicKey(privateKeyFromSeed(seed32)).export({ type: 'spki', format: 'der' })
46
- return new Uint8Array(der.subarray(-32))
13
+ function inputBytes(data) {
14
+ if (typeof data === 'string') return new TextEncoder().encode(data)
15
+ return toBytes(data)
47
16
  }
48
17
 
49
18
  /**
50
- * 由种子字节派生密钥对
51
- *
52
- * @param {Uint8Array|Buffer} seed 任意长度;非 32 字节时 sha256 派生
19
+ * @param {Uint8Array|ArrayBufferView|ArrayBuffer} seed 任意长度;非 32 字节时 sha256 派生
53
20
  * @returns {{ publicKey: Uint8Array, secretKey: Uint8Array }} 32 字节私钥与对应公钥
54
21
  */
55
22
  export function keyPairFromSeed(seed) {
56
- const u = seed instanceof Uint8Array ? seed : new Uint8Array(seed)
57
- const sk = u.length === 32 ? u : new Uint8Array(createHash('sha256').update(u).digest())
58
- return { publicKey: rawPublicKeyFromSeed(sk), secretKey: new Uint8Array(sk) }
23
+ const u = toBytes(seed)
24
+ const sk = u.length === 32 ? u : sha256(u)
25
+ return { publicKey: ed25519.getPublicKey(sk), secretKey: new Uint8Array(sk) }
59
26
  }
60
27
 
61
28
  /**
62
- * 随机生成密钥对
63
- *
64
29
  * @returns {Promise<{ publicKey: Uint8Array, secretKey: Uint8Array }>} 随机密钥对
65
30
  */
66
31
  export async function randomKeyPair() {
67
- const sk = randomBytes(32)
32
+ const sk = new Uint8Array(32)
33
+ globalThis.crypto.getRandomValues(sk)
68
34
  return keyPairFromSeed(sk)
69
35
  }
70
36
 
71
37
  /**
72
- * 由 32 字节私钥种子导出对应公钥
73
38
  * @param {Uint8Array} secretKey 私钥种子
74
39
  * @returns {Uint8Array} 公钥
75
40
  */
76
41
  export function publicKeyFromSeed(secretKey) {
77
- return rawPublicKeyFromSeed(secretKey)
42
+ return ed25519.getPublicKey(toBytes(secretKey).subarray(0, 32))
78
43
  }
79
44
 
80
45
  /**
81
- * 签名
82
- *
83
- * @param {Uint8Array} message 待签名消息字节
46
+ * @param {Uint8Array|ArrayBufferView|ArrayBuffer|string} message 待签名消息
84
47
  * @param {Uint8Array} secretKey 私钥(取前 32 字节为种子)
85
48
  * @returns {Promise<Uint8Array>} 64 字节签名
86
49
  */
87
50
  export async function sign(message, secretKey) {
88
- const sig = nodeSign(null, Buffer.from(message), privateKeyFromSeed(secretKey))
89
- return new Uint8Array(sig)
51
+ return ed25519.sign(inputBytes(message), toBytes(secretKey).subarray(0, 32))
90
52
  }
91
53
 
92
54
  /**
93
- * 校验签名
94
- *
95
- * @param {Uint8Array} signature 64 字节签名
96
- * @param {Uint8Array} message 原始消息字节
97
- * @param {Uint8Array} publicKey 公钥
55
+ * @param {Uint8Array|ArrayBufferView|ArrayBuffer} signature 64 字节签名
56
+ * @param {Uint8Array|ArrayBufferView|ArrayBuffer|string} message 原始消息
57
+ * @param {Uint8Array|ArrayBufferView|ArrayBuffer} publicKey 公钥
98
58
  * @returns {Promise<boolean>} 合法为 true;异常或失败为 false
99
59
  */
100
60
  export async function verify(signature, message, publicKey) {
101
61
  try {
102
- return nodeVerify(null, Buffer.from(message), publicKeyFromRaw(publicKey), Buffer.from(signature))
62
+ return ed25519.verify(toBytes(signature), inputBytes(message), toBytes(publicKey))
103
63
  }
104
64
  catch {
105
65
  return false
@@ -107,52 +67,25 @@ export async function verify(signature, message, publicKey) {
107
67
  }
108
68
 
109
69
  /**
110
- * 公钥的 sha256 十六进制指纹(小写、无 0x 前缀)
111
- *
112
- * @param {Uint8Array} publicKey 公钥字节
70
+ * @param {Uint8Array|ArrayBufferView|ArrayBuffer} publicKey 公钥字节
113
71
  * @returns {string} 64 字符 hex
114
72
  */
115
73
  export function pubKeyHash(publicKey) {
116
- return bufferToHexSimple(hashPubKeyBytes(publicKey))
117
- }
118
-
119
- /**
120
- * 公钥 sha256 摘要
121
- *
122
- * @param {Uint8Array} publicKey 公钥字节
123
- * @returns {Buffer} 32 字节 digest
124
- */
125
- function hashPubKeyBytes(publicKey) {
126
- return createHash('sha256').update(publicKey).digest()
74
+ return bytesToHex(sha256(toBytes(publicKey)))
127
75
  }
128
76
 
129
77
  /**
130
- * 任意输入的 SHA-256 十六进制(小写、无 0x 前缀)。
131
- *
132
- * @param {Uint8Array|Buffer|string} data 字节或 UTF-8 文本
78
+ * @param {Uint8Array|ArrayBufferView|ArrayBuffer|string} data 字节或 UTF-8 文本
133
79
  * @returns {string} 64 字符 hex
134
80
  */
135
81
  export function sha256Hex(data) {
136
- const input = typeof data === 'string' ? Buffer.from(data, 'utf8') : Buffer.from(data)
137
- return createHash('sha256').update(input).digest('hex')
82
+ return bytesToHex(sha256(inputBytes(data)))
138
83
  }
139
84
 
140
85
  /**
141
- * UTF-8 文本 SHA-256 十六进制(小写、无 0x 前缀)。
142
- *
143
86
  * @param {string} text 文本
144
87
  * @returns {string} 64 字符 hex
145
88
  */
146
89
  export function sha256TextHex(text) {
147
90
  return sha256Hex(String(text ?? ''))
148
91
  }
149
-
150
- /**
151
- * Buffer / Uint8Array → 小写 hex 字符串
152
- *
153
- * @param {Uint8Array|Buffer} buffer 二进制缓冲
154
- * @returns {string} 小写十六进制文本
155
- */
156
- function bufferToHexSimple(buffer) {
157
- return Buffer.from(buffer).toString('hex')
158
- }
@@ -26,8 +26,8 @@ const manifestInflight = createInflightTable({
26
26
 
27
27
  /**
28
28
  * 拉取公开 manifest;默认不写盘。`cache: true` 或 `cachePublicManifest` 才缓存。
29
- * 本地已有 publicSig 时仍会 fanout 再校验,按 publishedAt 择新;超时则回退本地。
30
- * key in-flight 去重;调用方外层超时不 abort,后台继续填缓存。
29
+ * 本地已有 publicSig 时立即返回,并后台 fanout;`cache: true` 时按 publishedAt 择新写盘。
30
+ * miss 仍等 fanout。同 key in-flight 去重;调用方外层超时不 abort,后台继续填缓存。
31
31
  * @param {{ username: string, ownerEntityHash: string, logicalPath: string, cache?: boolean, timeoutMs?: number }} context - 拉取上下文
32
32
  * @returns {Promise<import('./manifest.mjs').FileManifest | null>} 验签后的 manifest,失败为 null
33
33
  */
@@ -42,6 +42,7 @@ export async function fetchPublicManifest(context) {
42
42
  : DEFAULT_MANIFEST_FETCH_TIMEOUT_MS
43
43
  const expectedKey = manifestFetchExpectedKey(ownerEntityHash, logicalPath)
44
44
  const inflightKey = `${username}\0${expectedKey}`
45
+ const wantCache = context.cache === true
45
46
 
46
47
  // 先挂 in-flight(同步),再读本地 — 避免并发调用在 await 间隙各自 start
47
48
  const localPromise = loadFileManifest(ownerEntityHash, logicalPath)
@@ -67,15 +68,21 @@ export async function fetchPublicManifest(context) {
67
68
  const hasLocalPublic = local?.transferKeyDescriptor?.type === 'public' && !!local?.meta?.publicSig
68
69
  if (!shared) return hasLocalPublic ? local : null
69
70
 
70
- const result = await shared
71
-
72
- if (result && (!hasLocalPublic || shouldPreferIncomingPublicManifest(local, result))) {
73
- if (context.cache === true)
74
- await cachePublicManifest(ownerEntityHash, logicalPath, result)
75
- return result
71
+ if (hasLocalPublic) {
72
+ // SWR:立刻回本地;后台 fanout 择新写盘(需 cache: true)
73
+ if (wantCache) {
74
+ void shared.then(async result => {
75
+ if (result && shouldPreferIncomingPublicManifest(local, result))
76
+ await cachePublicManifest(ownerEntityHash, logicalPath, result)
77
+ })
78
+ }
79
+ return local
76
80
  }
77
- if (hasLocalPublic) return local
78
- return null
81
+
82
+ const result = await shared
83
+ if (!result) return null
84
+ if (wantCache) await cachePublicManifest(ownerEntityHash, logicalPath, result)
85
+ return result
79
86
  }
80
87
 
81
88
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steve02081504/fount-p2p",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "description": "fount federation P2P layer — link, trust graph, mailbox, DAG, EVFS.",
5
5
  "keywords": [
6
6
  "network",
@@ -71,6 +71,7 @@
71
71
  },
72
72
  "dependencies": {
73
73
  "@noble/curves": "latest",
74
+ "@noble/hashes": "latest",
74
75
  "node-rtc-connection": "latest",
75
76
  "on-shutdown": "latest",
76
77
  "ws": "latest"