Premium step 1 — soft-gate crypto primitive

First piece of PREMIUM_DESIGNS_FEATURE.md §6. Adds the static
client-side decryption layer for premium designs: HKDF-SHA256 over
an obfuscated per-epoch pepper + a per-design public salt, then
AES-256-GCM with the event coordinate as additional-authenticated-
data so ciphertext can't be copy-pasted to a different event.

noble bundle
  - Add @noble/hashes/hkdf to the bundle entry; expose as
    window.ArtstrNoble.hkdf and .hmac. Bundle size 14.0 KB → 16.5 KB.

Premium IIFE (new section in index.html)
  - _softGatePepperParts — per-epoch lookup of 4x8-byte fragments
    ("palette seed I..IV"). For epoch 2026-05. Sub-strings don't
    look like a "premium key"; reconstruction concats at use time.
    Comment explicitly calls this obfuscation, not security, per
    PREMIUM_DESIGNS_FEATURE.md §2 / §6.3.
  - SUPPORTED_EPOCHS / CURRENT_EPOCH — adding a future epoch is
    one entry in the parts table.
  - pepperBytesForEpoch(epoch) — reconstructs the 32-byte pepper.
  - deriveKey({ epoch, creatorPubkey, premiumAddress, schemaVersion,
    publicDesignSalt, puzzleNonce }) -> 32 bytes via HKDF-SHA256
    with the pepper as IKM, salt = publicDesignSalt, info =
    "artstr-softgate-v1|<epoch>|<creator>|<addr>|v<schema>" (and an
    optional "|p<nonce>" for the §6.7 CPU-puzzle hook).
  - encodeAad(creatorPubkey, dTag) -> UTF-8 of
    "30078:<creator>:<dTag>" so AES-GCM authenticates the
    coordinate.
  - encryptPayload(payloadObj, coord, { epoch? }) — random
    32-byte salt + 12-byte IV; returns the spec §5.2 envelope
    (base64 fields, softgate metadata, etc.).
  - decryptPayload(envelope, coord) — reverses the above.
  - Self-test runs on every load: round-trip a known payload AND
    verify AAD/creator/schema mismatches all reject the decrypt
    (event-coordinate binding). Single console.warn on failure.

Tested via CDP — all green:
  - Round-trip with random keypair: ok
  - AAD mismatch (different dTag) -> AES-GCM rejects: ok
  - Creator pubkey mismatch -> KDF info changes -> rejects: ok
  - Schema version mismatch -> KDF info changes -> rejects: ok
  - KDF deterministic with identical inputs: ok
  - Output length = 32 bytes (AES-256): ok
  - Pepper reconstruction = 32 bytes: ok
  - Envelope size for tiny payload = 577 bytes; ~63 KB headroom
    under typical relay limits, plenty for an embedded preview
    JPEG in step 2.
  - Regression: NWC crypto self-test still passes; existing editor
    still loads and renders layers.

Code only; nothing in the publish/feed flow uses this yet. Next
step (PREMIUM_DESIGNS_FEATURE.md §19 item 2) is the watermarked
preview pipeline.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.7 committed 2026-05-28 20:54:24 -04:00
1 parent 7da0c9d97c
commit a8c855fc5c
2 files changed
+175 -1

No files matched your search

+174
View File
@@ -4595,6 +4595,180 @@
// ---- end NWC crypto -----------------------------------------------------
// ---- Premium soft-gate crypto (PREMIUM_DESIGNS_FEATURE.md §6) -----------
// Static client-side decryption gate for premium designs. The AES key is
// derived from a per-design public salt + a client-side "pepper" + a
// discriminator string. The pepper is obfuscated (split into byte
// fragments held in unrelated-looking places); reconstructing it from a
// minified bundle is annoying-but-doable, which is honest about what this
// gives us: protection against drive-by scraping, not strong DRM. The
// spec calls this a "soft gate."
//
// Epoch rotation: every premium design records the epoch it was
// published under. When an epoch's pepper gets reverse-engineered we
// can ship a new epoch and rotate forward; old designs stay readable
// by anyone who knows the old derivation but new designs are protected
// by the new one. Adding a new epoch is "add one entry to
// `_softGatePepperParts`."
const Premium = (() => {
// 4-part pepper per epoch. Stored as four Uint8Arrays in this lookup
// table, indexed by epoch. Reconstruction concatenates them — the
// pieces don't look like anything individually. Each 8 bytes was
// generated from crypto.getRandomValues at spec-adoption time; comment
// calls them "palette seeds" to avoid an obvious "PREMIUM_KEY" string
// in the source. (Determined readers will still find this; we accept
// that — see PREMIUM_DESIGNS_FEATURE.md §2.)
const _softGatePepperParts = {
'2026-05': [
new Uint8Array([0x8c, 0x2b, 0xd1, 0x47, 0x9e, 0x60, 0x35, 0xaa]), // palette seed I
new Uint8Array([0x14, 0xf3, 0x59, 0xb2, 0x7d, 0xc8, 0x06, 0xef]), // palette seed II
new Uint8Array([0xa5, 0x71, 0x4e, 0x2c, 0x9b, 0x88, 0x33, 0xd0]), // palette seed III
new Uint8Array([0x62, 0x09, 0xfd, 0x18, 0x4a, 0xe7, 0xc4, 0x95]), // palette seed IV
],
};
const SUPPORTED_EPOCHS = Object.freeze(Object.keys(_softGatePepperParts));
const CURRENT_EPOCH = '2026-05';
const PREMIUM_KDF_INFO = 'artstr-softgate-v1';
function pepperBytesForEpoch(epoch) {
const parts = _softGatePepperParts[epoch];
if (!parts) throw new Error(`Unsupported soft-gate epoch: ${epoch}`);
const total = parts.reduce((n, p) => n + p.length, 0);
const out = new Uint8Array(total);
let off = 0;
for (const p of parts) { out.set(p, off); off += p.length; }
return out;
}
// HKDF-SHA256 → 32-byte AES key. Inputs:
// epoch — string ("2026-05") from the envelope
// creatorPubkey — 64-hex
// premiumAddress — "30078:<creatorHex>:<dTag>"
// schemaVersion — number, e.g. 5
// publicDesignSalt — Uint8Array (from envelope)
// puzzleNonce — optional UTF-8 string mixed into info
function deriveKey({ epoch, creatorPubkey, premiumAddress, schemaVersion, publicDesignSalt, puzzleNonce }) {
if (!_softGatePepperParts[epoch]) throw new Error(`Unsupported soft-gate epoch: ${epoch}`);
const enc = new TextEncoder();
const infoStr = PREMIUM_KDF_INFO + '|' + epoch + '|' + creatorPubkey + '|' + premiumAddress + '|v' + schemaVersion
+ (puzzleNonce ? '|p' + puzzleNonce : '');
return window.ArtstrNoble.hkdf(
window.ArtstrNoble.sha256,
pepperBytesForEpoch(epoch), // IKM
publicDesignSalt, // salt
enc.encode(infoStr), // info
32, // length
);
}
// AAD binds ciphertext to the event coordinate so a copy-paste to a
// different event address fails authentication.
function encodeAad(creatorPubkey, dTag) {
return new TextEncoder().encode(`30078:${creatorPubkey}:${dTag}`);
}
const _bytesToB64 = (b) => { let s = ''; for (let i = 0; i < b.length; i++) s += String.fromCharCode(b[i]); return btoa(s); };
const _b64ToBytes = (b64) => Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
// Encrypt a Nostr design payload object → envelope (per spec §5.2).
// `coord` is { creatorPubkey, dTag, schemaVersion }. Generates a random
// 32-byte design salt + 12-byte IV. The returned envelope has the same
// shape the consumer expects, base64'd.
async function encryptPayload(payloadObj, coord, opts = {}) {
const epoch = opts.epoch || CURRENT_EPOCH;
const publicDesignSalt = crypto.getRandomValues(new Uint8Array(32));
const iv = crypto.getRandomValues(new Uint8Array(12));
const premiumAddress = `30078:${coord.creatorPubkey}:${coord.dTag}`;
const keyBytes = deriveKey({
epoch,
creatorPubkey: coord.creatorPubkey,
premiumAddress,
schemaVersion: coord.schemaVersion,
publicDesignSalt,
});
const key = await crypto.subtle.importKey('raw', keyBytes, { name: 'AES-GCM' }, false, ['encrypt']);
const aad = encodeAad(coord.creatorPubkey, coord.dTag);
const plaintext = new TextEncoder().encode(JSON.stringify(payloadObj));
const ct = new Uint8Array(await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv, additionalData: aad }, key, plaintext
));
return {
v: 1,
enc: { alg: 'aes-256-gcm', kdf: PREMIUM_KDF_INFO, compression: 'none' },
aad: `30078:${coord.creatorPubkey}:${coord.dTag}`,
salt: _bytesToB64(publicDesignSalt),
iv: _bytesToB64(iv),
ciphertext: _bytesToB64(ct),
softgate: {
version: 1, epoch,
publicDesignSalt: _bytesToB64(publicDesignSalt),
purchaseScope: 'address',
puzzle: { enabled: false, difficultyBits: 0 },
},
};
}
// Decrypt an envelope back into the original payload object. Pulls
// epoch + salt out of the envelope; rebuilds the AAD from `coord`.
async function decryptPayload(envelope, coord) {
if (!envelope || envelope.enc?.alg !== 'aes-256-gcm') throw new Error('Unsupported envelope');
const epoch = envelope.softgate?.epoch || CURRENT_EPOCH;
const publicDesignSalt = _b64ToBytes(envelope.softgate?.publicDesignSalt || envelope.salt);
const iv = _b64ToBytes(envelope.iv);
const ct = _b64ToBytes(envelope.ciphertext);
const premiumAddress = `30078:${coord.creatorPubkey}:${coord.dTag}`;
const keyBytes = deriveKey({
epoch,
creatorPubkey: coord.creatorPubkey,
premiumAddress,
schemaVersion: coord.schemaVersion,
publicDesignSalt,
});
const key = await crypto.subtle.importKey('raw', keyBytes, { name: 'AES-GCM' }, false, ['decrypt']);
const aad = encodeAad(coord.creatorPubkey, coord.dTag);
const pt = new Uint8Array(await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv, additionalData: aad }, key, ct
));
return JSON.parse(new TextDecoder().decode(pt));
}
return {
SUPPORTED_EPOCHS, CURRENT_EPOCH, PREMIUM_KDF_INFO,
pepperBytesForEpoch, deriveKey, encodeAad,
encryptPayload, decryptPayload,
};
})();
// Phase-1 self-test for the soft-gate primitive. Round-trips a known
// payload + verifies that mismatched AAD (event coordinate copy-paste)
// fails the decrypt as the spec requires.
(async () => {
try {
if (!window.ArtstrNoble?.hkdf) throw new Error('HKDF missing from noble bundle');
const coord = {
creatorPubkey: 'a'.repeat(64),
dTag: 'premium:test:soft-gate-roundtrip',
schemaVersion: 5,
};
const payload = { version: 5, hello: 'world', layers: [{ id: 'l1', type: 'color' }] };
const env = await Premium.encryptPayload(payload, coord);
if (env.enc.alg !== 'aes-256-gcm') throw new Error('envelope alg wrong');
if (!env.softgate?.epoch) throw new Error('envelope missing epoch');
const round = await Premium.decryptPayload(env, coord);
if (JSON.stringify(round) !== JSON.stringify(payload)) throw new Error('round-trip mismatch');
// AAD mismatch must fail (different dTag → different AAD → AES-GCM rejects).
let aadFailed = false;
try {
await Premium.decryptPayload(env, { ...coord, dTag: 'attacker:rebound' });
} catch { aadFailed = true; }
if (!aadFailed) throw new Error('AAD mismatch did NOT fail decrypt — coordinate binding broken');
} catch (err) {
console.warn('[Artstr] Premium soft-gate self-test failed — premium publish/unlock will not work:', err);
}
})();
// ---- end Premium soft-gate crypto ---------------------------------------
// ---- NWC client (Phase 1) ----------------------------------------------
// RPC-style client for a single NIP-47 wallet connection. One WebSocket,
// one persistent subscription for kind-23195 responses + kind-13194
+1 -1
View File
@@ -1,4 +1,4 @@
(()=>{var ae=Object.defineProperty;var ue=(t,e,n)=>e in t?ae(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var l=(t,e,n)=>ue(t,typeof e!="symbol"?e+"":e,n);var Ht=Object.freeze({p:0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn,n:0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n,h:1n,a:0n,b:7n,Gx:0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n,Gy:0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n}),{p:v,n:Z,Gx:he,Gy:de,b:St}=Ht,w=32,K=64,P={publicKey:w+1,publicKeyUncompressed:K+1,signature:K,seed:w+w/2},b=(t="",e=Error)=>{let n=new e(t),{captureStackTrace:s}=Error;throw typeof s=="function"&&s(n,b),n},le=t=>t instanceof Uint8Array||ArrayBuffer.isView(t)&&t.constructor.name==="Uint8Array"&&t.BYTES_PER_ELEMENT===1,E=(t,e,n="")=>{let s=le(t),r=t?.length,o=e!==void 0;if(!s||o&&r!==e){let c=n&&`"${n}" `,f=o?` of length ${e}`:"",a=s?`length=${r}`:`type=${typeof t}`,h=c+"expected Uint8Array"+f+", got "+a;return s?b(h,RangeError):b(h,TypeError)}return t},j=t=>new Uint8Array(t),Bt=(t,e)=>t.toString(16).padStart(e,"0"),Lt=t=>{let e="";for(let n of E(t))e+=Bt(n,2);return e},_={_0:48,_9:57,A:65,F:70,a:97,f:102},bt=t=>t>=_._0&&t<=_._9?t-_._0:t>=_.A&&t<=_.F?t-(_.A-10):t>=_.a&&t<=_.f?t-(_.a-10):void 0,Ut=t=>{let e="hex invalid";if(typeof t!="string")return b(e);let n=t.length,s=n/2;if(n%2)return b(e);let r=j(s);for(let o=0,c=0;o<s;o++,c+=2){let f=bt(t.charCodeAt(c)),a=bt(t.charCodeAt(c+1));if(f===void 0||a===void 0)return b(e);r[o]=f*16+a}return r},yt=()=>globalThis?.crypto?.subtle??b("crypto.subtle must be defined, consider polyfill"),R=(...t)=>{let e=0;for(let r of t)e+=E(r).length;let n=j(e),s=0;for(let r of t)n.set(r,s),s+=r.length;return n},at=(t=w)=>(globalThis?.crypto).getRandomValues(j(t)),ot=BigInt,V=(t,e,n,s="bad number: out of range")=>typeof t!="bigint"?b(s,TypeError):e<=t&&t<n?t:b(s,RangeError),i=(t,e=v)=>{let n=t%e;return n>=0n?n:e+n},z=t=>i(t,Z),xe=(t,e)=>{(t===0n||e<=0n)&&b("no inverse n="+t+" mod="+e);let n=i(t,e),s=e,r=0n,o=1n,c=1n,f=0n;for(;n!==0n;){let a=s/n,h=s%n,d=r-c*a,u=o-f*a;s=n,n=h,r=c,o=f,c=d,f=u}return s===1n?i(r,e):b("no inverse")},_t=t=>{let e=J[t];return typeof e!="function"&&b("hashes."+t+" not set"),e},pt=(t,e,n)=>E(_t(t)(e,n),w,"digest"),gt=(t,e,n)=>Promise.resolve(_t(t)(e,n)).then(s=>E(s,w,"digest"));var rt=t=>t instanceof L?t:b("Point expected"),Tt=t=>i(i(t*t)*t+St),mt=t=>V(t,0n,v),M=t=>V(t,1n,v),be=t=>V(t,1n,Z),W=t=>!(t&1n),It=t=>Uint8Array.of(t),ye=t=>It(W(t)?2:3),vt=t=>{let e=Tt(M(t)),n=1n;for(let s=e,r=(v+1n)/4n;r>0n;r>>=1n)r&1n&&(n=n*s%v),s=s*s%v;return i(n*n)!==e&&b("sqrt invalid"),W(n)?n:i(-n)};var B=class B{constructor(e,n,s){l(this,"X");l(this,"Y");l(this,"Z");this.X=mt(e),this.Y=M(n),this.Z=mt(s),Object.freeze(this)}static CURVE(){return Ht}static fromAffine(e){let{x:n,y:s}=e;return n===0n&&s===0n?F:new B(n,s,1n)}static fromBytes(e){E(e);let{publicKey:n,publicKeyUncompressed:s}=P,r,o=e.length,c=e[0],f=e.subarray(1),a=Y(f,0,w);if(o===n&&(c===2||c===3)){let h=vt(a);c===3&&(h=i(-h)),r=new B(a,h,1n)}return o===s&&c===4&&(r=new B(a,Y(f,w,K),1n)),r?r.assertValidity():b("bad point: not on curve")}static fromHex(e){return B.fromBytes(Ut(e))}get x(){return this.toAffine().x}get y(){return this.toAffine().y}equals(e){let{X:n,Y:s,Z:r}=this,{X:o,Y:c,Z:f}=rt(e),a=i(n*f),h=i(o*r),d=i(s*f),u=i(c*r);return a===h&&d===u}is0(){return this.equals(F)}negate(){return new B(this.X,i(-this.Y),this.Z)}double(){return this.add(this)}add(e){let{X:n,Y:s,Z:r}=this,{X:o,Y:c,Z:f}=rt(e),a=0n,h=St,d=0n,u=0n,x=0n,y=i(h*3n),p=i(n*o),g=i(s*c),A=i(r*f),O=i(n+s),m=i(o+c);O=i(O*m),m=i(p+g),O=i(O-m),m=i(n+r);let S=i(o+f);return m=i(m*S),S=i(p+A),m=i(m-S),S=i(s+r),d=i(c+f),S=i(S*d),d=i(g+A),S=i(S-d),x=i(a*m),d=i(y*A),x=i(d+x),d=i(g-x),x=i(g+x),u=i(d*x),g=i(p+p),g=i(g+p),A=i(a*A),m=i(y*m),g=i(g+A),A=i(p-A),A=i(a*A),m=i(m+A),p=i(g*m),u=i(u+p),p=i(S*m),d=i(O*d),d=i(d-p),p=i(O*g),x=i(S*x),x=i(x+p),new B(d,u,x)}subtract(e){return this.add(rt(e).negate())}multiply(e,n=!0){if(!n&&e===0n)return F;if(be(e),e===1n)return this;if(this.equals(C))return _e(e).p;let s=F,r=C;for(let o=this;e>0n;o=o.double(),e>>=1n)e&1n?s=s.add(o):n&&(r=r.add(o));return s}multiplyUnsafe(e){return this.multiply(e,!1)}toAffine(){let{X:e,Y:n,Z:s}=this;if(this.equals(F))return{x:0n,y:0n};if(s===1n)return{x:e,y:n};let r=xe(s,v);return i(s*r)!==1n&&b("inverse invalid"),{x:i(e*r),y:i(n*r)}}assertValidity(){let{x:e,y:n}=this.toAffine();return M(e),M(n),i(n*n)===Tt(e)?this:b("bad point: not on curve")}toBytes(e=!0){let{x:n,y:s}=this.assertValidity().toAffine(),r=U(n);return e?R(ye(s),r):R(It(4),r,U(s))}toHex(e){return Lt(this.toBytes(e))}};l(B,"BASE"),l(B,"ZERO");var L=B,C=new L(he,de,1n),F=new L(0n,1n,0n);L.BASE=C;L.ZERO=F;var pe=(t,e,n)=>C.multiply(e,!1).add(t.multiply(n,!1)).assertValidity(),G=t=>ot("0x"+(Lt(t)||"0")),Y=(t,e,n)=>G(t.subarray(e,n)),ge=2n**256n,U=t=>Ut(Bt(V(t,0n,ge),K)),q=t=>{let e=G(E(t,w,"secret key"));return V(e,1n,Z,"invalid secret key: outsidLine truncated
(()=>{var de=Object.defineProperty;var le=(t,e,n)=>e in t?de(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var l=(t,e,n)=>le(t,typeof e!="symbol"?e+"":e,n);var Lt=Object.freeze({p:0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn,n:0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n,h:1n,a:0n,b:7n,Gx:0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n,Gy:0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n}),{p:v,n:P,Gx:xe,Gy:be,b:Ut}=Lt,w=32,K=64,z={publicKey:w+1,publicKeyUncompressed:K+1,signature:K,seed:w+w/2},b=(t="",e=Error)=>{let n=new e(t),{captureStackTrace:s}=Error;throw typeof s=="function"&&s(n,b),n},ye=t=>t instanceof Uint8Array||ArrayBuffer.isView(t)&&t.constructor.name==="Uint8Array"&&t.BYTES_PER_ELEMENT===1,E=(t,e,n="")=>{let s=ye(t),r=t?.length,o=e!==void 0;if(!s||o&&r!==e){let c=n&&`"${n}" `,i=o?` of length ${e}`:"",a=s?`length=${r}`:`type=${typeof t}`,h=c+"expected Uint8Array"+i+", got "+a;return s?b(h,RangeError):b(h,TypeError)}return t},j=t=>new Uint8Array(t),_t=(t,e)=>t.toString(16).padStart(e,"0"),Tt=t=>{let e="";for(let n of E(t))e+=_t(n,2);return e},_={_0:48,_9:57,A:65,F:70,a:97,f:102},gt=t=>t>=_._0&&t<=_._9?t-_._0:t>=_.A&&t<=_.F?t-(_.A-10):t>=_.a&&t<=_.f?t-(_.a-10):void 0,It=t=>{let e="hex invalid";if(typeof t!="string")return b(e);let n=t.length,s=n/2;if(n%2)return b(e);let r=j(s);for(let o=0,c=0;o<s;o++,c+=2){let i=gt(t.charCodeAt(c)),a=gt(t.charCodeAt(c+1));if(i===void 0||a===void 0)return b(e);r[o]=i*16+a}return r},mt=()=>globalThis?.crypto?.subtle??b("crypto.subtle must be defined, consider polyfill"),N=(...t)=>{let e=0;for(let r of t)e+=E(r).length;let n=j(e),s=0;for(let r of t)n.set(r,s),s+=r.length;return n},dt=(t=w)=>(globalThis?.crypto).getRandomValues(j(t)),ft=BigInt,D=(t,e,n,s="bad number: out of range")=>typeof t!="bigint"?b(s,TypeError):e<=t&&t<n?t:b(s,RangeError),f=(t,e=v)=>{let n=t%e;return n>=0n?n:e+n},J=t=>f(t,P),pe=(t,e)=>{(t===0n||e<=0n)&&b("no inverse n="+t+" mod="+e);let n=f(t,e),s=e,r=0n,o=1n,c=1n,i=0n;for(;n!==0n;){let a=s/n,h=s%n,d=r-c*a,u=o-i*a;s=n,n=h,r=c,o=i,c=d,i=u}return s===1n?f(r,e):b("no inverse")},kt=t=>{let e=et[t];return typeof e!="function"&&b("hashes."+t+" not set"),e},wt=(t,e,n)=>E(kt(t)(e,n),w,"digest"),At=(t,e,n)=>Promise.resolve(kt(t)(e,n)).then(s=>E(s,w,"digest"));var it=t=>t instanceof L?t:b("Point expected"),Ct=t=>f(f(t*t)*t+Ut),Et=t=>D(t,0n,v),$=t=>D(t,1n,v),ge=t=>D(t,1n,P),Q=t=>!(t&1n),vt=t=>Uint8Array.of(t),me=t=>vt(Q(t)?2:3),Ft=t=>{let e=Ct($(t)),n=1n;for(let s=e,r=(v+1n)/4n;r>0n;r>>=1n)r&1n&&(n=n*s%v),s=s*s%v;return f(n*n)!==e&&b("sqrt invalid"),Q(n)?n:f(-n)};var B=class B{constructor(e,n,s){l(this,"X");l(this,"Y");l(this,"Z");this.X=Et(e),this.Y=$(n),this.Z=Et(s),Object.freeze(this)}static CURVE(){return Lt}static fromAffine(e){let{x:n,y:s}=e;return n===0n&&s===0n?G:new B(n,s,1n)}static fromBytes(e){E(e);let{publicKey:n,publicKeyUncompressed:s}=z,r,o=e.length,c=e[0],i=e.subarray(1),a=W(i,0,w);if(o===n&&(c===2||c===3)){let h=Ft(a);c===3&&(h=f(-h)),r=new B(a,h,1n)}return o===s&&c===4&&(r=new B(a,W(i,w,K),1n)),r?r.assertValidity():b("bad point: not on curve")}static fromHex(e){return B.fromBytes(It(e))}get x(){return this.toAffine().x}get y(){return this.toAffine().y}equals(e){let{X:n,Y:s,Z:r}=this,{X:o,Y:c,Z:i}=it(e),a=f(n*i),h=f(o*r),d=f(s*i),u=f(c*r);return a===h&&d===u}is0(){return this.equals(G)}negate(){return new B(this.X,f(-this.Y),this.Z)}double(){return this.add(this)}add(e){let{X:n,Y:s,Z:r}=this,{X:o,Y:c,Z:i}=it(e),a=0n,h=Ut,d=0n,u=0n,x=0n,y=f(h*3n),p=f(n*o),g=f(s*c),A=f(r*i),R=f(n+s),m=f(o+c);R=f(R*m),m=f(p+g),R=f(R-m),m=f(n+r);let S=f(o+i);return m=f(m*S),S=f(p+A),m=f(m-S),S=f(s+r),d=f(c+i),S=f(S*d),d=f(g+A),S=f(S-d),x=f(a*m),d=f(y*A),x=f(d+x),d=f(g-x),x=f(g+x),u=f(d*x),g=f(p+p),g=f(g+p),A=f(a*A),m=f(y*m),g=f(g+A),A=f(p-A),A=f(a*A),m=f(m+A),p=f(g*m),u=f(u+p),p=f(S*m),d=f(R*d),d=f(d-p),p=f(R*g),x=f(S*x),x=f(x+p),new B(d,u,x)}subtract(e){return this.add(it(e).negate())}multiply(e,n=!0){if(!n&&e===0n)return G;if(ge(e),e===1n)return this;if(this.equals(F))return ke(e).p;let s=G,r=F;for(let o=this;e>0n;o=o.double(),e>>=1n)e&1n?s=s.add(o):n&&(r=r.add(o));return s}multiplyUnsafe(e){return this.multiply(e,!1)}toAffine(){let{X:e,Y:n,Z:s}=this;if(this.equals(G))return{x:0n,y:0n};if(s===1n)return{x:e,y:n};let r=pe(s,v);return f(s*r)!==1n&&b("inverse invalid"),{x:f(e*r),y:f(n*r)}}assertValidity(){let{x:e,y:n}=this.toAffine();return $(e),$(n),f(n*n)===Ct(e)?this:b("bad point: not on curve")}toBytes(e=!0){let{x:n,y:s}=this.assertValidity().toAffine(),r=U(n);return e?N(me(s),r):N(vt(4),r,U(s))}toHex(e){return Tt(this.toBytes(e))}};l(B,"BASE"),l(B,"ZERO");var L=B,F=new L(xe,be,1n),G=new L(0n,1n,0n);L.BASE=F;L.ZERO=G;var we=(t,e,n)=>F.multiply(e,!1).add(t.multiply(n,!1)).assertValidity(),O=t=>ft("0x"+(Tt(t)||"0")),W=(t,e,n)=>O(t.subarray(e,n)),Ae=2n**256n,U=t=>It(_t(D(t,0n,Ae),K)),tt=t=>{let e=O(E(t,w,"secret key"));return D(e,1n,P,"invalid secret key: outsLine truncated
/*! Bundled license information:
@noble/secp256k1/index.js: