GPT: сгенерено и не проверено (смена ключей пользователя)

This commit is contained in:
AidarKC
2026-09-27 15:50:27 +03:00
parent 13693a0a53
commit 151a2c1754
94 changed files with 5327 additions and 815 deletions
+78
View File
@@ -123,3 +123,81 @@ export async function createAns104DataItem({ owner32, privateKey, data, tags = [
data,
);
}
function readUint64LE(bytes, offset) {
if (!(bytes instanceof Uint8Array) || offset < 0 || offset + 8 > bytes.length) throw new Error('ANS-104 truncated uint64');
let value = 0n;
for (let i = 7; i >= 0; i -= 1) value = (value << 8n) | BigInt(bytes[offset + i]);
if (value > BigInt(Number.MAX_SAFE_INTEGER)) throw new Error('ANS-104 uint64 exceeds JS safe integer');
return Number(value);
}
/** Parse enough of an Ed25519 ANS-104 DataItem to re-sign its exact SHiNE payload. */
export function parseAns104DataItem(rawBytes) {
const raw = rawBytes instanceof Uint8Array ? rawBytes : Uint8Array.from(rawBytes || []);
let p = 0;
if (raw.length < 2 + 64 + 32 + 1 + 1 + 8 + 8) throw new Error('ANS-104 DataItem is too short');
const sigType = new DataView(raw.buffer, raw.byteOffset, raw.byteLength).getUint16(p, true); p += 2;
if (sigType !== ANS104_ED25519_SIGNATURE_TYPE) throw new Error(`Unsupported ANS-104 signature type: ${sigType}`);
const signature = raw.slice(p, p + 64); p += 64;
const owner32 = raw.slice(p, p + 32); p += 32;
const targetPresent = raw[p++] !== 0;
const target = targetPresent ? raw.slice(p, p + 32) : new Uint8Array(0);
if (targetPresent) p += 32;
const anchorPresent = raw[p++] !== 0;
const anchor = anchorPresent ? raw.slice(p, p + 32) : new Uint8Array(0);
if (anchorPresent) p += 32;
const tagsCount = readUint64LE(raw, p); p += 8;
const tagsBytesLength = readUint64LE(raw, p); p += 8;
if (p + tagsBytesLength > raw.length) throw new Error('ANS-104 tags are truncated');
const rawTags = raw.slice(p, p + tagsBytesLength); p += tagsBytesLength;
const data = raw.slice(p);
return { sigType, signature, owner32, target, anchor, tagsCount, rawTags, data, raw };
}
async function ans104SigningMessageRaw({ owner32, target = new Uint8Array(0), anchor = new Uint8Array(0), rawTags, data }) {
if (!(owner32 instanceof Uint8Array) || owner32.length !== 32) throw new Error('ANS-104 owner must be 32 bytes');
if (!(target instanceof Uint8Array) || (target.length !== 0 && target.length !== 32)) throw new Error('ANS-104 target must be empty or 32 bytes');
if (!(anchor instanceof Uint8Array) || (anchor.length !== 0 && anchor.length !== 32)) throw new Error('ANS-104 anchor must be empty or 32 bytes');
if (!(rawTags instanceof Uint8Array)) throw new Error('ANS-104 rawTags must be Uint8Array');
if (!(data instanceof Uint8Array)) throw new Error('ANS-104 data must be Uint8Array');
return deepHash([
TE.encode('dataitem'),
TE.encode('1'),
TE.encode(String(ANS104_ED25519_SIGNATURE_TYPE)),
owner32,
target,
anchor,
rawTags,
data,
]);
}
/**
* Re-sign a DataItem while preserving exact raw tags/target/anchor/data bytes.
* Used by key rotation so SHA-256(SHiNE Frame) is unchanged across forks.
*/
export async function createAns104DataItemWithRawParts({
owner32,
privateKey,
data,
rawTags,
tagsCount,
target = new Uint8Array(0),
anchor = new Uint8Array(0),
}) {
const signingMessage = await ans104SigningMessageRaw({ owner32, target, anchor, rawTags, data });
const signature = new Uint8Array(await crypto.subtle.sign('Ed25519', privateKey, signingMessage));
if (signature.length !== 64) throw new Error(`Unexpected Ed25519 signature length: ${signature.length}`);
return concatBytes(
uint16LE(ANS104_ED25519_SIGNATURE_TYPE),
signature,
owner32,
Uint8Array.of(target.length ? 1 : 0), target,
Uint8Array.of(anchor.length ? 1 : 0), anchor,
uint64LE(tagsCount),
uint64LE(rawTags.length),
rawTags,
data,
);
}