Last active
June 25, 2021 11:58
-
-
Save TransparentLC/abdddb1eeade0f694e36b9b9b8c6717d to your computer and use it in GitHub Desktop.
10 KB 左右的 Ed25519 签名和验证算法实现,在 https://github.com/paulmillr/noble-ed25519 的基础上添加了 IIFE,将使用的 SHA-512 从 Web Crypto API 替换成 https://github.com/emn178/js-sha512 从而实现同步调用,并进行了部分修改以减小 minify 后的大小。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Ed25519 = require('./ed25519.min.js'); | |
const hexToBytes = e => new Uint8Array(e.match(/[0-9a-f]{2}/gi).map(e => parseInt(e, 16))); | |
const bytesToHex = e => Array.from(e).map(e => e.toString(16).padStart(2, 0)).join(''); | |
// Test vectors from: | |
// https://datatracker.ietf.org/doc/html/rfc8032#section-7.1 | |
const privateA = hexToBytes('4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb'); | |
const messageA = hexToBytes('72'); | |
const publicA = Ed25519.getPublicKey(privateA); | |
console.assert(bytesToHex(publicA) === '3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c', 'Public A'); | |
const signA = Ed25519.sign(messageA, privateA); | |
console.assert( | |
bytesToHex(signA) === '92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00', | |
'Sign A' | |
); | |
const verifyA = Ed25519.verify(messageA, signA, publicA); | |
console.assert(verifyA, 'Verify A'); | |
const privateB = hexToBytes('c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7'); | |
const messageB = hexToBytes('af82'); | |
const publicB = Ed25519.getPublicKey(privateB); | |
console.assert(bytesToHex(publicB) === 'fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025', 'Public B'); | |
const signB = Ed25519.sign(messageB, privateB); | |
console.assert( | |
bytesToHex(signB) === '6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a', | |
'Sign B' | |
); | |
const verifyB = Ed25519.verify(messageB, signB, publicB); | |
console.assert(verifyB, 'Verify B'); | |
const privateC = hexToBytes('833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42'); | |
const messageC = hexToBytes('' | |
+ 'ddaf35a193617abacc417349ae204131' | |
+ '12e6fa4e89a97ea20a9eeee64b55d39a' | |
+ '2192992a274fc1a836ba3c23a3feebbd' | |
+ '454d4423643ce80e2a9ac94fa54ca49f' | |
); | |
const publicC = Ed25519.getPublicKey(privateC); | |
console.assert(bytesToHex(publicC) === 'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf', 'Public C'); | |
const signC = Ed25519.sign(messageC, privateC); | |
console.assert( | |
bytesToHex(signC) === 'dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704', | |
'Sign C' | |
); | |
const verifyC = Ed25519.verify(messageC, signC, publicC); | |
console.assert(verifyC, 'Verify C'); | |
const privateD = hexToBytes('f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5'); | |
const messageD = hexToBytes('' | |
+ '08b8b2b733424243760fe426a4b54908' | |
+ '632110a66c2f6591eabd3345e3e4eb98' | |
+ 'fa6e264bf09efe12ee50f8f54e9f77b1' | |
+ 'e355f6c50544e23fb1433ddf73be84d8' | |
+ '79de7c0046dc4996d9e773f4bc9efe57' | |
+ '38829adb26c81b37c93a1b270b20329d' | |
+ '658675fc6ea534e0810a4432826bf58c' | |
+ '941efb65d57a338bbd2e26640f89ffbc' | |
+ '1a858efcb8550ee3a5e1998bd177e93a' | |
+ '7363c344fe6b199ee5d02e82d522c4fe' | |
+ 'ba15452f80288a821a579116ec6dad2b' | |
+ '3b310da903401aa62100ab5d1a36553e' | |
+ '06203b33890cc9b832f79ef80560ccb9' | |
+ 'a39ce767967ed628c6ad573cb116dbef' | |
+ 'efd75499da96bd68a8a97b928a8bbc10' | |
+ '3b6621fcde2beca1231d206be6cd9ec7' | |
+ 'aff6f6c94fcd7204ed3455c68c83f4a4' | |
+ '1da4af2b74ef5c53f1d8ac70bdcb7ed1' | |
+ '85ce81bd84359d44254d95629e9855a9' | |
+ '4a7c1958d1f8ada5d0532ed8a5aa3fb2' | |
+ 'd17ba70eb6248e594e1a2297acbbb39d' | |
+ '502f1a8c6eb6f1ce22b3de1a1f40cc24' | |
+ '554119a831a9aad6079cad88425de6bd' | |
+ 'e1a9187ebb6092cf67bf2b13fd65f270' | |
+ '88d78b7e883c8759d2c4f5c65adb7553' | |
+ '878ad575f9fad878e80a0c9ba63bcbcc' | |
+ '2732e69485bbc9c90bfbd62481d9089b' | |
+ 'eccf80cfe2df16a2cf65bd92dd597b07' | |
+ '07e0917af48bbb75fed413d238f5555a' | |
+ '7a569d80c3414a8d0859dc65a46128ba' | |
+ 'b27af87a71314f318c782b23ebfe808b' | |
+ '82b0ce26401d2e22f04d83d1255dc51a' | |
+ 'ddd3b75a2b1ae0784504df543af8969b' | |
+ 'e3ea7082ff7fc9888c144da2af58429e' | |
+ 'c96031dbcad3dad9af0dcbaaaf268cb8' | |
+ 'fcffead94f3c7ca495e056a9b47acdb7' | |
+ '51fb73e666c6c655ade8297297d07ad1' | |
+ 'ba5e43f1bca32301651339e22904cc8c' | |
+ '42f58c30c04aafdb038dda0847dd988d' | |
+ 'cda6f3bfd15c4b4c4525004aa06eeff8' | |
+ 'ca61783aacec57fb3d1f92b0fe2fd1a8' | |
+ '5f6724517b65e614ad6808d6f6ee34df' | |
+ 'f7310fdc82aebfd904b01e1dc54b2927' | |
+ '094b2db68d6f903b68401adebf5a7e08' | |
+ 'd78ff4ef5d63653a65040cf9bfd4aca7' | |
+ '984a74d37145986780fc0b16ac451649' | |
+ 'de6188a7dbdf191f64b5fc5e2ab47b57' | |
+ 'f7f7276cd419c17a3ca8e1b939ae49e4' | |
+ '88acba6b965610b5480109c8b17b80e1' | |
+ 'b7b750dfc7598d5d5011fd2dcc5600a3' | |
+ '2ef5b52a1ecc820e308aa342721aac09' | |
+ '43bf6686b64b2579376504ccc493d97e' | |
+ '6aed3fb0f9cd71a43dd497f01f17c0e2' | |
+ 'cb3797aa2a2f256656168e6c496afc5f' | |
+ 'b93246f6b1116398a346f1a641f3b041' | |
+ 'e989f7914f90cc2c7fff357876e506b5' | |
+ '0d334ba77c225bc307ba537152f3f161' | |
+ '0e4eafe595f6d9d90d11faa933a15ef1' | |
+ '369546868a7f3a45a96768d40fd9d034' | |
+ '12c091c6315cf4fde7cb68606937380d' | |
+ 'b2eaaa707b4c4185c32eddcdd306705e' | |
+ '4dc1ffc872eeee475a64dfac86aba41c' | |
+ '0618983f8741c5ef68d3a101e8a3b8ca' | |
+ 'c60c905c15fc910840b94c00a0b9d0' | |
); | |
const publicD = Ed25519.getPublicKey(privateD); | |
console.assert(bytesToHex(publicD) === '278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e', 'Public D'); | |
const signD = Ed25519.sign(messageD, privateD); | |
console.assert( | |
bytesToHex(signD) === '0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03', | |
'Sign D' | |
); | |
const verifyD = Ed25519.verify(messageD, signD, publicD); | |
console.assert(verifyD, 'Verify D'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! noble-ed25519 - MIT License (c) Paul Miller (paulmillr.com) */ | |
/* | |
* [js-sha512]{@link https://github.com/emn178/js-sha512} | |
* | |
* @version 0.8.0 | |
* @author Chen, Yi-Cyuan [[email protected]] | |
* @copyright Chen, Yi-Cyuan 2014-2018 | |
* @license MIT | |
*/ | |
(() => { | |
/** @type {globalThis} */ | |
const GLOBAL = typeof globalThis !== 'undefined' ? globalThis : (global || self); | |
const { | |
BigInt, | |
Error, | |
Int32Array, | |
Uint8Array, | |
} = GLOBAL; | |
const K = new Int32Array([ | |
0x428A2F98, 0xD728AE22, 0x71374491, 0x23EF65CD, | |
0xB5C0FBCF, 0xEC4D3B2F, 0xE9B5DBA5, 0x8189DBBC, | |
0x3956C25B, 0xF348B538, 0x59F111F1, 0xB605D019, | |
0x923F82A4, 0xAF194F9B, 0xAB1C5ED5, 0xDA6D8118, | |
0xD807AA98, 0xA3030242, 0x12835B01, 0x45706FBE, | |
0x243185BE, 0x4EE4B28C, 0x550C7DC3, 0xD5FFB4E2, | |
0x72BE5D74, 0xF27B896F, 0x80DEB1FE, 0x3B1696B1, | |
0x9BDC06A7, 0x25C71235, 0xC19BF174, 0xCF692694, | |
0xE49B69C1, 0x9EF14AD2, 0xEFBE4786, 0x384F25E3, | |
0x0FC19DC6, 0x8B8CD5B5, 0x240CA1CC, 0x77AC9C65, | |
0x2DE92C6F, 0x592B0275, 0x4A7484AA, 0x6EA6E483, | |
0x5CB0A9DC, 0xBD41FBD4, 0x76F988DA, 0x831153B5, | |
0x983E5152, 0xEE66DFAB, 0xA831C66D, 0x2DB43210, | |
0xB00327C8, 0x98FB213F, 0xBF597FC7, 0xBEEF0EE4, | |
0xC6E00BF3, 0x3DA88FC2, 0xD5A79147, 0x930AA725, | |
0x06CA6351, 0xE003826F, 0x14292967, 0x0A0E6E70, | |
0x27B70A85, 0x46D22FFC, 0x2E1B2138, 0x5C26C926, | |
0x4D2C6DFC, 0x5AC42AED, 0x53380D13, 0x9D95B3DF, | |
0x650A7354, 0x8BAF63DE, 0x766A0ABB, 0x3C77B2A8, | |
0x81C2C92E, 0x47EDAEE6, 0x92722C85, 0x1482353B, | |
0xA2BFE8A1, 0x4CF10364, 0xA81A664B, 0xBC423001, | |
0xC24B8B70, 0xD0F89791, 0xC76C51A3, 0x0654BE30, | |
0xD192E819, 0xD6EF5218, 0xD6990624, 0x5565A910, | |
0xF40E3585, 0x5771202A, 0x106AA070, 0x32BBD1B8, | |
0x19A4C116, 0xB8D2D0C8, 0x1E376C08, 0x5141AB53, | |
0x2748774C, 0xDF8EEB99, 0x34B0BCB5, 0xE19B48A8, | |
0x391C0CB3, 0xC5C95A63, 0x4ED8AA4A, 0xE3418ACB, | |
0x5B9CCA4F, 0x7763E373, 0x682E6FF3, 0xD6B2B8A3, | |
0x748F82EE, 0x5DEFB2FC, 0x78A5636F, 0x43172F60, | |
0x84C87814, 0xA1F0AB72, 0x8CC70208, 0x1A6439EC, | |
0x90BEFFFA, 0x23631E28, 0xA4506CEB, 0xDE82BDE9, | |
0xBEF9A3F7, 0xB2C67915, 0xC67178F2, 0xE372532B, | |
0xCA273ECE, 0xEA26619C, 0xD186B8C7, 0x21C0C207, | |
0xEADA7DD6, 0xCDE0EB1E, 0xF57D4F7F, 0xEE6ED178, | |
0x06F067AA, 0x72176FBA, 0x0A637DC5, 0xA2C898A6, | |
0x113F9804, 0xBEF90DAE, 0x1B710B35, 0x131C471B, | |
0x28DB77F5, 0x23047D84, 0x32CAAB7B, 0x40C72493, | |
0x3C9EBE0A, 0x15C9BEBC, 0x431D67C4, 0x9C100D4C, | |
0x4CC5D4BE, 0xCB3E42B6, 0x597F299C, 0xFC657E2A, | |
0x5FCB6FAB, 0x3AD6FAEC, 0x6C44198C, 0x4A475817 | |
]); | |
class Sha512 { | |
constructor() { | |
this.blocks = new Int32Array(160); | |
this.h = new Int32Array([ | |
0x6A09E667, 0xF3BCC908, | |
0xBB67AE85, 0x84CAA73B, | |
0x3C6EF372, 0xFE94F82B, | |
0xA54FF53A, 0x5F1D36F1, | |
0x510E527F, 0xADE682D1, | |
0x9B05688C, 0x2B3E6C1F, | |
0x1F83D9AB, 0xFB41BD6B, | |
0x5BE0CD19, 0x137E2179, | |
]); | |
this.block = this.start = this.bytes = this.hBytes = 0; | |
this.finalized = this.hashed = false; | |
} | |
/** | |
* @param {Uint8Array} message | |
* @returns | |
*/ | |
update(message) { | |
if (this.finalized) return; | |
let index = 0; | |
let i; | |
const length = message.length; | |
const blocks = this.blocks; | |
while (index < length) { | |
if (this.hashed) { | |
this.hashed = false; | |
blocks.set(new Int32Array(33)); | |
blocks[0] = this.block; | |
} | |
for (i = this.start; index < length && i < 128; index++) { | |
blocks[i >> 2] |= message[index] << (3 - (i++ & 3) << 3); | |
} | |
this.lastByteIndex = i; | |
this.bytes += i - this.start; | |
if (i >= 128) { | |
this.block = blocks[32]; | |
this.start = i - 128; | |
this.hash(); | |
this.hashed = true; | |
} else { | |
this.start = i; | |
} | |
} | |
if (this.bytes > 4294967295) { | |
this.hBytes += this.bytes / 4294967296 << 0; | |
this.bytes %= 4294967296; | |
} | |
} | |
/** | |
* @returns {Uint8Array} | |
*/ | |
final() { | |
if (this.finalized) return; | |
this.finalized = true; | |
const blocks = this.blocks; | |
let i = this.lastByteIndex; | |
blocks[32] = this.block; | |
blocks[i >> 2] |= 0x80 << (3 - (i & 3) << 3); | |
this.block = blocks[32]; | |
if (i >= 112) { | |
if (!this.hashed) { | |
this.hash(); | |
} | |
blocks.set(new Int32Array(33)); | |
blocks[0] = this.block; | |
} | |
blocks[30] = this.hBytes << 3 | this.bytes >>> 29; | |
blocks[31] = this.bytes << 3; | |
this.hash(); | |
const hash = new Uint8Array(64); | |
const dataView = new DataView(hash.buffer); | |
for (let i = 0; i < 16; i++) { | |
dataView.setInt32(i << 2, this.h[i]); | |
} | |
return hash; | |
} | |
hash() { | |
const blocks = this.blocks; | |
const h = this.h; | |
const g = this.h.slice(); | |
const s = new Int32Array(4); | |
const c = new Int32Array(4); | |
const a = new Int32Array(8); | |
let i, j, majh, majl, t1h, t1l, t2h, t2l, chh, chl; | |
for (j = 32; j < 160; j += 2) { | |
t1h = blocks[j - 30]; | |
t1l = blocks[j - 29]; | |
s[0] = ((t1h >>> 1) | (t1l << 31)) ^ ((t1h >>> 8) | (t1l << 24)) ^ (t1h >>> 7); | |
s[1] = ((t1l >>> 1) | (t1h << 31)) ^ ((t1l >>> 8) | (t1h << 24)) ^ ((t1l >>> 7) | t1h << 25); | |
t1h = blocks[j - 4]; | |
t1l = blocks[j - 3]; | |
s[2] = ((t1h >>> 19) | (t1l << 13)) ^ ((t1l >>> 29) | (t1h << 3)) ^ (t1h >>> 6); | |
s[3] = ((t1l >>> 19) | (t1h << 13)) ^ ((t1h >>> 29) | (t1l << 3)) ^ ((t1l >>> 6) | t1h << 26); | |
t1h = blocks[j - 32]; | |
t1l = blocks[j - 31]; | |
t2h = blocks[j - 14]; | |
t2l = blocks[j - 13]; | |
c[0] = (t2l & 0xFFFF) + (t1l & 0xFFFF) + (s[1] & 0xFFFF) + (s[3] & 0xFFFF); | |
c[1] = (t2l >>> 16) + (t1l >>> 16) + (s[1] >>> 16) + (s[3] >>> 16) + (c[0] >>> 16); | |
c[2] = (t2h & 0xFFFF) + (t1h & 0xFFFF) + (s[0] & 0xFFFF) + (s[2] & 0xFFFF) + (c[1] >>> 16); | |
c[3] = (t2h >>> 16) + (t1h >>> 16) + (s[0] >>> 16) + (s[2] >>> 16) + (c[2] >>> 16); | |
blocks[j] = (c[3] << 16) | (c[2] & 0xFFFF); | |
blocks[j + 1] = (c[1] << 16) | (c[0] & 0xFFFF); | |
} | |
a[6] = g[2] & g[4]; | |
a[7] = g[3] & g[5]; | |
for (j = 0; j < 160; j += 8) { | |
for (i = 0; i < 4; i++) { // [0, 1, 2, 3] | |
const m = (8 - (i << 1) & 7); // [0, 6, 4, 2] | |
const n = m + 1; // [1, 7, 5, 3] | |
const o = m + 8; // [8, 14, 12, 10] | |
const p = o + 1; // [9, 15, 13, 11] | |
const q = i << 1; // [0, 2, 4, 6] | |
const r = q + 1; // [1, 3, 5, 7] | |
s[0] = ((g[m] >>> 28) | (g[n] << 4)) ^ ((g[n] >>> 2) | (g[m] << 30)) ^ ((g[n] >>> 7) | (g[m] << 25)); | |
s[1] = ((g[n] >>> 28) | (g[m] << 4)) ^ ((g[m] >>> 2) | (g[n] << 30)) ^ ((g[m] >>> 7) | (g[n] << 25)); | |
s[2] = ((g[o] >>> 14) | (g[p] << 18)) ^ ((g[o] >>> 18) | (g[p] << 14)) ^ ((g[p] >>> 9) | (g[o] << 23)); | |
s[3] = ((g[p] >>> 14) | (g[o] << 18)) ^ ((g[p] >>> 18) | (g[o] << 14)) ^ ((g[o] >>> 9) | (g[p] << 23)); | |
a[q] = g[m] & g[(m + 2) & 7]; | |
a[r] = g[n] & g[(n + 2) & 7]; | |
majh = a[q] ^ (g[q] & g[(q + 4) & 7]) ^ a[(q + 6) & 7]; | |
majl = a[r] ^ (g[r] & g[(r + 4) & 7]) ^ a[(r + 6) & 7]; | |
chh = (g[o] & g[((m + 2) & 7) + 8]) ^ (~g[o] & g[((m + 4) & 7) + 8]); | |
chl = (g[p] & g[((n + 2) & 7) + 8]) ^ (~g[p] & g[((n + 4) & 7) + 8]); | |
t1h = blocks[j + q]; | |
t1l = blocks[j + r]; | |
t2h = K[j + q]; | |
t2l = K[j + r]; | |
c[0] = (t2l & 0xFFFF) + (t1l & 0xFFFF) + (chl & 0xFFFF) + (s[3] & 0xFFFF) + (g[16 - r] & 0xFFFF); | |
c[1] = (t2l >>> 16) + (t1l >>> 16) + (chl >>> 16) + (s[3] >>> 16) + (g[16 - r] >>> 16) + (c[0] >>> 16); | |
c[2] = (t2h & 0xFFFF) + (t1h & 0xFFFF) + (chh & 0xFFFF) + (s[2] & 0xFFFF) + (g[15 - r] & 0xFFFF) + (c[1] >>> 16); | |
c[3] = (t2h >>> 16) + (t1h >>> 16) + (chh >>> 16) + (s[2] >>> 16) + (g[15 - r] >>> 16) + (c[2] >>> 16); | |
t1h = (c[3] << 16) | (c[2] & 0xFFFF); | |
t1l = (c[1] << 16) | (c[0] & 0xFFFF); | |
c[0] = (majl & 0xFFFF) + (s[1] & 0xFFFF); | |
c[1] = (majl >>> 16) + (s[1] >>> 16) + (c[0] >>> 16); | |
c[2] = (majh & 0xFFFF) + (s[0] & 0xFFFF) + (c[1] >>> 16); | |
c[3] = (majh >>> 16) + (s[0] >>> 16) + (c[2] >>> 16); | |
t2h = (c[3] << 16) | (c[2] & 0xFFFF); | |
t2l = (c[1] << 16) | (c[0] & 0xFFFF); | |
c[0] = (g[8 - r] & 0xFFFF) + (t1l & 0xFFFF); | |
c[1] = (g[8 - r] >>> 16) + (t1l >>> 16) + (c[0] >>> 16); | |
c[2] = (g[7 - r] & 0xFFFF) + (t1h & 0xFFFF) + (c[1] >>> 16); | |
c[3] = (g[7 - r] >>> 16) + (t1h >>> 16) + (c[2] >>> 16); | |
g[14 - q] = (c[3] << 16) | (c[2] & 0xFFFF); | |
g[15 - q] = (c[1] << 16) | (c[0] & 0xFFFF); | |
c[0] = (t2l & 0xFFFF) + (t1l & 0xFFFF); | |
c[1] = (t2l >>> 16) + (t1l >>> 16) + (c[0] >>> 16); | |
c[2] = (t2h & 0xFFFF) + (t1h & 0xFFFF) + (c[1] >>> 16); | |
c[3] = (t2h >>> 16) + (t1h >>> 16) + (c[2] >>> 16); | |
g[6 - q] = (c[3] << 16) | (c[2] & 0xFFFF); | |
g[7 - q] = (c[1] << 16) | (c[0] & 0xFFFF); | |
} | |
} | |
for (j = 0; j < 16; j += 2) { | |
c[0] = (h[j + 1] & 0xFFFF) + (g[j + 1] & 0xFFFF); | |
c[1] = (h[j + 1] >>> 16) + (g[j + 1] >>> 16) + (c[0] >>> 16); | |
c[2] = (h[j] & 0xFFFF) + (g[j] & 0xFFFF) + (c[1] >>> 16); | |
c[3] = (h[j] >>> 16) + (g[j] >>> 16) + (c[2] >>> 16); | |
this.h[j] = (c[3] << 16) | (c[2] & 0xFFFF); | |
this.h[j + 1] = (c[1] << 16) | (c[0] & 0xFFFF); | |
} | |
} | |
} | |
const sha512 = message => { | |
const s = new Sha512; | |
s.update(message); | |
return s.final(); | |
}; | |
const CURVE = { | |
a: -1n, | |
d: 37095705934669439343138083508754565189542113879843219016388785533085940283555n, | |
P: 2n ** 255n - 19n, | |
n: 2n ** 252n + 27742317777372353535851937790883648493n, | |
h: 8n, | |
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n, | |
Gy: 46316835694926478169428394003475163141307993866256225615783033603165251855960n, | |
}; | |
const SQRT_M1 = 19681161376707505956807079304988542015446066515923890162744021073123829784752n; | |
const concatBytes = (...arrays) => { | |
if (arrays.length === 1) | |
return arrays[0]; | |
const length = arrays.reduce((a, arr) => a + arr.length, 0); | |
const result = new Uint8Array(length); | |
for (let i = 0, pad = 0; i < arrays.length; i++) { | |
const arr = arrays[i]; | |
result.set(arr, pad); | |
pad += arr.length; | |
} | |
return result; | |
} | |
const hexToBytes = hex => { | |
const array = new Uint8Array(hex.length / 2); | |
for (let i = 0; i < array.length; i++) { | |
const j = i * 2; | |
array[i] = parseInt(hex.slice(j, j + 2), 16); | |
} | |
return array; | |
} | |
const numberToHex = num => { | |
const hex = num.toString(16); | |
return hex.length & 1 ? `0${hex}` : hex; | |
} | |
const edIsNegative = num => (mod(num) & 1n) === 1n; | |
const isValidScalar = num => (typeof num === 'bigint' && num > 0n) || (typeof num === 'number' && num > 0 && Number.isSafeInteger(num)); | |
const bytesToNumberLE = uint8a => { | |
let value = 0n; | |
for (let i = 0; i < uint8a.length; i++) { | |
value += BigInt(uint8a[i]) << (8n * BigInt(i)); | |
} | |
return value; | |
} | |
const mod = (a, b = CURVE.P) => { | |
const res = a % b; | |
return res >= 0n ? res : b + res; | |
} | |
const invert = (number, modulo = CURVE.P) => { | |
if (number === 0n || modulo <= 0n) { | |
throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`); | |
} | |
let a = mod(number, modulo); | |
let b = modulo; | |
let [x, y, u, v] = [0n, 1n, 1n, 0n]; | |
while (a !== 0n) { | |
const q = b / a; | |
const r = b % a; | |
const m = x - u * q; | |
const n = y - v * q; | |
[b, a] = [a, r]; | |
[x, y] = [u, v]; | |
[u, v] = [m, n]; | |
} | |
const gcd = b; | |
if (gcd !== 1n) throw new Error('invert: does not exist'); | |
return mod(x, modulo); | |
} | |
const invertBatch = (nums, n = CURVE.P) => { | |
const len = nums.length; | |
const scratch = []; | |
scratch.length = len; | |
let acc = 1n; | |
for (let i = 0; i < len; i++) { | |
if (nums[i] === 0n) | |
continue; | |
scratch[i] = acc; | |
acc = mod(acc * nums[i], n); | |
} | |
acc = invert(acc, n); | |
for (let i = len - 1; i >= 0; i--) { | |
if (nums[i] === 0n) | |
continue; | |
let tmp = mod(acc * nums[i], n); | |
nums[i] = mod(acc * scratch[i], n); | |
acc = tmp; | |
} | |
return nums; | |
} | |
const pow2 = (x, power) => { | |
const { P } = CURVE; | |
let res = x; | |
while (power-- > 0n) { | |
res *= res; | |
res %= P; | |
} | |
return res; | |
} | |
const pow_2_252_3 = x => { | |
const { P } = CURVE; | |
const x2 = (x * x) % P; | |
const b2 = (x2 * x) % P; | |
const b4 = (pow2(b2, 2n) * b2) % P; | |
const b5 = (pow2(b4, 1n) * x) % P; | |
const b10 = (pow2(b5, 5n) * b5) % P; | |
const b20 = (pow2(b10, 10n) * b10) % P; | |
const b40 = (pow2(b20, 20n) * b20) % P; | |
const b80 = (pow2(b40, 40n) * b40) % P; | |
const b160 = (pow2(b80, 80n) * b80) % P; | |
const b240 = (pow2(b160, 80n) * b80) % P; | |
const b250 = (pow2(b240, 10n) * b10) % P; | |
const pow_p_5_8 = (pow2(b250, 2n) * x) % P; | |
return pow_p_5_8; | |
} | |
const uvRatio = (u, v) => { | |
const v3 = mod(v * v * v); | |
const v7 = mod(v3 * v3 * v); | |
let x = mod(u * v3 * pow_2_252_3(u * v7)); | |
const vx2 = mod(v * x * x); | |
const root1 = x; | |
const root2 = mod(x * SQRT_M1); | |
const useRoot1 = vx2 === u; | |
const useRoot2 = vx2 === mod(-u); | |
const noRoot = vx2 === mod(-u * SQRT_M1); | |
if (useRoot1) | |
x = root1; | |
if (useRoot2 || noRoot) | |
x = root2; | |
if (edIsNegative(x)) | |
x = mod(-x); | |
return { isValid: useRoot1 || useRoot2, value: x }; | |
} | |
const sha512ToNumberLE = (...args) => { | |
const messageArray = concatBytes(...args); | |
const hash = sha512(messageArray); | |
const value = bytesToNumberLE(hash); | |
return mod(value, CURVE.n); | |
} | |
const encodePrivate = privateBytes => { | |
const head = privateBytes.slice(0, 32); | |
head[0] &= 248; | |
head[31] &= 127; | |
head[31] |= 64; | |
return mod(bytesToNumberLE(head), CURVE.n); | |
} | |
const isWithinCurveOrder = num => 0 < num && num < CURVE.n; | |
class ExtendedPoint { | |
constructor(x, y, z, t) { | |
this.x = x; | |
this.y = y; | |
this.z = z; | |
this.t = t; | |
} | |
static fromAffine(p) { | |
if (!(p instanceof Point)) { | |
throw new Error('ExtendedPoint#fromAffine: expected Point'); | |
} | |
if (p.equals(Point.ZERO)) | |
return ExtendedPoint.ZERO; | |
return new ExtendedPoint(p.x, p.y, 1n, mod(p.x * p.y)); | |
} | |
static toAffineBatch(points) { | |
const toInv = invertBatch(points.map((p) => p.z)); | |
return points.map((p, i) => p.toAffine(toInv[i])); | |
} | |
static normalizeZ(points) { | |
return this.toAffineBatch(points).map(this.fromAffine); | |
} | |
equals(other) { | |
const a = this; | |
const b = other; | |
const [T1, T2, Z1, Z2] = [a.t, b.t, a.z, b.z]; | |
return mod(T1 * Z2) === mod(T2 * Z1); | |
} | |
negate() { | |
return new ExtendedPoint(mod(-this.x), this.y, this.z, mod(-this.t)); | |
} | |
double() { | |
const X1 = this.x; | |
const Y1 = this.y; | |
const Z1 = this.z; | |
const { a } = CURVE; | |
const A = mod(X1 ** 2n); | |
const B = mod(Y1 ** 2n); | |
const C = mod(2n * Z1 ** 2n); | |
const D = mod(a * A); | |
const E = mod((X1 + Y1) ** 2n - A - B); | |
const G = mod(D + B); | |
const F = mod(G - C); | |
const H = mod(D - B); | |
const X3 = mod(E * F); | |
const Y3 = mod(G * H); | |
const T3 = mod(E * H); | |
const Z3 = mod(F * G); | |
return new ExtendedPoint(X3, Y3, Z3, T3); | |
} | |
add(other) { | |
const X1 = this.x; | |
const Y1 = this.y; | |
const Z1 = this.z; | |
const T1 = this.t; | |
const X2 = other.x; | |
const Y2 = other.y; | |
const Z2 = other.z; | |
const T2 = other.t; | |
const A = mod((Y1 - X1) * (Y2 + X2)); | |
const B = mod((Y1 + X1) * (Y2 - X2)); | |
const F = mod(B - A); | |
if (F === 0n) { | |
return this.double(); | |
} | |
const C = mod(Z1 * 2n * T2); | |
const D = mod(T1 * 2n * Z2); | |
const E = mod(D + C); | |
const G = mod(B + A); | |
const H = mod(D - C); | |
const X3 = mod(E * F); | |
const Y3 = mod(G * H); | |
const T3 = mod(E * H); | |
const Z3 = mod(F * G); | |
return new ExtendedPoint(X3, Y3, Z3, T3); | |
} | |
subtract(other) { | |
return this.add(other.negate()); | |
} | |
multiplyUnsafe(scalar) { | |
if (!isValidScalar(scalar)) | |
throw new Error('Point#multiply: expected number or bigint'); | |
let n = mod(BigInt(scalar), CURVE.n); | |
if (n === 1n) | |
return this; | |
let p = ExtendedPoint.ZERO; | |
let d = this; | |
while (n > 0n) { | |
if (n & 1n) | |
p = p.add(d); | |
d = d.double(); | |
n >>= 1n; | |
} | |
return p; | |
} | |
precomputeWindow(W) { | |
const windows = 256 / W + 1; | |
let points = []; | |
let p = this; | |
let base = p; | |
for (let window = 0; window < windows; window++) { | |
base = p; | |
points.push(base); | |
for (let i = 1; i < 2 ** (W - 1); i++) { | |
base = base.add(p); | |
points.push(base); | |
} | |
p = base.double(); | |
} | |
return points; | |
} | |
wNAF(n, affinePoint) { | |
if (!affinePoint && this.equals(ExtendedPoint.BASE)) | |
affinePoint = Point.BASE; | |
const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1; | |
if (256 % W) { | |
throw new Error('Point#wNAF: Invalid precomputation window, must be power of 2'); | |
} | |
let precomputes = ExtendedPoint.normalizeZ(this.precomputeWindow(W)); | |
let p = ExtendedPoint.ZERO; | |
let f = ExtendedPoint.ZERO; | |
const windows = 256 / W + 1; | |
const windowSize = 2 ** (W - 1); | |
const mask = BigInt(2 ** W - 1); | |
const maxNumber = 2 ** W; | |
const shiftBy = BigInt(W); | |
for (let window = 0; window < windows; window++) { | |
const offset = window * windowSize; | |
let wbits = Number(n & mask); | |
n >>= shiftBy; | |
if (wbits > windowSize) { | |
wbits -= maxNumber; | |
n += 1n; | |
} | |
if (wbits === 0) { | |
f = f.add(window % 2 ? precomputes[offset].negate() : precomputes[offset]); | |
} | |
else { | |
const cached = precomputes[offset + Math.abs(wbits) - 1]; | |
p = p.add(wbits < 0 ? cached.negate() : cached); | |
} | |
} | |
return [p, f]; | |
} | |
multiply(scalar, affinePoint) { | |
if (!isValidScalar(scalar)) | |
throw new Error('Point#multiply: expected number or bigint'); | |
const n = mod(BigInt(scalar), CURVE.n); | |
return ExtendedPoint.normalizeZ(this.wNAF(n, affinePoint))[0]; | |
} | |
toAffine(invZ = invert(this.z)) { | |
const x = mod(this.x * invZ); | |
const y = mod(this.y * invZ); | |
return new Point(x, y); | |
} | |
} | |
ExtendedPoint.BASE = new ExtendedPoint(CURVE.Gx, CURVE.Gy, 1n, mod(CURVE.Gx * CURVE.Gy)); | |
ExtendedPoint.ZERO = new ExtendedPoint(0n, 1n, 1n, 0n); | |
class Point { | |
constructor(x, y) { | |
this.x = x; | |
this.y = y; | |
} | |
static fromHex(hash) { | |
const { d, P } = CURVE; | |
const bytes = hash; | |
if (bytes.length !== 32) | |
throw new Error('Point.fromHex: expected 32 bytes'); | |
const last = bytes[31]; | |
const normedLast = last & ~0x80; | |
const isLastByteOdd = (last & 0x80) !== 0; | |
const normed = Uint8Array.from(Array.from(bytes.slice(0, 31)).concat(normedLast)); | |
const y = bytesToNumberLE(normed); | |
if (y >= P) | |
throw new Error('Point.fromHex expects hex <= Fp'); | |
const y2 = mod(y * y); | |
const u = mod(y2 - 1n); | |
const v = mod(d * y2 + 1n); | |
let { isValid, value: x } = uvRatio(u, v); | |
if (!isValid) throw new Error('Point.fromHex: invalid y coordinate'); | |
const isXOdd = (x & 1n) === 1n; | |
if (isLastByteOdd !== isXOdd) { | |
x = mod(-x); | |
} | |
return new Point(x, y); | |
} | |
static fromPrivateKey(privateKey) { | |
return Point.BASE.multiply(encodePrivate(sha512(privateKey))); | |
} | |
toRawBytes() { | |
const hex = numberToHex(this.y); | |
const u8 = new Uint8Array(32); | |
for (let i = hex.length - 2, j = 0; j < 32 && i >= 0; i -= 2, j++) { | |
u8[j] = parseInt(hex[i] + hex[i + 1], 16); | |
} | |
const mask = this.x & 1n ? 0x80 : 0; | |
u8[31] |= mask; | |
return u8; | |
} | |
equals(other) { | |
return this.x === other.x && this.y === other.y; | |
} | |
// negate() { | |
// return new Point(mod(-this.x), this.y); | |
// } | |
// add(other) { | |
// return ExtendedPoint.fromAffine(this).add(ExtendedPoint.fromAffine(other)).toAffine(); | |
// } | |
// subtract(other) { | |
// return this.add(other.negate()); | |
// } | |
multiply(scalar) { | |
return ExtendedPoint.fromAffine(this).multiply(scalar, this).toAffine(); | |
} | |
} | |
Point.BASE = new Point(CURVE.Gx, CURVE.Gy); | |
Point.ZERO = new Point(0n, 1n); | |
class Signature { | |
constructor(r, s) { | |
this.r = r; | |
this.s = s; | |
} | |
static fromHex(hex) { | |
const r = Point.fromHex(hex.slice(0, 32)); | |
const s = bytesToNumberLE(hex.slice(32)); | |
if (!isWithinCurveOrder(s)) | |
throw new Error('Signature.fromHex expects s <= CURVE.n'); | |
return new Signature(r, s); | |
} | |
toRawBytes() { | |
const numberBytes = hexToBytes(numberToHex(this.s)).reverse(); | |
const sBytes = new Uint8Array(32); | |
sBytes.set(numberBytes); | |
const res = new Uint8Array(64); | |
res.set(this.r.toRawBytes()); | |
res.set(sBytes, 32); | |
return res; | |
} | |
} | |
const Ed25519 = { | |
/** | |
* @param {Uint8Array} privateKey | |
* @returns {Uint8Array} | |
*/ | |
'getPublicKey': privateKey => Point.fromPrivateKey(privateKey).toRawBytes(), | |
/** | |
* | |
* @param {Uint8Array} message | |
* @param {Uint8Array} privateKey | |
* @returns {Uint8Array} | |
*/ | |
'sign': (message, privateKey) => { | |
const privBytes = sha512(privateKey); | |
const p = encodePrivate(privBytes); | |
const P = Point.BASE.multiply(p); | |
const r = sha512ToNumberLE(privBytes.slice(32), message); | |
const R = Point.BASE.multiply(r); | |
const h = sha512ToNumberLE(R.toRawBytes(), P.toRawBytes(), message); | |
const S = mod(r + h * p, CURVE.n); | |
const sig = new Signature(R, S); | |
return sig.toRawBytes(); | |
}, | |
/** | |
* @param {Uint8Array} message | |
* @param {Uint8Array} signature | |
* @param {Uint8Array} publicKey | |
* @returns {Boolean} | |
*/ | |
'verify': (message, signature, publicKey) => { | |
publicKey = Point.fromHex(publicKey); | |
signature = Signature.fromHex(signature); | |
const hs = sha512ToNumberLE(signature.r.toRawBytes(), publicKey.toRawBytes(), message); | |
const Ph = ExtendedPoint.fromAffine(publicKey).multiplyUnsafe(hs); | |
const Gs = ExtendedPoint.BASE.multiply(signature.s); | |
const RPh = ExtendedPoint.fromAffine(signature.r).add(Ph); | |
return RPh.subtract(Gs).multiplyUnsafe(8n).equals(ExtendedPoint.ZERO); | |
}, | |
}; | |
if (typeof module !== 'undefined') { | |
module.exports = Ed25519; | |
} else { | |
GLOBAL['Ed25519'] = Ed25519; | |
} | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(()=>{const t="undefined"!=typeof globalThis?globalThis:global||self,{BigInt:n,Error:e,Int32Array:s,Uint8Array:i}=t,r=new s([1116352408,3609767458,1899447441,602891725,3049323471,3964484399,3921009573,2173295548,961987163,4081628472,1508970993,3053834265,2453635748,2937671579,2870763221,3664609560,3624381080,2734883394,310598401,1164996542,607225278,1323610764,1426881987,3590304994,1925078388,4068182383,2162078206,991336113,2614888103,633803317,3248222580,3479774868,3835390401,2666613458,4022224774,944711139,264347078,2341262773,604807628,2007800933,770255983,1495990901,1249150122,1856431235,1555081692,3175218132,1996064986,2198950837,2554220882,3999719339,2821834349,766784016,2952996808,2566594879,3210313671,3203337956,3336571891,1034457026,3584528711,2466948901,113926993,3758326383,338241895,168717936,666307205,1188179964,773529912,1546045734,1294757372,1522805485,1396182291,2643833823,1695183700,2343527390,1986661051,1014477480,2177026350,1206759142,2456956037,344077627,2730485921,1290863460,2820302411,3158454273,3259730800,3505952657,3345764771,106217008,3516065817,3606008344,3600352804,1432725776,4094571909,1467031594,275423344,851169720,430227734,3100823752,506948616,1363258195,659060556,3750685593,883997877,3785050280,958139571,3318307427,1322822218,3812723403,1537002063,2003034995,1747873779,3602036899,1955562222,1575990012,2024104815,1125592928,2227730452,2716904306,2361852424,442776044,2428436474,593698344,2756734187,3733110249,3204031479,2999351573,3329325298,3815920427,3391569614,3928383900,3515267271,566280711,3940187606,3454069534,4118630271,4000239992,116418474,1914138554,174292421,2731055270,289380356,3203993006,460393269,320620315,685471733,587496836,852142971,1086792851,1017036298,365543100,1126000580,2618297676,1288033470,3409855158,1501505948,4234509866,1607167915,987167468,1816402316,1246189591]);class o{constructor(){this.t=new s(160),this.i=new s([1779033703,4089235720,3144134277,2227873595,1013904242,4271175723,2773480762,1595750129,1359893119,2917565137,2600822924,725511199,528734635,4215389547,1541459225,327033209]),this.o=this.start=this.h=this.u=0,this.l=this.m=!1}update(t){if(this.l)return;let n,e=0;const i=t.length,r=this.t;for(;e<i;){for(this.m&&(this.m=!1,r.set(new s(33)),r[0]=this.o),n=this.start;e<i&&n<128;e++)r[n>>2]|=t[e]<<(3-(3&n++)<<3);this.p=n,this.h+=n-this.start,n>=128?(this.o=r[32],this.start=n-128,this.hash(),this.m=!0):this.start=n}this.h>4294967295&&(this.u+=this.h/4294967296<<0,this.h%=4294967296)}P(){if(this.l)return;this.l=!0;const t=this.t;let n=this.p;t[32]=this.o,t[n>>2]|=128<<(3-(3&n)<<3),this.o=t[32],n>=112&&(this.m||this.hash(),t.set(new s(33)),t[0]=this.o),t[30]=this.u<<3|this.h>>>29,t[31]=this.h<<3,this.hash();const e=new i(64),r=new DataView(e.buffer);for(let t=0;t<16;t++)r.setInt32(t<<2,this.i[t]);return e}hash(){const t=this.t,n=this.i,e=this.i.slice(),i=new s(4),o=new s(4),h=new s(8);let c,u,f,l,w,a,d,m,p,y;for(u=32;u<160;u+=2)w=t[u-30],a=t[u-29],i[0]=(w>>>1|a<<31)^(w>>>8|a<<24)^w>>>7,i[1]=(a>>>1|w<<31)^(a>>>8|w<<24)^(a>>>7|w<<25),w=t[u-4],a=t[u-3],i[2]=(w>>>19|a<<13)^(a>>>29|w<<3)^w>>>6,i[3]=(a>>>19|w<<13)^(w>>>29|a<<3)^(a>>>6|w<<26),w=t[u-32],a=t[u-31],d=t[u-14],m=t[u-13],o[0]=(65535&m)+(65535&a)+(65535&i[1])+(65535&i[3]),o[1]=(m>>>16)+(a>>>16)+(i[1]>>>16)+(i[3]>>>16)+(o[0]>>>16),o[2]=(65535&d)+(65535&w)+(65535&i[0])+(65535&i[2])+(o[1]>>>16),o[3]=(d>>>16)+(w>>>16)+(i[0]>>>16)+(i[2]>>>16)+(o[2]>>>16),t[u]=o[3]<<16|65535&o[2],t[u+1]=o[1]<<16|65535&o[0];for(h[6]=e[2]&e[4],h[7]=e[3]&e[5],u=0;u<160;u+=8)for(c=0;c<4;c++){const n=8-(c<<1)&7,s=n+1,x=n+8,b=x+1,P=c<<1,g=P+1;i[0]=(e[n]>>>28|e[s]<<4)^(e[s]>>>2|e[n]<<30)^(e[s]>>>7|e[n]<<25),i[1]=(e[s]>>>28|e[n]<<4)^(e[n]>>>2|e[s]<<30)^(e[n]>>>7|e[s]<<25),i[2]=(e[x]>>>14|e[b]<<18)^(e[x]>>>18|e[b]<<14)^(e[b]>>>9|e[x]<<23),i[3]=(e[b]>>>14|e[x]<<18)^(e[b]>>>18|e[x]<<14)^(e[x]>>>9|e[b]<<23),h[P]=e[n]&e[n+2&7],h[g]=e[s]&e[s+2&7],f=h[P]^e[P]&e[P+4&7]^h[P+6&7],l=h[g]^e[g]&e[g+4&7]^h[g+6&7],p=e[x]&e[8+(n+2&7)]^~e[x]&e[8+(n+4&7)],y=e[b]&e[8+(s+2&7)]^~e[b]&e[8+(s+4&7)],w=t[u+P],a=t[u+g],d=r[u+P],m=r[u+g],o[0]=(65535&m)+(65535&a)+(65535&y)+(65535&i[3])+(65535&e[16-g]),o[1]=(m>>>16)+(a>>>16)+(y>>>16)+(i[3]>>>16)+(e[16-g]>>>16)+(o[0]>>>16),o[2]=(65535&d)+(65535&w)+(65535&p)+(65535&i[2])+(65535&e[15-g])+(o[1]>>>16),o[3]=(d>>>16)+(w>>>16)+(p>>>16)+(i[2]>>>16)+(e[15-g]>>>16)+(o[2]>>>16),w=o[3]<<16|65535&o[2],a=o[1]<<16|65535&o[0],o[0]=(65535&l)+(65535&i[1]),o[1]=(l>>>16)+(i[1]>>>16)+(o[0]>>>16),o[2]=(65535&f)+(65535&i[0])+(o[1]>>>16),o[3]=(f>>>16)+(i[0]>>>16)+(o[2]>>>16),d=o[3]<<16|65535&o[2],m=o[1]<<16|65535&o[0],o[0]=(65535&e[8-g])+(65535&a),o[1]=(e[8-g]>>>16)+(a>>>16)+(o[0]>>>16),o[2]=(65535&e[7-g])+(65535&w)+(o[1]>>>16),o[3]=(e[7-g]>>>16)+(w>>>16)+(o[2]>>>16),e[14-P]=o[3]<<16|65535&o[2],e[15-P]=o[1]<<16|65535&o[0],o[0]=(65535&m)+(65535&a),o[1]=(m>>>16)+(a>>>16)+(o[0]>>>16),o[2]=(65535&d)+(65535&w)+(o[1]>>>16),o[3]=(d>>>16)+(w>>>16)+(o[2]>>>16),e[6-P]=o[3]<<16|65535&o[2],e[7-P]=o[1]<<16|65535&o[0]}for(u=0;u<16;u+=2)o[0]=(65535&n[u+1])+(65535&e[u+1]),o[1]=(n[u+1]>>>16)+(e[u+1]>>>16)+(o[0]>>>16),o[2]=(65535&n[u])+(65535&e[u])+(o[1]>>>16),o[3]=(n[u]>>>16)+(e[u]>>>16)+(o[2]>>>16),this.i[u]=o[3]<<16|65535&o[2],this.i[u+1]=o[1]<<16|65535&o[0]}}const h=t=>{const n=new o;return n.update(t),n.P()},c={a:-1n,d:37095705934669439343138083508754565189542113879843219016388785533085940283555n,g:2n**255n-19n,n:2n**252n+27742317777372353535851937790883648493n,i:8n,v:15112221349535400772501151409588531511454012693041857206046113283949847762202n,A:46316835694926478169428394003475163141307993866256225615783033603165251855960n},u=19681161376707505956807079304988542015446066515923890162744021073123829784752n,f=t=>{const n=t.toString(16);return 1&n.length?`0${n}`:n},l=t=>"bigint"==typeof t&&t>0n||"number"==typeof t&&t>0&&Number.isSafeInteger(t),w=t=>{let e=0n;for(let s=0;s<t.length;s++)e+=n(t[s])<<8n*n(s);return e},a=(t,n=c.g)=>{const e=t%n;return e>=0n?e:n+e},d=(t,n=c.g)=>{if(0n===t||n<=0n)throw new e(`invert: expected positive integers, got n=${t} mod=${n}`);let s=a(t,n),i=n,[r,o,h,u]=[0n,1n,1n,0n];for(;0n!==s;){const t=i/s,n=i%s,e=r-h*t,c=o-u*t;[i,s]=[s,n],[r,o]=[h,u],[h,u]=[e,c]}if(1n!==i)throw new e("invert: does not exist");return a(r,n)},m=(t,n)=>{const{g:e}=c;let s=t;for(;n-- >0n;)s*=s,s%=e;return s},p=(...t)=>{const n=((...t)=>{if(1===t.length)return t[0];const n=t.reduce(((t,n)=>t+n.length),0),e=new i(n);for(let n=0,s=0;n<t.length;n++){const i=t[n];e.set(i,s),s+=i.length}return e})(...t),e=h(n),s=w(e);return a(s,c.n)},y=t=>{const n=t.slice(0,32);return n[0]&=248,n[31]&=127,n[31]|=64,a(w(n),c.n)};class x{constructor(t,n,e,s){this.x=t,this.y=n,this.z=e,this.H=s}static I(t){if(!(t instanceof b))throw new e("ExtendedPoint#fromAffine: expected Point");return t.equals(b.ZERO)?x.ZERO:new x(t.x,t.y,1n,a(t.x*t.y))}static B(t){const n=((t,n=c.g)=>{const e=t.length,s=[];s.length=e;let i=1n;for(let r=0;r<e;r++)0n!==t[r]&&(s[r]=i,i=a(i*t[r],n));i=d(i,n);for(let r=e-1;r>=0;r--){if(0n===t[r])continue;let e=a(i*t[r],n);t[r]=a(i*s[r],n),i=e}return t})(t.map((t=>t.z)));return t.map(((t,e)=>t.N(n[e])))}static V(t){return this.B(t).map(this.I)}equals(t){const n=t,[e,s,i,r]=[this.H,n.H,this.z,n.z];return a(e*r)===a(s*i)}F(){return new x(a(-this.x),this.y,this.z,a(-this.H))}R(){const t=this.x,n=this.y,e=this.z,{a:s}=c,i=a(t**2n),r=a(n**2n),o=a(2n*e**2n),h=a(s*i),u=a((t+n)**2n-i-r),f=a(h+r),l=a(f-o),w=a(h-r),d=a(u*l),m=a(f*w),p=a(u*w),y=a(l*f);return new x(d,m,y,p)}add(t){const n=this.x,e=this.y,s=this.z,i=this.H,r=t.x,o=t.y,h=t.z,c=t.H,u=a((e-n)*(o+r)),f=a((e+n)*(o-r)),l=a(f-u);if(0n===l)return this.R();const w=a(2n*s*c),d=a(2n*i*h),m=a(d+w),p=a(f+u),y=a(d-w),b=a(m*l),P=a(p*y),g=a(m*y),v=a(l*p);return new x(b,P,v,g)}U(t){return this.add(t.F())}$(t){if(!l(t))throw new e("Point#multiply: expected number or bigint");let s=a(n(t),c.n);if(1n===s)return this;let i=x.ZERO,r=this;for(;s>0n;)1n&s&&(i=i.add(r)),r=r.R(),s>>=1n;return i}q(t){const n=256/t+1;let e=[],s=this,i=s;for(let r=0;r<n;r++){i=s,e.push(i);for(let n=1;n<2**(t-1);n++)i=i.add(s),e.push(i);s=i.R()}return e}G(t,s){!s&&this.equals(x.K)&&(s=b.K);const i=s&&s.T||1;if(256%i)throw new e("Point#wNAF: Invalid precomputation window, must be power of 2");let r=x.V(this.q(i)),o=x.ZERO,h=x.ZERO;const c=256/i+1,u=2**(i-1),f=n(2**i-1),l=2**i,w=n(i);for(let n=0;n<c;n++){const e=n*u;let s=Number(t&f);if(t>>=w,s>u&&(s-=l,t+=1n),0===s)h=h.add(n%2?r[e].F():r[e]);else{const t=r[e+Math.abs(s)-1];o=o.add(s<0?t.F():t)}}return[o,h]}multiply(t,s){if(!l(t))throw new e("Point#multiply: expected number or bigint");const i=a(n(t),c.n);return x.V(this.G(i,s))[0]}N(t=d(this.z)){const n=a(this.x*t),e=a(this.y*t);return new b(n,e)}}x.K=new x(c.v,c.A,1n,a(c.v*c.A)),x.ZERO=new x(0n,1n,1n,0n);class b{constructor(t,n){this.x=t,this.y=n}static C(t){const{d:n,g:s}=c,r=t;if(32!==r.length)throw new e("Point.fromHex: expected 32 bytes");const o=r[31],h=-129&o,f=0!=(128&o),l=i.from(Array.from(r.slice(0,31)).concat(h)),d=w(l);if(d>=s)throw new e("Point.fromHex expects hex <= Fp");const p=a(d*d),y=a(p-1n),x=a(n*p+1n);let{D:P,value:g}=((t,n)=>{const e=a(n*n*n),s=a(e*e*n);let i=a(t*e*(t=>{const{g:n}=c,e=t*t%n*t%n,s=m(e,2n)*e%n,i=m(s,1n)*t%n,r=m(i,5n)*i%n,o=m(r,10n)*r%n,h=m(o,20n)*o%n,u=m(h,40n)*h%n,f=m(u,80n)*u%n,l=m(f,80n)*u%n,w=m(l,10n)*r%n;return m(w,2n)*t%n})(t*s));const r=a(n*i*i),o=i,h=a(i*u),f=r===t,l=r===a(-t),w=r===a(-t*u);return f&&(i=o),(l||w)&&(i=h),1n===(1n&a(i))&&(i=a(-i)),{D:f||l,value:i}})(y,x);if(!P)throw new e("Point.fromHex: invalid y coordinate");return f!==(1n===(1n&g))&&(g=a(-g)),new b(g,d)}static M(t){return b.K.multiply(y(h(t)))}S(){const t=f(this.y),n=new i(32);for(let e=t.length-2,s=0;s<32&&e>=0;e-=2,s++)n[s]=parseInt(t[e]+t[e+1],16);const e=1n&this.x?128:0;return n[31]|=e,n}equals(t){return this.x===t.x&&this.y===t.y}multiply(t){return x.I(this).multiply(t,this).N()}}b.K=new b(c.v,c.A),b.ZERO=new b(0n,1n);class P{constructor(t,n){this.r=t,this.s=n}static C(t){const n=b.C(t.slice(0,32)),s=w(t.slice(32));if(!(0<(i=s)&&i<c.n))throw new e("Signature.fromHex expects s <= CURVE.n");var i;return new P(n,s)}S(){const t=(t=>{const n=new i(t.length/2);for(let e=0;e<n.length;e++){const s=2*e;n[e]=parseInt(t.slice(s,s+2),16)}return n})(f(this.s)).reverse(),n=new i(32);n.set(t);const e=new i(64);return e.set(this.r.S()),e.set(n,32),e}}const g={getPublicKey:t=>b.M(t).S(),sign:(t,n)=>{const e=h(n),s=y(e),i=b.K.multiply(s),r=p(e.slice(32),t),o=b.K.multiply(r),u=p(o.S(),i.S(),t),f=a(r+u*s,c.n);return new P(o,f).S()},verify:(t,n,e)=>{e=b.C(e),n=P.C(n);const s=p(n.r.S(),e.S(),t),i=x.I(e).$(s),r=x.K.multiply(n.s);return x.I(n.r).add(i).U(r).$(8n).equals(x.ZERO)}};"undefined"!=typeof module?module.exports=g:t.Ed25519=g})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment