| … | … | @@ -0,0 +1,86 @@ |
| 1 | /** | |
| 2 | * Auth adapter for hyper-social-web | |
| 3 | * | |
| 4 | * Wraps hyper-identity's AuthManager with social-specific permission checking. | |
| 5 | * Uses Autobase-backed IdentityRegistry for multi-writer identity consensus. | |
| 6 | */ | |
| 7 | ||
| 8 | import { AuthManager, IdentityRegistry, ANProto } from 'hyper-identity' | |
| 9 | import Hyperbee from 'hyperbee' | |
| 10 | ||
| 11 | /** | |
| 12 | * Initialize the auth system | |
| 13 | * | |
| 14 | * @param {Corestore} store - Corestore instance (shared with feed/accounts storage) | |
| 15 | * @returns {{ authManager: AuthManager, identityRegistry: IdentityRegistry }} | |
| 16 | */ | |
| 17 | export async function createAuth (store) { | |
| 18 | // Separate Hyperbee for identity direct-write storage | |
| 19 | const identityBee = new Hyperbee(store.get({ name: 'identity-db' }), { | |
| 20 | keyEncoding: 'utf-8', | |
| 21 | valueEncoding: 'json' | |
| 22 | }) | |
| 23 | await identityBee.ready() | |
| 24 | ||
| 25 | const registry = new IdentityRegistry(store, { db: identityBee }) | |
| 26 | await registry.ready() | |
| 27 | ||
| 28 | const authManager = new AuthManager({ | |
| 29 | identityRegistry: registry, | |
| 30 | db: identityBee, | |
| 31 | enableSSH: false, | |
| 32 | checkPermission: checkSocialPermission, | |
| 33 | rateLimits: { | |
| 34 | auth: { tokens: 100, refillRate: 20, refillInterval: 60 * 1000 }, | |
| 35 | api: { tokens: 1000, refillRate: 200, refillInterval: 60 * 1000 }, | |
| 36 | 'identity-generate': { tokens: 5, refillRate: 1, refillInterval: 60 * 60 * 1000 } | |
| 37 | } | |
| 38 | }) | |
| 39 | ||
| 40 | return { authManager, identityRegistry: registry, identityBee } | |
| 41 | } | |
| 42 | ||
| 43 | /** | |
| 44 | * Social permission checker — injectable into AuthManager | |
| 45 | * | |
| 46 | * Resources in hyper-social: 'posts', 'accounts', 'settings', '*' | |
| 47 | */ | |
| 48 | function checkSocialPermission (auth, action, resourceName) { | |
| 49 | if (!auth || !auth.verified) return false | |
| 50 | const perms = auth.permissions | |
| 51 | if (!perms) return false | |
| 52 | if (perms.admin) return true | |
| 53 | if (!perms[action]) return false | |
| 54 | if (resourceName) { | |
| 55 | return perms.resources && (perms.resources.includes('*') || perms.resources.includes(resourceName)) | |
| 56 | } | |
| 57 | return true | |
| 58 | } | |
| 59 | ||
| 60 | /** | |
| 61 | * Extract auth from request (non-throwing) | |
| 62 | * Returns auth object if authenticated, null if anonymous. | |
| 63 | */ | |
| 64 | export async function extractAuth (req, authManager) { | |
| 65 | try { | |
| 66 | const result = await authManager.authenticate(req) | |
| 67 | return result || null | |
| 68 | } catch { | |
| 69 | return null | |
| 70 | } | |
| 71 | } | |
| 72 | ||
| 73 | /** | |
| 74 | * Require auth — throws if not authenticated | |
| 75 | */ | |
| 76 | export async function requireAuth (req, authManager) { | |
| 77 | const auth = await extractAuth(req, authManager) | |
| 78 | if (!auth) { | |
| 79 | const err = new Error('Authentication required') | |
| 80 | err.status = 401 | |
| 81 | throw err | |
| 82 | } | |
| 83 | return auth | |
| 84 | } | |
| 85 | ||
| 86 | export { ANProto } |
| … | … | @@ -24,6 +24,7 @@ class FeedStore { |
| 24 | 24 | source: opts.source || 'local', |
| 25 | 25 | author: opts.author || 'unknown', |
| 26 | 26 | authorId: opts.authorId || opts.author || 'unknown', |
| 27 | authorPublicKey: opts.authorPublicKey || null, | |
| 27 | 28 | text: opts.text || '', |
| 28 | 29 | timestamp: opts.timestamp || now, |
| 29 | 30 | type: opts.type || 'post', |
| … | … | @@ -32,7 +33,8 @@ class FeedStore { |
| 32 | 33 | mediaUrl: opts.mediaUrl || null, |
| 33 | 34 | mediaType: opts.mediaType || null, |
| 34 | 35 | metadata: opts.metadata || {}, |
| 35 | syndicated: opts.syndicated || null | |
| 36 | syndicated: opts.syndicated || null, | |
| 37 | anproto: opts.anproto || null | |
| 36 | 38 | } |
| 37 | 39 | |
| 38 | 40 | const batch = this.bee.batch() |
| … | … | @@ -0,0 +1,50 @@ |
| 1 | /** | |
| 2 | * Post signing for hyper-social-web | |
| 3 | * | |
| 4 | * Signs outgoing posts with ANProto content-signing (Wiredove-compatible). | |
| 5 | * Server-side verification of incoming signed posts. | |
| 6 | */ | |
| 7 | ||
| 8 | import { ANProto } from 'hyper-identity' | |
| 9 | ||
| 10 | /** | |
| 11 | * Sign a post's content with ANProto | |
| 12 | * | |
| 13 | * @param {string} text - Post text | |
| 14 | * @param {{ publicKey: Buffer, secretKey: Buffer }} keypair - Server or user keypair | |
| 15 | * @returns {{ anproto: string, hash: string }} | |
| 16 | */ | |
| 17 | export function signPost (text, keypair) { | |
| 18 | const hash = ANProto.hash(text) | |
| 19 | const anproto = ANProto.signContent(hash, keypair) | |
| 20 | return { anproto, hash } | |
| 21 | } | |
| 22 | ||
| 23 | /** | |
| 24 | * Verify a signed post | |
| 25 | * | |
| 26 | * @param {{ text: string, anproto: string }} post | |
| 27 | * @returns {{ verified: boolean, author: string, timestamp: number } | { verified: false }} | |
| 28 | */ | |
| 29 | export function verifyPost (post) { | |
| 30 | if (!post || !post.anproto) { | |
| 31 | return { verified: false } | |
| 32 | } | |
| 33 | ||
| 34 | try { | |
| 35 | const opened = ANProto.open(post.anproto) | |
| 36 | const expectedHash = ANProto.hash(post.text) | |
| 37 | ||
| 38 | if (opened.hash !== expectedHash) { | |
| 39 | return { verified: false } | |
| 40 | } | |
| 41 | ||
| 42 | return { | |
| 43 | verified: true, | |
| 44 | author: opened.publicKey, | |
| 45 | timestamp: opened.timestamp | |
| 46 | } | |
| 47 | } catch { | |
| 48 | return { verified: false } | |
| 49 | } | |
| 50 | } |
| … | … | @@ -10,6 +10,8 @@ import { POSSEEngine } from './posse.js' |
| 10 | 10 | import { MastodonClient } from './clients/mastodon-client.js' |
| 11 | 11 | import { AtprotoClient } from './clients/atproto-client.js' |
| 12 | 12 | import { processMedia } from './media.js' |
| 13 | import { createAuth, extractAuth, requireAuth } from './auth.js' | |
| 14 | import { signPost, verifyPost } from './post-signer.js' | |
| 13 | 15 | |
| 14 | 16 | const MIME = { |
| 15 | 17 | '.html': 'text/html; charset=utf-8', |
| … | … | @@ -151,6 +153,9 @@ async function createServer (opts = {}) { |
| 151 | 153 | await accountsBee.ready() |
| 152 | 154 | await loadAccounts(accountsBee, posse) |
| 153 | 155 | |
| 156 | // Identity / auth | |
| 157 | const { authManager, identityRegistry } = await createAuth(store) | |
| 158 | ||
| 154 | 159 | // HTTP server |
| 155 | 160 | const server = http.createServer(async (req, res) => { |
| 156 | 161 | let url = req.url |
| … | … | @@ -164,21 +169,31 @@ async function createServer (opts = {}) { |
| 164 | 169 | // CORS for local dev |
| 165 | 170 | res.setHeader('Access-Control-Allow-Origin', '*') |
| 166 | 171 | res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS') |
| 167 | res.setHeader('Access-Control-Allow-Headers', 'Content-Type') | |
| 172 | res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Identity-PublicKey, X-Identity-Signature, X-Identity-Challenge') | |
| 168 | 173 | if (req.method === 'OPTIONS') { res.statusCode = 204; res.end(); return } |
| 169 | 174 | |
| 170 | 175 | try { |
| 171 | 176 | // --- API routes --- |
| 172 | 177 | if (url.startsWith('/api/')) { |
| 173 | await handleApi(url, req, res, { posse, feedStore, accountsBee, prefix }) | |
| 178 | await handleApi(url, req, res, { posse, feedStore, accountsBee, prefix, authManager, identityRegistry }) | |
| 174 | 179 | return |
| 175 | 180 | } |
| 176 | 181 | |
| 177 | 182 | // --- Static files --- |
| 178 | 183 | await serveStatic(url, res, webDir) |
| 179 | 184 | } catch (err) { |
| 180 | console.error('[server] Error:', err.message) | |
| 181 | error(res, 'Internal server error', 500) | |
| 185 | if (err.status === 401) { | |
| 186 | error(res, err.message || 'Authentication required', 401) | |
| 187 | } else if (err.status === 403) { | |
| 188 | error(res, err.message || 'Forbidden', 403) | |
| 189 | } else if (err.status === 409) { | |
| 190 | error(res, err.message || 'Conflict', 409) | |
| 191 | } else if (err.status === 400) { | |
| 192 | error(res, err.message || 'Bad request', 400) | |
| 193 | } else { | |
| 194 | console.error('[server] Error:', err.message) | |
| 195 | error(res, 'Internal server error', 500) | |
| 196 | } | |
| 182 | 197 | } |
| 183 | 198 | }) |
| 184 | 199 | |
| … | … | @@ -187,19 +202,62 @@ async function createServer (opts = {}) { |
| 187 | 202 | } |
| 188 | 203 | |
| 189 | 204 | async function handleApi (url, req, res, ctx) { |
| 190 | const { posse, feedStore, accountsBee, prefix } = ctx | |
| 205 | const { posse, feedStore, accountsBee, prefix, authManager, identityRegistry } = ctx | |
| 191 | 206 | const method = req.method |
| 192 | 207 | |
| 208 | // --- Auth endpoints (public) --- | |
| 209 | ||
| 210 | // POST /api/auth/challenge — get a challenge for signing | |
| 211 | if (url === '/api/auth/challenge' && method === 'POST') { | |
| 212 | const challenge = await authManager.generateChallenge() | |
| 213 | json(res, challenge) | |
| 214 | return | |
| 215 | } | |
| 216 | ||
| 217 | // GET /api/auth/me — get current user (if authenticated) | |
| 218 | if (url === '/api/auth/me' && method === 'GET') { | |
| 219 | const auth = await extractAuth(req, authManager) | |
| 220 | if (!auth) { json(res, { user: null }); return } | |
| 221 | ||
| 222 | const user = auth.user || await identityRegistry.getUser(auth.userId) | |
| 223 | json(res, { | |
| 224 | user: user ? { userId: user.userId, username: user.username, name: user.name } : null, | |
| 225 | publicKey: auth.identityId | |
| 226 | }) | |
| 227 | return | |
| 228 | } | |
| 229 | ||
| 230 | // POST /api/auth/username — set username (requires auth) | |
| 231 | if (url === '/api/auth/username' && method === 'POST') { | |
| 232 | const auth = await requireAuth(req, authManager) | |
| 233 | const body = await bodyJson(req) | |
| 234 | if (!body.username) { error(res, 'username required'); return } | |
| 235 | ||
| 236 | const user = await identityRegistry.setUsername(auth.userId, body.username) | |
| 237 | json(res, { user: { userId: user.userId, username: user.username, name: user.name } }) | |
| 238 | return | |
| 239 | } | |
| 240 | ||
| 241 | // --- Write endpoints (require auth) --- | |
| 242 | ||
| 193 | 243 | // POST /api/post — POSSE publish (text only, JSON body) |
| 194 | 244 | if (url === '/api/post' && method === 'POST') { |
| 245 | const auth = await requireAuth(req, authManager) | |
| 195 | 246 | const body = await bodyJson(req) |
| 196 | 247 | const result = await posse.publish(body.text, body.targets || [], body) |
| 248 | ||
| 249 | // Sign the post content | |
| 250 | const signed = signPost(body.text, { publicKey: Buffer.from(auth.identityId || '', 'base64'), secretKey: Buffer.alloc(64) }) | |
| 251 | result.anproto = body.anproto || null | |
| 252 | result.authorPublicKey = auth.identityId || null | |
| 253 | ||
| 197 | 254 | json(res, result) |
| 198 | 255 | return |
| 199 | 256 | } |
| 200 | 257 | |
| 201 | 258 | // POST /api/post-media — POSSE publish with media (multipart) |
| 202 | 259 | if (url === '/api/post-media' && method === 'POST') { |
| 260 | await requireAuth(req, authManager) | |
| 203 | 261 | const { fields, file } = await parseMultipart(req) |
| 204 | 262 | |
| 205 | 263 | if (!file) { error(res, 'No file uploaded'); return } |
| … | … | @@ -250,8 +308,9 @@ async function handleApi (url, req, res, ctx) { |
| 250 | 308 | return |
| 251 | 309 | } |
| 252 | 310 | |
| 253 | // POST /api/accounts/connect — connect a new account | |
| 311 | // POST /api/accounts/connect — connect a new account (requires auth) | |
| 254 | 312 | if (url === '/api/accounts/connect' && method === 'POST') { |
| 313 | await requireAuth(req, authManager) | |
| 255 | 314 | const body = await bodyJson(req) |
| 256 | 315 | const { platform, instance, handle, token, password } = body |
| 257 | 316 | |
| … | … | @@ -272,8 +331,9 @@ async function handleApi (url, req, res, ctx) { |
| 272 | 331 | return |
| 273 | 332 | } |
| 274 | 333 | |
| 275 | // DELETE /api/accounts/:id | |
| 334 | // DELETE /api/accounts/:id (requires auth) | |
| 276 | 335 | if (url.startsWith('/api/accounts/') && method === 'DELETE') { |
| 336 | await requireAuth(req, authManager) | |
| 277 | 337 | const id = decodeURIComponent(url.slice('/api/accounts/'.length)) |
| 278 | 338 | await accountsBee.del(`account/${id}`) |
| 279 | 339 | posse.removeClient(id) |
| … | … | @@ -22,9 +22,31 @@ |
| 22 | 22 | "bridge-vault": "file:../bridge-vault", |
| 23 | 23 | "corestore": "^7.9.2", |
| 24 | 24 | "hyper-feed": "file:../hyper-feed", |
| 25 | "hyper-identity": "file:../../hyper-fleeky/packages/hyper-identity", | |
| 25 | 26 | "hyperbee": "^2.27.3" |
| 26 | 27 | } |
| 27 | 28 | }, |
| 29 | "../../hyper-fleeky/packages/hyper-identity": { | |
| 30 | "version": "1.0.0", | |
| 31 | "license": "MIT", | |
| 32 | "dependencies": { | |
| 33 | "autobase": "^7.25.1", | |
| 34 | "b4a": "^1.6.4", | |
| 35 | "bip39-mnemonic": "^2.5.0", | |
| 36 | "blind-pairing": "^2.3.1", | |
| 37 | "corestore": "^7.8.0", | |
| 38 | "hyperbee": "^2.27.3", | |
| 39 | "hypercore": "^11.24.0", | |
| 40 | "hypercore-crypto": "^3.0.0", | |
| 41 | "hyperswarm": "^4.16.0", | |
| 42 | "keet-identity-key": "github:holepunchto/keet-identity-key", | |
| 43 | "sodium-native": "^4.3.1" | |
| 44 | }, | |
| 45 | "devDependencies": { | |
| 46 | "brittle": "^3.19.1", | |
| 47 | "random-access-memory": "^6.2.1" | |
| 48 | } | |
| 49 | }, | |
| 28 | 50 | "../bridge-envelope": { |
| 29 | 51 | "version": "1.0.0", |
| 30 | 52 | "license": "MIT", |
| … | … | @@ -1129,6 +1151,10 @@ |
| 1129 | 1151 | "resolved": "../hyper-feed", |
| 1130 | 1152 | "link": true |
| 1131 | 1153 | }, |
| 1154 | "node_modules/hyper-identity": { | |
| 1155 | "resolved": "../../hyper-fleeky/packages/hyper-identity", | |
| 1156 | "link": true | |
| 1157 | }, | |
| 1132 | 1158 | "node_modules/hyperbee": { |
| 1133 | 1159 | "version": "2.27.3", |
| 1134 | 1160 | "resolved": "https://registry.npmjs.org/hyperbee/-/hyperbee-2.27.3.tgz", |
| … | … | @@ -21,6 +21,7 @@ |
| 21 | 21 | "bare-subprocess": "^5.2.3", |
| 22 | 22 | "bridge-envelope": "file:../bridge-envelope", |
| 23 | 23 | "bridge-vault": "file:../bridge-vault", |
| 24 | "hyper-identity": "file:../../hyper-fleeky/packages/hyper-identity", | |
| 24 | 25 | "corestore": "^7.9.2", |
| 25 | 26 | "hyper-feed": "file:../hyper-feed", |
| 26 | 27 | "hyperbee": "^2.27.3" |
| … | … | @@ -98,6 +98,7 @@ |
| 98 | 98 | </div> |
| 99 | 99 | |
| 100 | 100 | <script src="js/api.js"></script> |
| 101 | <script src="js/identity.js"></script> | |
| 101 | 102 | <script src="js/timeline.js"></script> |
| 102 | 103 | <script src="js/composer.js"></script> |
| 103 | 104 | <script src="js/app.js"></script> |
| … | … | @@ -1,16 +1,27 @@ |
| 1 | 1 | /** |
| 2 | 2 | * API client for hyper-social-web backend |
| 3 | * | |
| 4 | * Uses Identity.authFetch() for authenticated requests. | |
| 5 | * Read endpoints (timeline, accounts list, notifications) work without auth. | |
| 6 | * Write endpoints (post, connect, disconnect) require auth. | |
| 3 | 7 | */ |
| 4 | 8 | const API = { |
| 5 | 9 | // Base path — empty for direct access, or prefix like '/social' behind reverse proxy |
| 6 | // Detected from the path that loaded index.html | |
| 7 | 10 | base: (function () { |
| 8 | 11 | const p = window.location.pathname.replace(/\/index\.html$/, '').replace(/\/+$/, '') |
| 9 | 12 | return p || '' |
| 10 | 13 | })(), |
| 11 | 14 | |
| 15 | // Authenticated fetch wrapper — uses Identity if available | |
| 16 | async _fetch (url, opts = {}) { | |
| 17 | if (typeof Identity !== 'undefined' && Identity.isReady) { | |
| 18 | return Identity.authFetch(this.base + url, opts) | |
| 19 | } | |
| 20 | return fetch(this.base + url, opts) | |
| 21 | }, | |
| 22 | ||
| 12 | 23 | async post (text, targets) { |
| 13 | const res = await fetch(this.base + '/api/post', { | |
| 24 | const res = await this._fetch('/api/post', { | |
| 14 | 25 | method: 'POST', |
| 15 | 26 | headers: { 'Content-Type': 'application/json' }, |
| 16 | 27 | body: JSON.stringify({ text, targets }) |
| … | … | @@ -39,7 +50,7 @@ const API = { |
| 39 | 50 | }, |
| 40 | 51 | |
| 41 | 52 | async connect (data) { |
| 42 | const res = await fetch(this.base + '/api/accounts/connect', { | |
| 53 | const res = await this._fetch('/api/accounts/connect', { | |
| 43 | 54 | method: 'POST', |
| 44 | 55 | headers: { 'Content-Type': 'application/json' }, |
| 45 | 56 | body: JSON.stringify(data) |
| … | … | @@ -48,7 +59,7 @@ const API = { |
| 48 | 59 | }, |
| 49 | 60 | |
| 50 | 61 | async disconnect (id) { |
| 51 | const res = await fetch(this.base + '/api/accounts/' + encodeURIComponent(id), { | |
| 62 | const res = await this._fetch('/api/accounts/' + encodeURIComponent(id), { | |
| 52 | 63 | method: 'DELETE' |
| 53 | 64 | }) |
| 54 | 65 | return res.json() |
| … | … | @@ -57,5 +68,19 @@ const API = { |
| 57 | 68 | async notifications () { |
| 58 | 69 | const res = await fetch(this.base + '/api/notifications') |
| 59 | 70 | return res.json() |
| 71 | }, | |
| 72 | ||
| 73 | async me () { | |
| 74 | const res = await this._fetch('/api/auth/me') | |
| 75 | return res.json() | |
| 76 | }, | |
| 77 | ||
| 78 | async setUsername (username) { | |
| 79 | const res = await this._fetch('/api/auth/username', { | |
| 80 | method: 'POST', | |
| 81 | headers: { 'Content-Type': 'application/json' }, | |
| 82 | body: JSON.stringify({ username }) | |
| 83 | }) | |
| 84 | return res.json() | |
| 60 | 85 | } |
| 61 | 86 | } |
| … | … | @@ -119,7 +119,12 @@ async function disconnectAccount (id) { |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | 121 | // --- Init --- |
| 122 | document.addEventListener('DOMContentLoaded', () => { | |
| 122 | document.addEventListener('DOMContentLoaded', async () => { | |
| 123 | // Initialize identity (generates keypair on first visit) | |
| 124 | if (typeof Identity !== 'undefined') { | |
| 125 | await Identity.init(API.base) | |
| 126 | } | |
| 127 | ||
| 123 | 128 | initComposer() |
| 124 | 129 | |
| 125 | 130 | // Filter buttons |
| … | … | @@ -0,0 +1,145 @@ |
| 1 | /** | |
| 2 | * Browser-side identity for hyper-social-web | |
| 3 | * | |
| 4 | * Generates ed25519 keypair in browser, stores in localStorage, | |
| 5 | * auto-signs all authenticated requests with challenge-response. | |
| 6 | * | |
| 7 | * Uses @noble/ed25519 (vendored) for browser-compatible signing. | |
| 8 | */ | |
| 9 | ||
| 10 | const Identity = { | |
| 11 | _ed25519: null, | |
| 12 | _keypair: null, // 132-char base64 | |
| 13 | _publicKey: null, // 44-char base64 | |
| 14 | _secretKey: null, // raw bytes (Uint8Array) | |
| 15 | _challenge: null, // cached challenge | |
| 16 | _challengeExpiry: 0, | |
| 17 | _username: null, | |
| 18 | _ready: false, | |
| 19 | ||
| 20 | async init (basePath) { | |
| 21 | // Load ed25519 library | |
| 22 | try { | |
| 23 | const [ed, hashes] = await Promise.all([ | |
| 24 | import((basePath || '') + '/vendor/noble-ed25519.js'), | |
| 25 | import((basePath || '') + '/vendor/noble-sha512.js') | |
| 26 | ]) | |
| 27 | ed.etc.sha512Sync = (...m) => hashes.sha512(ed.etc.concatBytes(...m)) | |
| 28 | ed.etc.sha512Async = async (...m) => hashes.sha512(ed.etc.concatBytes(...m)) | |
| 29 | this._ed25519 = ed | |
| 30 | } catch (err) { | |
| 31 | console.warn('[Identity] Failed to load ed25519:', err.message) | |
| 32 | return | |
| 33 | } | |
| 34 | ||
| 35 | // Load or generate keypair | |
| 36 | const stored = localStorage.getItem('hs-keypair') | |
| 37 | if (stored && stored.length === 132) { | |
| 38 | this._keypair = stored | |
| 39 | } else { | |
| 40 | this._keypair = await this._generateKeypair() | |
| 41 | localStorage.setItem('hs-keypair', this._keypair) | |
| 42 | } | |
| 43 | ||
| 44 | this._publicKey = this._keypair.substring(0, 44) | |
| 45 | this._secretKey = base64ToBytes(this._keypair.substring(44)) | |
| 46 | this._username = localStorage.getItem('hs-username') || null | |
| 47 | this._ready = true | |
| 48 | ||
| 49 | console.log('[Identity] Ready:', this._publicKey.substring(0, 8) + '...') | |
| 50 | }, | |
| 51 | ||
| 52 | async _generateKeypair () { | |
| 53 | const privateKey = this._ed25519.utils.randomPrivateKey() | |
| 54 | const publicKey = await this._ed25519.getPublicKeyAsync(privateKey) | |
| 55 | ||
| 56 | // ed25519 private key is 32 bytes, but sodium format is 64 bytes (priv + pub) | |
| 57 | const secretKey64 = new Uint8Array(64) | |
| 58 | secretKey64.set(privateKey, 0) | |
| 59 | secretKey64.set(publicKey, 32) | |
| 60 | ||
| 61 | return bytesToBase64(publicKey) + bytesToBase64(secretKey64) | |
| 62 | }, | |
| 63 | ||
| 64 | async sign (challenge) { | |
| 65 | if (!this._ed25519 || !this._secretKey) return null | |
| 66 | ||
| 67 | const challengeBytes = new TextEncoder().encode(challenge) | |
| 68 | // noble-ed25519 uses first 32 bytes of secretKey | |
| 69 | const privateKey = this._secretKey.slice(0, 32) | |
| 70 | const signature = await this._ed25519.signAsync(challengeBytes, privateKey) | |
| 71 | return bytesToBase64(signature) | |
| 72 | }, | |
| 73 | ||
| 74 | async getChallenge (base) { | |
| 75 | // Return cached challenge if still valid (1 min before expiry) | |
| 76 | if (this._challenge && Date.now() < this._challengeExpiry - 60000) { | |
| 77 | return this._challenge | |
| 78 | } | |
| 79 | ||
| 80 | try { | |
| 81 | const res = await fetch((base || '') + '/api/auth/challenge', { method: 'POST' }) | |
| 82 | if (!res.ok) return null | |
| 83 | ||
| 84 | const data = await res.json() | |
| 85 | this._challenge = data | |
| 86 | this._challengeExpiry = data.expiresAt || (Date.now() + 4 * 60 * 1000) | |
| 87 | return data | |
| 88 | } catch { | |
| 89 | return null | |
| 90 | } | |
| 91 | }, | |
| 92 | ||
| 93 | /** | |
| 94 | * Wrap fetch with auth headers | |
| 95 | */ | |
| 96 | async authFetch (url, opts = {}) { | |
| 97 | if (!this._ready || !this._publicKey) { | |
| 98 | return fetch(url, opts) | |
| 99 | } | |
| 100 | ||
| 101 | const headers = { ...(opts.headers || {}) } | |
| 102 | headers['X-Identity-PublicKey'] = this._publicKey | |
| 103 | ||
| 104 | // Get challenge and sign it | |
| 105 | const base = API ? API.base : '' | |
| 106 | const challenge = await this.getChallenge(base) | |
| 107 | if (challenge) { | |
| 108 | const sig = await this.sign(challenge.challenge) | |
| 109 | if (sig) { | |
| 110 | headers['X-Identity-Signature'] = sig | |
| 111 | headers['X-Identity-Challenge'] = challenge.challengeId | |
| 112 | } | |
| 113 | } | |
| 114 | ||
| 115 | return fetch(url, { ...opts, headers }) | |
| 116 | }, | |
| 117 | ||
| 118 | get publicKey () { return this._publicKey }, | |
| 119 | get username () { return this._username }, | |
| 120 | get isReady () { return this._ready }, | |
| 121 | ||
| 122 | setUsername (username) { | |
| 123 | this._username = username | |
| 124 | localStorage.setItem('hs-username', username) | |
| 125 | } | |
| 126 | } | |
| 127 | ||
| 128 | // --- Base64 helpers (browser-compatible, no Buffer) --- | |
| 129 | ||
| 130 | function bytesToBase64 (bytes) { | |
| 131 | let binary = '' | |
| 132 | for (let i = 0; i < bytes.length; i++) { | |
| 133 | binary += String.fromCharCode(bytes[i]) | |
| 134 | } | |
| 135 | return btoa(binary) | |
| 136 | } | |
| 137 | ||
| 138 | function base64ToBytes (base64) { | |
| 139 | const binary = atob(base64) | |
| 140 | const bytes = new Uint8Array(binary.length) | |
| 141 | for (let i = 0; i < binary.length; i++) { | |
| 142 | bytes[i] = binary.charCodeAt(i) | |
| 143 | } | |
| 144 | return bytes | |
| 145 | } |
| … | … | @@ -0,0 +1,9 @@ |
| 1 | /** | |
| 2 | * Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.39.0. | |
| 3 | * Original file: /npm/@noble/ed25519@2.0.0/index.js | |
| 4 | * | |
| 5 | * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files | |
| 6 | */ | |
| 7 | /*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ | |
| 8 | const t=2n**255n-19n,e=2n**252n+27742317777372353535851937790883648493n,n=0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,r=0x6666666666666666666666666666666666666666666666666666666666666658n,s={a:-1n,d:37095705934669439343138083508754565189542113879843219016388785533085940283555n,p:t,n:e,h:8,Gx:n,Gy:r},o=(t="")=>{throw new Error(t)},i=t=>"string"==typeof t,a=(t,e)=>!(t instanceof Uint8Array)||"number"==typeof e&&e>0&&t.length!==e?o("Uint8Array expected"):t,c=t=>new Uint8Array(t),l=(t,e)=>a(i(t)?m(t):c(t),e),u=(e,n=t)=>{let r=e%n;return r>=0n?r:n+r},d=t=>t instanceof y?t:o("Point expected");let h;class y{constructor(t,e,n,r){this.ex=t,this.ey=e,this.ez=n,this.et=r}static fromAffine(t){return new y(t.x,t.y,1n,u(t.x*t.y))}static fromHex(e,n=!0){const{d:r}=s,i=(e=l(e,32)).slice();i[31]=-129&e[31];const a=w(i);0n===a||(!n||0n<a&&a<t||o("bad y coord 1"),n||0n<a&&a<2n**256n||o("bad y coord 2"));const c=u(a*a),d=u(c-1n),h=u(r*c+1n);let{isValid:f,value:p}=E(d,h);f||o("bad y coordinate 3");const x=1n===(1n&p);return!!(128&e[31])!==x&&(p=u(-p)),new y(p,a,1n,u(p*a))}get x(){return this.toAffine().x}get y(){return this.toAffine().y}equals(t){const{ex:e,ey:n,ez:r}=this,{ex:s,ey:o,ez:i}=d(t),a=u(e*i),c=u(s*r),l=u(n*i),h=u(o*r);return a===c&&l===h}is0(){return this.equals(p)}negate(){return new y(u(-this.ex),this.ey,this.ez,u(-this.et))}double(){const{ex:t,ey:e,ez:n}=this,{a:r}=s,o=u(t*t),i=u(e*e),a=u(2n*u(n*n)),c=u(r*o),l=t+e,d=u(u(l*l)-o-i),h=c+i,f=h-a,p=c-i,x=u(d*f),b=u(h*p),m=u(d*p),g=u(f*h);return new y(x,b,g,m)}add(t){const{ex:e,ey:n,ez:r,et:o}=this,{ex:i,ey:a,ez:c,et:l}=d(t),{a:h,d:f}=s,p=u(e*i),x=u(n*a),b=u(o*f*l),m=u(r*c),g=u((e+n)*(i+a)-p-x),w=u(m-b),B=u(m+b),A=u(x-h*p),v=u(g*w),R=u(B*A),E=u(g*A),S=u(w*B);return new y(v,R,S,E)}mul(t,n=!0){if(0n===t)return!0===n?o("cannot multiply by 0"):p;if("bigint"==typeof t&&0n<t&&t<e||o("invalid scalar, must be < L"),!n&&this.is0()||1n===t)return this;if(this.equals(f))return M(t).p;let r=p,s=f;for(let e=this;t>0n;e=e.double(),t>>=1n)1n&t?r=r.add(e):n&&(s=s.add(e));return r}multiply(t){return this.mul(t)}clearCofactor(){return this.mul(BigInt(s.h),!1)}isSmallOrder(){return this.clearCofactor().is0()}isTorsionFree(){let t=this.mul(e/2n,!1).double();return t=t.add(this),t.is0()}toAffine(){const{ex:t,ey:e,ez:n}=this;if(this.is0())return{x:0n,y:0n};const r=A(n);return 1n!==u(n*r)&&o("invalid inverse"),{x:u(t*r),y:u(e*r)}}toRawBytes(){const{x:t,y:e}=this.toAffine(),n=g(e);return n[31]|=1n&t?128:0,n}toHex(){return b(this.toRawBytes())}}y.BASE=new y(n,r,1n,u(n*r)),y.ZERO=new y(0n,1n,1n,0n);const{BASE:f,ZERO:p}=y,x=(t,e)=>t.toString(16).padStart(e,"0"),b=t=>Array.from(t).map((t=>x(t,2))).join(""),m=t=>{const e=t.length;(!i(t)||e%2)&&o("hex invalid 1");const n=c(e/2);for(let e=0;e<n.length;e++){const r=2*e,s=t.slice(r,r+2),i=Number.parseInt(s,16);(Number.isNaN(i)||i<0)&&o("hex invalid 2"),n[e]=i}return n},g=t=>m(x(t,64)).reverse(),w=t=>BigInt("0x"+b(c(a(t)).reverse())),B=(...t)=>{const e=c(t.reduce(((t,e)=>t+a(e).length),0));let n=0;return t.forEach((t=>{e.set(t,n),n+=t.length})),e},A=(e,n=t)=>{(0n===e||n<=0n)&&o("no inverse n="+e+" mod="+n);let r=u(e,n),s=n,i=0n,a=1n;for(;0n!==r;){const t=s%r,e=i-a*(s/r);s=r,r=t,i=a,a=e}return 1n===s?u(i,n):o("no inverse")},v=(e,n)=>{let r=e;for(;n-- >0n;)r*=r,r%=t;return r},R=19681161376707505956807079304988542015446066515923890162744021073123829784752n,E=(e,n)=>{const r=u(n*n*n),s=(e=>{const n=e*e%t*e%t,r=v(n,2n)*n%t,s=v(r,1n)*e%t,o=v(s,5n)*s%t,i=v(o,10n)*o%t,a=v(i,20n)*i%t,c=v(a,40n)*a%t,l=v(c,80n)*c%t,u=v(l,80n)*c%t,d=v(u,10n)*o%t;return{pow_p_5_8:v(d,2n)*e%t,b2:n}})(e*u(r*r*n)).pow_p_5_8;let o=u(e*r*s);const i=u(n*o*o),a=o,c=u(o*R),l=i===e,d=i===u(-e),h=i===u(-e*R);return l&&(o=a),(d||h)&&(o=c),1n===(1n&u(o))&&(o=u(-o)),{isValid:l||d,value:o}},S=t=>u(w(t),e);let z;const H=(...t)=>F.sha512Async(...t),P=(...t)=>"function"==typeof z?z(...t):o("etc.sha512Sync not set"),T=t=>{const e=t.slice(0,32);e[0]&=248,e[31]&=127,e[31]|=64;const n=t.slice(32,64),r=S(e),s=f.mul(r),o=s.toRawBytes();return{head:e,prefix:n,scalar:r,point:s,pointBytes:o}},_=t=>H(l(t,32)).then(T),I=t=>T(P(l(t,32))),N=t=>_(t).then((t=>t.pointBytes)),O=t=>I(t).pointBytes;function V(t,e){return t?H(e.hashable).then(e.finish):e.finish(P(e.hashable))}const j=(t,n,r)=>{const{pointBytes:s,scalar:o}=t,i=S(n),c=f.mul(i).toRawBytes();return{hashable:B(c,s,r),finish:t=>{const n=u(i+S(t)*o,e);return a(B(c,g(n)),64)}}},q=async(t,e)=>{const n=l(t),r=await _(e),s=await H(r.prefix,n);return V(!0,j(r,s,n))},C=(t,e)=>{const n=l(t),r=I(e),s=P(r.prefix,n);return V(!1,j(r,s,n))},K=(t,e,n)=>{e=l(e),t=l(t,64);const r=y.fromHex(n,!1),s=y.fromHex(t.slice(0,32),!1),o=w(t.slice(32,64)),i=f.mul(o,!1);return{hashable:B(s.toRawBytes(),r.toRawBytes(),e),finish:t=>{const e=S(t);return s.add(r.mul(e,!1)).add(i.negate()).clearCofactor().is0()}}},U=async(t,e,n)=>V(!0,K(t,e,n)),G=(t,e,n)=>V(!1,K(t,e,n)),Z=()=>"object"==typeof globalThis&&"crypto"in globalThis?globalThis.crypto:void 0,F={bytesToHex:b,hexToBytes:m,concatBytes:B,mod:u,invert:A,randomBytes:t=>{const e=Z();return e||o("crypto.getRandomValues must be defined"),e.getRandomValues(c(t))},sha512Async:async(...t)=>{const e=Z();e||o("crypto.subtle or etc.sha512Async must be defined");const n=B(...t);return c(await e.subtle.digest("SHA-512",n.buffer))},sha512Sync:void 0};Object.defineProperties(F,{sha512Sync:{configurable:!1,get:()=>z,set(t){z||(z=t)}}});const L={getExtendedPublicKeyAsync:_,getExtendedPublicKey:I,randomPrivateKey:()=>F.randomBytes(32),precompute:(t=8,e=f)=>(e.multiply(3n),e)},M=t=>{const e=h||(h=(()=>{const t=[];let e=f,n=e;for(let r=0;r<33;r++){n=e,t.push(n);for(let r=1;r<128;r++)n=n.add(e),t.push(n);e=n.double()}return t})()),n=(t,e)=>{let n=e.negate();return t?n:e};let r=p,s=f;const o=BigInt(255),i=BigInt(8);for(let a=0;a<33;a++){const c=128*a;let l=Number(t&o);t>>=i,l>128&&(l-=256,t+=1n);const u=c,d=c+Math.abs(l)-1,h=a%2!=0,y=l<0;0===l?s=s.add(n(h,e[u])):r=r.add(n(y,e[d]))}return{p:r,f:s}};export{s as CURVE,y as ExtendedPoint,F as etc,O as getPublicKey,N as getPublicKeyAsync,C as sign,q as signAsync,L as utils,G as verify,U as verifyAsync};export default null; | |
| 9 | //# sourceMappingURL=/sm/cd01a92102818c7c46dbe0b9ea67492dd1f0856266ce83a45f80684e6e019347.map |
| … | … | @@ -0,0 +1,8 @@ |
| 1 | /** | |
| 2 | * Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.39.0. | |
| 3 | * Original file: /npm/@noble/hashes@1.3.0/esm/sha512.js | |
| 4 | * | |
| 5 | * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files | |
| 6 | */ | |
| 7 | import"./noble-crypto.js";function t(t){if(!Number.isSafeInteger(t)||t<0)throw new Error(`Wrong positive integer: ${t}`)}function e(t,...e){if(!(t instanceof Uint8Array))throw new TypeError("Expected Uint8Array");if(e.length>0&&!e.includes(t.length))throw new TypeError(`Expected Uint8Array of length ${e}, not of length=${t.length}`)}const s={number:t,bool:function(t){if("boolean"!=typeof t)throw new Error(`Expected boolean, not ${t}`)},bytes:e,hash:function(e){if("function"!=typeof e||"function"!=typeof e.create)throw new Error("Hash should be wrapped by utils.wrapConstructor");t(e.outputLen),t(e.blockLen)},exists:function(t,e=!0){if(t.destroyed)throw new Error("Hash instance has been destroyed");if(e&&t.finished)throw new Error("Hash#digest() has already been called")},output:function(t,s){e(t);const h=s.outputLen;if(t.length<h)throw new Error(`digestInto() expects output buffer of length at least ${h}`)}},h=t=>new DataView(t.buffer,t.byteOffset,t.byteLength);if(!(68===new Uint8Array(new Uint32Array([287454020]).buffer)[0]))throw new Error("Non little-endian hardware is not supported");function i(t){if("string"==typeof t&&(t=function(t){if("string"!=typeof t)throw new TypeError("utf8ToBytes expected string, got "+typeof t);return(new TextEncoder).encode(t)}(t)),!(t instanceof Uint8Array))throw new TypeError(`Expected input type is Uint8Array (got ${typeof t})`);return t}Array.from({length:256},((t,e)=>e.toString(16).padStart(2,"0")));class r{clone(){return this._cloneInto()}}function n(t){const e=e=>t().update(i(e)).digest(),s=t();return e.outputLen=s.outputLen,e.blockLen=s.blockLen,e.create=()=>t(),e}class o extends r{constructor(t,e,s,i){super(),this.blockLen=t,this.outputLen=e,this.padOffset=s,this.isLE=i,this.finished=!1,this.length=0,this.pos=0,this.destroyed=!1,this.buffer=new Uint8Array(t),this.view=h(this.buffer)}update(t){s.exists(this);const{view:e,buffer:r,blockLen:n}=this,o=(t=i(t)).length;for(let s=0;s<o;){const i=Math.min(n-this.pos,o-s);if(i!==n)r.set(t.subarray(s,s+i),this.pos),this.pos+=i,s+=i,this.pos===n&&(this.process(e,0),this.pos=0);else{const e=h(t);for(;n<=o-s;s+=n)this.process(e,s)}}return this.length+=t.length,this.roundClean(),this}digestInto(t){s.exists(this),s.output(t,this),this.finished=!0;const{buffer:e,view:i,blockLen:r,isLE:n}=this;let{pos:o}=this;e[o++]=128,this.buffer.subarray(o).fill(0),this.padOffset>r-o&&(this.process(i,0),o=0);for(let t=o;t<r;t++)e[t]=0;!function(t,e,s,h){if("function"==typeof t.setBigUint64)return t.setBigUint64(e,s,h);const i=BigInt(32),r=BigInt(4294967295),n=Number(s>>i&r),o=Number(s&r),c=h?4:0,a=h?0:4;t.setUint32(e+c,n,h),t.setUint32(e+a,o,h)}(i,r-8,BigInt(8*this.length),n),this.process(i,0);const c=h(t),a=this.outputLen;if(a%4)throw new Error("_sha2: outputLen should be aligned to 32bit");const f=a/4,d=this.get();if(f>d.length)throw new Error("_sha2: outputLen bigger than state");for(let t=0;t<f;t++)c.setUint32(4*t,d[t],n)}digest(){const{buffer:t,outputLen:e}=this;this.digestInto(t);const s=t.slice(0,e);return this.destroy(),s}_cloneInto(t){t||(t=new this.constructor),t.set(...this.get());const{blockLen:e,buffer:s,length:h,finished:i,destroyed:r,pos:n}=this;return t.length=h,t.pos=n,t.finished=i,t.destroyed=r,h%e&&t.buffer.set(s),t}}const c=BigInt(2**32-1),a=BigInt(32);function f(t,e=!1){return e?{h:Number(t&c),l:Number(t>>a&c)}:{h:0|Number(t>>a&c),l:0|Number(t&c)}}const d={fromBig:f,split:function(t,e=!1){let s=new Uint32Array(t.length),h=new Uint32Array(t.length);for(let i=0;i<t.length;i++){const{h:r,l:n}=f(t[i],e);[s[i],h[i]]=[r,n]}return[s,h]},toBig:(t,e)=>BigInt(t>>>0)<<a|BigInt(e>>>0),shrSH:(t,e,s)=>t>>>s,shrSL:(t,e,s)=>t<<32-s|e>>>s,rotrSH:(t,e,s)=>t>>>s|e<<32-s,rotrSL:(t,e,s)=>t<<32-s|e>>>s,rotrBH:(t,e,s)=>t<<64-s|e>>>s-32,rotrBL:(t,e,s)=>t>>>s-32|e<<64-s,rotr32H:(t,e)=>e,rotr32L:(t,e)=>t,rotlSH:(t,e,s)=>t<<s|e>>>32-s,rotlSL:(t,e,s)=>e<<s|t>>>32-s,rotlBH:(t,e,s)=>e<<s-32|t>>>64-s,rotlBL:(t,e,s)=>t<<s-32|e>>>64-s,add:function(t,e,s,h){const i=(e>>>0)+(h>>>0);return{h:t+s+(i/2**32|0)|0,l:0|i}},add3L:(t,e,s)=>(t>>>0)+(e>>>0)+(s>>>0),add3H:(t,e,s,h)=>e+s+h+(t/2**32|0)|0,add4L:(t,e,s,h)=>(t>>>0)+(e>>>0)+(s>>>0)+(h>>>0),add4H:(t,e,s,h,i)=>e+s+h+i+(t/2**32|0)|0,add5H:(t,e,s,h,i,r)=>e+s+h+i+r+(t/2**32|0)|0,add5L:(t,e,s,h,i)=>(t>>>0)+(e>>>0)+(s>>>0)+(h>>>0)+(i>>>0)},[l,b]=d.split(["0x428a2f98d728ae22","0x7137449123ef65cd","0xb5c0fbcfec4d3b2f","0xe9b5dba58189dbbc","0x3956c25bf348b538","0x59f111f1b605d019","0x923f82a4af194f9b","0xab1c5ed5da6d8118","0xd807aa98a3030242","0x12835b0145706fbe","0x243185be4ee4b28c","0x550c7dc3d5ffb4e2","0x72be5d74f27b896f","0x80deb1fe3b1696b1","0x9bdc06a725c71235","0xc19bf174cf692694","0xe49b69c19ef14ad2","0xefbe4786384f25e3","0x0fc19dc68b8cd5b5","0x240ca1cc77ac9c65","0x2de92c6f592b0275","0x4a7484aa6ea6e483","0x5cb0a9dcbd41fbd4","0x76f988da831153b5","0x983e5152ee66dfab","0xa831c66d2db43210","0xb00327c898fb213f","0xbf597fc7beef0ee4","0xc6e00bf33da88fc2","0xd5a79147930aa725","0x06ca6351e003826f","0x142929670a0e6e70","0x27b70a8546d22ffc","0x2e1b21385c26c926","0x4d2c6dfc5ac42aed","0x53380d139d95b3df","0x650a73548baf63de","0x766a0abb3c77b2a8","0x81c2c92e47edaee6","0x92722c851482353b","0xa2bfe8a14cf10364","0xa81a664bbc423001","0xc24b8b70d0f89791","0xc76c51a30654be30","0xd192e819d6ef5218","0xd69906245565a910","0xf40e35855771202a","0x106aa07032bbd1b8","0x19a4c116b8d2d0c8","0x1e376c085141ab53","0x2748774cdf8eeb99","0x34b0bcb5e19b48a8","0x391c0cb3c5c95a63","0x4ed8aa4ae3418acb","0x5b9cca4f7763e373","0x682e6ff3d6b2b8a3","0x748f82ee5defb2fc","0x78a5636f43172f60","0x84c87814a1f0ab72","0x8cc702081a6439ec","0x90befffa23631e28","0xa4506cebde82bde9","0xbef9a3f7b2c67915","0xc67178f2e372532b","0xca273eceea26619c","0xd186b8c721c0c207","0xeada7dd6cde0eb1e","0xf57d4f7fee6ed178","0x06f067aa72176fba","0x0a637dc5a2c898a6","0x113f9804bef90dae","0x1b710b35131c471b","0x28db77f523047d84","0x32caab7b40c72493","0x3c9ebe0a15c9bebc","0x431d67c49c100d4c","0x4cc5d4becb3e42b6","0x597f299cfc657e2a","0x5fcb6fab3ad6faec","0x6c44198c4a475817"].map((t=>BigInt(t)))),u=new Uint32Array(80),x=new Uint32Array(80);class p extends o{constructor(){super(128,64,16,!1),this.Ah=1779033703,this.Al=-205731576,this.Bh=-1150833019,this.Bl=-2067093701,this.Ch=1013904242,this.Cl=-23791573,this.Dh=-1521486534,this.Dl=1595750129,this.Eh=1359893119,this.El=-1377402159,this.Fh=-1694144372,this.Fl=725511199,this.Gh=528734635,this.Gl=-79577749,this.Hh=1541459225,this.Hl=327033209}get(){const{Ah:t,Al:e,Bh:s,Bl:h,Ch:i,Cl:r,Dh:n,Dl:o,Eh:c,El:a,Fh:f,Fl:d,Gh:l,Gl:b,Hh:u,Hl:x}=this;return[t,e,s,h,i,r,n,o,c,a,f,d,l,b,u,x]}set(t,e,s,h,i,r,n,o,c,a,f,d,l,b,u,x){this.Ah=0|t,this.Al=0|e,this.Bh=0|s,this.Bl=0|h,this.Ch=0|i,this.Cl=0|r,this.Dh=0|n,this.Dl=0|o,this.Eh=0|c,this.El=0|a,this.Fh=0|f,this.Fl=0|d,this.Gh=0|l,this.Gl=0|b,this.Hh=0|u,this.Hl=0|x}process(t,e){for(let s=0;s<16;s++,e+=4)u[s]=t.getUint32(e),x[s]=t.getUint32(e+=4);for(let t=16;t<80;t++){const e=0|u[t-15],s=0|x[t-15],h=d.rotrSH(e,s,1)^d.rotrSH(e,s,8)^d.shrSH(e,s,7),i=d.rotrSL(e,s,1)^d.rotrSL(e,s,8)^d.shrSL(e,s,7),r=0|u[t-2],n=0|x[t-2],o=d.rotrSH(r,n,19)^d.rotrBH(r,n,61)^d.shrSH(r,n,6),c=d.rotrSL(r,n,19)^d.rotrBL(r,n,61)^d.shrSL(r,n,6),a=d.add4L(i,c,x[t-7],x[t-16]),f=d.add4H(a,h,o,u[t-7],u[t-16]);u[t]=0|f,x[t]=0|a}let{Ah:s,Al:h,Bh:i,Bl:r,Ch:n,Cl:o,Dh:c,Dl:a,Eh:f,El:p,Fh:g,Fl:w,Gh:L,Gl:H,Hh:y,Hl:B}=this;for(let t=0;t<80;t++){const e=d.rotrSH(f,p,14)^d.rotrSH(f,p,18)^d.rotrBH(f,p,41),E=d.rotrSL(f,p,14)^d.rotrSL(f,p,18)^d.rotrBL(f,p,41),A=f&g^~f&L,S=p&w^~p&H,C=d.add5L(B,E,S,b[t],x[t]),U=d.add5H(C,y,e,A,l[t],u[t]),D=0|C,F=d.rotrSH(s,h,28)^d.rotrBH(s,h,34)^d.rotrBH(s,h,39),G=d.rotrSL(s,h,28)^d.rotrBL(s,h,34)^d.rotrBL(s,h,39),m=s&i^s&n^i&n,I=h&r^h&o^r&o;y=0|L,B=0|H,L=0|g,H=0|w,g=0|f,w=0|p,({h:f,l:p}=d.add(0|c,0|a,0|U,0|D)),c=0|n,a=0|o,n=0|i,o=0|r,i=0|s,r=0|h;const N=d.add3L(D,G,I);s=d.add3H(N,U,F,m),h=0|N}({h:s,l:h}=d.add(0|this.Ah,0|this.Al,0|s,0|h)),({h:i,l:r}=d.add(0|this.Bh,0|this.Bl,0|i,0|r)),({h:n,l:o}=d.add(0|this.Ch,0|this.Cl,0|n,0|o)),({h:c,l:a}=d.add(0|this.Dh,0|this.Dl,0|c,0|a)),({h:f,l:p}=d.add(0|this.Eh,0|this.El,0|f,0|p)),({h:g,l:w}=d.add(0|this.Fh,0|this.Fl,0|g,0|w)),({h:L,l:H}=d.add(0|this.Gh,0|this.Gl,0|L,0|H)),({h:y,l:B}=d.add(0|this.Hh,0|this.Hl,0|y,0|B)),this.set(s,h,i,r,n,o,c,a,f,p,g,w,L,H,y,B)}roundClean(){u.fill(0),x.fill(0)}destroy(){this.buffer.fill(0),this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)}}class g extends p{constructor(){super(),this.Ah=-1942145080,this.Al=424955298,this.Bh=1944164710,this.Bl=-1982016298,this.Ch=502970286,this.Cl=855612546,this.Dh=1738396948,this.Dl=1479516111,this.Eh=258812777,this.El=2077511080,this.Fh=2011393907,this.Fl=79989058,this.Gh=1067287976,this.Gl=1780299464,this.Hh=286451373,this.Hl=-1848208735,this.outputLen=28}}class w extends p{constructor(){super(),this.Ah=573645204,this.Al=-64227540,this.Bh=-1621794909,this.Bl=-934517566,this.Ch=596883563,this.Cl=1867755857,this.Dh=-1774684391,this.Dl=1497426621,this.Eh=-1775747358,this.El=-1467023389,this.Fh=-1101128155,this.Fl=1401305490,this.Gh=721525244,this.Gl=746961066,this.Hh=246885852,this.Hl=-2117784414,this.outputLen=32}}class L extends p{constructor(){super(),this.Ah=-876896931,this.Al=-1056596264,this.Bh=1654270250,this.Bl=914150663,this.Ch=-1856437926,this.Cl=812702999,this.Dh=355462360,this.Dl=-150054599,this.Eh=1731405415,this.El=-4191439,this.Fh=-1900787065,this.Fl=1750603025,this.Gh=-619958771,this.Gl=1694076839,this.Hh=1203062813,this.Hl=-1090891868,this.outputLen=48}}const H=n((()=>new p)),y=n((()=>new g)),B=n((()=>new w)),E=n((()=>new L));export{p as SHA512,E as sha384,H as sha512,y as sha512_224,B as sha512_256};export default null; | |
| 8 | //# sourceMappingURL=/sm/25ef7ec32d52ab376d05edb8d663f6f9885235f7c4d8ebc8cb0aab306fd143bb.map |