Fix StartOS 0.4 TypeScript packaging to match SDK API
This commit is contained in:
+5
-5
@@ -793,11 +793,11 @@ accounts.google.com FALSE / TRUE 1808253661 __Host-3PLSID s.youtube:g.a0007whSQ6
|
|||||||
.youtube.com TRUE / FALSE 0 PREF f6=80&f7=4100&hl=en&tz=UTC
|
.youtube.com TRUE / FALSE 0 PREF f6=80&f7=4100&hl=en&tz=UTC
|
||||||
.youtube.com TRUE / TRUE 1806760505 __Secure-1PSIDTS sidts-CjQBWhotCRA5YMKYuDyG5wumZa4VFuY3F7Fh2duwXD0UmuF6N7mEL_UYQOvz_1NTpoI1B-f4EAA
|
.youtube.com TRUE / TRUE 1806760505 __Secure-1PSIDTS sidts-CjQBWhotCRA5YMKYuDyG5wumZa4VFuY3F7Fh2duwXD0UmuF6N7mEL_UYQOvz_1NTpoI1B-f4EAA
|
||||||
.youtube.com TRUE / TRUE 1806760505 __Secure-3PSIDTS sidts-CjQBWhotCRA5YMKYuDyG5wumZa4VFuY3F7Fh2duwXD0UmuF6N7mEL_UYQOvz_1NTpoI1B-f4EAA
|
.youtube.com TRUE / TRUE 1806760505 __Secure-3PSIDTS sidts-CjQBWhotCRA5YMKYuDyG5wumZa4VFuY3F7Fh2duwXD0UmuF6N7mEL_UYQOvz_1NTpoI1B-f4EAA
|
||||||
.youtube.com TRUE / FALSE 1807301008 SIDCC AKEyXzUUyF0XN5paW4VLY0NtxejSk0M28OtfHP5n4n5pOvmXQZyuMzKtfpKiNzQdoxyuNYzuog
|
.youtube.com TRUE / FALSE 1807301085 SIDCC AKEyXzXgdAiG1XmsO6K4iH5FtGh7LIwOuNXfcpr5EGRc9-ui2HNWRgT22phg7UqdZ7T0IbHkAQ
|
||||||
.youtube.com TRUE / TRUE 1807301008 __Secure-1PSIDCC AKEyXzUKO_KXpaA8y_dIx4hE0QqX4gfoqvsJg_JCzh5xMDXVTRzzXQWoHvptSZthdcHfYa15NA
|
.youtube.com TRUE / TRUE 1807301085 __Secure-1PSIDCC AKEyXzWuJrc5O2Tj22mrqnWkZHm7OWgSdYcSpgQzFBUdNkN59NPPP5dvoYyklnoIcjYBRJ6fig
|
||||||
.youtube.com TRUE / TRUE 1807301008 __Secure-3PSIDCC AKEyXzURyWvFIoT3UnuYN9789UBC3QqMjdjMkg4gfrVyPT5SQOJwjtlYGwRzqd0HCuQeYSwdjxY
|
.youtube.com TRUE / TRUE 1807301085 __Secure-3PSIDCC AKEyXzWOuhBtJ1lUFiMkfoaPFM9tv_48nUP_moR0NXf3Z29M7YN2J8XV973XZiE1Pgea4WIHud8
|
||||||
.youtube.com TRUE / TRUE 1791317008 VISITOR_INFO1_LIVE ENb1E2-qMSc
|
.youtube.com TRUE / TRUE 1791317085 VISITOR_INFO1_LIVE ENb1E2-qMSc
|
||||||
.youtube.com TRUE / TRUE 1791317008 VISITOR_PRIVACY_METADATA CgJVUxIEGgAgUg%3D%3D
|
.youtube.com TRUE / TRUE 1791317085 VISITOR_PRIVACY_METADATA CgJVUxIEGgAgUg%3D%3D
|
||||||
.youtube.com TRUE / TRUE 1791313395 __Secure-ROLLOUT_TOKEN CLav7oDA64mAEBDv_MPOo6WTAxiar9XZuuGTAw%3D%3D
|
.youtube.com TRUE / TRUE 1791313395 __Secure-ROLLOUT_TOKEN CLav7oDA64mAEBDv_MPOo6WTAxiar9XZuuGTAw%3D%3D
|
||||||
.youtube.com TRUE / TRUE 1780182149 __Secure-YNID 13.YT=uPn267RhS6_DPP8X1r3X0mUsAq8X0L4zeknD1-ypQ02nK4yp2NT8iig-SVdbf_A0GP18-P4CgEglD8GsSkPjJVXW0IPFBWBykHuwczveAI30caCVgCUue4RQg2a9nLaU0s1BA2YJRygITXiXP7DGUp4lUDUhfK8-AEdzSeF_NcrFxnhs1lSzfO-og_isBQuS1O3Mv-kcOPgdkhUGWl4ZbIazInJka_fj45KQYi0IpoyMATQrN21FBdpCB-elz4s0Jk7cSB265FSf9McS8oQf0jIEW3pRbBJ2gTYIv5CDgJBGde-A43F8W2QnG2FLgc8huPihY9duf8fWyL6P9a5YLw
|
.youtube.com TRUE / TRUE 1780182149 __Secure-YNID 13.YT=uPn267RhS6_DPP8X1r3X0mUsAq8X0L4zeknD1-ypQ02nK4yp2NT8iig-SVdbf_A0GP18-P4CgEglD8GsSkPjJVXW0IPFBWBykHuwczveAI30caCVgCUue4RQg2a9nLaU0s1BA2YJRygITXiXP7DGUp4lUDUhfK8-AEdzSeF_NcrFxnhs1lSzfO-og_isBQuS1O3Mv-kcOPgdkhUGWl4ZbIazInJka_fj45KQYi0IpoyMATQrN21FBdpCB-elz4s0Jk7cSB265FSf9McS8oQf0jIEW3pRbBJ2gTYIv5CDgJBGde-A43F8W2QnG2FLgc8huPihY9duf8fWyL6P9a5YLw
|
||||||
.youtube.com TRUE / TRUE 0 YSC JFh3yO0TPqY
|
.youtube.com TRUE / TRUE 0 YSC JFh3yO0TPqY
|
||||||
|
|||||||
@@ -9,6 +9,26 @@
|
|||||||
"subscriptionId": "sub-1772423650145",
|
"subscriptionId": "sub-1772423650145",
|
||||||
"subscriptionName": "Macro Voices",
|
"subscriptionName": "Macro Voices",
|
||||||
"status": "pending"
|
"status": "pending"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "auto-1775764996750-LPDabHeg7co",
|
||||||
|
"videoId": "LPDabHeg7co",
|
||||||
|
"url": "https://www.youtube.com/watch?v=LPDabHeg7co",
|
||||||
|
"title": "RABBIT HOLE RECAP #404: THE RISE OF THE PETROSAT WITH ODELL AND MARTY BENT",
|
||||||
|
"uploadDate": "20260409",
|
||||||
|
"subscriptionId": "sub-1771016441117",
|
||||||
|
"subscriptionName": "TFTC",
|
||||||
|
"status": "pending"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "auto-1775765083579-EaAtMvvZrrg",
|
||||||
|
"videoId": "EaAtMvvZrrg",
|
||||||
|
"url": "https://www.youtube.com/watch?v=EaAtMvvZrrg",
|
||||||
|
"title": "MacroVoices #527 Adam Rozencwajg: What Comes Next After The Iran Crisis",
|
||||||
|
"uploadDate": "20260409",
|
||||||
|
"subscriptionId": "sub-1772423650145",
|
||||||
|
"subscriptionName": "Macro Voices",
|
||||||
|
"status": "pending"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
+13
-13
@@ -5,7 +5,7 @@
|
|||||||
"url": "https://www.youtube.com/@TFTC/streams",
|
"url": "https://www.youtube.com/@TFTC/streams",
|
||||||
"name": "TFTC",
|
"name": "TFTC",
|
||||||
"createdAt": "2026-02-06T00:00:00.000Z",
|
"createdAt": "2026-02-06T00:00:00.000Z",
|
||||||
"lastChecked": "2026-04-09T19:03:16.671Z",
|
"lastChecked": "2026-04-09T20:03:16.751Z",
|
||||||
"paused": false,
|
"paused": false,
|
||||||
"channelId": "UCtdbWsnfA08KhSUO4amVLaQ"
|
"channelId": "UCtdbWsnfA08KhSUO4amVLaQ"
|
||||||
},
|
},
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
"url": "https://www.youtube.com/@MartyBentTFTC/videos",
|
"url": "https://www.youtube.com/@MartyBentTFTC/videos",
|
||||||
"name": "Marty Bent",
|
"name": "Marty Bent",
|
||||||
"createdAt": "2026-02-08T00:00:00.000Z",
|
"createdAt": "2026-02-08T00:00:00.000Z",
|
||||||
"lastChecked": "2026-04-09T19:03:25.378Z",
|
"lastChecked": "2026-04-09T20:03:25.378Z",
|
||||||
"paused": false,
|
"paused": false,
|
||||||
"channelId": "UCUQcW3jxfQfEUS8kqR5pJtQ"
|
"channelId": "UCUQcW3jxfQfEUS8kqR5pJtQ"
|
||||||
},
|
},
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
"url": "https://www.youtube.com/@thejackmallersshow/streams",
|
"url": "https://www.youtube.com/@thejackmallersshow/streams",
|
||||||
"name": "THE JACK MALLERS SHOW",
|
"name": "THE JACK MALLERS SHOW",
|
||||||
"createdAt": "2026-02-01T00:00:00.000Z",
|
"createdAt": "2026-02-01T00:00:00.000Z",
|
||||||
"lastChecked": "2026-04-09T19:03:26.591Z",
|
"lastChecked": "2026-04-09T20:03:26.490Z",
|
||||||
"paused": false,
|
"paused": false,
|
||||||
"channelId": "UC3ol9RQbQHqle_Uly6w9LfA"
|
"channelId": "UC3ol9RQbQHqle_Uly6w9LfA"
|
||||||
},
|
},
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"url": "https://www.youtube.com/@Bg2Pod/videos",
|
"url": "https://www.youtube.com/@Bg2Pod/videos",
|
||||||
"name": "@Bg2Pod",
|
"name": "@Bg2Pod",
|
||||||
"createdAt": "2025-10-01T00:00:00.000Z",
|
"createdAt": "2025-10-01T00:00:00.000Z",
|
||||||
"lastChecked": "2026-04-09T19:03:27.833Z",
|
"lastChecked": "2026-04-09T20:03:27.651Z",
|
||||||
"paused": false,
|
"paused": false,
|
||||||
"channelId": "UC-yRDvpR99LUc5l7i7jLzew"
|
"channelId": "UC-yRDvpR99LUc5l7i7jLzew"
|
||||||
},
|
},
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
"url": "https://www.youtube.com/@PrestonPysh/videos",
|
"url": "https://www.youtube.com/@PrestonPysh/videos",
|
||||||
"name": "@PrestonPysh",
|
"name": "@PrestonPysh",
|
||||||
"createdAt": "2025-10-01T00:00:00.000Z",
|
"createdAt": "2025-10-01T00:00:00.000Z",
|
||||||
"lastChecked": "2026-04-09T19:04:25.611Z",
|
"lastChecked": "2026-04-09T20:04:24.347Z",
|
||||||
"paused": false,
|
"paused": false,
|
||||||
"channelId": "UCLTdCY-fNXc1GqzIuflK-OQ"
|
"channelId": "UCLTdCY-fNXc1GqzIuflK-OQ"
|
||||||
},
|
},
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
"name": "Andrew Huberman",
|
"name": "Andrew Huberman",
|
||||||
"channelId": "UC2D2CMWXMOVWx7giW1n3LIg",
|
"channelId": "UC2D2CMWXMOVWx7giW1n3LIg",
|
||||||
"createdAt": "2026-02-01T00:00:00.000Z",
|
"createdAt": "2026-02-01T00:00:00.000Z",
|
||||||
"lastChecked": "2026-04-09T19:04:26.755Z",
|
"lastChecked": "2026-04-09T20:04:25.512Z",
|
||||||
"paused": false
|
"paused": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
"name": "Finding Mastery",
|
"name": "Finding Mastery",
|
||||||
"channelId": "UCSZrR-KiOLjUPbhPoPYCxNQ",
|
"channelId": "UCSZrR-KiOLjUPbhPoPYCxNQ",
|
||||||
"createdAt": "2026-02-14T00:20:33.890Z",
|
"createdAt": "2026-02-14T00:20:33.890Z",
|
||||||
"lastChecked": "2026-04-09T19:04:35.806Z",
|
"lastChecked": "2026-04-09T20:04:33.639Z",
|
||||||
"paused": false
|
"paused": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
"type": "youtube",
|
"type": "youtube",
|
||||||
"channelId": "UC5Gat6FdyiG5ydUUHqPTAEQ",
|
"channelId": "UC5Gat6FdyiG5ydUUHqPTAEQ",
|
||||||
"createdAt": "2026-02-10T00:00:00.000Z",
|
"createdAt": "2026-02-10T00:00:00.000Z",
|
||||||
"lastChecked": "2026-04-09T19:04:42.980Z",
|
"lastChecked": "2026-04-09T20:04:40.387Z",
|
||||||
"paused": false
|
"paused": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
"type": "podcast",
|
"type": "podcast",
|
||||||
"channelId": null,
|
"channelId": null,
|
||||||
"createdAt": "2026-02-08T00:00:00.000Z",
|
"createdAt": "2026-02-08T00:00:00.000Z",
|
||||||
"lastChecked": "2026-04-09T19:04:43.388Z",
|
"lastChecked": "2026-04-09T20:04:40.777Z",
|
||||||
"paused": false
|
"paused": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
"type": "youtube",
|
"type": "youtube",
|
||||||
"channelId": "UCESLZhusAkFfsNsApnjF_Cg",
|
"channelId": "UCESLZhusAkFfsNsApnjF_Cg",
|
||||||
"createdAt": "2026-02-13T00:00:00.000Z",
|
"createdAt": "2026-02-13T00:00:00.000Z",
|
||||||
"lastChecked": "2026-04-09T19:04:44.606Z",
|
"lastChecked": "2026-04-09T20:04:41.995Z",
|
||||||
"paused": false
|
"paused": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
"type": "youtube",
|
"type": "youtube",
|
||||||
"channelId": "UCICRehoZjq3ZtAWgRJX118A",
|
"channelId": "UCICRehoZjq3ZtAWgRJX118A",
|
||||||
"createdAt": "2026-02-02T00:00:00.000Z",
|
"createdAt": "2026-02-02T00:00:00.000Z",
|
||||||
"lastChecked": "2026-04-09T19:04:46.151Z",
|
"lastChecked": "2026-04-09T20:04:43.579Z",
|
||||||
"paused": false
|
"paused": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
"type": "youtube",
|
"type": "youtube",
|
||||||
"channelId": "UCf0PBRjhf0rF8fWBIxTuoWA",
|
"channelId": "UCf0PBRjhf0rF8fWBIxTuoWA",
|
||||||
"createdAt": "2026-02-20T00:00:00.000Z",
|
"createdAt": "2026-02-20T00:00:00.000Z",
|
||||||
"lastChecked": "2026-04-09T19:04:47.083Z",
|
"lastChecked": "2026-04-09T20:04:44.528Z",
|
||||||
"paused": false
|
"paused": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
"type": "youtube",
|
"type": "youtube",
|
||||||
"channelId": "UCkrwgzhIBKccuDsi_SvZtnQ",
|
"channelId": "UCkrwgzhIBKccuDsi_SvZtnQ",
|
||||||
"createdAt": "2026-03-06T00:00:00.000Z",
|
"createdAt": "2026-03-06T00:00:00.000Z",
|
||||||
"lastChecked": "2026-04-09T19:04:48.067Z",
|
"lastChecked": "2026-04-09T20:04:45.373Z",
|
||||||
"paused": false
|
"paused": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../fast-xml-parser/src/cli/cli.js
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../mime/bin/cli.js
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../@vercel/ncc/dist/ncc/cli.js
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../prettier/bin/prettier.cjs
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../typescript/bin/tsc
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../typescript/bin/tsserver
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../yaml/bin.mjs
|
||||||
+318
@@ -0,0 +1,318 @@
|
|||||||
|
{
|
||||||
|
"name": "youtube-summarizer-startos",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"node_modules/@iarna/toml": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-td6ZUkz2oS3VeleBcN+m//Q6HlCFCPrnI0FZhrt/h4XqLEdOyYp2u21nd8MdsR+WJy5r9PTDaHTDDfhf4H4l6Q==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/@noble/curves": {
|
||||||
|
"version": "1.9.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz",
|
||||||
|
"integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "1.8.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.21.3 || >=16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@noble/hashes": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.21.3 || >=16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@start9labs/start-sdk": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-rtAfumVbMy90iw2WRbWH7fGcuwAvvuFfR4YwgSsh5R2Bz9MXtcEfmznwhnrp+ntQ6BOUSQ0wLzePbfsS6kUagg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@iarna/toml": "^3.0.0",
|
||||||
|
"@noble/curves": "^1.8.2",
|
||||||
|
"@noble/hashes": "^1.7.2",
|
||||||
|
"@types/ini": "^4.1.1",
|
||||||
|
"deep-equality-data-structures": "^2.0.0",
|
||||||
|
"fast-xml-parser": "^5.5.6",
|
||||||
|
"ini": "^5.0.0",
|
||||||
|
"isomorphic-fetch": "^3.0.0",
|
||||||
|
"mime": "^4.0.7",
|
||||||
|
"yaml": "^2.7.1",
|
||||||
|
"zod": "^4.3.6",
|
||||||
|
"zod-deep-partial": "^1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/ini": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ini/-/ini-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-MIyNUZipBTbyUNnhvuXJTY7B6qNI78meck9Jbv3wk0OgNwRyOOVEKDutAkOs1snB/tx0FafyR6/SN4Ps0hZPeg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "22.19.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz",
|
||||||
|
"integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~6.21.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vercel/ncc": {
|
||||||
|
"version": "0.38.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.4.tgz",
|
||||||
|
"integrity": "sha512-8LwjnlP39s08C08J5NstzriPvW1SP8Zfpp1BvC2sI35kPeZnHfxVkCwu4/+Wodgnd60UtT1n8K8zw+Mp7J9JmQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"ncc": "dist/ncc/cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/deep-equality-data-structures": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-equality-data-structures/-/deep-equality-data-structures-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-qgrUr7MKXq7VRN+WUpQ48QlXVGL0KdibAoTX8KRg18lgOgqbEKMAW1WZsVCtakY4+XX42pbAJzTz/DlXEFM2Fg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"object-hash": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fast-xml-builder": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"path-expression-matcher": "^1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fast-xml-parser": {
|
||||||
|
"version": "5.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.11.tgz",
|
||||||
|
"integrity": "sha512-QL0eb0YbSTVWF6tTf1+LEMSgtCEjBYPpnAjoLC8SscESlAjXEIRJ7cHtLG0pLeDFaZLa4VKZLArtA/60ZS7vyA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-xml-builder": "^1.1.4",
|
||||||
|
"path-expression-matcher": "^1.4.0",
|
||||||
|
"strnum": "^2.2.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"fxparser": "src/cli/cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ini": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/isomorphic-fetch": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "^2.6.1",
|
||||||
|
"whatwg-fetch": "^3.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/broofa"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"mime": "bin/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-hash": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-expression-matcher": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-s4DQMxIdhj3jLFWd9LxHOplj4p9yQ4ffMGowFf3cpEgrrJjEhN0V5nxw4Ye1EViAGDoL4/1AeO6qHpqYPOzE4Q==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "3.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
|
||||||
|
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin/prettier.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strnum": {
|
||||||
|
"version": "2.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz",
|
||||||
|
"integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "6.21.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-fetch": {
|
||||||
|
"version": "3.6.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz",
|
||||||
|
"integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yaml": {
|
||||||
|
"version": "2.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
|
||||||
|
"integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"bin": {
|
||||||
|
"yaml": "bin.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/eemeli"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zod": {
|
||||||
|
"version": "4.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
||||||
|
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zod-deep-partial": {
|
||||||
|
"version": "1.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod-deep-partial/-/zod-deep-partial-1.4.4.tgz",
|
||||||
|
"integrity": "sha512-aWkPl7hVStgE01WzbbSxCgX4O+sSpgt8JOjvFUtMTF75VgL6MhWQbiZi+AWGN85SfSTtI9gsOtL1vInoqfDVaA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^4.1.13"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+291
@@ -0,0 +1,291 @@
|
|||||||
|
# 3.0.0
|
||||||
|
|
||||||
|
[TOML v1.0.0-rc.1](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v1.0.0-rc.1.md)
|
||||||
|
has been released! This update brings support for it! It's not a huge
|
||||||
|
update, an evening of tests and an evening of parser updates, but the
|
||||||
|
changes as a consumer of TOML are actually pretty impactful:
|
||||||
|
|
||||||
|
* Inline arrays can have mixed types now!!
|
||||||
|
* Inline tables MAY NOT have trailing commas. (Inline arrays are still allowed to have trailing commas.)
|
||||||
|
* Control chars are no longer allowed in comments. Only tabs may be used in comments.
|
||||||
|
* Multiline basic and literal strings can have quotes nestled up next to the closing triplet, that is:
|
||||||
|
`""""Hello," she said, "this is a thing.""""` is the equivalent of the JSON `"\"Hello,\" she said, \"this is a thing.\""`.
|
||||||
|
* Subtables may not extend tables created via dotted keys, that is, the following is invalid:```
|
||||||
|
[a]
|
||||||
|
x.y = 1
|
||||||
|
[a.x]
|
||||||
|
z = 2
|
||||||
|
```
|
||||||
|
|
||||||
|
# 2.2.4
|
||||||
|
|
||||||
|
* Bug fix: Plain date literals (not datetime) immediately followed by another statement (no whitespace or blank line) would crash. Fixes [#19](https://github.com/iarna/iarna-toml/issues/19) and [#23](https://github.com/iarna/iarna-toml/issues/23), thank you [@arnau](https://github.com/arnau) and [@jschaf](https://github.com/jschaf) for reporting this!
|
||||||
|
* Bug fix: Hex literals with lowercase Es would throw errors. (Thank you [@DaeCatt](https://github.com/DaeCatt) for this fix!) Fixed [#20](https://github.com/iarna/iarna-toml/issues/20)
|
||||||
|
* Some minor doc tweaks
|
||||||
|
* Added Node 12 and 13 to Travis. (Node 6 is failing there now, mysteriously. It works on my machine™, shipping anyway. 🙃)
|
||||||
|
|
||||||
|
# 2.2.3
|
||||||
|
|
||||||
|
This release just updates the spec compliance tests and benchmark data to
|
||||||
|
better represent @ltd/j-toml.
|
||||||
|
|
||||||
|
# 2.2.2
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
* Support parsing and stringifying objects with `__proto__` properties. ([@LongTengDao](https://github.com/LongTengDao))
|
||||||
|
|
||||||
|
## Misc
|
||||||
|
|
||||||
|
* Updates for spec compliance and benchmarking:
|
||||||
|
* @sgarciac/bombadil -> 2.1.0
|
||||||
|
* toml -> 3.0.0
|
||||||
|
* Added spec compliance and benchmarking for:
|
||||||
|
* @ltd/j-toml
|
||||||
|
|
||||||
|
# 2.2.1
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
* Fix bug where keys with names matching javascript Object methods would
|
||||||
|
error. Thanks [@LongTengDao](https://github.com/LongTengDao) for finding this!
|
||||||
|
* Fix bug where a bundled version would fail if `util.inspect` wasn't
|
||||||
|
provided. This was supposed to be guarded against, but there was a bug in
|
||||||
|
the guard. Thanks [@agriffis](https://github.com/agriffis) for finding and fixing this!
|
||||||
|
|
||||||
|
## Misc
|
||||||
|
|
||||||
|
* Update the version of bombadil for spec compliance and benchmarking purposes to 2.0.0
|
||||||
|
|
||||||
|
## Did you know?
|
||||||
|
|
||||||
|
Node 6 and 8 are measurably slower than Node 6, 10 and 11, at least when it comes to parsing TOML!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 2.2.0
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Typescript: Lots of improvements to our type definitions, many many to
|
||||||
|
[@jorgegonzalez](https://github.com/jorgegonzalez) and [@momocow](https://github.com/momocow) for working through these.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
* Very large integers (>52bit) are stored as BigInts on runtimes that
|
||||||
|
support them. BigInts are 128bits, but the TOML spec limits its integers
|
||||||
|
to 64bits. We now limit our integers to 64bits
|
||||||
|
as well.
|
||||||
|
* Fix a bug in stringify where control characters were being emitted as unicode chars and not escape sequences.
|
||||||
|
|
||||||
|
## Misc
|
||||||
|
|
||||||
|
* Moved our spec tests out to an external repo
|
||||||
|
* Improved the styling of the spec compliance comparison
|
||||||
|
|
||||||
|
# 2.1.1
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
* Oops, type defs didn't end up in the tarball, ty [@jorgegonzalez](https://github.com/jorgegonzalez)‼
|
||||||
|
|
||||||
|
# 2.1.0
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Types for typescript support, thank you [@momocow](https://github.com/momocow)!
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
* stringify: always strip invalid dates. This fixes a bug where an
|
||||||
|
invalid date in an inline array would not be removed and would instead
|
||||||
|
result in an error.
|
||||||
|
* stringify: if an invalid type is found make sure it's thrown as an
|
||||||
|
error object. Previously the type name was, unhelpfully, being thrown.
|
||||||
|
* stringify: Multiline strings ending in a quote would generate invalid TOML.
|
||||||
|
* parse: Error if a signed integer has a leading zero, eg, `-01` or `+01`.
|
||||||
|
* parse: Error if \_ appears at the end of the integer part of a float, eg `1_.0`. \_ is only valid between _digits_.
|
||||||
|
|
||||||
|
## Fun
|
||||||
|
|
||||||
|
* BurntSushi's comprehensive TOML 0.4.0 test suite is now used in addition to our existing test suite.
|
||||||
|
* You can see exactly how the other JS TOML libraries stack up in testing
|
||||||
|
against both BurntSushi's tests and my own in the new
|
||||||
|
[TOML-SPEC-SUPPORT](TOML-SPEC-SUPPORT.md) doc.
|
||||||
|
|
||||||
|
# 2.0.0
|
||||||
|
|
||||||
|
With 2.0.0, @iarna/toml supports the TOML v0.5.0 specification. TOML 0.5.0
|
||||||
|
brings some changes:
|
||||||
|
|
||||||
|
* Delete characters (U+007F) are not allowed in plain strings. You can include them with
|
||||||
|
escaped unicode characters, eg `\u007f`.
|
||||||
|
* Integers are specified as being 64bit unsigned values. These are
|
||||||
|
supported using `BigInt`s if you are using Node 10 or later.
|
||||||
|
* Keys may be literal strings, that is, you can use single quoted strings to
|
||||||
|
quote key names, so the following is now valid:
|
||||||
|
'a"b"c' = 123
|
||||||
|
* The floating point values `nan`, `inf` and `-inf` are supported. The stringifier will no
|
||||||
|
longer strip NaN, Infinity and -Infinity, instead serializing them as these new values..
|
||||||
|
* Datetimes can separate the date and time with a space instead of a T, so
|
||||||
|
`2017-12-01T00:00:00Z` can be written as `2017-12-01 00:00:00Z`.
|
||||||
|
* Datetimes can be floating, that is, they can be represented without a timezone.
|
||||||
|
These are represented in javascript as Date objects whose `isFloating` property is true and
|
||||||
|
whose `toISOString` method will return a representation without a timezone.
|
||||||
|
* Dates without times are now supported. Dates do not have timezones. Dates
|
||||||
|
are represented in javascript as a Date object whose `isDate` property is true and
|
||||||
|
whose `toISOString` method returns just the date.
|
||||||
|
* Times without dates are now supported. Times do not have timezones. Times
|
||||||
|
are represented in javascript as a Date object whose `isTime` property is true and
|
||||||
|
whose `toISOString` method returns just the time.
|
||||||
|
* Keys can now include dots to directly address deeper structures, so `a.b = 23` is
|
||||||
|
the equivalent of `a = {b = 23}` or ```[a]
|
||||||
|
b = 23```. These can be used both as keys to regular tables and inline tables.
|
||||||
|
* Integers can now be specified in binary, octal and hexadecimal by prefixing the
|
||||||
|
number with `0b`, `0o` and `0x` respectively. It is now illegal to left
|
||||||
|
pad a decimal value with zeros.
|
||||||
|
|
||||||
|
Some parser details were also fixed:
|
||||||
|
|
||||||
|
* Negative zero (`-0.0`) and positive zero (`0.0`) are distinct floating point values.
|
||||||
|
* Negative integer zero (`-0`) is not distinguished from positive zero (`0`).
|
||||||
|
|
||||||
|
# 1.7.1
|
||||||
|
|
||||||
|
Another 18% speed boost on our overall benchmarks! This time it came from
|
||||||
|
switching from string comparisons to integer by converting each character to
|
||||||
|
its respective code point. This also necessitated rewriting the boolean
|
||||||
|
parser to actually parse character-by-character as it should. End-of-stream
|
||||||
|
is now marked with a numeric value outside of the Unicode range, rather than
|
||||||
|
a Symbol, meaning that the parser's char property is now monomorphic.
|
||||||
|
|
||||||
|
Bug fix, previously, `'abc''def'''` was accepted (as the value: `abcdef`).
|
||||||
|
Now it will correctly raise an error.
|
||||||
|
|
||||||
|
Spec tests now run against bombadil as well (it fails some, which is unsurprising
|
||||||
|
given its incomplete state).
|
||||||
|
|
||||||
|
# 1.7.0
|
||||||
|
|
||||||
|
This release features an overall 15% speed boost on our benchmarks. This
|
||||||
|
came from a few things:
|
||||||
|
|
||||||
|
* Date parsing was rewritten to not use regexps, resulting in a huge speed increase.
|
||||||
|
* Strings of all kinds and bare keywords now use tight loops to collect characters when this will help.
|
||||||
|
* Regexps in general were mostly removed. This didn't result in a speed
|
||||||
|
change, but it did allow refactoring the parser to be a lot easier to
|
||||||
|
follow.
|
||||||
|
* The internal state tracking now uses a class and is constructed with a
|
||||||
|
fixed set of properties, allowing v8's optimizer to be more effective.
|
||||||
|
|
||||||
|
In the land of new features:
|
||||||
|
|
||||||
|
* Errors in the syntax of your TOML will now have the `fromTOML` property
|
||||||
|
set to true. This is in addition to the `line`, `col` and `pos`
|
||||||
|
properties they already have.
|
||||||
|
|
||||||
|
The main use of this is to make it possible to distinguish between errors
|
||||||
|
in the TOML and errors in the parser code itself. This is of particular utility
|
||||||
|
when testing parse errors.
|
||||||
|
|
||||||
|
# 1.6.0
|
||||||
|
|
||||||
|
**FIXES**
|
||||||
|
|
||||||
|
* TOML.stringify: Allow toJSON properties that aren't functions, to align with JSON.stringify's behavior.
|
||||||
|
* TOML.stringify: Don't use ever render keys as literal strings.
|
||||||
|
* TOML.stringify: Don't try to escape control characters in literal strings.
|
||||||
|
|
||||||
|
**FEATURES**
|
||||||
|
|
||||||
|
* New Export: TOML.stringify.value, for encoding a stand alone inline value as TOML would. This produces
|
||||||
|
a TOML fragment, not a complete valid document.
|
||||||
|
|
||||||
|
# 1.5.6
|
||||||
|
|
||||||
|
* String literals are NOT supported as key names.
|
||||||
|
* Accessing a shallower table after accessing it more deeply is ok and no longer crashes, eg:
|
||||||
|
```toml
|
||||||
|
[a.b]
|
||||||
|
[a]
|
||||||
|
```
|
||||||
|
* Unicode characters in the reserved range now crash.
|
||||||
|
* Empty bare keys, eg `[.abc]` or `[]` now crash.
|
||||||
|
* Multiline backslash trimming supports CRs.
|
||||||
|
* Multiline post quote trimming supports CRs.
|
||||||
|
* Strings may not contain bare control chars (0x00-0x1f), except for \n, \r and \t.
|
||||||
|
|
||||||
|
# 1.5.5
|
||||||
|
|
||||||
|
* Yet MORE README fixes. 🙃
|
||||||
|
|
||||||
|
# 1.5.4
|
||||||
|
|
||||||
|
* README fix
|
||||||
|
|
||||||
|
# 1.5.3
|
||||||
|
|
||||||
|
* Benchmarks!
|
||||||
|
* More tests!
|
||||||
|
* More complete LICENSE information (some dev files are from other, MIT
|
||||||
|
licensed, projects, this is now more explicitly documented.)
|
||||||
|
|
||||||
|
# 1.5.2
|
||||||
|
|
||||||
|
* parse: Arrays with mixed types now throw errors, per the spec.
|
||||||
|
* parse: Fix a parser bug that would result in errors when trying to parse arrays of numbers or dates
|
||||||
|
that were not separated by a space from the closing ].
|
||||||
|
* parse: Fix a bug in the error pretty printer that resulted in errors on
|
||||||
|
the first line not getting the pretty print treatment.
|
||||||
|
* stringify: Fix long standing bug where an array of Numbers, some of which required
|
||||||
|
decimals, would be emitted in a way that parsers would treat as mixed
|
||||||
|
Integer and Float values. Now if any Numbers in an array must be
|
||||||
|
represented with a decimal then all will be emitted such that parsers will
|
||||||
|
understand them to be Float.
|
||||||
|
|
||||||
|
# 1.5.1
|
||||||
|
|
||||||
|
* README fix
|
||||||
|
|
||||||
|
# 1.5.0
|
||||||
|
|
||||||
|
* A brand new TOML parser, from scratch, that performs like `toml-j0.4`
|
||||||
|
without the crashes and with vastly better error messages.
|
||||||
|
* 100% test coverage for both the new parser and the existing stringifier. Some subtle bugs squashed!
|
||||||
|
|
||||||
|
# v1.4.2
|
||||||
|
|
||||||
|
* Revert fallback due to its having issues with the same files. (New plan
|
||||||
|
will be to write my own.)
|
||||||
|
|
||||||
|
# v1.4.1
|
||||||
|
|
||||||
|
* Depend on both `toml` and `toml-j0.4` with fallback from the latter to the
|
||||||
|
former when the latter crashes.
|
||||||
|
|
||||||
|
# v1.4.0
|
||||||
|
|
||||||
|
* Ducktype dates to make them compatible with `moment` and other `Date` replacements.
|
||||||
|
|
||||||
|
# v1.3.1
|
||||||
|
|
||||||
|
* Update docs with new toml module.
|
||||||
|
|
||||||
|
# v1.3.0
|
||||||
|
|
||||||
|
* Switch from `toml` to `toml-j0.4`, which is between 20x and 200x faster.
|
||||||
|
(The larger the input, the faster it is compared to `toml`).
|
||||||
|
|
||||||
|
# v1.2.0
|
||||||
|
|
||||||
|
* Return null when passed in null as the top level object.
|
||||||
|
* Detect and skip invalid dates and numbers
|
||||||
|
|
||||||
|
# v1.1.0
|
||||||
|
|
||||||
|
* toJSON transformations are now honored (for everything except Date objects, as JSON represents them as strings).
|
||||||
|
* Undefined/null values no longer result in exceptions, they now just result in the associated key being elided.
|
||||||
|
|
||||||
|
# v1.0.1
|
||||||
|
|
||||||
|
* Initial release
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
Copyright (c) 2016, Rebecca Turner <me@re-becca.org>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
+299
@@ -0,0 +1,299 @@
|
|||||||
|
# @iarna/toml
|
||||||
|
|
||||||
|
Better TOML parsing and stringifying all in that familiar JSON interface.
|
||||||
|
|
||||||
|
[](https://coveralls.io/github/iarna/iarna-toml)
|
||||||
|
|
||||||
|
# ** TOML 1.0.0-rc.1 **
|
||||||
|
|
||||||
|
### TOML Spec Support
|
||||||
|
|
||||||
|
The most recent version as of 2019-04-21: [1.0.0-rc.1](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v1.0.0-rc.1.md)
|
||||||
|
|
||||||
|
### Other Versions
|
||||||
|
|
||||||
|
1.0.0-rc.1 parsers can load almost any TOML 0.4 and TOML 0.5 document, but
|
||||||
|
TOML 1.0.0-rc.1 docs are not always compatible with TOML 0.4 and TOML 0.5
|
||||||
|
parsers. If you're using this to generate TOML documents and you want an
|
||||||
|
older parser to be able to read them you may want to use the
|
||||||
|
[latest TOML 0.5 version of this module](https://www.npmjs.com/package/@iarna/toml/v/toml-0.5).
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```js
|
||||||
|
const TOML = require('@iarna/toml')
|
||||||
|
const obj = TOML.parse(`[abc]
|
||||||
|
foo = 123
|
||||||
|
bar = [1,2,3]`)
|
||||||
|
/* obj =
|
||||||
|
{abc: {foo: 123, bar: [1,2,3]}}
|
||||||
|
*/
|
||||||
|
const str = TOML.stringify(obj)
|
||||||
|
/* str =
|
||||||
|
[abc]
|
||||||
|
foo = 123
|
||||||
|
bar = [ 1, 2, 3 ]
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
Visit the project github [for more examples](https://github.com/iarna/iarna-toml/tree/latest/examples)!
|
||||||
|
|
||||||
|
|
||||||
|
## Why @iarna/toml
|
||||||
|
|
||||||
|
* Support for TOML 1.0.0-rc.1!
|
||||||
|
* Highly correct! Careful adherence to spec.
|
||||||
|
* See [TOML-SPEC-SUPPORT](https://shared.by.re-becca.org/misc/TOML-SPEC-SUPPORT-v1.html) for a comparison of which TOML features
|
||||||
|
are supported by the various Node.js TOML parsers.
|
||||||
|
* Speedy! See benchmarks at end.
|
||||||
|
* BigInt support on Node 10!
|
||||||
|
* 100% test coverage.
|
||||||
|
* Small parser bundle (if you use `@iarna/toml/parse-string`).
|
||||||
|
* No deps.
|
||||||
|
* Detailed and easy to read error messages‼
|
||||||
|
|
||||||
|
```console
|
||||||
|
> TOML.parse(src)
|
||||||
|
Error: Unexpected character, expecting string, number, datetime, boolean, inline array or inline table at row 6, col 5, pos 87:
|
||||||
|
5: "abc\"" = { abc=123,def="abc" }
|
||||||
|
6> foo=sdkfj
|
||||||
|
^
|
||||||
|
7:
|
||||||
|
```
|
||||||
|
|
||||||
|
## TOML.parse(str) → Object [(example)](https://github.com/iarna/iarna-toml/blob/latest/examples/parse.js)
|
||||||
|
|
||||||
|
Also available with: `require('@iarna/toml/parse-string')`
|
||||||
|
|
||||||
|
Synchronously parse a TOML string and return an object.
|
||||||
|
|
||||||
|
|
||||||
|
## TOML.stringify(obj) → String [(example)](https://github.com/iarna/iarna-toml/blob/latest/examples/stringify.js)
|
||||||
|
|
||||||
|
Also available with: `require('@iarna/toml/stringify)`
|
||||||
|
|
||||||
|
Serialize an object as TOML.
|
||||||
|
|
||||||
|
## [your-object].toJSON
|
||||||
|
|
||||||
|
If an object `TOML.stringify` is serializing has a `toJSON` method then it
|
||||||
|
will call it to transform the object before serializing it. This matches
|
||||||
|
the behavior of `JSON.stringify`.
|
||||||
|
|
||||||
|
The one exception to this is that `toJSON` is not called for `Date` objects
|
||||||
|
because `JSON` represents dates as strings and TOML can represent them natively.
|
||||||
|
|
||||||
|
[`moment`](https://www.npmjs.com/package/moment) objects are treated the
|
||||||
|
same as native `Date` objects, in this respect.
|
||||||
|
|
||||||
|
## TOML.stringify.value(obj) -> String
|
||||||
|
|
||||||
|
Also available with: `require('@iarna/toml/stringify').value`
|
||||||
|
|
||||||
|
Serialize a value as TOML would. This is a fragment and not a complete
|
||||||
|
valid TOML document.
|
||||||
|
|
||||||
|
## Promises and Streaming
|
||||||
|
|
||||||
|
The parser provides alternative async and streaming interfaces, for times
|
||||||
|
that you're working with really absurdly big TOML files and don't want to
|
||||||
|
tie-up the event loop while it parses.
|
||||||
|
|
||||||
|
### TOML.parse.async(str[, opts]) → Promise(Object) [(example)](https://github.com/iarna/iarna-toml/blob/latest/examples/parse-async.js)
|
||||||
|
|
||||||
|
Also available with: `require('@iarna/toml/parse-async')`
|
||||||
|
|
||||||
|
`opts.blocksize` is the amount text to parser per pass through the event loop. Defaults to 40kb.
|
||||||
|
|
||||||
|
Asynchronously parse a TOML string and return a promise of the resulting object.
|
||||||
|
|
||||||
|
### TOML.parse.stream(readable) → Promise(Object) [(example)](https://github.com/iarna/iarna-toml/blob/latest/examples/parse-stream-readable.js)
|
||||||
|
|
||||||
|
Also available with: `require('@iarna/toml/parse-stream')`
|
||||||
|
|
||||||
|
Given a readable stream, parse it as it feeds us data. Return a promise of the resulting object.
|
||||||
|
|
||||||
|
### readable.pipe(TOML.parse.stream()) → Transform [(example)](https://github.com/iarna/iarna-toml/blob/latest/examples/parse-stream-through.js)
|
||||||
|
|
||||||
|
Also available with: `require('@iarna/toml/parse-stream')`
|
||||||
|
|
||||||
|
Returns a transform stream in object mode. When it completes, emit the
|
||||||
|
resulting object. Only one object will ever be emitted.
|
||||||
|
|
||||||
|
## Lowlevel Interface [(example)](https://github.com/iarna/iarna-toml/blob/latest/examples/parse-lowlevel.js) [(example w/ parser debugging)](https://github.com/iarna/iarna-toml/blob/latest/examples/parse-lowlevel-debug.js)
|
||||||
|
|
||||||
|
You construct a parser object, per TOML file you want to process:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const TOMLParser = require('@iarna/toml/lib/toml-parser.js')
|
||||||
|
const parser = new TOMLParser()
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you call the `parse` method for each chunk as you read them, or in a
|
||||||
|
single call:
|
||||||
|
|
||||||
|
```js
|
||||||
|
parser.parse(`hello = 'world'`)
|
||||||
|
```
|
||||||
|
|
||||||
|
And finally, you call the `finish` method to complete parsing and retrieve
|
||||||
|
the resulting object.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const data = parser.finish()
|
||||||
|
```
|
||||||
|
|
||||||
|
Both the `parse` method and `finish` method will throw if they find a
|
||||||
|
problem with the string they were given. Error objects thrown from the
|
||||||
|
parser have `pos`, `line` and `col` attributes. `TOML.parse` adds a visual
|
||||||
|
summary of where in the source string there were issues using
|
||||||
|
`parse-pretty-error` and you can too:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const prettyError = require('./parse-pretty-error.js')
|
||||||
|
const newErr = prettyError(err, sourceString)
|
||||||
|
```
|
||||||
|
|
||||||
|
## What's Different
|
||||||
|
|
||||||
|
Version 3 of this module supports TOML 1.0.0-rc.1. Please see the
|
||||||
|
[CHANGELOG](CHANGELOG.md#3.0.0) for details on exactly whats changed.
|
||||||
|
|
||||||
|
## TOML we can't do
|
||||||
|
|
||||||
|
* `-nan` is a valid TOML value and is converted into `NaN`. There is no way to
|
||||||
|
produce `-nan` when stringifying. Stringification will produce positive `nan`.
|
||||||
|
* Detecting and erroring on invalid utf8 documents: This is because Node's
|
||||||
|
UTF8 processing converts invalid sequences into the placeholder character
|
||||||
|
and does not have facilities for reporting these as errors instead. We
|
||||||
|
_can_ detect the placeholder character, but it's valid to intentionally
|
||||||
|
include them in documents, so erroring on them is not great.
|
||||||
|
* On versions of Node < 10, very large Integer values will lose precision.
|
||||||
|
On Node >=10, bigints are used.
|
||||||
|
* Floating/local dates and times are still represented by JavaScript Date
|
||||||
|
objects, which don't actually support these concepts. The objects
|
||||||
|
returned have been modified so that you can determine what kind of thing
|
||||||
|
they are (with `isFloating`, `isDate`, `isTime` properties) and that
|
||||||
|
their ISO representation (via `toISOString`) are representative of their
|
||||||
|
TOML value. They will correctly round trip if you pass them to
|
||||||
|
`TOML.stringify`.
|
||||||
|
* Binary, hexadecimal and octal values are converted to ordinary integers and
|
||||||
|
will be decimal if you stringify them.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
I write a by hand, honest-to-god,
|
||||||
|
[CHANGELOG](https://github.com/iarna/iarna-toml/blob/latest/CHANGELOG.md)
|
||||||
|
for this project. It's a description of what went into a release that you
|
||||||
|
the consumer of the module could care about, not a list of git commits, so
|
||||||
|
please check it out!
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
You can run them yourself with:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ npm run benchmark
|
||||||
|
```
|
||||||
|
|
||||||
|
The results below are from my desktop using Node 13.13.0. The library
|
||||||
|
versions tested were `@iarna/toml@3.0.0`, `toml-j0.4@1.1.1`, `toml@3.0.0`,
|
||||||
|
`@sgarciac/bombadil@2.3.0`, `@ltd/j-toml@0.5.107`, and `fast-toml@0.5.4`.
|
||||||
|
The speed value is megabytes-per-second that the parser can process of that
|
||||||
|
document type. Bigger is better. The percentage after average results is
|
||||||
|
the margin of error.
|
||||||
|
|
||||||
|
New here is fast-toml. fast-toml is very fast, for some datatypes, but it
|
||||||
|
also is missing most error checking demanded by the spec. For 0.4, it is
|
||||||
|
complete except for detail of multiline strings caught by the compliance
|
||||||
|
tests. Its support for 0.5 is incomplete. Check out the
|
||||||
|
[spec compliance](https://shared.by.re-becca.org/misc/TOML-SPEC-SUPPORT.html) doc
|
||||||
|
for details.
|
||||||
|
|
||||||
|
As this table is getting a little wide, with how npm and github display it,
|
||||||
|
you can also view it seperately in the
|
||||||
|
[BENCHMARK](https://shared.by.re-becca.org/misc/BENCHMARK.html) document.
|
||||||
|
|
||||||
|
| | @iarna/<wbr>toml | toml-j0.4 | toml | @sgarciac/<wbr>bombadil | @ltd/<wbr>j-toml | fast-toml |
|
||||||
|
| - | :---------: | :-------: | :--: | :----------------: | :---------: | :-------: |
|
||||||
|
| **Overall** | 28MB/sec<br><small>0.55%</small> | - | - | - | - | - |
|
||||||
|
| **01-small-doc-mixed-type-inline-array** | 5.3MB/sec<br><small>0.48%</small> | - | - | - | - | 12MB/sec<br><small>0.13%</small> |
|
||||||
|
| **Spec Example: v0.4.0** | 25MB/sec<br><small>0.40%</small> | 9.9MB/sec<br><small>0.15%</small> | 0.9MB/sec<br><small>0.37%</small> | 1.3MB/sec<br><small>1.02%</small> | 28MB/sec<br><small>0.33%</small> | - |
|
||||||
|
| **Spec Example: Hard Unicode** | 63MB/sec<br><small>0.47%</small> | 17MB/sec<br><small>0.21%</small> | 2MB/sec<br><small>0.25%</small> | 0.6MB/sec<br><small>0.47%</small> | 65MB/sec<br><small>0.27%</small> | 79MB/sec<br><small>0.09%</small> |
|
||||||
|
| **Types: Array, Inline** | 7.2MB/sec<br><small>0.53%</small> | 4.1MB/sec<br><small>0.09%</small> | 0.1MB/sec<br><small>0.69%</small> | 1.4MB/sec<br><small>0.86%</small> | 10MB/sec<br><small>0.33%</small> | 9MB/sec<br><small>0.16%</small> |
|
||||||
|
| **Types: Array** | 6.8MB/sec<br><small>0.09%</small> | 6.8MB/sec<br><small>0.20%</small> | 0.2MB/sec<br><small>0.81%</small> | 1.3MB/sec<br><small>0.82%</small> | 8.9MB/sec<br><small>0.36%</small> | 29MB/sec<br><small>0.16%</small> |
|
||||||
|
| **Types: Boolean,** | 20MB/sec<br><small>0.22%</small> | 9.3MB/sec<br><small>0.29%</small> | 0.2MB/sec<br><small>0.91%</small> | 1.9MB/sec<br><small>0.85%</small> | 16MB/sec<br><small>0.29%</small> | 8.6MB/sec<br><small>0.22%</small> |
|
||||||
|
| **Types: Datetime** | 17MB/sec<br><small>0.09%</small> | 11MB/sec<br><small>0.17%</small> | 0.3MB/sec<br><small>0.75%</small> | 1.6MB/sec<br><small>0.42%</small> | 9.8MB/sec<br><small>0.40%</small> | 6.5MB/sec<br><small>0.11%</small> |
|
||||||
|
| **Types: Float** | 8.5MB/sec<br><small>0.29%</small> | 5.8MB/sec<br><small>0.33%</small> | 0.2MB/sec<br><small>0.91%</small> | 2.2MB/sec<br><small>0.91%</small> | 14MB/sec<br><small>0.25%</small> | 7.9MB/sec<br><small>0.33%</small> |
|
||||||
|
| **Types: Int** | 5.8MB/sec<br><small>0.13%</small> | 4.5MB/sec<br><small>0.14%</small> | 0.1MB/sec<br><small>0.63%</small> | 1.5MB/sec<br><small>0.73%</small> | 9.8MB/sec<br><small>0.14%</small> | 8.1MB/sec<br><small>0.16%</small> |
|
||||||
|
| **Types: Literal String, 7 char** | 25MB/sec<br><small>0.15%</small> | 8.3MB/sec<br><small>0.38%</small> | 0.2MB/sec<br><small>0.71%</small> | 2.3MB/sec<br><small>1.04%</small> | 23MB/sec<br><small>0.28%</small> | 14MB/sec<br><small>0.21%</small> |
|
||||||
|
| **Types: Literal String, 92 char** | 44MB/sec<br><small>0.23%</small> | 12MB/sec<br><small>0.14%</small> | 0.3MB/sec<br><small>0.63%</small> | 13MB/sec<br><small>1.12%</small> | 100MB/sec<br><small>0.14%</small> | 77MB/sec<br><small>0.15%</small> |
|
||||||
|
| **Types: Literal String, Multiline, 1079 char** | 23MB/sec<br><small>0.35%</small> | 7.2MB/sec<br><small>0.34%</small> | 0.9MB/sec<br><small>0.86%</small> | 47MB/sec<br><small>1.07%</small> | 380MB/sec<br><small>0.13%</small> | 641MB/sec<br><small>0.14%</small> |
|
||||||
|
| **Types: Basic String, 7 char** | 25MB/sec<br><small>0.09%</small> | 7MB/sec<br><small>0.08%</small> | 0.2MB/sec<br><small>0.82%</small> | 2.3MB/sec<br><small>1.02%</small> | 15MB/sec<br><small>0.12%</small> | 13MB/sec<br><small>0.14%</small> |
|
||||||
|
| **Types: Basic String, 92 char** | 44MB/sec<br><small>0.15%</small> | 8MB/sec<br><small>0.39%</small> | 0.1MB/sec<br><small>1.52%</small> | 12MB/sec<br><small>1.53%</small> | 70MB/sec<br><small>0.17%</small> | 71MB/sec<br><small>0.16%</small> |
|
||||||
|
| **Types: Basic String, 1079 char** | 24MB/sec<br><small>0.36%</small> | 5.7MB/sec<br><small>0.12%</small> | 0.1MB/sec<br><small>3.65%</small> | 42MB/sec<br><small>1.67%</small> | 93MB/sec<br><small>0.13%</small> | 617MB/sec<br><small>0.14%</small> |
|
||||||
|
| **Types: Table, Inline** | 9.4MB/sec<br><small>0.21%</small> | 5.2MB/sec<br><small>0.23%</small> | 0.1MB/sec<br><small>1.18%</small> | 1.4MB/sec<br><small>1.20%</small> | 8.5MB/sec<br><small>0.68%</small> | 8.7MB/sec<br><small>0.30%</small> |
|
||||||
|
| **Types: Table** | 6.8MB/sec<br><small>0.13%</small> | 5.5MB/sec<br><small>0.22%</small> | 0.1MB/sec<br><small>1.10%</small> | 1.5MB/sec<br><small>1.05%</small> | 7.3MB/sec<br><small>0.54%</small> | 19MB/sec<br><small>0.21%</small> |
|
||||||
|
| **Scaling: Array, Inline, 1000 elements** | 40MB/sec<br><small>0.27%</small> | 2.4MB/sec<br><small>0.20%</small> | 0.1MB/sec<br><small>1.90%</small> | 1.6MB/sec<br><small>1.14%</small> | 18MB/sec<br><small>0.16%</small> | 32MB/sec<br><small>0.12%</small> |
|
||||||
|
| **Scaling: Array, Nested, 1000 deep** | 2MB/sec<br><small>0.17%</small> | 1.6MB/sec<br><small>0.09%</small> | 0.3MB/sec<br><small>0.62%</small> | - | 1.8MB/sec<br><small>0.80%</small> | 13MB/sec<br><small>0.19%</small> |
|
||||||
|
| **Scaling: Literal String, 40kb** | 59MB/sec<br><small>0.26%</small> | 10MB/sec<br><small>0.14%</small> | 3MB/sec<br><small>0.91%</small> | 13MB/sec<br><small>0.40%</small> | 479MB/sec<br><small>0.25%</small> | 19kMB/sec<br><small>0.20%</small> |
|
||||||
|
| **Scaling: Literal String, Multiline, 40kb** | 61MB/sec<br><small>0.23%</small> | 5.3MB/sec<br><small>0.30%</small> | 0.2MB/sec<br><small>1.78%</small> | 12MB/sec<br><small>0.55%</small> | 276MB/sec<br><small>0.16%</small> | 21kMB/sec<br><small>0.10%</small> |
|
||||||
|
| **Scaling: Basic String, Multiline, 40kb** | 61MB/sec<br><small>0.21%</small> | 6MB/sec<br><small>0.40%</small> | 2.8MB/sec<br><small>0.75%</small> | 12MB/sec<br><small>0.60%</small> | 1kMB/sec<br><small>0.13%</small> | 27kMB/sec<br><small>0.14%</small> |
|
||||||
|
| **Scaling: Basic String, 40kb** | 60MB/sec<br><small>0.13%</small> | 6.6MB/sec<br><small>0.13%</small> | 0.2MB/sec<br><small>1.67%</small> | 13MB/sec<br><small>0.30%</small> | 504MB/sec<br><small>0.26%</small> | 19kMB/sec<br><small>0.22%</small> |
|
||||||
|
| **Scaling: Table, Inline, 1000 elements** | 26MB/sec<br><small>0.17%</small> | 7.3MB/sec<br><small>0.83%</small> | 0.3MB/sec<br><small>0.95%</small> | 2.5MB/sec<br><small>1.24%</small> | 5.4MB/sec<br><small>0.22%</small> | 13MB/sec<br><small>0.22%</small> |
|
||||||
|
| **Scaling: Table, Inline, Nested, 1000 deep** | 8MB/sec<br><small>0.10%</small> | 5.2MB/sec<br><small>0.25%</small> | 0.1MB/sec<br><small>0.45%</small> | - | 3.1MB/sec<br><small>0.58%</small> | 10MB/sec<br><small>0.19%</small> |
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
The test suite is maintained at 100% coverage: [](https://coveralls.io/github/iarna/iarna-toml)
|
||||||
|
|
||||||
|
The spec was carefully hand converted into a series of test framework
|
||||||
|
independent (and mostly language independent) assertions, as pairs of TOML
|
||||||
|
and YAML files. You can find those files here:
|
||||||
|
[spec-test](https://github.com/iarna/toml-spec-test/).
|
||||||
|
|
||||||
|
Further tests were written to increase coverage to 100%, these may be more
|
||||||
|
implementation specific, but they can be found in [coverage](https://github.com/iarna/iarna-toml/blob/latest/test/coverage.js) and
|
||||||
|
[coverage-error](https://github.com/iarna/iarna-toml/blob/latest/test/coverage-error.js).
|
||||||
|
|
||||||
|
I've also written some quality assurance style tests, which don't contribute
|
||||||
|
to coverage but do cover scenarios that could easily be problematic for some
|
||||||
|
implementations can be found in:
|
||||||
|
[test/qa.js](https://github.com/iarna/iarna-toml/blob/latest/test/qa.js) and
|
||||||
|
[test/qa-error.js](https://github.com/iarna/iarna-toml/blob/latest/test/qa-error.js).
|
||||||
|
|
||||||
|
All of the official example files from the TOML spec are run through this
|
||||||
|
parser and compared to the official YAML files when available. These files are from the TOML spec as of:
|
||||||
|
[357a4ba6](https://github.com/toml-lang/toml/tree/357a4ba6782e48ff26e646780bab11c90ed0a7bc)
|
||||||
|
and specifically are:
|
||||||
|
|
||||||
|
* [github.com/toml-lang/toml/tree/357a4ba6/examples](https://github.com/toml-lang/toml/tree/357a4ba6782e48ff26e646780bab11c90ed0a7bc/examples)
|
||||||
|
* [github.com/toml-lang/toml/tree/357a4ba6/tests](https://github.com/toml-lang/toml/tree/357a4ba6782e48ff26e646780bab11c90ed0a7bc/tests)
|
||||||
|
|
||||||
|
The stringifier is tested by round-tripping these same files, asserting that
|
||||||
|
`TOML.parse(sourcefile)` deepEqual
|
||||||
|
`TOML.parse(TOML.stringify(TOML.parse(sourcefile))`. This is done in
|
||||||
|
[test/roundtrip-examples.js](https://github.com/iarna/iarna-toml/blob/latest/test/round-tripping.js)
|
||||||
|
There are also some tests written to complete coverage from stringification in:
|
||||||
|
[test/stringify.js](https://github.com/iarna/iarna-toml/blob/latest/test/stringify.js)
|
||||||
|
|
||||||
|
Tests for the async and streaming interfaces are in [test/async.js](https://github.com/iarna/iarna-toml/blob/latest/test/async.js) and [test/stream.js](https://github.com/iarna/iarna-toml/blob/latest/test/stream.js) respectively.
|
||||||
|
|
||||||
|
Tests for the parser's debugging mode live in [test/devel.js](https://github.com/iarna/iarna-toml/blob/latest/test/devel.js).
|
||||||
|
|
||||||
|
And finally, many more stringification tests were borrowed from [@othiym23](https://github.com/othiym23)'s
|
||||||
|
[toml-stream](https://npmjs.com/package/toml-stream) module. They were fetched as of
|
||||||
|
[b6f1e26b572d49742d49fa6a6d11524d003441fa](https://github.com/othiym23/toml-stream/tree/b6f1e26b572d49742d49fa6a6d11524d003441fa/test) and live in
|
||||||
|
[test/toml-stream](https://github.com/iarna/iarna-toml/blob/latest/test/toml-stream/).
|
||||||
|
|
||||||
|
## Improvements to make
|
||||||
|
|
||||||
|
* In stringify:
|
||||||
|
* Any way to produce comments. As a JSON stand-in I'm not too worried
|
||||||
|
about this. That said, a document orientated fork is something I'd like
|
||||||
|
to look at eventually…
|
||||||
|
* Stringification could use some work on its error reporting. It reports
|
||||||
|
_what's_ wrong, but not where in your data structure it was.
|
||||||
|
* Further optimize the parser:
|
||||||
|
* There are some debugging assertions left in the main parser, these should be moved to a subclass.
|
||||||
|
* Make the whole debugging parser thing work as a mixin instead of as a superclass.
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
import { Transform } from "stream";
|
||||||
|
|
||||||
|
type JsonValue = boolean | number | string | JsonMap | JsonArray | Date
|
||||||
|
type JsonArray = JsonValue[]
|
||||||
|
type AnyJson = boolean | number | string | JsonMap | Date | JsonArray | JsonArray[]
|
||||||
|
|
||||||
|
interface JsonMap {
|
||||||
|
[key: string]: AnyJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParseOptions {
|
||||||
|
/**
|
||||||
|
* The amount text to parser per pass through the event loop. Defaults to 40kb (`40000`).
|
||||||
|
*/
|
||||||
|
blocksize: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FuncParse {
|
||||||
|
/**
|
||||||
|
* Synchronously parse a TOML string and return an object.
|
||||||
|
*/
|
||||||
|
(toml: string): JsonMap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously parse a TOML string and return a promise of the resulting object.
|
||||||
|
*/
|
||||||
|
async (toml: string, options?: ParseOptions): Promise<JsonMap>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a readable stream, parse it as it feeds us data. Return a promise of the resulting object.
|
||||||
|
*/
|
||||||
|
stream (readable: NodeJS.ReadableStream): Promise<JsonMap>
|
||||||
|
stream (): Transform
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FuncStringify {
|
||||||
|
/**
|
||||||
|
* Serialize an object as TOML.
|
||||||
|
*
|
||||||
|
* If an object `TOML.stringify` is serializing has a `toJSON` method
|
||||||
|
* then it will call it to transform the object before serializing it.
|
||||||
|
* This matches the behavior of JSON.stringify.
|
||||||
|
*
|
||||||
|
* The one exception to this is that `toJSON` is not called for `Date` objects
|
||||||
|
* because JSON represents dates as strings and TOML can represent them natively.
|
||||||
|
*
|
||||||
|
* `moment` objects are treated the same as native `Date` objects, in this respect.
|
||||||
|
*/
|
||||||
|
(obj: JsonMap): string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize a value as TOML would. This is a fragment and not a complete valid TOML document.
|
||||||
|
*/
|
||||||
|
value (any: AnyJson): string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parse: FuncParse
|
||||||
|
export const stringify: FuncStringify
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
'use strict'
|
||||||
|
const f = require('./format-num.js')
|
||||||
|
const DateTime = global.Date
|
||||||
|
|
||||||
|
class Date extends DateTime {
|
||||||
|
constructor (value) {
|
||||||
|
super(value)
|
||||||
|
this.isDate = true
|
||||||
|
}
|
||||||
|
toISOString () {
|
||||||
|
return `${this.getUTCFullYear()}-${f(2, this.getUTCMonth() + 1)}-${f(2, this.getUTCDate())}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = value => {
|
||||||
|
const date = new Date(value)
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (isNaN(date)) {
|
||||||
|
throw new TypeError('Invalid Datetime')
|
||||||
|
} else {
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
'use strict'
|
||||||
|
const f = require('./format-num.js')
|
||||||
|
|
||||||
|
class FloatingDateTime extends Date {
|
||||||
|
constructor (value) {
|
||||||
|
super(value + 'Z')
|
||||||
|
this.isFloating = true
|
||||||
|
}
|
||||||
|
toISOString () {
|
||||||
|
const date = `${this.getUTCFullYear()}-${f(2, this.getUTCMonth() + 1)}-${f(2, this.getUTCDate())}`
|
||||||
|
const time = `${f(2, this.getUTCHours())}:${f(2, this.getUTCMinutes())}:${f(2, this.getUTCSeconds())}.${f(3, this.getUTCMilliseconds())}`
|
||||||
|
return `${date}T${time}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = value => {
|
||||||
|
const date = new FloatingDateTime(value)
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (isNaN(date)) {
|
||||||
|
throw new TypeError('Invalid Datetime')
|
||||||
|
} else {
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
'use strict'
|
||||||
|
module.exports = value => {
|
||||||
|
const date = new Date(value)
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (isNaN(date)) {
|
||||||
|
throw new TypeError('Invalid Datetime')
|
||||||
|
} else {
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
'use strict'
|
||||||
|
const f = require('./format-num.js')
|
||||||
|
|
||||||
|
class Time extends Date {
|
||||||
|
constructor (value) {
|
||||||
|
super(`0000-01-01T${value}Z`)
|
||||||
|
this.isTime = true
|
||||||
|
}
|
||||||
|
toISOString () {
|
||||||
|
return `${f(2, this.getUTCHours())}:${f(2, this.getUTCMinutes())}:${f(2, this.getUTCSeconds())}.${f(3, this.getUTCMilliseconds())}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = value => {
|
||||||
|
const date = new Time(value)
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (isNaN(date)) {
|
||||||
|
throw new TypeError('Invalid Datetime')
|
||||||
|
} else {
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
'use strict'
|
||||||
|
module.exports = (d, num) => {
|
||||||
|
num = String(num)
|
||||||
|
while (num.length < d) num = '0' + num
|
||||||
|
return num
|
||||||
|
}
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
'use strict'
|
||||||
|
const Parser = require('./parser.js')
|
||||||
|
const util = require('util')
|
||||||
|
|
||||||
|
const dump = _ => util.inspect(_, {colors: true, depth: 10, breakLength: Infinity})
|
||||||
|
class DebugParser extends Parser {
|
||||||
|
stateName (state) {
|
||||||
|
// istanbul ignore next
|
||||||
|
return (state.parser && state.parser.name) || state.name || ('anonymous')
|
||||||
|
}
|
||||||
|
runOne () {
|
||||||
|
const callStack = this.stack.concat(this.state).map(_ => this.stateName(_)).join(' <- ')
|
||||||
|
console.log('RUN', callStack, dump({line: this.line, col: this.col, char: this.char, ret: this.state.returned}))
|
||||||
|
return super.runOne()
|
||||||
|
}
|
||||||
|
finish () {
|
||||||
|
const obj = super.finish()
|
||||||
|
// istanbul ignore if
|
||||||
|
if (this.stack.length !== 0) {
|
||||||
|
throw new Parser.Error('All states did not return by end of stream')
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
callStack () {
|
||||||
|
const callStack = this.stack.map(_ => this.stateName(_)).join(' ').replace(/\S/g, ' ')
|
||||||
|
return callStack ? callStack + ' ' : ''
|
||||||
|
}
|
||||||
|
next (fn) {
|
||||||
|
console.log(' ', this.callStack(), 'NEXT', this.stateName(fn))
|
||||||
|
return super.next(fn)
|
||||||
|
}
|
||||||
|
goto (fn) {
|
||||||
|
console.log(' ', this.callStack(), 'GOTO', this.stateName(fn))
|
||||||
|
super.next(fn)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
call (fn, returnWith) {
|
||||||
|
console.log(' ', this.callStack(), 'CALL', fn.name, returnWith ? '-> ' + returnWith.name : '')
|
||||||
|
if (returnWith) super.next(returnWith)
|
||||||
|
this.stack.push(this.state)
|
||||||
|
this.state = {parser: fn, buf: '', returned: null}
|
||||||
|
}
|
||||||
|
callNow (fn, returnWith) {
|
||||||
|
console.log(' ', this.callStack(), 'CALLNOW', fn.name, returnWith ? '-> ' + returnWith.name : '')
|
||||||
|
if (returnWith) super.next(returnWith)
|
||||||
|
this.stack.push(this.state)
|
||||||
|
this.state = {parser: fn, buf: '', returned: null}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return (value) {
|
||||||
|
console.log(' ', this.callStack(), 'RETURN')
|
||||||
|
return super.return(value)
|
||||||
|
}
|
||||||
|
returnNow (value) {
|
||||||
|
console.log(' ', this.callStack(), 'RETURNNOW')
|
||||||
|
super.return(value)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = DebugParser
|
||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
'use strict'
|
||||||
|
const ParserEND = 0x110000
|
||||||
|
class ParserError extends Error {
|
||||||
|
/* istanbul ignore next */
|
||||||
|
constructor (msg, filename, linenumber) {
|
||||||
|
super('[ParserError] ' + msg, filename, linenumber)
|
||||||
|
this.name = 'ParserError'
|
||||||
|
this.code = 'ParserError'
|
||||||
|
if (Error.captureStackTrace) Error.captureStackTrace(this, ParserError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class State {
|
||||||
|
constructor (parser) {
|
||||||
|
this.parser = parser
|
||||||
|
this.buf = ''
|
||||||
|
this.returned = null
|
||||||
|
this.result = null
|
||||||
|
this.resultTable = null
|
||||||
|
this.resultArr = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Parser {
|
||||||
|
constructor () {
|
||||||
|
this.pos = 0
|
||||||
|
this.col = 0
|
||||||
|
this.line = 0
|
||||||
|
this.obj = {}
|
||||||
|
this.ctx = this.obj
|
||||||
|
this.stack = []
|
||||||
|
this._buf = ''
|
||||||
|
this.char = null
|
||||||
|
this.ii = 0
|
||||||
|
this.state = new State(this.parseStart)
|
||||||
|
}
|
||||||
|
|
||||||
|
parse (str) {
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (str.length === 0 || str.length == null) return
|
||||||
|
|
||||||
|
this._buf = String(str)
|
||||||
|
this.ii = -1
|
||||||
|
this.char = -1
|
||||||
|
let getNext
|
||||||
|
while (getNext === false || this.nextChar()) {
|
||||||
|
getNext = this.runOne()
|
||||||
|
}
|
||||||
|
this._buf = null
|
||||||
|
}
|
||||||
|
nextChar () {
|
||||||
|
if (this.char === 0x0A) {
|
||||||
|
++this.line
|
||||||
|
this.col = -1
|
||||||
|
}
|
||||||
|
++this.ii
|
||||||
|
this.char = this._buf.codePointAt(this.ii)
|
||||||
|
++this.pos
|
||||||
|
++this.col
|
||||||
|
return this.haveBuffer()
|
||||||
|
}
|
||||||
|
haveBuffer () {
|
||||||
|
return this.ii < this._buf.length
|
||||||
|
}
|
||||||
|
runOne () {
|
||||||
|
return this.state.parser.call(this, this.state.returned)
|
||||||
|
}
|
||||||
|
finish () {
|
||||||
|
this.char = ParserEND
|
||||||
|
let last
|
||||||
|
do {
|
||||||
|
last = this.state.parser
|
||||||
|
this.runOne()
|
||||||
|
} while (this.state.parser !== last)
|
||||||
|
|
||||||
|
this.ctx = null
|
||||||
|
this.state = null
|
||||||
|
this._buf = null
|
||||||
|
|
||||||
|
return this.obj
|
||||||
|
}
|
||||||
|
next (fn) {
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (typeof fn !== 'function') throw new ParserError('Tried to set state to non-existent state: ' + JSON.stringify(fn))
|
||||||
|
this.state.parser = fn
|
||||||
|
}
|
||||||
|
goto (fn) {
|
||||||
|
this.next(fn)
|
||||||
|
return this.runOne()
|
||||||
|
}
|
||||||
|
call (fn, returnWith) {
|
||||||
|
if (returnWith) this.next(returnWith)
|
||||||
|
this.stack.push(this.state)
|
||||||
|
this.state = new State(fn)
|
||||||
|
}
|
||||||
|
callNow (fn, returnWith) {
|
||||||
|
this.call(fn, returnWith)
|
||||||
|
return this.runOne()
|
||||||
|
}
|
||||||
|
return (value) {
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (this.stack.length === 0) throw this.error(new ParserError('Stack underflow'))
|
||||||
|
if (value === undefined) value = this.state.buf
|
||||||
|
this.state = this.stack.pop()
|
||||||
|
this.state.returned = value
|
||||||
|
}
|
||||||
|
returnNow (value) {
|
||||||
|
this.return(value)
|
||||||
|
return this.runOne()
|
||||||
|
}
|
||||||
|
consume () {
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (this.char === ParserEND) throw this.error(new ParserError('Unexpected end-of-buffer'))
|
||||||
|
this.state.buf += this._buf[this.ii]
|
||||||
|
}
|
||||||
|
error (err) {
|
||||||
|
err.line = this.line
|
||||||
|
err.col = this.col
|
||||||
|
err.pos = this.pos
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
/* istanbul ignore next */
|
||||||
|
parseStart () {
|
||||||
|
throw new ParserError('Must declare a parseStart method')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Parser.END = ParserEND
|
||||||
|
Parser.Error = ParserError
|
||||||
|
module.exports = Parser
|
||||||
+1421
File diff suppressed because it is too large
Load Diff
+82
@@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"name": "@iarna/toml",
|
||||||
|
"version": "3.0.0",
|
||||||
|
"main": "toml.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "tap -J --100 test/*.js test/toml-stream/*.js",
|
||||||
|
"benchmark": "node benchmark.js && node benchmark-per-file.js && node results2table.js",
|
||||||
|
"prerelease": "npm t",
|
||||||
|
"prepack": "rm -f *~",
|
||||||
|
"postpublish": "git push --follow-tags",
|
||||||
|
"pretest": "iarna-standard",
|
||||||
|
"update-coc": "weallbehave -o . && git add CODE_OF_CONDUCT.md && git commit -m 'docs(coc): updated CODE_OF_CONDUCT.md'",
|
||||||
|
"update-contrib": "weallcontribute -o . && git add CONTRIBUTING.md && git commit -m 'docs(contributing): updated CONTRIBUTING.md'",
|
||||||
|
"setup-burntsushi-toml-suite": "[ -d test/burntsushi-toml-test ] || (git clone https://github.com/BurntSushi/toml-test test/burntsushi-toml-test; rimraf test/burntsushi-toml-test/.git/hooks/*); cd test/burntsushi-toml-test; git pull",
|
||||||
|
"setup-iarna-toml-suite": "[ -d test/spec-test ] || (git clone https://github.com/iarna/toml-spec-tests -b 1.0.0-rc.1 test/spec-test; rimraf test/spec-test/.git/hooks/*); cd test/spec-test; git pull",
|
||||||
|
"prepare": "npm run setup-burntsushi-toml-suite && npm run setup-iarna-toml-suite"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"toml",
|
||||||
|
"toml-parser",
|
||||||
|
"toml-stringifier",
|
||||||
|
"parser",
|
||||||
|
"stringifer",
|
||||||
|
"emitter",
|
||||||
|
"ini",
|
||||||
|
"tomlify",
|
||||||
|
"encoder",
|
||||||
|
"decoder"
|
||||||
|
],
|
||||||
|
"author": "Rebecca Turner <me@re-becca.org> (http://re-becca.org/)",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "Better TOML parsing and stringifying all in that familiar JSON interface.",
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"@iarna/standard": "^2.0.2",
|
||||||
|
"@ltd/j-toml": "^0.5.107",
|
||||||
|
"@perl/qx": "^1.0.2",
|
||||||
|
"@sgarciac/bombadil": "^2.3.0",
|
||||||
|
"ansi": "^0.3.1",
|
||||||
|
"approximate-number": "^2.0.0",
|
||||||
|
"benchmark": "^2.1.4",
|
||||||
|
"fast-toml": "^0.5.4",
|
||||||
|
"funstream": "^3.0.0",
|
||||||
|
"glob": "^7.1.2",
|
||||||
|
"js-yaml": "^3.13.1",
|
||||||
|
"rimraf": "^2.6.2",
|
||||||
|
"tap": "^12.0.1",
|
||||||
|
"toml": "^3.0.0",
|
||||||
|
"toml-j0.4": "^1.1.1",
|
||||||
|
"weallbehave": "*",
|
||||||
|
"weallcontribute": "*"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"toml.js",
|
||||||
|
"stringify.js",
|
||||||
|
"parse.js",
|
||||||
|
"parse-string.js",
|
||||||
|
"parse-stream.js",
|
||||||
|
"parse-async.js",
|
||||||
|
"parse-pretty-error.js",
|
||||||
|
"lib/parser.js",
|
||||||
|
"lib/parser-debug.js",
|
||||||
|
"lib/toml-parser.js",
|
||||||
|
"lib/create-datetime.js",
|
||||||
|
"lib/create-date.js",
|
||||||
|
"lib/create-datetime-float.js",
|
||||||
|
"lib/create-time.js",
|
||||||
|
"lib/format-num.js",
|
||||||
|
"index.d.ts"
|
||||||
|
],
|
||||||
|
"directories": {
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/iarna/iarna-toml.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/iarna/iarna-toml/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/iarna/iarna-toml#readme"
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
'use strict'
|
||||||
|
module.exports = parseAsync
|
||||||
|
|
||||||
|
const TOMLParser = require('./lib/toml-parser.js')
|
||||||
|
const prettyError = require('./parse-pretty-error.js')
|
||||||
|
|
||||||
|
function parseAsync (str, opts) {
|
||||||
|
if (!opts) opts = {}
|
||||||
|
const index = 0
|
||||||
|
const blocksize = opts.blocksize || 40960
|
||||||
|
const parser = new TOMLParser()
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
setImmediate(parseAsyncNext, index, blocksize, resolve, reject)
|
||||||
|
})
|
||||||
|
function parseAsyncNext (index, blocksize, resolve, reject) {
|
||||||
|
if (index >= str.length) {
|
||||||
|
try {
|
||||||
|
return resolve(parser.finish())
|
||||||
|
} catch (err) {
|
||||||
|
return reject(prettyError(err, str))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
parser.parse(str.slice(index, index + blocksize))
|
||||||
|
setImmediate(parseAsyncNext, index + blocksize, blocksize, resolve, reject)
|
||||||
|
} catch (err) {
|
||||||
|
reject(prettyError(err, str))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
'use strict'
|
||||||
|
module.exports = prettyError
|
||||||
|
|
||||||
|
function prettyError (err, buf) {
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (err.pos == null || err.line == null) return err
|
||||||
|
let msg = err.message
|
||||||
|
msg += ` at row ${err.line + 1}, col ${err.col + 1}, pos ${err.pos}:\n`
|
||||||
|
|
||||||
|
/* istanbul ignore else */
|
||||||
|
if (buf && buf.split) {
|
||||||
|
const lines = buf.split(/\n/)
|
||||||
|
const lineNumWidth = String(Math.min(lines.length, err.line + 3)).length
|
||||||
|
let linePadding = ' '
|
||||||
|
while (linePadding.length < lineNumWidth) linePadding += ' '
|
||||||
|
for (let ii = Math.max(0, err.line - 1); ii < Math.min(lines.length, err.line + 2); ++ii) {
|
||||||
|
let lineNum = String(ii + 1)
|
||||||
|
if (lineNum.length < lineNumWidth) lineNum = ' ' + lineNum
|
||||||
|
if (err.line === ii) {
|
||||||
|
msg += lineNum + '> ' + lines[ii] + '\n'
|
||||||
|
msg += linePadding + ' '
|
||||||
|
for (let hh = 0; hh < err.col; ++hh) {
|
||||||
|
msg += ' '
|
||||||
|
}
|
||||||
|
msg += '^\n'
|
||||||
|
} else {
|
||||||
|
msg += lineNum + ': ' + lines[ii] + '\n'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err.message = msg + '\n'
|
||||||
|
return err
|
||||||
|
}
|
||||||
+80
@@ -0,0 +1,80 @@
|
|||||||
|
'use strict'
|
||||||
|
module.exports = parseStream
|
||||||
|
|
||||||
|
const stream = require('stream')
|
||||||
|
const TOMLParser = require('./lib/toml-parser.js')
|
||||||
|
|
||||||
|
function parseStream (stm) {
|
||||||
|
if (stm) {
|
||||||
|
return parseReadable(stm)
|
||||||
|
} else {
|
||||||
|
return parseTransform(stm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseReadable (stm) {
|
||||||
|
const parser = new TOMLParser()
|
||||||
|
stm.setEncoding('utf8')
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let readable
|
||||||
|
let ended = false
|
||||||
|
let errored = false
|
||||||
|
function finish () {
|
||||||
|
ended = true
|
||||||
|
if (readable) return
|
||||||
|
try {
|
||||||
|
resolve(parser.finish())
|
||||||
|
} catch (err) {
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function error (err) {
|
||||||
|
errored = true
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
stm.once('end', finish)
|
||||||
|
stm.once('error', error)
|
||||||
|
readNext()
|
||||||
|
|
||||||
|
function readNext () {
|
||||||
|
readable = true
|
||||||
|
let data
|
||||||
|
while ((data = stm.read()) !== null) {
|
||||||
|
try {
|
||||||
|
parser.parse(data)
|
||||||
|
} catch (err) {
|
||||||
|
return error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
readable = false
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (ended) return finish()
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (errored) return
|
||||||
|
stm.once('readable', readNext)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseTransform () {
|
||||||
|
const parser = new TOMLParser()
|
||||||
|
return new stream.Transform({
|
||||||
|
objectMode: true,
|
||||||
|
transform (chunk, encoding, cb) {
|
||||||
|
try {
|
||||||
|
parser.parse(chunk.toString(encoding))
|
||||||
|
} catch (err) {
|
||||||
|
this.emit('error', err)
|
||||||
|
}
|
||||||
|
cb()
|
||||||
|
},
|
||||||
|
flush (cb) {
|
||||||
|
try {
|
||||||
|
this.push(parser.finish())
|
||||||
|
} catch (err) {
|
||||||
|
this.emit('error', err)
|
||||||
|
}
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
'use strict'
|
||||||
|
module.exports = parseString
|
||||||
|
|
||||||
|
const TOMLParser = require('./lib/toml-parser.js')
|
||||||
|
const prettyError = require('./parse-pretty-error.js')
|
||||||
|
|
||||||
|
function parseString (str) {
|
||||||
|
if (global.Buffer && global.Buffer.isBuffer(str)) {
|
||||||
|
str = str.toString('utf8')
|
||||||
|
}
|
||||||
|
const parser = new TOMLParser()
|
||||||
|
try {
|
||||||
|
parser.parse(str)
|
||||||
|
return parser.finish()
|
||||||
|
} catch (err) {
|
||||||
|
throw prettyError(err, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
'use strict'
|
||||||
|
module.exports = require('./parse-string.js')
|
||||||
|
module.exports.async = require('./parse-async.js')
|
||||||
|
module.exports.stream = require('./parse-stream.js')
|
||||||
|
module.exports.prettyError = require('./parse-pretty-error.js')
|
||||||
+270
@@ -0,0 +1,270 @@
|
|||||||
|
'use strict'
|
||||||
|
module.exports = stringify
|
||||||
|
module.exports.value = stringifyInline
|
||||||
|
|
||||||
|
function stringify (obj) {
|
||||||
|
if (obj === null) throw typeError('null')
|
||||||
|
if (obj === void (0)) throw typeError('undefined')
|
||||||
|
if (typeof obj !== 'object') throw typeError(typeof obj)
|
||||||
|
|
||||||
|
if (typeof obj.toJSON === 'function') obj = obj.toJSON()
|
||||||
|
if (obj == null) return null
|
||||||
|
const type = tomlType(obj)
|
||||||
|
if (type !== 'table') throw typeError(type)
|
||||||
|
return stringifyObject('', '', obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
function typeError (type) {
|
||||||
|
return new Error('Can only stringify objects, not ' + type)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInlineKeys (obj) {
|
||||||
|
return Object.keys(obj).filter(key => isInline(obj[key]))
|
||||||
|
}
|
||||||
|
function getComplexKeys (obj) {
|
||||||
|
return Object.keys(obj).filter(key => !isInline(obj[key]))
|
||||||
|
}
|
||||||
|
|
||||||
|
function toJSON (obj) {
|
||||||
|
let nobj = Array.isArray(obj) ? [] : Object.prototype.hasOwnProperty.call(obj, '__proto__') ? {['__proto__']: undefined} : {}
|
||||||
|
for (let prop of Object.keys(obj)) {
|
||||||
|
if (obj[prop] && typeof obj[prop].toJSON === 'function' && !('toISOString' in obj[prop])) {
|
||||||
|
nobj[prop] = obj[prop].toJSON()
|
||||||
|
} else {
|
||||||
|
nobj[prop] = obj[prop]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nobj
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyObject (prefix, indent, obj) {
|
||||||
|
obj = toJSON(obj)
|
||||||
|
let inlineKeys
|
||||||
|
let complexKeys
|
||||||
|
inlineKeys = getInlineKeys(obj)
|
||||||
|
complexKeys = getComplexKeys(obj)
|
||||||
|
const result = []
|
||||||
|
const inlineIndent = indent || ''
|
||||||
|
inlineKeys.forEach(key => {
|
||||||
|
var type = tomlType(obj[key])
|
||||||
|
if (type !== 'undefined' && type !== 'null') {
|
||||||
|
result.push(inlineIndent + stringifyKey(key) + ' = ' + stringifyAnyInline(obj[key], true))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (result.length > 0) result.push('')
|
||||||
|
const complexIndent = prefix && inlineKeys.length > 0 ? indent + ' ' : ''
|
||||||
|
complexKeys.forEach(key => {
|
||||||
|
result.push(stringifyComplex(prefix, complexIndent, key, obj[key]))
|
||||||
|
})
|
||||||
|
return result.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
function isInline (value) {
|
||||||
|
switch (tomlType(value)) {
|
||||||
|
case 'undefined':
|
||||||
|
case 'null':
|
||||||
|
case 'integer':
|
||||||
|
case 'nan':
|
||||||
|
case 'float':
|
||||||
|
case 'boolean':
|
||||||
|
case 'string':
|
||||||
|
case 'datetime':
|
||||||
|
return true
|
||||||
|
case 'array':
|
||||||
|
return value.length === 0 || tomlType(value[0]) !== 'table'
|
||||||
|
case 'table':
|
||||||
|
return Object.keys(value).length === 0
|
||||||
|
/* istanbul ignore next */
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tomlType (value) {
|
||||||
|
if (value === undefined) {
|
||||||
|
return 'undefined'
|
||||||
|
} else if (value === null) {
|
||||||
|
return 'null'
|
||||||
|
/* eslint-disable valid-typeof */
|
||||||
|
} else if (typeof value === 'bigint' || (Number.isInteger(value) && !Object.is(value, -0))) {
|
||||||
|
return 'integer'
|
||||||
|
} else if (typeof value === 'number') {
|
||||||
|
return 'float'
|
||||||
|
} else if (typeof value === 'boolean') {
|
||||||
|
return 'boolean'
|
||||||
|
} else if (typeof value === 'string') {
|
||||||
|
return 'string'
|
||||||
|
} else if ('toISOString' in value) {
|
||||||
|
return isNaN(value) ? 'undefined' : 'datetime'
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
return 'array'
|
||||||
|
} else {
|
||||||
|
return 'table'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyKey (key) {
|
||||||
|
const keyStr = String(key)
|
||||||
|
if (/^[-A-Za-z0-9_]+$/.test(keyStr)) {
|
||||||
|
return keyStr
|
||||||
|
} else {
|
||||||
|
return stringifyBasicString(keyStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyBasicString (str) {
|
||||||
|
return '"' + escapeString(str).replace(/"/g, '\\"') + '"'
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyLiteralString (str) {
|
||||||
|
return "'" + str + "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
function numpad (num, str) {
|
||||||
|
while (str.length < num) str = '0' + str
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeString (str) {
|
||||||
|
return str.replace(/\\/g, '\\\\')
|
||||||
|
.replace(/[\b]/g, '\\b')
|
||||||
|
.replace(/\t/g, '\\t')
|
||||||
|
.replace(/\n/g, '\\n')
|
||||||
|
.replace(/\f/g, '\\f')
|
||||||
|
.replace(/\r/g, '\\r')
|
||||||
|
/* eslint-disable no-control-regex */
|
||||||
|
.replace(/([\u0000-\u001f\u007f])/, c => '\\u' + numpad(4, c.codePointAt(0).toString(16)))
|
||||||
|
/* eslint-enable no-control-regex */
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyMultilineString (str) {
|
||||||
|
let escaped = str.split(/\n/).map(str => {
|
||||||
|
return escapeString(str).replace(/"(?="")/g, '\\"')
|
||||||
|
}).join('\n')
|
||||||
|
if (escaped.slice(-1) === '"') escaped += '\\\n'
|
||||||
|
return '"""\n' + escaped + '"""'
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyAnyInline (value, multilineOk) {
|
||||||
|
let type = tomlType(value)
|
||||||
|
if (type === 'string') {
|
||||||
|
if (multilineOk && /\n/.test(value)) {
|
||||||
|
type = 'string-multiline'
|
||||||
|
} else if (!/[\b\t\n\f\r']/.test(value) && /"/.test(value)) {
|
||||||
|
type = 'string-literal'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stringifyInline(value, type)
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyInline (value, type) {
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (!type) type = tomlType(value)
|
||||||
|
switch (type) {
|
||||||
|
case 'string-multiline':
|
||||||
|
return stringifyMultilineString(value)
|
||||||
|
case 'string':
|
||||||
|
return stringifyBasicString(value)
|
||||||
|
case 'string-literal':
|
||||||
|
return stringifyLiteralString(value)
|
||||||
|
case 'integer':
|
||||||
|
return stringifyInteger(value)
|
||||||
|
case 'float':
|
||||||
|
return stringifyFloat(value)
|
||||||
|
case 'boolean':
|
||||||
|
return stringifyBoolean(value)
|
||||||
|
case 'datetime':
|
||||||
|
return stringifyDatetime(value)
|
||||||
|
case 'array':
|
||||||
|
return stringifyInlineArray(value.filter(_ => tomlType(_) !== 'null' && tomlType(_) !== 'undefined' && tomlType(_) !== 'nan'))
|
||||||
|
case 'table':
|
||||||
|
return stringifyInlineTable(value)
|
||||||
|
/* istanbul ignore next */
|
||||||
|
default:
|
||||||
|
throw typeError(type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyInteger (value) {
|
||||||
|
/* eslint-disable security/detect-unsafe-regex */
|
||||||
|
return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, '_')
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyFloat (value) {
|
||||||
|
if (value === Infinity) {
|
||||||
|
return 'inf'
|
||||||
|
} else if (value === -Infinity) {
|
||||||
|
return '-inf'
|
||||||
|
} else if (Object.is(value, NaN)) {
|
||||||
|
return 'nan'
|
||||||
|
} else if (Object.is(value, -0)) {
|
||||||
|
return '-0.0'
|
||||||
|
}
|
||||||
|
const [int, dec] = String(value).split('.')
|
||||||
|
return stringifyInteger(int) + '.' + dec
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyBoolean (value) {
|
||||||
|
return String(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyDatetime (value) {
|
||||||
|
return value.toISOString()
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyInlineArray (values) {
|
||||||
|
values = toJSON(values)
|
||||||
|
let result = '['
|
||||||
|
const stringified = values.map(_ => stringifyInline(_))
|
||||||
|
if (stringified.join(', ').length > 60 || /\n/.test(stringified)) {
|
||||||
|
result += '\n ' + stringified.join(',\n ') + '\n'
|
||||||
|
} else {
|
||||||
|
result += ' ' + stringified.join(', ') + (stringified.length > 0 ? ' ' : '')
|
||||||
|
}
|
||||||
|
return result + ']'
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyInlineTable (value) {
|
||||||
|
value = toJSON(value)
|
||||||
|
const result = []
|
||||||
|
Object.keys(value).forEach(key => {
|
||||||
|
result.push(stringifyKey(key) + ' = ' + stringifyAnyInline(value[key], false))
|
||||||
|
})
|
||||||
|
return '{ ' + result.join(', ') + (result.length > 0 ? ' ' : '') + '}'
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyComplex (prefix, indent, key, value) {
|
||||||
|
const valueType = tomlType(value)
|
||||||
|
/* istanbul ignore else */
|
||||||
|
if (valueType === 'array') {
|
||||||
|
return stringifyArrayOfTables(prefix, indent, key, value)
|
||||||
|
} else if (valueType === 'table') {
|
||||||
|
return stringifyComplexTable(prefix, indent, key, value)
|
||||||
|
} else {
|
||||||
|
throw typeError(valueType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyArrayOfTables (prefix, indent, key, values) {
|
||||||
|
values = toJSON(values)
|
||||||
|
const firstValueType = tomlType(values[0])
|
||||||
|
/* istanbul ignore if */
|
||||||
|
if (firstValueType !== 'table') throw typeError(firstValueType)
|
||||||
|
const fullKey = prefix + stringifyKey(key)
|
||||||
|
let result = ''
|
||||||
|
values.forEach(table => {
|
||||||
|
if (result.length > 0) result += '\n'
|
||||||
|
result += indent + '[[' + fullKey + ']]\n'
|
||||||
|
result += stringifyObject(fullKey + '.', indent, table)
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringifyComplexTable (prefix, indent, key, value) {
|
||||||
|
const fullKey = prefix + stringifyKey(key)
|
||||||
|
let result = ''
|
||||||
|
if (getInlineKeys(value).length > 0) {
|
||||||
|
result += indent + '[' + fullKey + ']\n'
|
||||||
|
}
|
||||||
|
return result + stringifyObject(fullKey + '.', indent, value)
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
'use strict'
|
||||||
|
exports.parse = require('./parse.js')
|
||||||
|
exports.stringify = require('./stringify.js')
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the “Software”), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
+1009
File diff suppressed because it is too large
Load Diff
+19
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Utilities for short weierstrass curves, combined with noble-hashes.
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
import { type CurveFn, type CurveType } from './abstract/weierstrass.ts';
|
||||||
|
import type { CHash } from './utils.ts';
|
||||||
|
/** connects noble-curves to noble-hashes */
|
||||||
|
export declare function getHash(hash: CHash): {
|
||||||
|
hash: CHash;
|
||||||
|
};
|
||||||
|
/** Same API as @noble/hashes, with ability to create curve with custom hash */
|
||||||
|
export type CurveDef = Readonly<Omit<CurveType, 'hash'>>;
|
||||||
|
export type CurveFnWithCreate = CurveFn & {
|
||||||
|
create: (hash: CHash) => CurveFn;
|
||||||
|
};
|
||||||
|
/** @deprecated use new `weierstrass()` and `ecdsa()` methods */
|
||||||
|
export declare function createCurve(curveDef: CurveDef, defHash: CHash): CurveFnWithCreate;
|
||||||
|
//# sourceMappingURL=_shortw_utils.d.ts.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"_shortw_utils.d.ts","sourceRoot":"","sources":["src/_shortw_utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,sEAAsE;AACtE,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,2BAA2B,CAAC;AACtF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,4CAA4C;AAC5C,wBAAgB,OAAO,CAAC,IAAI,EAAE,KAAK,GAAG;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,CAEpD;AACD,+EAA+E;AAC/E,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;AACzD,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG;IAAE,MAAM,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,OAAO,CAAA;CAAE,CAAC;AAE/E,gEAAgE;AAChE,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,GAAG,iBAAiB,CAGjF"}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.getHash = getHash;
|
||||||
|
exports.createCurve = createCurve;
|
||||||
|
/**
|
||||||
|
* Utilities for short weierstrass curves, combined with noble-hashes.
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
const weierstrass_ts_1 = require("./abstract/weierstrass.js");
|
||||||
|
/** connects noble-curves to noble-hashes */
|
||||||
|
function getHash(hash) {
|
||||||
|
return { hash };
|
||||||
|
}
|
||||||
|
/** @deprecated use new `weierstrass()` and `ecdsa()` methods */
|
||||||
|
function createCurve(curveDef, defHash) {
|
||||||
|
const create = (hash) => (0, weierstrass_ts_1.weierstrass)({ ...curveDef, hash: hash });
|
||||||
|
return { ...create(defHash), create };
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=_shortw_utils.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"_shortw_utils.js","sourceRoot":"","sources":["src/_shortw_utils.ts"],"names":[],"mappings":";;AASA,0BAEC;AAMD,kCAGC;AApBD;;;GAGG;AACH,sEAAsE;AACtE,8DAAsF;AAGtF,4CAA4C;AAC5C,SAAgB,OAAO,CAAC,IAAW;IACjC,OAAO,EAAE,IAAI,EAAE,CAAC;AAClB,CAAC;AAKD,gEAAgE;AAChE,SAAgB,WAAW,CAAC,QAAkB,EAAE,OAAc;IAC5D,MAAM,MAAM,GAAG,CAAC,IAAW,EAAW,EAAE,CAAC,IAAA,4BAAW,EAAC,EAAE,GAAG,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAClF,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;AACxC,CAAC"}
|
||||||
+190
@@ -0,0 +1,190 @@
|
|||||||
|
/**
|
||||||
|
* BLS != BLS.
|
||||||
|
* The file implements BLS (Boneh-Lynn-Shacham) signatures.
|
||||||
|
* Used in both BLS (Barreto-Lynn-Scott) and BN (Barreto-Naehrig)
|
||||||
|
* families of pairing-friendly curves.
|
||||||
|
* Consists of two curves: G1 and G2:
|
||||||
|
* - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.
|
||||||
|
* - G2 is a subgroup of ((x₁, x₂+i), (y₁, y₂+i)) E(Fq²) over y² = x³ + 4(1 + i) where i is √-1
|
||||||
|
* - Gt, created by bilinear (ate) pairing e(G1, G2), consists of p-th roots of unity in
|
||||||
|
* Fq^k where k is embedding degree. Only degree 12 is currently supported, 24 is not.
|
||||||
|
* Pairing is used to aggregate and verify signatures.
|
||||||
|
* There are two modes of operation:
|
||||||
|
* - Long signatures: X-byte keys + 2X-byte sigs (G1 keys + G2 sigs).
|
||||||
|
* - Short signatures: 2X-byte keys + X-byte sigs (G2 keys + G1 sigs).
|
||||||
|
* @module
|
||||||
|
**/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
import { type CHash, type Hex, type PrivKey } from '../utils.ts';
|
||||||
|
import { type H2CHasher, type H2CHashOpts, type H2COpts, type htfBasicOpts, type MapToCurve } from './hash-to-curve.ts';
|
||||||
|
import { type IField } from './modular.ts';
|
||||||
|
import type { Fp12, Fp12Bls, Fp2, Fp2Bls, Fp6Bls } from './tower.ts';
|
||||||
|
import { type CurvePointsRes, type CurvePointsType, type WeierstrassPoint, type WeierstrassPointCons } from './weierstrass.ts';
|
||||||
|
type Fp = bigint;
|
||||||
|
export type TwistType = 'multiplicative' | 'divisive';
|
||||||
|
export type ShortSignatureCoder<Fp> = {
|
||||||
|
fromBytes(bytes: Uint8Array): WeierstrassPoint<Fp>;
|
||||||
|
fromHex(hex: Hex): WeierstrassPoint<Fp>;
|
||||||
|
toBytes(point: WeierstrassPoint<Fp>): Uint8Array;
|
||||||
|
toHex(point: WeierstrassPoint<Fp>): string;
|
||||||
|
/** @deprecated use `toBytes` */
|
||||||
|
toRawBytes(point: WeierstrassPoint<Fp>): Uint8Array;
|
||||||
|
};
|
||||||
|
export type SignatureCoder<Fp> = {
|
||||||
|
fromBytes(bytes: Uint8Array): WeierstrassPoint<Fp>;
|
||||||
|
fromHex(hex: Hex): WeierstrassPoint<Fp>;
|
||||||
|
toBytes(point: WeierstrassPoint<Fp>): Uint8Array;
|
||||||
|
toHex(point: WeierstrassPoint<Fp>): string;
|
||||||
|
/** @deprecated use `toBytes` */
|
||||||
|
toRawBytes(point: WeierstrassPoint<Fp>): Uint8Array;
|
||||||
|
};
|
||||||
|
export type BlsFields = {
|
||||||
|
Fp: IField<Fp>;
|
||||||
|
Fr: IField<bigint>;
|
||||||
|
Fp2: Fp2Bls;
|
||||||
|
Fp6: Fp6Bls;
|
||||||
|
Fp12: Fp12Bls;
|
||||||
|
};
|
||||||
|
export type PostPrecomputePointAddFn = (Rx: Fp2, Ry: Fp2, Rz: Fp2, Qx: Fp2, Qy: Fp2) => {
|
||||||
|
Rx: Fp2;
|
||||||
|
Ry: Fp2;
|
||||||
|
Rz: Fp2;
|
||||||
|
};
|
||||||
|
export type PostPrecomputeFn = (Rx: Fp2, Ry: Fp2, Rz: Fp2, Qx: Fp2, Qy: Fp2, pointAdd: PostPrecomputePointAddFn) => void;
|
||||||
|
export type BlsPairing = {
|
||||||
|
Fp12: Fp12Bls;
|
||||||
|
calcPairingPrecomputes: (p: WeierstrassPoint<Fp2>) => Precompute;
|
||||||
|
millerLoopBatch: (pairs: [Precompute, Fp, Fp][]) => Fp12;
|
||||||
|
pairing: (P: WeierstrassPoint<Fp>, Q: WeierstrassPoint<Fp2>, withFinalExponent?: boolean) => Fp12;
|
||||||
|
pairingBatch: (pairs: {
|
||||||
|
g1: WeierstrassPoint<Fp>;
|
||||||
|
g2: WeierstrassPoint<Fp2>;
|
||||||
|
}[], withFinalExponent?: boolean) => Fp12;
|
||||||
|
};
|
||||||
|
export type BlsPairingParams = {
|
||||||
|
ateLoopSize: bigint;
|
||||||
|
xNegative: boolean;
|
||||||
|
twistType: TwistType;
|
||||||
|
postPrecompute?: PostPrecomputeFn;
|
||||||
|
};
|
||||||
|
export type CurveType = {
|
||||||
|
G1: CurvePointsType<Fp> & {
|
||||||
|
ShortSignature: SignatureCoder<Fp>;
|
||||||
|
mapToCurve: MapToCurve<Fp>;
|
||||||
|
htfDefaults: H2COpts;
|
||||||
|
};
|
||||||
|
G2: CurvePointsType<Fp2> & {
|
||||||
|
Signature: SignatureCoder<Fp2>;
|
||||||
|
mapToCurve: MapToCurve<Fp2>;
|
||||||
|
htfDefaults: H2COpts;
|
||||||
|
};
|
||||||
|
fields: BlsFields;
|
||||||
|
params: {
|
||||||
|
ateLoopSize: BlsPairingParams['ateLoopSize'];
|
||||||
|
xNegative: BlsPairingParams['xNegative'];
|
||||||
|
r: bigint;
|
||||||
|
twistType: BlsPairingParams['twistType'];
|
||||||
|
};
|
||||||
|
htfDefaults: H2COpts;
|
||||||
|
hash: CHash;
|
||||||
|
randomBytes?: (bytesLength?: number) => Uint8Array;
|
||||||
|
postPrecompute?: PostPrecomputeFn;
|
||||||
|
};
|
||||||
|
type PrecomputeSingle = [Fp2, Fp2, Fp2][];
|
||||||
|
type Precompute = PrecomputeSingle[];
|
||||||
|
/**
|
||||||
|
* BLS consists of two curves: G1 and G2:
|
||||||
|
* - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.
|
||||||
|
* - G2 is a subgroup of ((x₁, x₂+i), (y₁, y₂+i)) E(Fq²) over y² = x³ + 4(1 + i) where i is √-1
|
||||||
|
*/
|
||||||
|
export interface BLSCurvePair {
|
||||||
|
longSignatures: BLSSigs<bigint, Fp2>;
|
||||||
|
shortSignatures: BLSSigs<Fp2, bigint>;
|
||||||
|
millerLoopBatch: BlsPairing['millerLoopBatch'];
|
||||||
|
pairing: BlsPairing['pairing'];
|
||||||
|
pairingBatch: BlsPairing['pairingBatch'];
|
||||||
|
G1: {
|
||||||
|
Point: WeierstrassPointCons<bigint>;
|
||||||
|
} & H2CHasher<Fp>;
|
||||||
|
G2: {
|
||||||
|
Point: WeierstrassPointCons<Fp2>;
|
||||||
|
} & H2CHasher<Fp2>;
|
||||||
|
fields: {
|
||||||
|
Fp: IField<Fp>;
|
||||||
|
Fp2: Fp2Bls;
|
||||||
|
Fp6: Fp6Bls;
|
||||||
|
Fp12: Fp12Bls;
|
||||||
|
Fr: IField<bigint>;
|
||||||
|
};
|
||||||
|
utils: {
|
||||||
|
randomSecretKey: () => Uint8Array;
|
||||||
|
/** @deprecated use randomSecretKey */
|
||||||
|
randomPrivateKey: () => Uint8Array;
|
||||||
|
calcPairingPrecomputes: BlsPairing['calcPairingPrecomputes'];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export type CurveFn = BLSCurvePair & {
|
||||||
|
/** @deprecated use `longSignatures.getPublicKey` */
|
||||||
|
getPublicKey: (secretKey: PrivKey) => Uint8Array;
|
||||||
|
/** @deprecated use `shortSignatures.getPublicKey` */
|
||||||
|
getPublicKeyForShortSignatures: (secretKey: PrivKey) => Uint8Array;
|
||||||
|
/** @deprecated use `longSignatures.sign` */
|
||||||
|
sign: {
|
||||||
|
(message: Hex, secretKey: PrivKey, htfOpts?: htfBasicOpts): Uint8Array;
|
||||||
|
(message: WeierstrassPoint<Fp2>, secretKey: PrivKey, htfOpts?: htfBasicOpts): WeierstrassPoint<Fp2>;
|
||||||
|
};
|
||||||
|
/** @deprecated use `shortSignatures.sign` */
|
||||||
|
signShortSignature: {
|
||||||
|
(message: Hex, secretKey: PrivKey, htfOpts?: htfBasicOpts): Uint8Array;
|
||||||
|
(message: WeierstrassPoint<Fp>, secretKey: PrivKey, htfOpts?: htfBasicOpts): WeierstrassPoint<Fp>;
|
||||||
|
};
|
||||||
|
/** @deprecated use `longSignatures.verify` */
|
||||||
|
verify: (signature: Hex | WeierstrassPoint<Fp2>, message: Hex | WeierstrassPoint<Fp2>, publicKey: Hex | WeierstrassPoint<Fp>, htfOpts?: htfBasicOpts) => boolean;
|
||||||
|
/** @deprecated use `shortSignatures.verify` */
|
||||||
|
verifyShortSignature: (signature: Hex | WeierstrassPoint<Fp>, message: Hex | WeierstrassPoint<Fp>, publicKey: Hex | WeierstrassPoint<Fp2>, htfOpts?: htfBasicOpts) => boolean;
|
||||||
|
verifyBatch: (signature: Hex | WeierstrassPoint<Fp2>, messages: (Hex | WeierstrassPoint<Fp2>)[], publicKeys: (Hex | WeierstrassPoint<Fp>)[], htfOpts?: htfBasicOpts) => boolean;
|
||||||
|
/** @deprecated use `longSignatures.aggregatePublicKeys` */
|
||||||
|
aggregatePublicKeys: {
|
||||||
|
(publicKeys: Hex[]): Uint8Array;
|
||||||
|
(publicKeys: WeierstrassPoint<Fp>[]): WeierstrassPoint<Fp>;
|
||||||
|
};
|
||||||
|
/** @deprecated use `longSignatures.aggregateSignatures` */
|
||||||
|
aggregateSignatures: {
|
||||||
|
(signatures: Hex[]): Uint8Array;
|
||||||
|
(signatures: WeierstrassPoint<Fp2>[]): WeierstrassPoint<Fp2>;
|
||||||
|
};
|
||||||
|
/** @deprecated use `shortSignatures.aggregateSignatures` */
|
||||||
|
aggregateShortSignatures: {
|
||||||
|
(signatures: Hex[]): Uint8Array;
|
||||||
|
(signatures: WeierstrassPoint<Fp>[]): WeierstrassPoint<Fp>;
|
||||||
|
};
|
||||||
|
G1: CurvePointsRes<Fp> & H2CHasher<Fp>;
|
||||||
|
G2: CurvePointsRes<Fp2> & H2CHasher<Fp2>;
|
||||||
|
/** @deprecated use `longSignatures.Signature` */
|
||||||
|
Signature: SignatureCoder<Fp2>;
|
||||||
|
/** @deprecated use `shortSignatures.Signature` */
|
||||||
|
ShortSignature: ShortSignatureCoder<Fp>;
|
||||||
|
params: {
|
||||||
|
ateLoopSize: bigint;
|
||||||
|
r: bigint;
|
||||||
|
twistType: TwistType;
|
||||||
|
/** @deprecated */
|
||||||
|
G1b: bigint;
|
||||||
|
/** @deprecated */
|
||||||
|
G2b: Fp2;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
type BLSInput = Hex | Uint8Array;
|
||||||
|
export interface BLSSigs<P, S> {
|
||||||
|
getPublicKey(secretKey: PrivKey): WeierstrassPoint<P>;
|
||||||
|
sign(hashedMessage: WeierstrassPoint<S>, secretKey: PrivKey): WeierstrassPoint<S>;
|
||||||
|
verify(signature: WeierstrassPoint<S> | BLSInput, message: WeierstrassPoint<S>, publicKey: WeierstrassPoint<P> | BLSInput): boolean;
|
||||||
|
verifyBatch: (signature: WeierstrassPoint<S> | BLSInput, messages: WeierstrassPoint<S>[], publicKeys: (WeierstrassPoint<P> | BLSInput)[]) => boolean;
|
||||||
|
aggregatePublicKeys(publicKeys: (WeierstrassPoint<P> | BLSInput)[]): WeierstrassPoint<P>;
|
||||||
|
aggregateSignatures(signatures: (WeierstrassPoint<S> | BLSInput)[]): WeierstrassPoint<S>;
|
||||||
|
hash(message: Uint8Array, DST?: string | Uint8Array, hashOpts?: H2CHashOpts): WeierstrassPoint<S>;
|
||||||
|
Signature: SignatureCoder<S>;
|
||||||
|
}
|
||||||
|
export declare function bls(CURVE: CurveType): CurveFn;
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=bls.d.ts.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+411
@@ -0,0 +1,411 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.bls = bls;
|
||||||
|
/**
|
||||||
|
* BLS != BLS.
|
||||||
|
* The file implements BLS (Boneh-Lynn-Shacham) signatures.
|
||||||
|
* Used in both BLS (Barreto-Lynn-Scott) and BN (Barreto-Naehrig)
|
||||||
|
* families of pairing-friendly curves.
|
||||||
|
* Consists of two curves: G1 and G2:
|
||||||
|
* - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.
|
||||||
|
* - G2 is a subgroup of ((x₁, x₂+i), (y₁, y₂+i)) E(Fq²) over y² = x³ + 4(1 + i) where i is √-1
|
||||||
|
* - Gt, created by bilinear (ate) pairing e(G1, G2), consists of p-th roots of unity in
|
||||||
|
* Fq^k where k is embedding degree. Only degree 12 is currently supported, 24 is not.
|
||||||
|
* Pairing is used to aggregate and verify signatures.
|
||||||
|
* There are two modes of operation:
|
||||||
|
* - Long signatures: X-byte keys + 2X-byte sigs (G1 keys + G2 sigs).
|
||||||
|
* - Short signatures: 2X-byte keys + X-byte sigs (G2 keys + G1 sigs).
|
||||||
|
* @module
|
||||||
|
**/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
const utils_ts_1 = require("../utils.js");
|
||||||
|
const curve_ts_1 = require("./curve.js");
|
||||||
|
const hash_to_curve_ts_1 = require("./hash-to-curve.js");
|
||||||
|
const modular_ts_1 = require("./modular.js");
|
||||||
|
const weierstrass_ts_1 = require("./weierstrass.js");
|
||||||
|
// prettier-ignore
|
||||||
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
||||||
|
// Not used with BLS12-381 (no sequential `11` in X). Useful for other curves.
|
||||||
|
function NAfDecomposition(a) {
|
||||||
|
const res = [];
|
||||||
|
// a>1 because of marker bit
|
||||||
|
for (; a > _1n; a >>= _1n) {
|
||||||
|
if ((a & _1n) === _0n)
|
||||||
|
res.unshift(0);
|
||||||
|
else if ((a & _3n) === _3n) {
|
||||||
|
res.unshift(-1);
|
||||||
|
a += _1n;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
res.unshift(1);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
function aNonEmpty(arr) {
|
||||||
|
if (!Array.isArray(arr) || arr.length === 0)
|
||||||
|
throw new Error('expected non-empty array');
|
||||||
|
}
|
||||||
|
// This should be enough for bn254, no need to export full stuff?
|
||||||
|
function createBlsPairing(fields, G1, G2, params) {
|
||||||
|
const { Fp2, Fp12 } = fields;
|
||||||
|
const { twistType, ateLoopSize, xNegative, postPrecompute } = params;
|
||||||
|
// Applies sparse multiplication as line function
|
||||||
|
let lineFunction;
|
||||||
|
if (twistType === 'multiplicative') {
|
||||||
|
lineFunction = (c0, c1, c2, f, Px, Py) => Fp12.mul014(f, c0, Fp2.mul(c1, Px), Fp2.mul(c2, Py));
|
||||||
|
}
|
||||||
|
else if (twistType === 'divisive') {
|
||||||
|
// NOTE: it should be [c0, c1, c2], but we use different order here to reduce complexity of
|
||||||
|
// precompute calculations.
|
||||||
|
lineFunction = (c0, c1, c2, f, Px, Py) => Fp12.mul034(f, Fp2.mul(c2, Py), Fp2.mul(c1, Px), c0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new Error('bls: unknown twist type');
|
||||||
|
const Fp2div2 = Fp2.div(Fp2.ONE, Fp2.mul(Fp2.ONE, _2n));
|
||||||
|
function pointDouble(ell, Rx, Ry, Rz) {
|
||||||
|
const t0 = Fp2.sqr(Ry); // Ry²
|
||||||
|
const t1 = Fp2.sqr(Rz); // Rz²
|
||||||
|
const t2 = Fp2.mulByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
|
||||||
|
const t3 = Fp2.mul(t2, _3n); // 3 * T2
|
||||||
|
const t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
|
||||||
|
const c0 = Fp2.sub(t2, t0); // T2 - T0 (i)
|
||||||
|
const c1 = Fp2.mul(Fp2.sqr(Rx), _3n); // 3 * Rx²
|
||||||
|
const c2 = Fp2.neg(t4); // -T4 (-h)
|
||||||
|
ell.push([c0, c1, c2]);
|
||||||
|
Rx = Fp2.mul(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), Fp2div2); // ((T0 - T3) * Rx * Ry) / 2
|
||||||
|
Ry = Fp2.sub(Fp2.sqr(Fp2.mul(Fp2.add(t0, t3), Fp2div2)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
|
||||||
|
Rz = Fp2.mul(t0, t4); // T0 * T4
|
||||||
|
return { Rx, Ry, Rz };
|
||||||
|
}
|
||||||
|
function pointAdd(ell, Rx, Ry, Rz, Qx, Qy) {
|
||||||
|
// Addition
|
||||||
|
const t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
|
||||||
|
const t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
|
||||||
|
const c0 = Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)); // T0 * Qx - T1 * Qy == Ry * Qx - Rx * Qy
|
||||||
|
const c1 = Fp2.neg(t0); // -T0 == Qy * Rz - Ry
|
||||||
|
const c2 = t1; // == Rx - Qx * Rz
|
||||||
|
ell.push([c0, c1, c2]);
|
||||||
|
const t2 = Fp2.sqr(t1); // T1²
|
||||||
|
const t3 = Fp2.mul(t2, t1); // T2 * T1
|
||||||
|
const t4 = Fp2.mul(t2, Rx); // T2 * Rx
|
||||||
|
const t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
|
||||||
|
Rx = Fp2.mul(t1, t5); // T1 * T5
|
||||||
|
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
|
||||||
|
Rz = Fp2.mul(Rz, t3); // Rz * T3
|
||||||
|
return { Rx, Ry, Rz };
|
||||||
|
}
|
||||||
|
// Pre-compute coefficients for sparse multiplication
|
||||||
|
// Point addition and point double calculations is reused for coefficients
|
||||||
|
// pointAdd happens only if bit set, so wNAF is reasonable. Unfortunately we cannot combine
|
||||||
|
// add + double in windowed precomputes here, otherwise it would be single op (since X is static)
|
||||||
|
const ATE_NAF = NAfDecomposition(ateLoopSize);
|
||||||
|
const calcPairingPrecomputes = (0, utils_ts_1.memoized)((point) => {
|
||||||
|
const p = point;
|
||||||
|
const { x, y } = p.toAffine();
|
||||||
|
// prettier-ignore
|
||||||
|
const Qx = x, Qy = y, negQy = Fp2.neg(y);
|
||||||
|
// prettier-ignore
|
||||||
|
let Rx = Qx, Ry = Qy, Rz = Fp2.ONE;
|
||||||
|
const ell = [];
|
||||||
|
for (const bit of ATE_NAF) {
|
||||||
|
const cur = [];
|
||||||
|
({ Rx, Ry, Rz } = pointDouble(cur, Rx, Ry, Rz));
|
||||||
|
if (bit)
|
||||||
|
({ Rx, Ry, Rz } = pointAdd(cur, Rx, Ry, Rz, Qx, bit === -1 ? negQy : Qy));
|
||||||
|
ell.push(cur);
|
||||||
|
}
|
||||||
|
if (postPrecompute) {
|
||||||
|
const last = ell[ell.length - 1];
|
||||||
|
postPrecompute(Rx, Ry, Rz, Qx, Qy, pointAdd.bind(null, last));
|
||||||
|
}
|
||||||
|
return ell;
|
||||||
|
});
|
||||||
|
function millerLoopBatch(pairs, withFinalExponent = false) {
|
||||||
|
let f12 = Fp12.ONE;
|
||||||
|
if (pairs.length) {
|
||||||
|
const ellLen = pairs[0][0].length;
|
||||||
|
for (let i = 0; i < ellLen; i++) {
|
||||||
|
f12 = Fp12.sqr(f12); // This allows us to do sqr only one time for all pairings
|
||||||
|
// NOTE: we apply multiple pairings in parallel here
|
||||||
|
for (const [ell, Px, Py] of pairs) {
|
||||||
|
for (const [c0, c1, c2] of ell[i])
|
||||||
|
f12 = lineFunction(c0, c1, c2, f12, Px, Py);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (xNegative)
|
||||||
|
f12 = Fp12.conjugate(f12);
|
||||||
|
return withFinalExponent ? Fp12.finalExponentiate(f12) : f12;
|
||||||
|
}
|
||||||
|
// Calculates product of multiple pairings
|
||||||
|
// This up to x2 faster than just `map(({g1, g2})=>pairing({g1,g2}))`
|
||||||
|
function pairingBatch(pairs, withFinalExponent = true) {
|
||||||
|
const res = [];
|
||||||
|
// Cache precomputed toAffine for all points
|
||||||
|
(0, curve_ts_1.normalizeZ)(G1, pairs.map(({ g1 }) => g1));
|
||||||
|
(0, curve_ts_1.normalizeZ)(G2, pairs.map(({ g2 }) => g2));
|
||||||
|
for (const { g1, g2 } of pairs) {
|
||||||
|
if (g1.is0() || g2.is0())
|
||||||
|
throw new Error('pairing is not available for ZERO point');
|
||||||
|
// This uses toAffine inside
|
||||||
|
g1.assertValidity();
|
||||||
|
g2.assertValidity();
|
||||||
|
const Qa = g1.toAffine();
|
||||||
|
res.push([calcPairingPrecomputes(g2), Qa.x, Qa.y]);
|
||||||
|
}
|
||||||
|
return millerLoopBatch(res, withFinalExponent);
|
||||||
|
}
|
||||||
|
// Calculates bilinear pairing
|
||||||
|
function pairing(Q, P, withFinalExponent = true) {
|
||||||
|
return pairingBatch([{ g1: Q, g2: P }], withFinalExponent);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
Fp12, // NOTE: we re-export Fp12 here because pairing results are Fp12!
|
||||||
|
millerLoopBatch,
|
||||||
|
pairing,
|
||||||
|
pairingBatch,
|
||||||
|
calcPairingPrecomputes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function createBlsSig(blsPairing, PubCurve, SigCurve, SignatureCoder, isSigG1) {
|
||||||
|
const { Fp12, pairingBatch } = blsPairing;
|
||||||
|
function normPub(point) {
|
||||||
|
return point instanceof PubCurve.Point ? point : PubCurve.Point.fromHex(point);
|
||||||
|
}
|
||||||
|
function normSig(point) {
|
||||||
|
return point instanceof SigCurve.Point ? point : SigCurve.Point.fromHex(point);
|
||||||
|
}
|
||||||
|
function amsg(m) {
|
||||||
|
if (!(m instanceof SigCurve.Point))
|
||||||
|
throw new Error(`expected valid message hashed to ${!isSigG1 ? 'G2' : 'G1'} curve`);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
// What matters here is what point pairing API accepts as G1 or G2, not actual size or names
|
||||||
|
const pair = !isSigG1
|
||||||
|
? (a, b) => ({ g1: a, g2: b })
|
||||||
|
: (a, b) => ({ g1: b, g2: a });
|
||||||
|
return {
|
||||||
|
// P = pk x G
|
||||||
|
getPublicKey(secretKey) {
|
||||||
|
// TODO: replace with
|
||||||
|
// const sec = PubCurve.Point.Fn.fromBytes(secretKey);
|
||||||
|
const sec = (0, weierstrass_ts_1._normFnElement)(PubCurve.Point.Fn, secretKey);
|
||||||
|
return PubCurve.Point.BASE.multiply(sec);
|
||||||
|
},
|
||||||
|
// S = pk x H(m)
|
||||||
|
sign(message, secretKey, unusedArg) {
|
||||||
|
if (unusedArg != null)
|
||||||
|
throw new Error('sign() expects 2 arguments');
|
||||||
|
// TODO: replace with
|
||||||
|
// PubCurve.Point.Fn.fromBytes(secretKey)
|
||||||
|
const sec = (0, weierstrass_ts_1._normFnElement)(PubCurve.Point.Fn, secretKey);
|
||||||
|
amsg(message).assertValidity();
|
||||||
|
return message.multiply(sec);
|
||||||
|
},
|
||||||
|
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
|
||||||
|
// e(P, H(m)) == e(G, S)
|
||||||
|
// e(S, G) == e(H(m), P)
|
||||||
|
verify(signature, message, publicKey, unusedArg) {
|
||||||
|
if (unusedArg != null)
|
||||||
|
throw new Error('verify() expects 3 arguments');
|
||||||
|
signature = normSig(signature);
|
||||||
|
publicKey = normPub(publicKey);
|
||||||
|
const P = publicKey.negate();
|
||||||
|
const G = PubCurve.Point.BASE;
|
||||||
|
const Hm = amsg(message);
|
||||||
|
const S = signature;
|
||||||
|
// This code was changed in 1.9.x:
|
||||||
|
// Before it was G.negate() in G2, now it's always pubKey.negate
|
||||||
|
// e(P, -Q)===e(-P, Q)==e(P, Q)^-1. Negate can be done anywhere (as long it is done once per pair).
|
||||||
|
// We just moving sign, but since pairing is multiplicative, we doing X * X^-1 = 1
|
||||||
|
const exp = pairingBatch([pair(P, Hm), pair(G, S)]);
|
||||||
|
return Fp12.eql(exp, Fp12.ONE);
|
||||||
|
},
|
||||||
|
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
|
||||||
|
// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
|
||||||
|
// TODO: maybe `{message: G2Hex, publicKey: G1Hex}[]` instead?
|
||||||
|
verifyBatch(signature, messages, publicKeys) {
|
||||||
|
aNonEmpty(messages);
|
||||||
|
if (publicKeys.length !== messages.length)
|
||||||
|
throw new Error('amount of public keys and messages should be equal');
|
||||||
|
const sig = normSig(signature);
|
||||||
|
const nMessages = messages;
|
||||||
|
const nPublicKeys = publicKeys.map(normPub);
|
||||||
|
// NOTE: this works only for exact same object
|
||||||
|
const messagePubKeyMap = new Map();
|
||||||
|
for (let i = 0; i < nPublicKeys.length; i++) {
|
||||||
|
const pub = nPublicKeys[i];
|
||||||
|
const msg = nMessages[i];
|
||||||
|
let keys = messagePubKeyMap.get(msg);
|
||||||
|
if (keys === undefined) {
|
||||||
|
keys = [];
|
||||||
|
messagePubKeyMap.set(msg, keys);
|
||||||
|
}
|
||||||
|
keys.push(pub);
|
||||||
|
}
|
||||||
|
const paired = [];
|
||||||
|
const G = PubCurve.Point.BASE;
|
||||||
|
try {
|
||||||
|
for (const [msg, keys] of messagePubKeyMap) {
|
||||||
|
const groupPublicKey = keys.reduce((acc, msg) => acc.add(msg));
|
||||||
|
paired.push(pair(groupPublicKey, msg));
|
||||||
|
}
|
||||||
|
paired.push(pair(G.negate(), sig));
|
||||||
|
return Fp12.eql(pairingBatch(paired), Fp12.ONE);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Adds a bunch of public key points together.
|
||||||
|
// pk1 + pk2 + pk3 = pkA
|
||||||
|
aggregatePublicKeys(publicKeys) {
|
||||||
|
aNonEmpty(publicKeys);
|
||||||
|
publicKeys = publicKeys.map((pub) => normPub(pub));
|
||||||
|
const agg = publicKeys.reduce((sum, p) => sum.add(p), PubCurve.Point.ZERO);
|
||||||
|
agg.assertValidity();
|
||||||
|
return agg;
|
||||||
|
},
|
||||||
|
// Adds a bunch of signature points together.
|
||||||
|
// pk1 + pk2 + pk3 = pkA
|
||||||
|
aggregateSignatures(signatures) {
|
||||||
|
aNonEmpty(signatures);
|
||||||
|
signatures = signatures.map((sig) => normSig(sig));
|
||||||
|
const agg = signatures.reduce((sum, s) => sum.add(s), SigCurve.Point.ZERO);
|
||||||
|
agg.assertValidity();
|
||||||
|
return agg;
|
||||||
|
},
|
||||||
|
hash(messageBytes, DST) {
|
||||||
|
(0, utils_ts_1.abytes)(messageBytes);
|
||||||
|
const opts = DST ? { DST } : undefined;
|
||||||
|
return SigCurve.hashToCurve(messageBytes, opts);
|
||||||
|
},
|
||||||
|
Signature: SignatureCoder,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// G1_Point: ProjConstructor<bigint>, G2_Point: ProjConstructor<Fp2>,
|
||||||
|
function bls(CURVE) {
|
||||||
|
// Fields are specific for curve, so for now we'll need to pass them with opts
|
||||||
|
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
|
||||||
|
// Point on G1 curve: (x, y)
|
||||||
|
const G1_ = (0, weierstrass_ts_1.weierstrassPoints)(CURVE.G1);
|
||||||
|
const G1 = Object.assign(G1_, (0, hash_to_curve_ts_1.createHasher)(G1_.Point, CURVE.G1.mapToCurve, {
|
||||||
|
...CURVE.htfDefaults,
|
||||||
|
...CURVE.G1.htfDefaults,
|
||||||
|
}));
|
||||||
|
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
|
||||||
|
const G2_ = (0, weierstrass_ts_1.weierstrassPoints)(CURVE.G2);
|
||||||
|
const G2 = Object.assign(G2_, (0, hash_to_curve_ts_1.createHasher)(G2_.Point, CURVE.G2.mapToCurve, {
|
||||||
|
...CURVE.htfDefaults,
|
||||||
|
...CURVE.G2.htfDefaults,
|
||||||
|
}));
|
||||||
|
const pairingRes = createBlsPairing(CURVE.fields, G1.Point, G2.Point, {
|
||||||
|
...CURVE.params,
|
||||||
|
postPrecompute: CURVE.postPrecompute,
|
||||||
|
});
|
||||||
|
const { millerLoopBatch, pairing, pairingBatch, calcPairingPrecomputes } = pairingRes;
|
||||||
|
const longSignatures = createBlsSig(pairingRes, G1, G2, CURVE.G2.Signature, false);
|
||||||
|
const shortSignatures = createBlsSig(pairingRes, G2, G1, CURVE.G1.ShortSignature, true);
|
||||||
|
const rand = CURVE.randomBytes || utils_ts_1.randomBytes;
|
||||||
|
const randomSecretKey = () => {
|
||||||
|
const length = (0, modular_ts_1.getMinHashLength)(Fr.ORDER);
|
||||||
|
return (0, modular_ts_1.mapHashToField)(rand(length), Fr.ORDER);
|
||||||
|
};
|
||||||
|
const utils = {
|
||||||
|
randomSecretKey,
|
||||||
|
randomPrivateKey: randomSecretKey,
|
||||||
|
calcPairingPrecomputes,
|
||||||
|
};
|
||||||
|
const { ShortSignature } = CURVE.G1;
|
||||||
|
const { Signature } = CURVE.G2;
|
||||||
|
function normP1Hash(point, htfOpts) {
|
||||||
|
return point instanceof G1.Point
|
||||||
|
? point
|
||||||
|
: shortSignatures.hash((0, utils_ts_1.ensureBytes)('point', point), htfOpts?.DST);
|
||||||
|
}
|
||||||
|
function normP2Hash(point, htfOpts) {
|
||||||
|
return point instanceof G2.Point
|
||||||
|
? point
|
||||||
|
: longSignatures.hash((0, utils_ts_1.ensureBytes)('point', point), htfOpts?.DST);
|
||||||
|
}
|
||||||
|
function getPublicKey(privateKey) {
|
||||||
|
return longSignatures.getPublicKey(privateKey).toBytes(true);
|
||||||
|
}
|
||||||
|
function getPublicKeyForShortSignatures(privateKey) {
|
||||||
|
return shortSignatures.getPublicKey(privateKey).toBytes(true);
|
||||||
|
}
|
||||||
|
function sign(message, privateKey, htfOpts) {
|
||||||
|
const Hm = normP2Hash(message, htfOpts);
|
||||||
|
const S = longSignatures.sign(Hm, privateKey);
|
||||||
|
return message instanceof G2.Point ? S : Signature.toBytes(S);
|
||||||
|
}
|
||||||
|
function signShortSignature(message, privateKey, htfOpts) {
|
||||||
|
const Hm = normP1Hash(message, htfOpts);
|
||||||
|
const S = shortSignatures.sign(Hm, privateKey);
|
||||||
|
return message instanceof G1.Point ? S : ShortSignature.toBytes(S);
|
||||||
|
}
|
||||||
|
function verify(signature, message, publicKey, htfOpts) {
|
||||||
|
const Hm = normP2Hash(message, htfOpts);
|
||||||
|
return longSignatures.verify(signature, Hm, publicKey);
|
||||||
|
}
|
||||||
|
function verifyShortSignature(signature, message, publicKey, htfOpts) {
|
||||||
|
const Hm = normP1Hash(message, htfOpts);
|
||||||
|
return shortSignatures.verify(signature, Hm, publicKey);
|
||||||
|
}
|
||||||
|
function aggregatePublicKeys(publicKeys) {
|
||||||
|
const agg = longSignatures.aggregatePublicKeys(publicKeys);
|
||||||
|
return publicKeys[0] instanceof G1.Point ? agg : agg.toBytes(true);
|
||||||
|
}
|
||||||
|
function aggregateSignatures(signatures) {
|
||||||
|
const agg = longSignatures.aggregateSignatures(signatures);
|
||||||
|
return signatures[0] instanceof G2.Point ? agg : Signature.toBytes(agg);
|
||||||
|
}
|
||||||
|
function aggregateShortSignatures(signatures) {
|
||||||
|
const agg = shortSignatures.aggregateSignatures(signatures);
|
||||||
|
return signatures[0] instanceof G1.Point ? agg : ShortSignature.toBytes(agg);
|
||||||
|
}
|
||||||
|
function verifyBatch(signature, messages, publicKeys, htfOpts) {
|
||||||
|
const Hm = messages.map((m) => normP2Hash(m, htfOpts));
|
||||||
|
return longSignatures.verifyBatch(signature, Hm, publicKeys);
|
||||||
|
}
|
||||||
|
G1.Point.BASE.precompute(4);
|
||||||
|
return {
|
||||||
|
longSignatures,
|
||||||
|
shortSignatures,
|
||||||
|
millerLoopBatch,
|
||||||
|
pairing,
|
||||||
|
pairingBatch,
|
||||||
|
verifyBatch,
|
||||||
|
fields: {
|
||||||
|
Fr,
|
||||||
|
Fp,
|
||||||
|
Fp2,
|
||||||
|
Fp6,
|
||||||
|
Fp12,
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
ateLoopSize: CURVE.params.ateLoopSize,
|
||||||
|
twistType: CURVE.params.twistType,
|
||||||
|
// deprecated
|
||||||
|
r: CURVE.params.r,
|
||||||
|
G1b: CURVE.G1.b,
|
||||||
|
G2b: CURVE.G2.b,
|
||||||
|
},
|
||||||
|
utils,
|
||||||
|
// deprecated
|
||||||
|
getPublicKey,
|
||||||
|
getPublicKeyForShortSignatures,
|
||||||
|
sign,
|
||||||
|
signShortSignature,
|
||||||
|
verify,
|
||||||
|
verifyShortSignature,
|
||||||
|
aggregatePublicKeys,
|
||||||
|
aggregateSignatures,
|
||||||
|
aggregateShortSignatures,
|
||||||
|
G1,
|
||||||
|
G2,
|
||||||
|
Signature,
|
||||||
|
ShortSignature,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=bls.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+231
@@ -0,0 +1,231 @@
|
|||||||
|
import { type IField } from './modular.ts';
|
||||||
|
export type AffinePoint<T> = {
|
||||||
|
x: T;
|
||||||
|
y: T;
|
||||||
|
} & {
|
||||||
|
Z?: never;
|
||||||
|
};
|
||||||
|
export interface Group<T extends Group<T>> {
|
||||||
|
double(): T;
|
||||||
|
negate(): T;
|
||||||
|
add(other: T): T;
|
||||||
|
subtract(other: T): T;
|
||||||
|
equals(other: T): boolean;
|
||||||
|
multiply(scalar: bigint): T;
|
||||||
|
toAffine?(invertedZ?: any): AffinePoint<any>;
|
||||||
|
}
|
||||||
|
/** Base interface for all elliptic curve Points. */
|
||||||
|
export interface CurvePoint<F, P extends CurvePoint<F, P>> extends Group<P> {
|
||||||
|
/** Affine x coordinate. Different from projective / extended X coordinate. */
|
||||||
|
x: F;
|
||||||
|
/** Affine y coordinate. Different from projective / extended Y coordinate. */
|
||||||
|
y: F;
|
||||||
|
Z?: F;
|
||||||
|
double(): P;
|
||||||
|
negate(): P;
|
||||||
|
add(other: P): P;
|
||||||
|
subtract(other: P): P;
|
||||||
|
equals(other: P): boolean;
|
||||||
|
multiply(scalar: bigint): P;
|
||||||
|
assertValidity(): void;
|
||||||
|
clearCofactor(): P;
|
||||||
|
is0(): boolean;
|
||||||
|
isTorsionFree(): boolean;
|
||||||
|
isSmallOrder(): boolean;
|
||||||
|
multiplyUnsafe(scalar: bigint): P;
|
||||||
|
/**
|
||||||
|
* Massively speeds up `p.multiply(n)` by using precompute tables (caching). See {@link wNAF}.
|
||||||
|
* @param isLazy calculate cache now. Default (true) ensures it's deferred to first `multiply()`
|
||||||
|
*/
|
||||||
|
precompute(windowSize?: number, isLazy?: boolean): P;
|
||||||
|
/** Converts point to 2D xy affine coordinates */
|
||||||
|
toAffine(invertedZ?: F): AffinePoint<F>;
|
||||||
|
toBytes(): Uint8Array;
|
||||||
|
toHex(): string;
|
||||||
|
}
|
||||||
|
/** Base interface for all elliptic curve Point constructors. */
|
||||||
|
export interface CurvePointCons<P extends CurvePoint<any, P>> {
|
||||||
|
[Symbol.hasInstance]: (item: unknown) => boolean;
|
||||||
|
BASE: P;
|
||||||
|
ZERO: P;
|
||||||
|
/** Field for basic curve math */
|
||||||
|
Fp: IField<P_F<P>>;
|
||||||
|
/** Scalar field, for scalars in multiply and others */
|
||||||
|
Fn: IField<bigint>;
|
||||||
|
/** Creates point from x, y. Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
||||||
|
fromAffine(p: AffinePoint<P_F<P>>): P;
|
||||||
|
fromBytes(bytes: Uint8Array): P;
|
||||||
|
fromHex(hex: Uint8Array | string): P;
|
||||||
|
}
|
||||||
|
/** Returns Fp type from Point (P_F<P> == P.F) */
|
||||||
|
export type P_F<P extends CurvePoint<any, P>> = P extends CurvePoint<infer F, P> ? F : never;
|
||||||
|
/** Returns Fp type from PointCons (PC_F<PC> == PC.P.F) */
|
||||||
|
export type PC_F<PC extends CurvePointCons<CurvePoint<any, any>>> = PC['Fp']['ZERO'];
|
||||||
|
/** Returns Point type from PointCons (PC_P<PC> == PC.P) */
|
||||||
|
export type PC_P<PC extends CurvePointCons<CurvePoint<any, any>>> = PC['ZERO'];
|
||||||
|
export type PC_ANY = CurvePointCons<CurvePoint<any, CurvePoint<any, CurvePoint<any, CurvePoint<any, CurvePoint<any, CurvePoint<any, CurvePoint<any, CurvePoint<any, CurvePoint<any, CurvePoint<any, any>>>>>>>>>>>;
|
||||||
|
export interface CurveLengths {
|
||||||
|
secretKey?: number;
|
||||||
|
publicKey?: number;
|
||||||
|
publicKeyUncompressed?: number;
|
||||||
|
publicKeyHasPrefix?: boolean;
|
||||||
|
signature?: number;
|
||||||
|
seed?: number;
|
||||||
|
}
|
||||||
|
export type GroupConstructor<T> = {
|
||||||
|
BASE: T;
|
||||||
|
ZERO: T;
|
||||||
|
};
|
||||||
|
/** @deprecated */
|
||||||
|
export type ExtendedGroupConstructor<T> = GroupConstructor<T> & {
|
||||||
|
Fp: IField<any>;
|
||||||
|
Fn: IField<bigint>;
|
||||||
|
fromAffine(ap: AffinePoint<any>): T;
|
||||||
|
};
|
||||||
|
export type Mapper<T> = (i: T[]) => T[];
|
||||||
|
export declare function negateCt<T extends {
|
||||||
|
negate: () => T;
|
||||||
|
}>(condition: boolean, item: T): T;
|
||||||
|
/**
|
||||||
|
* Takes a bunch of Projective Points but executes only one
|
||||||
|
* inversion on all of them. Inversion is very slow operation,
|
||||||
|
* so this improves performance massively.
|
||||||
|
* Optimization: converts a list of projective points to a list of identical points with Z=1.
|
||||||
|
*/
|
||||||
|
export declare function normalizeZ<P extends CurvePoint<any, P>, PC extends CurvePointCons<P>>(c: PC, points: P[]): P[];
|
||||||
|
/** Internal wNAF opts for specific W and scalarBits */
|
||||||
|
export type WOpts = {
|
||||||
|
windows: number;
|
||||||
|
windowSize: number;
|
||||||
|
mask: bigint;
|
||||||
|
maxNumber: number;
|
||||||
|
shiftBy: bigint;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Elliptic curve multiplication of Point by scalar. Fragile.
|
||||||
|
* Table generation takes **30MB of ram and 10ms on high-end CPU**,
|
||||||
|
* but may take much longer on slow devices. Actual generation will happen on
|
||||||
|
* first call of `multiply()`. By default, `BASE` point is precomputed.
|
||||||
|
*
|
||||||
|
* Scalars should always be less than curve order: this should be checked inside of a curve itself.
|
||||||
|
* Creates precomputation tables for fast multiplication:
|
||||||
|
* - private scalar is split by fixed size windows of W bits
|
||||||
|
* - every window point is collected from window's table & added to accumulator
|
||||||
|
* - since windows are different, same point inside tables won't be accessed more than once per calc
|
||||||
|
* - each multiplication is 'Math.ceil(CURVE_ORDER / 𝑊) + 1' point additions (fixed for any scalar)
|
||||||
|
* - +1 window is neccessary for wNAF
|
||||||
|
* - wNAF reduces table size: 2x less memory + 2x faster generation, but 10% slower multiplication
|
||||||
|
*
|
||||||
|
* @todo Research returning 2d JS array of windows, instead of a single window.
|
||||||
|
* This would allow windows to be in different memory locations
|
||||||
|
*/
|
||||||
|
export declare class wNAF<PC extends PC_ANY> {
|
||||||
|
private readonly BASE;
|
||||||
|
private readonly ZERO;
|
||||||
|
private readonly Fn;
|
||||||
|
readonly bits: number;
|
||||||
|
constructor(Point: PC, bits: number);
|
||||||
|
_unsafeLadder(elm: PC_P<PC>, n: bigint, p?: PC_P<PC>): PC_P<PC>;
|
||||||
|
/**
|
||||||
|
* Creates a wNAF precomputation window. Used for caching.
|
||||||
|
* Default window size is set by `utils.precompute()` and is equal to 8.
|
||||||
|
* Number of precomputed points depends on the curve size:
|
||||||
|
* 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
|
||||||
|
* - 𝑊 is the window size
|
||||||
|
* - 𝑛 is the bitlength of the curve order.
|
||||||
|
* For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
|
||||||
|
* @param point Point instance
|
||||||
|
* @param W window size
|
||||||
|
* @returns precomputed point tables flattened to a single array
|
||||||
|
*/
|
||||||
|
private precomputeWindow;
|
||||||
|
/**
|
||||||
|
* Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
|
||||||
|
* More compact implementation:
|
||||||
|
* https://github.com/paulmillr/noble-secp256k1/blob/47cb1669b6e506ad66b35fe7d76132ae97465da2/index.ts#L502-L541
|
||||||
|
* @returns real and fake (for const-time) points
|
||||||
|
*/
|
||||||
|
private wNAF;
|
||||||
|
/**
|
||||||
|
* Implements ec unsafe (non const-time) multiplication using precomputed tables and w-ary non-adjacent form.
|
||||||
|
* @param acc accumulator point to add result of multiplication
|
||||||
|
* @returns point
|
||||||
|
*/
|
||||||
|
private wNAFUnsafe;
|
||||||
|
private getPrecomputes;
|
||||||
|
cached(point: PC_P<PC>, scalar: bigint, transform?: Mapper<PC_P<PC>>): {
|
||||||
|
p: PC_P<PC>;
|
||||||
|
f: PC_P<PC>;
|
||||||
|
};
|
||||||
|
unsafe(point: PC_P<PC>, scalar: bigint, transform?: Mapper<PC_P<PC>>, prev?: PC_P<PC>): PC_P<PC>;
|
||||||
|
createCache(P: PC_P<PC>, W: number): void;
|
||||||
|
hasCache(elm: PC_P<PC>): boolean;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Endomorphism-specific multiplication for Koblitz curves.
|
||||||
|
* Cost: 128 dbl, 0-256 adds.
|
||||||
|
*/
|
||||||
|
export declare function mulEndoUnsafe<P extends CurvePoint<any, P>, PC extends CurvePointCons<P>>(Point: PC, point: P, k1: bigint, k2: bigint): {
|
||||||
|
p1: P;
|
||||||
|
p2: P;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Pippenger algorithm for multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).
|
||||||
|
* 30x faster vs naive addition on L=4096, 10x faster than precomputes.
|
||||||
|
* For N=254bit, L=1, it does: 1024 ADD + 254 DBL. For L=5: 1536 ADD + 254 DBL.
|
||||||
|
* Algorithmically constant-time (for same L), even when 1 point + scalar, or when scalar = 0.
|
||||||
|
* @param c Curve Point constructor
|
||||||
|
* @param fieldN field over CURVE.N - important that it's not over CURVE.P
|
||||||
|
* @param points array of L curve points
|
||||||
|
* @param scalars array of L scalars (aka secret keys / bigints)
|
||||||
|
*/
|
||||||
|
export declare function pippenger<P extends CurvePoint<any, P>, PC extends CurvePointCons<P>>(c: PC, fieldN: IField<bigint>, points: P[], scalars: bigint[]): P;
|
||||||
|
/**
|
||||||
|
* Precomputed multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).
|
||||||
|
* @param c Curve Point constructor
|
||||||
|
* @param fieldN field over CURVE.N - important that it's not over CURVE.P
|
||||||
|
* @param points array of L curve points
|
||||||
|
* @returns function which multiplies points with scaars
|
||||||
|
*/
|
||||||
|
export declare function precomputeMSMUnsafe<P extends CurvePoint<any, P>, PC extends CurvePointCons<P>>(c: PC, fieldN: IField<bigint>, points: P[], windowSize: number): (scalars: bigint[]) => P;
|
||||||
|
/**
|
||||||
|
* Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok.
|
||||||
|
* Though generator can be different (Fp2 / Fp6 for BLS).
|
||||||
|
*/
|
||||||
|
export type BasicCurve<T> = {
|
||||||
|
Fp: IField<T>;
|
||||||
|
n: bigint;
|
||||||
|
nBitLength?: number;
|
||||||
|
nByteLength?: number;
|
||||||
|
h: bigint;
|
||||||
|
hEff?: bigint;
|
||||||
|
Gx: T;
|
||||||
|
Gy: T;
|
||||||
|
allowInfinityPoint?: boolean;
|
||||||
|
};
|
||||||
|
/** @deprecated */
|
||||||
|
export declare function validateBasic<FP, T>(curve: BasicCurve<FP> & T): Readonly<{
|
||||||
|
readonly nBitLength: number;
|
||||||
|
readonly nByteLength: number;
|
||||||
|
} & BasicCurve<FP> & T & {
|
||||||
|
p: bigint;
|
||||||
|
}>;
|
||||||
|
export type ValidCurveParams<T> = {
|
||||||
|
p: bigint;
|
||||||
|
n: bigint;
|
||||||
|
h: bigint;
|
||||||
|
a: T;
|
||||||
|
b?: T;
|
||||||
|
d?: T;
|
||||||
|
Gx: T;
|
||||||
|
Gy: T;
|
||||||
|
};
|
||||||
|
export type FpFn<T> = {
|
||||||
|
Fp: IField<T>;
|
||||||
|
Fn: IField<bigint>;
|
||||||
|
};
|
||||||
|
/** Validates CURVE opts and creates fields */
|
||||||
|
export declare function _createCurveFields<T>(type: 'weierstrass' | 'edwards', CURVE: ValidCurveParams<T>, curveOpts?: Partial<FpFn<T>>, FpFnLE?: boolean): FpFn<T> & {
|
||||||
|
CURVE: ValidCurveParams<T>;
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=curve.d.ts.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+476
@@ -0,0 +1,476 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.wNAF = void 0;
|
||||||
|
exports.negateCt = negateCt;
|
||||||
|
exports.normalizeZ = normalizeZ;
|
||||||
|
exports.mulEndoUnsafe = mulEndoUnsafe;
|
||||||
|
exports.pippenger = pippenger;
|
||||||
|
exports.precomputeMSMUnsafe = precomputeMSMUnsafe;
|
||||||
|
exports.validateBasic = validateBasic;
|
||||||
|
exports._createCurveFields = _createCurveFields;
|
||||||
|
/**
|
||||||
|
* Methods for elliptic curve multiplication by scalars.
|
||||||
|
* Contains wNAF, pippenger.
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
const utils_ts_1 = require("../utils.js");
|
||||||
|
const modular_ts_1 = require("./modular.js");
|
||||||
|
const _0n = BigInt(0);
|
||||||
|
const _1n = BigInt(1);
|
||||||
|
function negateCt(condition, item) {
|
||||||
|
const neg = item.negate();
|
||||||
|
return condition ? neg : item;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Takes a bunch of Projective Points but executes only one
|
||||||
|
* inversion on all of them. Inversion is very slow operation,
|
||||||
|
* so this improves performance massively.
|
||||||
|
* Optimization: converts a list of projective points to a list of identical points with Z=1.
|
||||||
|
*/
|
||||||
|
function normalizeZ(c, points) {
|
||||||
|
const invertedZs = (0, modular_ts_1.FpInvertBatch)(c.Fp, points.map((p) => p.Z));
|
||||||
|
return points.map((p, i) => c.fromAffine(p.toAffine(invertedZs[i])));
|
||||||
|
}
|
||||||
|
function validateW(W, bits) {
|
||||||
|
if (!Number.isSafeInteger(W) || W <= 0 || W > bits)
|
||||||
|
throw new Error('invalid window size, expected [1..' + bits + '], got W=' + W);
|
||||||
|
}
|
||||||
|
function calcWOpts(W, scalarBits) {
|
||||||
|
validateW(W, scalarBits);
|
||||||
|
const windows = Math.ceil(scalarBits / W) + 1; // W=8 33. Not 32, because we skip zero
|
||||||
|
const windowSize = 2 ** (W - 1); // W=8 128. Not 256, because we skip zero
|
||||||
|
const maxNumber = 2 ** W; // W=8 256
|
||||||
|
const mask = (0, utils_ts_1.bitMask)(W); // W=8 255 == mask 0b11111111
|
||||||
|
const shiftBy = BigInt(W); // W=8 8
|
||||||
|
return { windows, windowSize, mask, maxNumber, shiftBy };
|
||||||
|
}
|
||||||
|
function calcOffsets(n, window, wOpts) {
|
||||||
|
const { windowSize, mask, maxNumber, shiftBy } = wOpts;
|
||||||
|
let wbits = Number(n & mask); // extract W bits.
|
||||||
|
let nextN = n >> shiftBy; // shift number by W bits.
|
||||||
|
// What actually happens here:
|
||||||
|
// const highestBit = Number(mask ^ (mask >> 1n));
|
||||||
|
// let wbits2 = wbits - 1; // skip zero
|
||||||
|
// if (wbits2 & highestBit) { wbits2 ^= Number(mask); // (~);
|
||||||
|
// split if bits > max: +224 => 256-32
|
||||||
|
if (wbits > windowSize) {
|
||||||
|
// we skip zero, which means instead of `>= size-1`, we do `> size`
|
||||||
|
wbits -= maxNumber; // -32, can be maxNumber - wbits, but then we need to set isNeg here.
|
||||||
|
nextN += _1n; // +256 (carry)
|
||||||
|
}
|
||||||
|
const offsetStart = window * windowSize;
|
||||||
|
const offset = offsetStart + Math.abs(wbits) - 1; // -1 because we skip zero
|
||||||
|
const isZero = wbits === 0; // is current window slice a 0?
|
||||||
|
const isNeg = wbits < 0; // is current window slice negative?
|
||||||
|
const isNegF = window % 2 !== 0; // fake random statement for noise
|
||||||
|
const offsetF = offsetStart; // fake offset for noise
|
||||||
|
return { nextN, offset, isZero, isNeg, isNegF, offsetF };
|
||||||
|
}
|
||||||
|
function validateMSMPoints(points, c) {
|
||||||
|
if (!Array.isArray(points))
|
||||||
|
throw new Error('array expected');
|
||||||
|
points.forEach((p, i) => {
|
||||||
|
if (!(p instanceof c))
|
||||||
|
throw new Error('invalid point at index ' + i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function validateMSMScalars(scalars, field) {
|
||||||
|
if (!Array.isArray(scalars))
|
||||||
|
throw new Error('array of scalars expected');
|
||||||
|
scalars.forEach((s, i) => {
|
||||||
|
if (!field.isValid(s))
|
||||||
|
throw new Error('invalid scalar at index ' + i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Since points in different groups cannot be equal (different object constructor),
|
||||||
|
// we can have single place to store precomputes.
|
||||||
|
// Allows to make points frozen / immutable.
|
||||||
|
const pointPrecomputes = new WeakMap();
|
||||||
|
const pointWindowSizes = new WeakMap();
|
||||||
|
function getW(P) {
|
||||||
|
// To disable precomputes:
|
||||||
|
// return 1;
|
||||||
|
return pointWindowSizes.get(P) || 1;
|
||||||
|
}
|
||||||
|
function assert0(n) {
|
||||||
|
if (n !== _0n)
|
||||||
|
throw new Error('invalid wNAF');
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Elliptic curve multiplication of Point by scalar. Fragile.
|
||||||
|
* Table generation takes **30MB of ram and 10ms on high-end CPU**,
|
||||||
|
* but may take much longer on slow devices. Actual generation will happen on
|
||||||
|
* first call of `multiply()`. By default, `BASE` point is precomputed.
|
||||||
|
*
|
||||||
|
* Scalars should always be less than curve order: this should be checked inside of a curve itself.
|
||||||
|
* Creates precomputation tables for fast multiplication:
|
||||||
|
* - private scalar is split by fixed size windows of W bits
|
||||||
|
* - every window point is collected from window's table & added to accumulator
|
||||||
|
* - since windows are different, same point inside tables won't be accessed more than once per calc
|
||||||
|
* - each multiplication is 'Math.ceil(CURVE_ORDER / 𝑊) + 1' point additions (fixed for any scalar)
|
||||||
|
* - +1 window is neccessary for wNAF
|
||||||
|
* - wNAF reduces table size: 2x less memory + 2x faster generation, but 10% slower multiplication
|
||||||
|
*
|
||||||
|
* @todo Research returning 2d JS array of windows, instead of a single window.
|
||||||
|
* This would allow windows to be in different memory locations
|
||||||
|
*/
|
||||||
|
class wNAF {
|
||||||
|
// Parametrized with a given Point class (not individual point)
|
||||||
|
constructor(Point, bits) {
|
||||||
|
this.BASE = Point.BASE;
|
||||||
|
this.ZERO = Point.ZERO;
|
||||||
|
this.Fn = Point.Fn;
|
||||||
|
this.bits = bits;
|
||||||
|
}
|
||||||
|
// non-const time multiplication ladder
|
||||||
|
_unsafeLadder(elm, n, p = this.ZERO) {
|
||||||
|
let d = elm;
|
||||||
|
while (n > _0n) {
|
||||||
|
if (n & _1n)
|
||||||
|
p = p.add(d);
|
||||||
|
d = d.double();
|
||||||
|
n >>= _1n;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Creates a wNAF precomputation window. Used for caching.
|
||||||
|
* Default window size is set by `utils.precompute()` and is equal to 8.
|
||||||
|
* Number of precomputed points depends on the curve size:
|
||||||
|
* 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
|
||||||
|
* - 𝑊 is the window size
|
||||||
|
* - 𝑛 is the bitlength of the curve order.
|
||||||
|
* For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
|
||||||
|
* @param point Point instance
|
||||||
|
* @param W window size
|
||||||
|
* @returns precomputed point tables flattened to a single array
|
||||||
|
*/
|
||||||
|
precomputeWindow(point, W) {
|
||||||
|
const { windows, windowSize } = calcWOpts(W, this.bits);
|
||||||
|
const points = [];
|
||||||
|
let p = point;
|
||||||
|
let base = p;
|
||||||
|
for (let window = 0; window < windows; window++) {
|
||||||
|
base = p;
|
||||||
|
points.push(base);
|
||||||
|
// i=1, bc we skip 0
|
||||||
|
for (let i = 1; i < windowSize; i++) {
|
||||||
|
base = base.add(p);
|
||||||
|
points.push(base);
|
||||||
|
}
|
||||||
|
p = base.double();
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
|
||||||
|
* More compact implementation:
|
||||||
|
* https://github.com/paulmillr/noble-secp256k1/blob/47cb1669b6e506ad66b35fe7d76132ae97465da2/index.ts#L502-L541
|
||||||
|
* @returns real and fake (for const-time) points
|
||||||
|
*/
|
||||||
|
wNAF(W, precomputes, n) {
|
||||||
|
// Scalar should be smaller than field order
|
||||||
|
if (!this.Fn.isValid(n))
|
||||||
|
throw new Error('invalid scalar');
|
||||||
|
// Accumulators
|
||||||
|
let p = this.ZERO;
|
||||||
|
let f = this.BASE;
|
||||||
|
// This code was first written with assumption that 'f' and 'p' will never be infinity point:
|
||||||
|
// since each addition is multiplied by 2 ** W, it cannot cancel each other. However,
|
||||||
|
// there is negate now: it is possible that negated element from low value
|
||||||
|
// would be the same as high element, which will create carry into next window.
|
||||||
|
// It's not obvious how this can fail, but still worth investigating later.
|
||||||
|
const wo = calcWOpts(W, this.bits);
|
||||||
|
for (let window = 0; window < wo.windows; window++) {
|
||||||
|
// (n === _0n) is handled and not early-exited. isEven and offsetF are used for noise
|
||||||
|
const { nextN, offset, isZero, isNeg, isNegF, offsetF } = calcOffsets(n, window, wo);
|
||||||
|
n = nextN;
|
||||||
|
if (isZero) {
|
||||||
|
// bits are 0: add garbage to fake point
|
||||||
|
// Important part for const-time getPublicKey: add random "noise" point to f.
|
||||||
|
f = f.add(negateCt(isNegF, precomputes[offsetF]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// bits are 1: add to result point
|
||||||
|
p = p.add(negateCt(isNeg, precomputes[offset]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert0(n);
|
||||||
|
// Return both real and fake points: JIT won't eliminate f.
|
||||||
|
// At this point there is a way to F be infinity-point even if p is not,
|
||||||
|
// which makes it less const-time: around 1 bigint multiply.
|
||||||
|
return { p, f };
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Implements ec unsafe (non const-time) multiplication using precomputed tables and w-ary non-adjacent form.
|
||||||
|
* @param acc accumulator point to add result of multiplication
|
||||||
|
* @returns point
|
||||||
|
*/
|
||||||
|
wNAFUnsafe(W, precomputes, n, acc = this.ZERO) {
|
||||||
|
const wo = calcWOpts(W, this.bits);
|
||||||
|
for (let window = 0; window < wo.windows; window++) {
|
||||||
|
if (n === _0n)
|
||||||
|
break; // Early-exit, skip 0 value
|
||||||
|
const { nextN, offset, isZero, isNeg } = calcOffsets(n, window, wo);
|
||||||
|
n = nextN;
|
||||||
|
if (isZero) {
|
||||||
|
// Window bits are 0: skip processing.
|
||||||
|
// Move to next window.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const item = precomputes[offset];
|
||||||
|
acc = acc.add(isNeg ? item.negate() : item); // Re-using acc allows to save adds in MSM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert0(n);
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
getPrecomputes(W, point, transform) {
|
||||||
|
// Calculate precomputes on a first run, reuse them after
|
||||||
|
let comp = pointPrecomputes.get(point);
|
||||||
|
if (!comp) {
|
||||||
|
comp = this.precomputeWindow(point, W);
|
||||||
|
if (W !== 1) {
|
||||||
|
// Doing transform outside of if brings 15% perf hit
|
||||||
|
if (typeof transform === 'function')
|
||||||
|
comp = transform(comp);
|
||||||
|
pointPrecomputes.set(point, comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
cached(point, scalar, transform) {
|
||||||
|
const W = getW(point);
|
||||||
|
return this.wNAF(W, this.getPrecomputes(W, point, transform), scalar);
|
||||||
|
}
|
||||||
|
unsafe(point, scalar, transform, prev) {
|
||||||
|
const W = getW(point);
|
||||||
|
if (W === 1)
|
||||||
|
return this._unsafeLadder(point, scalar, prev); // For W=1 ladder is ~x2 faster
|
||||||
|
return this.wNAFUnsafe(W, this.getPrecomputes(W, point, transform), scalar, prev);
|
||||||
|
}
|
||||||
|
// We calculate precomputes for elliptic curve point multiplication
|
||||||
|
// using windowed method. This specifies window size and
|
||||||
|
// stores precomputed values. Usually only base point would be precomputed.
|
||||||
|
createCache(P, W) {
|
||||||
|
validateW(W, this.bits);
|
||||||
|
pointWindowSizes.set(P, W);
|
||||||
|
pointPrecomputes.delete(P);
|
||||||
|
}
|
||||||
|
hasCache(elm) {
|
||||||
|
return getW(elm) !== 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.wNAF = wNAF;
|
||||||
|
/**
|
||||||
|
* Endomorphism-specific multiplication for Koblitz curves.
|
||||||
|
* Cost: 128 dbl, 0-256 adds.
|
||||||
|
*/
|
||||||
|
function mulEndoUnsafe(Point, point, k1, k2) {
|
||||||
|
let acc = point;
|
||||||
|
let p1 = Point.ZERO;
|
||||||
|
let p2 = Point.ZERO;
|
||||||
|
while (k1 > _0n || k2 > _0n) {
|
||||||
|
if (k1 & _1n)
|
||||||
|
p1 = p1.add(acc);
|
||||||
|
if (k2 & _1n)
|
||||||
|
p2 = p2.add(acc);
|
||||||
|
acc = acc.double();
|
||||||
|
k1 >>= _1n;
|
||||||
|
k2 >>= _1n;
|
||||||
|
}
|
||||||
|
return { p1, p2 };
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Pippenger algorithm for multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).
|
||||||
|
* 30x faster vs naive addition on L=4096, 10x faster than precomputes.
|
||||||
|
* For N=254bit, L=1, it does: 1024 ADD + 254 DBL. For L=5: 1536 ADD + 254 DBL.
|
||||||
|
* Algorithmically constant-time (for same L), even when 1 point + scalar, or when scalar = 0.
|
||||||
|
* @param c Curve Point constructor
|
||||||
|
* @param fieldN field over CURVE.N - important that it's not over CURVE.P
|
||||||
|
* @param points array of L curve points
|
||||||
|
* @param scalars array of L scalars (aka secret keys / bigints)
|
||||||
|
*/
|
||||||
|
function pippenger(c, fieldN, points, scalars) {
|
||||||
|
// If we split scalars by some window (let's say 8 bits), every chunk will only
|
||||||
|
// take 256 buckets even if there are 4096 scalars, also re-uses double.
|
||||||
|
// TODO:
|
||||||
|
// - https://eprint.iacr.org/2024/750.pdf
|
||||||
|
// - https://tches.iacr.org/index.php/TCHES/article/view/10287
|
||||||
|
// 0 is accepted in scalars
|
||||||
|
validateMSMPoints(points, c);
|
||||||
|
validateMSMScalars(scalars, fieldN);
|
||||||
|
const plength = points.length;
|
||||||
|
const slength = scalars.length;
|
||||||
|
if (plength !== slength)
|
||||||
|
throw new Error('arrays of points and scalars must have equal length');
|
||||||
|
// if (plength === 0) throw new Error('array must be of length >= 2');
|
||||||
|
const zero = c.ZERO;
|
||||||
|
const wbits = (0, utils_ts_1.bitLen)(BigInt(plength));
|
||||||
|
let windowSize = 1; // bits
|
||||||
|
if (wbits > 12)
|
||||||
|
windowSize = wbits - 3;
|
||||||
|
else if (wbits > 4)
|
||||||
|
windowSize = wbits - 2;
|
||||||
|
else if (wbits > 0)
|
||||||
|
windowSize = 2;
|
||||||
|
const MASK = (0, utils_ts_1.bitMask)(windowSize);
|
||||||
|
const buckets = new Array(Number(MASK) + 1).fill(zero); // +1 for zero array
|
||||||
|
const lastBits = Math.floor((fieldN.BITS - 1) / windowSize) * windowSize;
|
||||||
|
let sum = zero;
|
||||||
|
for (let i = lastBits; i >= 0; i -= windowSize) {
|
||||||
|
buckets.fill(zero);
|
||||||
|
for (let j = 0; j < slength; j++) {
|
||||||
|
const scalar = scalars[j];
|
||||||
|
const wbits = Number((scalar >> BigInt(i)) & MASK);
|
||||||
|
buckets[wbits] = buckets[wbits].add(points[j]);
|
||||||
|
}
|
||||||
|
let resI = zero; // not using this will do small speed-up, but will lose ct
|
||||||
|
// Skip first bucket, because it is zero
|
||||||
|
for (let j = buckets.length - 1, sumI = zero; j > 0; j--) {
|
||||||
|
sumI = sumI.add(buckets[j]);
|
||||||
|
resI = resI.add(sumI);
|
||||||
|
}
|
||||||
|
sum = sum.add(resI);
|
||||||
|
if (i !== 0)
|
||||||
|
for (let j = 0; j < windowSize; j++)
|
||||||
|
sum = sum.double();
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Precomputed multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).
|
||||||
|
* @param c Curve Point constructor
|
||||||
|
* @param fieldN field over CURVE.N - important that it's not over CURVE.P
|
||||||
|
* @param points array of L curve points
|
||||||
|
* @returns function which multiplies points with scaars
|
||||||
|
*/
|
||||||
|
function precomputeMSMUnsafe(c, fieldN, points, windowSize) {
|
||||||
|
/**
|
||||||
|
* Performance Analysis of Window-based Precomputation
|
||||||
|
*
|
||||||
|
* Base Case (256-bit scalar, 8-bit window):
|
||||||
|
* - Standard precomputation requires:
|
||||||
|
* - 31 additions per scalar × 256 scalars = 7,936 ops
|
||||||
|
* - Plus 255 summary additions = 8,191 total ops
|
||||||
|
* Note: Summary additions can be optimized via accumulator
|
||||||
|
*
|
||||||
|
* Chunked Precomputation Analysis:
|
||||||
|
* - Using 32 chunks requires:
|
||||||
|
* - 255 additions per chunk
|
||||||
|
* - 256 doublings
|
||||||
|
* - Total: (255 × 32) + 256 = 8,416 ops
|
||||||
|
*
|
||||||
|
* Memory Usage Comparison:
|
||||||
|
* Window Size | Standard Points | Chunked Points
|
||||||
|
* ------------|-----------------|---------------
|
||||||
|
* 4-bit | 520 | 15
|
||||||
|
* 8-bit | 4,224 | 255
|
||||||
|
* 10-bit | 13,824 | 1,023
|
||||||
|
* 16-bit | 557,056 | 65,535
|
||||||
|
*
|
||||||
|
* Key Advantages:
|
||||||
|
* 1. Enables larger window sizes due to reduced memory overhead
|
||||||
|
* 2. More efficient for smaller scalar counts:
|
||||||
|
* - 16 chunks: (16 × 255) + 256 = 4,336 ops
|
||||||
|
* - ~2x faster than standard 8,191 ops
|
||||||
|
*
|
||||||
|
* Limitations:
|
||||||
|
* - Not suitable for plain precomputes (requires 256 constant doublings)
|
||||||
|
* - Performance degrades with larger scalar counts:
|
||||||
|
* - Optimal for ~256 scalars
|
||||||
|
* - Less efficient for 4096+ scalars (Pippenger preferred)
|
||||||
|
*/
|
||||||
|
validateW(windowSize, fieldN.BITS);
|
||||||
|
validateMSMPoints(points, c);
|
||||||
|
const zero = c.ZERO;
|
||||||
|
const tableSize = 2 ** windowSize - 1; // table size (without zero)
|
||||||
|
const chunks = Math.ceil(fieldN.BITS / windowSize); // chunks of item
|
||||||
|
const MASK = (0, utils_ts_1.bitMask)(windowSize);
|
||||||
|
const tables = points.map((p) => {
|
||||||
|
const res = [];
|
||||||
|
for (let i = 0, acc = p; i < tableSize; i++) {
|
||||||
|
res.push(acc);
|
||||||
|
acc = acc.add(p);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
return (scalars) => {
|
||||||
|
validateMSMScalars(scalars, fieldN);
|
||||||
|
if (scalars.length > points.length)
|
||||||
|
throw new Error('array of scalars must be smaller than array of points');
|
||||||
|
let res = zero;
|
||||||
|
for (let i = 0; i < chunks; i++) {
|
||||||
|
// No need to double if accumulator is still zero.
|
||||||
|
if (res !== zero)
|
||||||
|
for (let j = 0; j < windowSize; j++)
|
||||||
|
res = res.double();
|
||||||
|
const shiftBy = BigInt(chunks * windowSize - (i + 1) * windowSize);
|
||||||
|
for (let j = 0; j < scalars.length; j++) {
|
||||||
|
const n = scalars[j];
|
||||||
|
const curr = Number((n >> shiftBy) & MASK);
|
||||||
|
if (!curr)
|
||||||
|
continue; // skip zero scalars chunks
|
||||||
|
res = res.add(tables[j][curr - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// TODO: remove
|
||||||
|
/** @deprecated */
|
||||||
|
function validateBasic(curve) {
|
||||||
|
(0, modular_ts_1.validateField)(curve.Fp);
|
||||||
|
(0, utils_ts_1.validateObject)(curve, {
|
||||||
|
n: 'bigint',
|
||||||
|
h: 'bigint',
|
||||||
|
Gx: 'field',
|
||||||
|
Gy: 'field',
|
||||||
|
}, {
|
||||||
|
nBitLength: 'isSafeInteger',
|
||||||
|
nByteLength: 'isSafeInteger',
|
||||||
|
});
|
||||||
|
// Set defaults
|
||||||
|
return Object.freeze({
|
||||||
|
...(0, modular_ts_1.nLength)(curve.n, curve.nBitLength),
|
||||||
|
...curve,
|
||||||
|
...{ p: curve.Fp.ORDER },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function createField(order, field, isLE) {
|
||||||
|
if (field) {
|
||||||
|
if (field.ORDER !== order)
|
||||||
|
throw new Error('Field.ORDER must match order: Fp == p, Fn == n');
|
||||||
|
(0, modular_ts_1.validateField)(field);
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (0, modular_ts_1.Field)(order, { isLE });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Validates CURVE opts and creates fields */
|
||||||
|
function _createCurveFields(type, CURVE, curveOpts = {}, FpFnLE) {
|
||||||
|
if (FpFnLE === undefined)
|
||||||
|
FpFnLE = type === 'edwards';
|
||||||
|
if (!CURVE || typeof CURVE !== 'object')
|
||||||
|
throw new Error(`expected valid ${type} CURVE object`);
|
||||||
|
for (const p of ['p', 'n', 'h']) {
|
||||||
|
const val = CURVE[p];
|
||||||
|
if (!(typeof val === 'bigint' && val > _0n))
|
||||||
|
throw new Error(`CURVE.${p} must be positive bigint`);
|
||||||
|
}
|
||||||
|
const Fp = createField(CURVE.p, curveOpts.Fp, FpFnLE);
|
||||||
|
const Fn = createField(CURVE.n, curveOpts.Fn, FpFnLE);
|
||||||
|
const _b = type === 'weierstrass' ? 'b' : 'd';
|
||||||
|
const params = ['Gx', 'Gy', 'a', _b];
|
||||||
|
for (const p of params) {
|
||||||
|
// @ts-ignore
|
||||||
|
if (!Fp.isValid(CURVE[p]))
|
||||||
|
throw new Error(`CURVE.${p} must be valid field element of CURVE.Fp`);
|
||||||
|
}
|
||||||
|
CURVE = Object.freeze(Object.assign({}, CURVE));
|
||||||
|
return { CURVE, Fp, Fn };
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=curve.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+243
@@ -0,0 +1,243 @@
|
|||||||
|
/**
|
||||||
|
* Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y².
|
||||||
|
* For design rationale of types / exports, see weierstrass module documentation.
|
||||||
|
* Untwisted Edwards curves exist, but they aren't used in real-world protocols.
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
import { type FHash, type Hex } from '../utils.ts';
|
||||||
|
import { type AffinePoint, type BasicCurve, type CurveLengths, type CurvePoint, type CurvePointCons } from './curve.ts';
|
||||||
|
import { type IField, type NLength } from './modular.ts';
|
||||||
|
export type UVRatio = (u: bigint, v: bigint) => {
|
||||||
|
isValid: boolean;
|
||||||
|
value: bigint;
|
||||||
|
};
|
||||||
|
/** Instance of Extended Point with coordinates in X, Y, Z, T. */
|
||||||
|
export interface EdwardsPoint extends CurvePoint<bigint, EdwardsPoint> {
|
||||||
|
/** extended X coordinate. Different from affine x. */
|
||||||
|
readonly X: bigint;
|
||||||
|
/** extended Y coordinate. Different from affine y. */
|
||||||
|
readonly Y: bigint;
|
||||||
|
/** extended Z coordinate */
|
||||||
|
readonly Z: bigint;
|
||||||
|
/** extended T coordinate */
|
||||||
|
readonly T: bigint;
|
||||||
|
/** @deprecated use `toBytes` */
|
||||||
|
toRawBytes(): Uint8Array;
|
||||||
|
/** @deprecated use `p.precompute(windowSize)` */
|
||||||
|
_setWindowSize(windowSize: number): void;
|
||||||
|
/** @deprecated use .X */
|
||||||
|
readonly ex: bigint;
|
||||||
|
/** @deprecated use .Y */
|
||||||
|
readonly ey: bigint;
|
||||||
|
/** @deprecated use .Z */
|
||||||
|
readonly ez: bigint;
|
||||||
|
/** @deprecated use .T */
|
||||||
|
readonly et: bigint;
|
||||||
|
}
|
||||||
|
/** Static methods of Extended Point with coordinates in X, Y, Z, T. */
|
||||||
|
export interface EdwardsPointCons extends CurvePointCons<EdwardsPoint> {
|
||||||
|
new (X: bigint, Y: bigint, Z: bigint, T: bigint): EdwardsPoint;
|
||||||
|
CURVE(): EdwardsOpts;
|
||||||
|
fromBytes(bytes: Uint8Array, zip215?: boolean): EdwardsPoint;
|
||||||
|
fromHex(hex: Hex, zip215?: boolean): EdwardsPoint;
|
||||||
|
/** @deprecated use `import { pippenger } from '@noble/curves/abstract/curve.js';` */
|
||||||
|
msm(points: EdwardsPoint[], scalars: bigint[]): EdwardsPoint;
|
||||||
|
}
|
||||||
|
/** @deprecated use EdwardsPoint */
|
||||||
|
export type ExtPointType = EdwardsPoint;
|
||||||
|
/** @deprecated use EdwardsPointCons */
|
||||||
|
export type ExtPointConstructor = EdwardsPointCons;
|
||||||
|
/**
|
||||||
|
* Twisted Edwards curve options.
|
||||||
|
*
|
||||||
|
* * a: formula param
|
||||||
|
* * d: formula param
|
||||||
|
* * p: prime characteristic (order) of finite field, in which arithmetics is done
|
||||||
|
* * n: order of prime subgroup a.k.a total amount of valid curve points
|
||||||
|
* * h: cofactor. h*n is group order; n is subgroup order
|
||||||
|
* * Gx: x coordinate of generator point a.k.a. base point
|
||||||
|
* * Gy: y coordinate of generator point
|
||||||
|
*/
|
||||||
|
export type EdwardsOpts = Readonly<{
|
||||||
|
p: bigint;
|
||||||
|
n: bigint;
|
||||||
|
h: bigint;
|
||||||
|
a: bigint;
|
||||||
|
d: bigint;
|
||||||
|
Gx: bigint;
|
||||||
|
Gy: bigint;
|
||||||
|
}>;
|
||||||
|
/**
|
||||||
|
* Extra curve options for Twisted Edwards.
|
||||||
|
*
|
||||||
|
* * Fp: redefined Field over curve.p
|
||||||
|
* * Fn: redefined Field over curve.n
|
||||||
|
* * uvRatio: helper function for decompression, calculating √(u/v)
|
||||||
|
*/
|
||||||
|
export type EdwardsExtraOpts = Partial<{
|
||||||
|
Fp: IField<bigint>;
|
||||||
|
Fn: IField<bigint>;
|
||||||
|
FpFnLE: boolean;
|
||||||
|
uvRatio: (u: bigint, v: bigint) => {
|
||||||
|
isValid: boolean;
|
||||||
|
value: bigint;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
/**
|
||||||
|
* EdDSA (Edwards Digital Signature algorithm) options.
|
||||||
|
*
|
||||||
|
* * hash: hash function used to hash secret keys and messages
|
||||||
|
* * adjustScalarBytes: clears bits to get valid field element
|
||||||
|
* * domain: Used for hashing
|
||||||
|
* * mapToCurve: for hash-to-curve standard
|
||||||
|
* * prehash: RFC 8032 pre-hashing of messages to sign() / verify()
|
||||||
|
* * randomBytes: function generating random bytes, used for randomSecretKey
|
||||||
|
*/
|
||||||
|
export type EdDSAOpts = Partial<{
|
||||||
|
adjustScalarBytes: (bytes: Uint8Array) => Uint8Array;
|
||||||
|
domain: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
||||||
|
mapToCurve: (scalar: bigint[]) => AffinePoint<bigint>;
|
||||||
|
prehash: FHash;
|
||||||
|
randomBytes: (bytesLength?: number) => Uint8Array;
|
||||||
|
}>;
|
||||||
|
/**
|
||||||
|
* EdDSA (Edwards Digital Signature algorithm) interface.
|
||||||
|
*
|
||||||
|
* Allows to create and verify signatures, create public and secret keys.
|
||||||
|
*/
|
||||||
|
export interface EdDSA {
|
||||||
|
keygen: (seed?: Uint8Array) => {
|
||||||
|
secretKey: Uint8Array;
|
||||||
|
publicKey: Uint8Array;
|
||||||
|
};
|
||||||
|
getPublicKey: (secretKey: Hex) => Uint8Array;
|
||||||
|
sign: (message: Hex, secretKey: Hex, options?: {
|
||||||
|
context?: Hex;
|
||||||
|
}) => Uint8Array;
|
||||||
|
verify: (sig: Hex, message: Hex, publicKey: Hex, options?: {
|
||||||
|
context?: Hex;
|
||||||
|
zip215: boolean;
|
||||||
|
}) => boolean;
|
||||||
|
Point: EdwardsPointCons;
|
||||||
|
utils: {
|
||||||
|
randomSecretKey: (seed?: Uint8Array) => Uint8Array;
|
||||||
|
isValidSecretKey: (secretKey: Uint8Array) => boolean;
|
||||||
|
isValidPublicKey: (publicKey: Uint8Array, zip215?: boolean) => boolean;
|
||||||
|
/**
|
||||||
|
* Converts ed public key to x public key.
|
||||||
|
*
|
||||||
|
* There is NO `fromMontgomery`:
|
||||||
|
* - There are 2 valid ed25519 points for every x25519, with flipped coordinate
|
||||||
|
* - Sometimes there are 0 valid ed25519 points, because x25519 *additionally*
|
||||||
|
* accepts inputs on the quadratic twist, which can't be moved to ed25519
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```js
|
||||||
|
* const someonesPub = ed25519.getPublicKey(ed25519.utils.randomSecretKey());
|
||||||
|
* const aPriv = x25519.utils.randomSecretKey();
|
||||||
|
* x25519.getSharedSecret(aPriv, ed25519.utils.toMontgomery(someonesPub))
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
toMontgomery: (publicKey: Uint8Array) => Uint8Array;
|
||||||
|
/**
|
||||||
|
* Converts ed secret key to x secret key.
|
||||||
|
* @example
|
||||||
|
* ```js
|
||||||
|
* const someonesPub = x25519.getPublicKey(x25519.utils.randomSecretKey());
|
||||||
|
* const aPriv = ed25519.utils.randomSecretKey();
|
||||||
|
* x25519.getSharedSecret(ed25519.utils.toMontgomerySecret(aPriv), someonesPub)
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
toMontgomerySecret: (privateKey: Uint8Array) => Uint8Array;
|
||||||
|
getExtendedPublicKey: (key: Hex) => {
|
||||||
|
head: Uint8Array;
|
||||||
|
prefix: Uint8Array;
|
||||||
|
scalar: bigint;
|
||||||
|
point: EdwardsPoint;
|
||||||
|
pointBytes: Uint8Array;
|
||||||
|
};
|
||||||
|
/** @deprecated use `randomSecretKey` */
|
||||||
|
randomPrivateKey: (seed?: Uint8Array) => Uint8Array;
|
||||||
|
/** @deprecated use `point.precompute()` */
|
||||||
|
precompute: (windowSize?: number, point?: EdwardsPoint) => EdwardsPoint;
|
||||||
|
};
|
||||||
|
lengths: CurveLengths;
|
||||||
|
}
|
||||||
|
export declare function edwards(params: EdwardsOpts, extraOpts?: EdwardsExtraOpts): EdwardsPointCons;
|
||||||
|
/**
|
||||||
|
* Base class for prime-order points like Ristretto255 and Decaf448.
|
||||||
|
* These points eliminate cofactor issues by representing equivalence classes
|
||||||
|
* of Edwards curve points.
|
||||||
|
*/
|
||||||
|
export declare abstract class PrimeEdwardsPoint<T extends PrimeEdwardsPoint<T>> implements CurvePoint<bigint, T> {
|
||||||
|
static BASE: PrimeEdwardsPoint<any>;
|
||||||
|
static ZERO: PrimeEdwardsPoint<any>;
|
||||||
|
static Fp: IField<bigint>;
|
||||||
|
static Fn: IField<bigint>;
|
||||||
|
protected readonly ep: EdwardsPoint;
|
||||||
|
constructor(ep: EdwardsPoint);
|
||||||
|
abstract toBytes(): Uint8Array;
|
||||||
|
abstract equals(other: T): boolean;
|
||||||
|
static fromBytes(_bytes: Uint8Array): any;
|
||||||
|
static fromHex(_hex: Hex): any;
|
||||||
|
get x(): bigint;
|
||||||
|
get y(): bigint;
|
||||||
|
clearCofactor(): T;
|
||||||
|
assertValidity(): void;
|
||||||
|
toAffine(invertedZ?: bigint): AffinePoint<bigint>;
|
||||||
|
toHex(): string;
|
||||||
|
toString(): string;
|
||||||
|
isTorsionFree(): boolean;
|
||||||
|
isSmallOrder(): boolean;
|
||||||
|
add(other: T): T;
|
||||||
|
subtract(other: T): T;
|
||||||
|
multiply(scalar: bigint): T;
|
||||||
|
multiplyUnsafe(scalar: bigint): T;
|
||||||
|
double(): T;
|
||||||
|
negate(): T;
|
||||||
|
precompute(windowSize?: number, isLazy?: boolean): T;
|
||||||
|
abstract is0(): boolean;
|
||||||
|
protected abstract assertSame(other: T): void;
|
||||||
|
protected abstract init(ep: EdwardsPoint): T;
|
||||||
|
/** @deprecated use `toBytes` */
|
||||||
|
toRawBytes(): Uint8Array;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Initializes EdDSA signatures over given Edwards curve.
|
||||||
|
*/
|
||||||
|
export declare function eddsa(Point: EdwardsPointCons, cHash: FHash, eddsaOpts?: EdDSAOpts): EdDSA;
|
||||||
|
export type CurveType = BasicCurve<bigint> & {
|
||||||
|
a: bigint;
|
||||||
|
d: bigint;
|
||||||
|
/** @deprecated the property will be removed in next release */
|
||||||
|
hash: FHash;
|
||||||
|
randomBytes?: (bytesLength?: number) => Uint8Array;
|
||||||
|
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
|
||||||
|
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
||||||
|
uvRatio?: UVRatio;
|
||||||
|
prehash?: FHash;
|
||||||
|
mapToCurve?: (scalar: bigint[]) => AffinePoint<bigint>;
|
||||||
|
};
|
||||||
|
export type CurveTypeWithLength = Readonly<CurveType & Partial<NLength>>;
|
||||||
|
export type CurveFn = {
|
||||||
|
/** @deprecated the property will be removed in next release */
|
||||||
|
CURVE: CurveType;
|
||||||
|
keygen: EdDSA['keygen'];
|
||||||
|
getPublicKey: EdDSA['getPublicKey'];
|
||||||
|
sign: EdDSA['sign'];
|
||||||
|
verify: EdDSA['verify'];
|
||||||
|
Point: EdwardsPointCons;
|
||||||
|
/** @deprecated use `Point` */
|
||||||
|
ExtendedPoint: EdwardsPointCons;
|
||||||
|
utils: EdDSA['utils'];
|
||||||
|
lengths: CurveLengths;
|
||||||
|
};
|
||||||
|
export type EdComposed = {
|
||||||
|
CURVE: EdwardsOpts;
|
||||||
|
curveOpts: EdwardsExtraOpts;
|
||||||
|
hash: FHash;
|
||||||
|
eddsaOpts: EdDSAOpts;
|
||||||
|
};
|
||||||
|
export declare function twistedEdwards(c: CurveTypeWithLength): CurveFn;
|
||||||
|
//# sourceMappingURL=edwards.d.ts.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+634
@@ -0,0 +1,634 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.PrimeEdwardsPoint = void 0;
|
||||||
|
exports.edwards = edwards;
|
||||||
|
exports.eddsa = eddsa;
|
||||||
|
exports.twistedEdwards = twistedEdwards;
|
||||||
|
/**
|
||||||
|
* Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y².
|
||||||
|
* For design rationale of types / exports, see weierstrass module documentation.
|
||||||
|
* Untwisted Edwards curves exist, but they aren't used in real-world protocols.
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
const utils_ts_1 = require("../utils.js");
|
||||||
|
const curve_ts_1 = require("./curve.js");
|
||||||
|
const modular_ts_1 = require("./modular.js");
|
||||||
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
||||||
|
// prettier-ignore
|
||||||
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _8n = BigInt(8);
|
||||||
|
function isEdValidXY(Fp, CURVE, x, y) {
|
||||||
|
const x2 = Fp.sqr(x);
|
||||||
|
const y2 = Fp.sqr(y);
|
||||||
|
const left = Fp.add(Fp.mul(CURVE.a, x2), y2);
|
||||||
|
const right = Fp.add(Fp.ONE, Fp.mul(CURVE.d, Fp.mul(x2, y2)));
|
||||||
|
return Fp.eql(left, right);
|
||||||
|
}
|
||||||
|
function edwards(params, extraOpts = {}) {
|
||||||
|
const validated = (0, curve_ts_1._createCurveFields)('edwards', params, extraOpts, extraOpts.FpFnLE);
|
||||||
|
const { Fp, Fn } = validated;
|
||||||
|
let CURVE = validated.CURVE;
|
||||||
|
const { h: cofactor } = CURVE;
|
||||||
|
(0, utils_ts_1._validateObject)(extraOpts, {}, { uvRatio: 'function' });
|
||||||
|
// Important:
|
||||||
|
// There are some places where Fp.BYTES is used instead of nByteLength.
|
||||||
|
// So far, everything has been tested with curves of Fp.BYTES == nByteLength.
|
||||||
|
// TODO: test and find curves which behave otherwise.
|
||||||
|
const MASK = _2n << (BigInt(Fn.BYTES * 8) - _1n);
|
||||||
|
const modP = (n) => Fp.create(n); // Function overrides
|
||||||
|
// sqrt(u/v)
|
||||||
|
const uvRatio = extraOpts.uvRatio ||
|
||||||
|
((u, v) => {
|
||||||
|
try {
|
||||||
|
return { isValid: true, value: Fp.sqrt(Fp.div(u, v)) };
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
return { isValid: false, value: _0n };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Validate whether the passed curve params are valid.
|
||||||
|
// equation ax² + y² = 1 + dx²y² should work for generator point.
|
||||||
|
if (!isEdValidXY(Fp, CURVE, CURVE.Gx, CURVE.Gy))
|
||||||
|
throw new Error('bad curve params: generator point');
|
||||||
|
/**
|
||||||
|
* Asserts coordinate is valid: 0 <= n < MASK.
|
||||||
|
* Coordinates >= Fp.ORDER are allowed for zip215.
|
||||||
|
*/
|
||||||
|
function acoord(title, n, banZero = false) {
|
||||||
|
const min = banZero ? _1n : _0n;
|
||||||
|
(0, utils_ts_1.aInRange)('coordinate ' + title, n, min, MASK);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
function aextpoint(other) {
|
||||||
|
if (!(other instanceof Point))
|
||||||
|
throw new Error('ExtendedPoint expected');
|
||||||
|
}
|
||||||
|
// Converts Extended point to default (x, y) coordinates.
|
||||||
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
||||||
|
const toAffineMemo = (0, utils_ts_1.memoized)((p, iz) => {
|
||||||
|
const { X, Y, Z } = p;
|
||||||
|
const is0 = p.is0();
|
||||||
|
if (iz == null)
|
||||||
|
iz = is0 ? _8n : Fp.inv(Z); // 8 was chosen arbitrarily
|
||||||
|
const x = modP(X * iz);
|
||||||
|
const y = modP(Y * iz);
|
||||||
|
const zz = Fp.mul(Z, iz);
|
||||||
|
if (is0)
|
||||||
|
return { x: _0n, y: _1n };
|
||||||
|
if (zz !== _1n)
|
||||||
|
throw new Error('invZ was invalid');
|
||||||
|
return { x, y };
|
||||||
|
});
|
||||||
|
const assertValidMemo = (0, utils_ts_1.memoized)((p) => {
|
||||||
|
const { a, d } = CURVE;
|
||||||
|
if (p.is0())
|
||||||
|
throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
|
||||||
|
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
|
||||||
|
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
|
||||||
|
const { X, Y, Z, T } = p;
|
||||||
|
const X2 = modP(X * X); // X²
|
||||||
|
const Y2 = modP(Y * Y); // Y²
|
||||||
|
const Z2 = modP(Z * Z); // Z²
|
||||||
|
const Z4 = modP(Z2 * Z2); // Z⁴
|
||||||
|
const aX2 = modP(X2 * a); // aX²
|
||||||
|
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
|
||||||
|
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
|
||||||
|
if (left !== right)
|
||||||
|
throw new Error('bad point: equation left != right (1)');
|
||||||
|
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
|
||||||
|
const XY = modP(X * Y);
|
||||||
|
const ZT = modP(Z * T);
|
||||||
|
if (XY !== ZT)
|
||||||
|
throw new Error('bad point: equation left != right (2)');
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
// Extended Point works in extended coordinates: (X, Y, Z, T) ∋ (x=X/Z, y=Y/Z, T=xy).
|
||||||
|
// https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
|
||||||
|
class Point {
|
||||||
|
constructor(X, Y, Z, T) {
|
||||||
|
this.X = acoord('x', X);
|
||||||
|
this.Y = acoord('y', Y);
|
||||||
|
this.Z = acoord('z', Z, true);
|
||||||
|
this.T = acoord('t', T);
|
||||||
|
Object.freeze(this);
|
||||||
|
}
|
||||||
|
static CURVE() {
|
||||||
|
return CURVE;
|
||||||
|
}
|
||||||
|
static fromAffine(p) {
|
||||||
|
if (p instanceof Point)
|
||||||
|
throw new Error('extended point not allowed');
|
||||||
|
const { x, y } = p || {};
|
||||||
|
acoord('x', x);
|
||||||
|
acoord('y', y);
|
||||||
|
return new Point(x, y, _1n, modP(x * y));
|
||||||
|
}
|
||||||
|
// Uses algo from RFC8032 5.1.3.
|
||||||
|
static fromBytes(bytes, zip215 = false) {
|
||||||
|
const len = Fp.BYTES;
|
||||||
|
const { a, d } = CURVE;
|
||||||
|
bytes = (0, utils_ts_1.copyBytes)((0, utils_ts_1._abytes2)(bytes, len, 'point'));
|
||||||
|
(0, utils_ts_1._abool2)(zip215, 'zip215');
|
||||||
|
const normed = (0, utils_ts_1.copyBytes)(bytes); // copy again, we'll manipulate it
|
||||||
|
const lastByte = bytes[len - 1]; // select last byte
|
||||||
|
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
||||||
|
const y = (0, utils_ts_1.bytesToNumberLE)(normed);
|
||||||
|
// zip215=true is good for consensus-critical apps. =false follows RFC8032 / NIST186-5.
|
||||||
|
// RFC8032 prohibits >= p, but ZIP215 doesn't
|
||||||
|
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
|
||||||
|
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
|
||||||
|
const max = zip215 ? MASK : Fp.ORDER;
|
||||||
|
(0, utils_ts_1.aInRange)('point.y', y, _0n, max);
|
||||||
|
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
|
||||||
|
// ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
|
||||||
|
const y2 = modP(y * y); // denominator is always non-0 mod p.
|
||||||
|
const u = modP(y2 - _1n); // u = y² - 1
|
||||||
|
const v = modP(d * y2 - a); // v = d y² + 1.
|
||||||
|
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
|
||||||
|
if (!isValid)
|
||||||
|
throw new Error('bad point: invalid y coordinate');
|
||||||
|
const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
|
||||||
|
const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit
|
||||||
|
if (!zip215 && x === _0n && isLastByteOdd)
|
||||||
|
// if x=0 and x_0 = 1, fail
|
||||||
|
throw new Error('bad point: x=0 and x_0=1');
|
||||||
|
if (isLastByteOdd !== isXOdd)
|
||||||
|
x = modP(-x); // if x_0 != x mod 2, set x = p-x
|
||||||
|
return Point.fromAffine({ x, y });
|
||||||
|
}
|
||||||
|
static fromHex(bytes, zip215 = false) {
|
||||||
|
return Point.fromBytes((0, utils_ts_1.ensureBytes)('point', bytes), zip215);
|
||||||
|
}
|
||||||
|
get x() {
|
||||||
|
return this.toAffine().x;
|
||||||
|
}
|
||||||
|
get y() {
|
||||||
|
return this.toAffine().y;
|
||||||
|
}
|
||||||
|
precompute(windowSize = 8, isLazy = true) {
|
||||||
|
wnaf.createCache(this, windowSize);
|
||||||
|
if (!isLazy)
|
||||||
|
this.multiply(_2n); // random number
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
// Useful in fromAffine() - not for fromBytes(), which always created valid points.
|
||||||
|
assertValidity() {
|
||||||
|
assertValidMemo(this);
|
||||||
|
}
|
||||||
|
// Compare one point to another.
|
||||||
|
equals(other) {
|
||||||
|
aextpoint(other);
|
||||||
|
const { X: X1, Y: Y1, Z: Z1 } = this;
|
||||||
|
const { X: X2, Y: Y2, Z: Z2 } = other;
|
||||||
|
const X1Z2 = modP(X1 * Z2);
|
||||||
|
const X2Z1 = modP(X2 * Z1);
|
||||||
|
const Y1Z2 = modP(Y1 * Z2);
|
||||||
|
const Y2Z1 = modP(Y2 * Z1);
|
||||||
|
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
||||||
|
}
|
||||||
|
is0() {
|
||||||
|
return this.equals(Point.ZERO);
|
||||||
|
}
|
||||||
|
negate() {
|
||||||
|
// Flips point sign to a negative one (-x, y in affine coords)
|
||||||
|
return new Point(modP(-this.X), this.Y, this.Z, modP(-this.T));
|
||||||
|
}
|
||||||
|
// Fast algo for doubling Extended Point.
|
||||||
|
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
||||||
|
// Cost: 4M + 4S + 1*a + 6add + 1*2.
|
||||||
|
double() {
|
||||||
|
const { a } = CURVE;
|
||||||
|
const { X: X1, Y: Y1, Z: Z1 } = this;
|
||||||
|
const A = modP(X1 * X1); // A = X12
|
||||||
|
const B = modP(Y1 * Y1); // B = Y12
|
||||||
|
const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
|
||||||
|
const D = modP(a * A); // D = a*A
|
||||||
|
const x1y1 = X1 + Y1;
|
||||||
|
const E = modP(modP(x1y1 * x1y1) - A - B); // E = (X1+Y1)2-A-B
|
||||||
|
const G = D + B; // G = D+B
|
||||||
|
const F = G - C; // F = G-C
|
||||||
|
const H = D - B; // H = D-B
|
||||||
|
const X3 = modP(E * F); // X3 = E*F
|
||||||
|
const Y3 = modP(G * H); // Y3 = G*H
|
||||||
|
const T3 = modP(E * H); // T3 = E*H
|
||||||
|
const Z3 = modP(F * G); // Z3 = F*G
|
||||||
|
return new Point(X3, Y3, Z3, T3);
|
||||||
|
}
|
||||||
|
// Fast algo for adding 2 Extended Points.
|
||||||
|
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
|
||||||
|
// Cost: 9M + 1*a + 1*d + 7add.
|
||||||
|
add(other) {
|
||||||
|
aextpoint(other);
|
||||||
|
const { a, d } = CURVE;
|
||||||
|
const { X: X1, Y: Y1, Z: Z1, T: T1 } = this;
|
||||||
|
const { X: X2, Y: Y2, Z: Z2, T: T2 } = other;
|
||||||
|
const A = modP(X1 * X2); // A = X1*X2
|
||||||
|
const B = modP(Y1 * Y2); // B = Y1*Y2
|
||||||
|
const C = modP(T1 * d * T2); // C = T1*d*T2
|
||||||
|
const D = modP(Z1 * Z2); // D = Z1*Z2
|
||||||
|
const E = modP((X1 + Y1) * (X2 + Y2) - A - B); // E = (X1+Y1)*(X2+Y2)-A-B
|
||||||
|
const F = D - C; // F = D-C
|
||||||
|
const G = D + C; // G = D+C
|
||||||
|
const H = modP(B - a * A); // H = B-a*A
|
||||||
|
const X3 = modP(E * F); // X3 = E*F
|
||||||
|
const Y3 = modP(G * H); // Y3 = G*H
|
||||||
|
const T3 = modP(E * H); // T3 = E*H
|
||||||
|
const Z3 = modP(F * G); // Z3 = F*G
|
||||||
|
return new Point(X3, Y3, Z3, T3);
|
||||||
|
}
|
||||||
|
subtract(other) {
|
||||||
|
return this.add(other.negate());
|
||||||
|
}
|
||||||
|
// Constant-time multiplication.
|
||||||
|
multiply(scalar) {
|
||||||
|
// 1 <= scalar < L
|
||||||
|
if (!Fn.isValidNot0(scalar))
|
||||||
|
throw new Error('invalid scalar: expected 1 <= sc < curve.n');
|
||||||
|
const { p, f } = wnaf.cached(this, scalar, (p) => (0, curve_ts_1.normalizeZ)(Point, p));
|
||||||
|
return (0, curve_ts_1.normalizeZ)(Point, [p, f])[0];
|
||||||
|
}
|
||||||
|
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
||||||
|
// It's faster, but should only be used when you don't care about
|
||||||
|
// an exposed private key e.g. sig verification.
|
||||||
|
// Does NOT allow scalars higher than CURVE.n.
|
||||||
|
// Accepts optional accumulator to merge with multiply (important for sparse scalars)
|
||||||
|
multiplyUnsafe(scalar, acc = Point.ZERO) {
|
||||||
|
// 0 <= scalar < L
|
||||||
|
if (!Fn.isValid(scalar))
|
||||||
|
throw new Error('invalid scalar: expected 0 <= sc < curve.n');
|
||||||
|
if (scalar === _0n)
|
||||||
|
return Point.ZERO;
|
||||||
|
if (this.is0() || scalar === _1n)
|
||||||
|
return this;
|
||||||
|
return wnaf.unsafe(this, scalar, (p) => (0, curve_ts_1.normalizeZ)(Point, p), acc);
|
||||||
|
}
|
||||||
|
// Checks if point is of small order.
|
||||||
|
// If you add something to small order point, you will have "dirty"
|
||||||
|
// point with torsion component.
|
||||||
|
// Multiplies point by cofactor and checks if the result is 0.
|
||||||
|
isSmallOrder() {
|
||||||
|
return this.multiplyUnsafe(cofactor).is0();
|
||||||
|
}
|
||||||
|
// Multiplies point by curve order and checks if the result is 0.
|
||||||
|
// Returns `false` is the point is dirty.
|
||||||
|
isTorsionFree() {
|
||||||
|
return wnaf.unsafe(this, CURVE.n).is0();
|
||||||
|
}
|
||||||
|
// Converts Extended point to default (x, y) coordinates.
|
||||||
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
||||||
|
toAffine(invertedZ) {
|
||||||
|
return toAffineMemo(this, invertedZ);
|
||||||
|
}
|
||||||
|
clearCofactor() {
|
||||||
|
if (cofactor === _1n)
|
||||||
|
return this;
|
||||||
|
return this.multiplyUnsafe(cofactor);
|
||||||
|
}
|
||||||
|
toBytes() {
|
||||||
|
const { x, y } = this.toAffine();
|
||||||
|
// Fp.toBytes() allows non-canonical encoding of y (>= p).
|
||||||
|
const bytes = Fp.toBytes(y);
|
||||||
|
// Each y has 2 valid points: (x, y), (x,-y).
|
||||||
|
// When compressing, it's enough to store y and use the last byte to encode sign of x
|
||||||
|
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0;
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
toHex() {
|
||||||
|
return (0, utils_ts_1.bytesToHex)(this.toBytes());
|
||||||
|
}
|
||||||
|
toString() {
|
||||||
|
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
|
||||||
|
}
|
||||||
|
// TODO: remove
|
||||||
|
get ex() {
|
||||||
|
return this.X;
|
||||||
|
}
|
||||||
|
get ey() {
|
||||||
|
return this.Y;
|
||||||
|
}
|
||||||
|
get ez() {
|
||||||
|
return this.Z;
|
||||||
|
}
|
||||||
|
get et() {
|
||||||
|
return this.T;
|
||||||
|
}
|
||||||
|
static normalizeZ(points) {
|
||||||
|
return (0, curve_ts_1.normalizeZ)(Point, points);
|
||||||
|
}
|
||||||
|
static msm(points, scalars) {
|
||||||
|
return (0, curve_ts_1.pippenger)(Point, Fn, points, scalars);
|
||||||
|
}
|
||||||
|
_setWindowSize(windowSize) {
|
||||||
|
this.precompute(windowSize);
|
||||||
|
}
|
||||||
|
toRawBytes() {
|
||||||
|
return this.toBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// base / generator point
|
||||||
|
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
||||||
|
// zero / infinity / identity point
|
||||||
|
Point.ZERO = new Point(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
|
||||||
|
// math field
|
||||||
|
Point.Fp = Fp;
|
||||||
|
// scalar field
|
||||||
|
Point.Fn = Fn;
|
||||||
|
const wnaf = new curve_ts_1.wNAF(Point, Fn.BITS);
|
||||||
|
Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
||||||
|
return Point;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Base class for prime-order points like Ristretto255 and Decaf448.
|
||||||
|
* These points eliminate cofactor issues by representing equivalence classes
|
||||||
|
* of Edwards curve points.
|
||||||
|
*/
|
||||||
|
class PrimeEdwardsPoint {
|
||||||
|
constructor(ep) {
|
||||||
|
this.ep = ep;
|
||||||
|
}
|
||||||
|
// Static methods that must be implemented by subclasses
|
||||||
|
static fromBytes(_bytes) {
|
||||||
|
(0, utils_ts_1.notImplemented)();
|
||||||
|
}
|
||||||
|
static fromHex(_hex) {
|
||||||
|
(0, utils_ts_1.notImplemented)();
|
||||||
|
}
|
||||||
|
get x() {
|
||||||
|
return this.toAffine().x;
|
||||||
|
}
|
||||||
|
get y() {
|
||||||
|
return this.toAffine().y;
|
||||||
|
}
|
||||||
|
// Common implementations
|
||||||
|
clearCofactor() {
|
||||||
|
// no-op for prime-order groups
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
assertValidity() {
|
||||||
|
this.ep.assertValidity();
|
||||||
|
}
|
||||||
|
toAffine(invertedZ) {
|
||||||
|
return this.ep.toAffine(invertedZ);
|
||||||
|
}
|
||||||
|
toHex() {
|
||||||
|
return (0, utils_ts_1.bytesToHex)(this.toBytes());
|
||||||
|
}
|
||||||
|
toString() {
|
||||||
|
return this.toHex();
|
||||||
|
}
|
||||||
|
isTorsionFree() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
isSmallOrder() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
add(other) {
|
||||||
|
this.assertSame(other);
|
||||||
|
return this.init(this.ep.add(other.ep));
|
||||||
|
}
|
||||||
|
subtract(other) {
|
||||||
|
this.assertSame(other);
|
||||||
|
return this.init(this.ep.subtract(other.ep));
|
||||||
|
}
|
||||||
|
multiply(scalar) {
|
||||||
|
return this.init(this.ep.multiply(scalar));
|
||||||
|
}
|
||||||
|
multiplyUnsafe(scalar) {
|
||||||
|
return this.init(this.ep.multiplyUnsafe(scalar));
|
||||||
|
}
|
||||||
|
double() {
|
||||||
|
return this.init(this.ep.double());
|
||||||
|
}
|
||||||
|
negate() {
|
||||||
|
return this.init(this.ep.negate());
|
||||||
|
}
|
||||||
|
precompute(windowSize, isLazy) {
|
||||||
|
return this.init(this.ep.precompute(windowSize, isLazy));
|
||||||
|
}
|
||||||
|
/** @deprecated use `toBytes` */
|
||||||
|
toRawBytes() {
|
||||||
|
return this.toBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.PrimeEdwardsPoint = PrimeEdwardsPoint;
|
||||||
|
/**
|
||||||
|
* Initializes EdDSA signatures over given Edwards curve.
|
||||||
|
*/
|
||||||
|
function eddsa(Point, cHash, eddsaOpts = {}) {
|
||||||
|
if (typeof cHash !== 'function')
|
||||||
|
throw new Error('"hash" function param is required');
|
||||||
|
(0, utils_ts_1._validateObject)(eddsaOpts, {}, {
|
||||||
|
adjustScalarBytes: 'function',
|
||||||
|
randomBytes: 'function',
|
||||||
|
domain: 'function',
|
||||||
|
prehash: 'function',
|
||||||
|
mapToCurve: 'function',
|
||||||
|
});
|
||||||
|
const { prehash } = eddsaOpts;
|
||||||
|
const { BASE, Fp, Fn } = Point;
|
||||||
|
const randomBytes = eddsaOpts.randomBytes || utils_ts_1.randomBytes;
|
||||||
|
const adjustScalarBytes = eddsaOpts.adjustScalarBytes || ((bytes) => bytes);
|
||||||
|
const domain = eddsaOpts.domain ||
|
||||||
|
((data, ctx, phflag) => {
|
||||||
|
(0, utils_ts_1._abool2)(phflag, 'phflag');
|
||||||
|
if (ctx.length || phflag)
|
||||||
|
throw new Error('Contexts/pre-hash are not supported');
|
||||||
|
return data;
|
||||||
|
}); // NOOP
|
||||||
|
// Little-endian SHA512 with modulo n
|
||||||
|
function modN_LE(hash) {
|
||||||
|
return Fn.create((0, utils_ts_1.bytesToNumberLE)(hash)); // Not Fn.fromBytes: it has length limit
|
||||||
|
}
|
||||||
|
// Get the hashed private scalar per RFC8032 5.1.5
|
||||||
|
function getPrivateScalar(key) {
|
||||||
|
const len = lengths.secretKey;
|
||||||
|
key = (0, utils_ts_1.ensureBytes)('private key', key, len);
|
||||||
|
// Hash private key with curve's hash function to produce uniformingly random input
|
||||||
|
// Check byte lengths: ensure(64, h(ensure(32, key)))
|
||||||
|
const hashed = (0, utils_ts_1.ensureBytes)('hashed private key', cHash(key), 2 * len);
|
||||||
|
const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
|
||||||
|
const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
|
||||||
|
const scalar = modN_LE(head); // The actual private scalar
|
||||||
|
return { head, prefix, scalar };
|
||||||
|
}
|
||||||
|
/** Convenience method that creates public key from scalar. RFC8032 5.1.5 */
|
||||||
|
function getExtendedPublicKey(secretKey) {
|
||||||
|
const { head, prefix, scalar } = getPrivateScalar(secretKey);
|
||||||
|
const point = BASE.multiply(scalar); // Point on Edwards curve aka public key
|
||||||
|
const pointBytes = point.toBytes();
|
||||||
|
return { head, prefix, scalar, point, pointBytes };
|
||||||
|
}
|
||||||
|
/** Calculates EdDSA pub key. RFC8032 5.1.5. */
|
||||||
|
function getPublicKey(secretKey) {
|
||||||
|
return getExtendedPublicKey(secretKey).pointBytes;
|
||||||
|
}
|
||||||
|
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
||||||
|
function hashDomainToScalar(context = Uint8Array.of(), ...msgs) {
|
||||||
|
const msg = (0, utils_ts_1.concatBytes)(...msgs);
|
||||||
|
return modN_LE(cHash(domain(msg, (0, utils_ts_1.ensureBytes)('context', context), !!prehash)));
|
||||||
|
}
|
||||||
|
/** Signs message with privateKey. RFC8032 5.1.6 */
|
||||||
|
function sign(msg, secretKey, options = {}) {
|
||||||
|
msg = (0, utils_ts_1.ensureBytes)('message', msg);
|
||||||
|
if (prehash)
|
||||||
|
msg = prehash(msg); // for ed25519ph etc.
|
||||||
|
const { prefix, scalar, pointBytes } = getExtendedPublicKey(secretKey);
|
||||||
|
const r = hashDomainToScalar(options.context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
|
||||||
|
const R = BASE.multiply(r).toBytes(); // R = rG
|
||||||
|
const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
|
||||||
|
const s = Fn.create(r + k * scalar); // S = (r + k * s) mod L
|
||||||
|
if (!Fn.isValid(s))
|
||||||
|
throw new Error('sign failed: invalid s'); // 0 <= s < L
|
||||||
|
const rs = (0, utils_ts_1.concatBytes)(R, Fn.toBytes(s));
|
||||||
|
return (0, utils_ts_1._abytes2)(rs, lengths.signature, 'result');
|
||||||
|
}
|
||||||
|
// verification rule is either zip215 or rfc8032 / nist186-5. Consult fromHex:
|
||||||
|
const verifyOpts = { zip215: true };
|
||||||
|
/**
|
||||||
|
* Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
|
||||||
|
* An extended group equation is checked.
|
||||||
|
*/
|
||||||
|
function verify(sig, msg, publicKey, options = verifyOpts) {
|
||||||
|
const { context, zip215 } = options;
|
||||||
|
const len = lengths.signature;
|
||||||
|
sig = (0, utils_ts_1.ensureBytes)('signature', sig, len);
|
||||||
|
msg = (0, utils_ts_1.ensureBytes)('message', msg);
|
||||||
|
publicKey = (0, utils_ts_1.ensureBytes)('publicKey', publicKey, lengths.publicKey);
|
||||||
|
if (zip215 !== undefined)
|
||||||
|
(0, utils_ts_1._abool2)(zip215, 'zip215');
|
||||||
|
if (prehash)
|
||||||
|
msg = prehash(msg); // for ed25519ph, etc
|
||||||
|
const mid = len / 2;
|
||||||
|
const r = sig.subarray(0, mid);
|
||||||
|
const s = (0, utils_ts_1.bytesToNumberLE)(sig.subarray(mid, len));
|
||||||
|
let A, R, SB;
|
||||||
|
try {
|
||||||
|
// zip215=true is good for consensus-critical apps. =false follows RFC8032 / NIST186-5.
|
||||||
|
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
|
||||||
|
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
|
||||||
|
A = Point.fromBytes(publicKey, zip215);
|
||||||
|
R = Point.fromBytes(r, zip215);
|
||||||
|
SB = BASE.multiplyUnsafe(s); // 0 <= s < l is done inside
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!zip215 && A.isSmallOrder())
|
||||||
|
return false; // zip215 allows public keys of small order
|
||||||
|
const k = hashDomainToScalar(context, R.toBytes(), A.toBytes(), msg);
|
||||||
|
const RkA = R.add(A.multiplyUnsafe(k));
|
||||||
|
// Extended group equation
|
||||||
|
// [8][S]B = [8]R + [8][k]A'
|
||||||
|
return RkA.subtract(SB).clearCofactor().is0();
|
||||||
|
}
|
||||||
|
const _size = Fp.BYTES; // 32 for ed25519, 57 for ed448
|
||||||
|
const lengths = {
|
||||||
|
secretKey: _size,
|
||||||
|
publicKey: _size,
|
||||||
|
signature: 2 * _size,
|
||||||
|
seed: _size,
|
||||||
|
};
|
||||||
|
function randomSecretKey(seed = randomBytes(lengths.seed)) {
|
||||||
|
return (0, utils_ts_1._abytes2)(seed, lengths.seed, 'seed');
|
||||||
|
}
|
||||||
|
function keygen(seed) {
|
||||||
|
const secretKey = utils.randomSecretKey(seed);
|
||||||
|
return { secretKey, publicKey: getPublicKey(secretKey) };
|
||||||
|
}
|
||||||
|
function isValidSecretKey(key) {
|
||||||
|
return (0, utils_ts_1.isBytes)(key) && key.length === Fn.BYTES;
|
||||||
|
}
|
||||||
|
function isValidPublicKey(key, zip215) {
|
||||||
|
try {
|
||||||
|
return !!Point.fromBytes(key, zip215);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const utils = {
|
||||||
|
getExtendedPublicKey,
|
||||||
|
randomSecretKey,
|
||||||
|
isValidSecretKey,
|
||||||
|
isValidPublicKey,
|
||||||
|
/**
|
||||||
|
* Converts ed public key to x public key. Uses formula:
|
||||||
|
* - ed25519:
|
||||||
|
* - `(u, v) = ((1+y)/(1-y), sqrt(-486664)*u/x)`
|
||||||
|
* - `(x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1))`
|
||||||
|
* - ed448:
|
||||||
|
* - `(u, v) = ((y-1)/(y+1), sqrt(156324)*u/x)`
|
||||||
|
* - `(x, y) = (sqrt(156324)*u/v, (1+u)/(1-u))`
|
||||||
|
*/
|
||||||
|
toMontgomery(publicKey) {
|
||||||
|
const { y } = Point.fromBytes(publicKey);
|
||||||
|
const size = lengths.publicKey;
|
||||||
|
const is25519 = size === 32;
|
||||||
|
if (!is25519 && size !== 57)
|
||||||
|
throw new Error('only defined for 25519 and 448');
|
||||||
|
const u = is25519 ? Fp.div(_1n + y, _1n - y) : Fp.div(y - _1n, y + _1n);
|
||||||
|
return Fp.toBytes(u);
|
||||||
|
},
|
||||||
|
toMontgomerySecret(secretKey) {
|
||||||
|
const size = lengths.secretKey;
|
||||||
|
(0, utils_ts_1._abytes2)(secretKey, size);
|
||||||
|
const hashed = cHash(secretKey.subarray(0, size));
|
||||||
|
return adjustScalarBytes(hashed).subarray(0, size);
|
||||||
|
},
|
||||||
|
/** @deprecated */
|
||||||
|
randomPrivateKey: randomSecretKey,
|
||||||
|
/** @deprecated */
|
||||||
|
precompute(windowSize = 8, point = Point.BASE) {
|
||||||
|
return point.precompute(windowSize, false);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return Object.freeze({
|
||||||
|
keygen,
|
||||||
|
getPublicKey,
|
||||||
|
sign,
|
||||||
|
verify,
|
||||||
|
utils,
|
||||||
|
Point,
|
||||||
|
lengths,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function _eddsa_legacy_opts_to_new(c) {
|
||||||
|
const CURVE = {
|
||||||
|
a: c.a,
|
||||||
|
d: c.d,
|
||||||
|
p: c.Fp.ORDER,
|
||||||
|
n: c.n,
|
||||||
|
h: c.h,
|
||||||
|
Gx: c.Gx,
|
||||||
|
Gy: c.Gy,
|
||||||
|
};
|
||||||
|
const Fp = c.Fp;
|
||||||
|
const Fn = (0, modular_ts_1.Field)(CURVE.n, c.nBitLength, true);
|
||||||
|
const curveOpts = { Fp, Fn, uvRatio: c.uvRatio };
|
||||||
|
const eddsaOpts = {
|
||||||
|
randomBytes: c.randomBytes,
|
||||||
|
adjustScalarBytes: c.adjustScalarBytes,
|
||||||
|
domain: c.domain,
|
||||||
|
prehash: c.prehash,
|
||||||
|
mapToCurve: c.mapToCurve,
|
||||||
|
};
|
||||||
|
return { CURVE, curveOpts, hash: c.hash, eddsaOpts };
|
||||||
|
}
|
||||||
|
function _eddsa_new_output_to_legacy(c, eddsa) {
|
||||||
|
const Point = eddsa.Point;
|
||||||
|
const legacy = Object.assign({}, eddsa, {
|
||||||
|
ExtendedPoint: Point,
|
||||||
|
CURVE: c,
|
||||||
|
nBitLength: Point.Fn.BITS,
|
||||||
|
nByteLength: Point.Fn.BYTES,
|
||||||
|
});
|
||||||
|
return legacy;
|
||||||
|
}
|
||||||
|
// TODO: remove. Use eddsa
|
||||||
|
function twistedEdwards(c) {
|
||||||
|
const { CURVE, curveOpts, hash, eddsaOpts } = _eddsa_legacy_opts_to_new(c);
|
||||||
|
const Point = edwards(CURVE, curveOpts);
|
||||||
|
const EDDSA = eddsa(Point, hash, eddsaOpts);
|
||||||
|
return _eddsa_new_output_to_legacy(c, EDDSA);
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=edwards.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+122
@@ -0,0 +1,122 @@
|
|||||||
|
/**
|
||||||
|
* Experimental implementation of NTT / FFT (Fast Fourier Transform) over finite fields.
|
||||||
|
* API may change at any time. The code has not been audited. Feature requests are welcome.
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
import type { IField } from './modular.ts';
|
||||||
|
export interface MutableArrayLike<T> {
|
||||||
|
[index: number]: T;
|
||||||
|
length: number;
|
||||||
|
slice(start?: number, end?: number): this;
|
||||||
|
[Symbol.iterator](): Iterator<T>;
|
||||||
|
}
|
||||||
|
/** Checks if integer is in form of `1 << X` */
|
||||||
|
export declare function isPowerOfTwo(x: number): boolean;
|
||||||
|
export declare function nextPowerOfTwo(n: number): number;
|
||||||
|
export declare function reverseBits(n: number, bits: number): number;
|
||||||
|
/** Similar to `bitLen(x)-1` but much faster for small integers, like indices */
|
||||||
|
export declare function log2(n: number): number;
|
||||||
|
/**
|
||||||
|
* Moves lowest bit to highest position, which at first step splits
|
||||||
|
* array on even and odd indices, then it applied again to each part,
|
||||||
|
* which is core of fft
|
||||||
|
*/
|
||||||
|
export declare function bitReversalInplace<T extends MutableArrayLike<any>>(values: T): T;
|
||||||
|
export declare function bitReversalPermutation<T>(values: T[]): T[];
|
||||||
|
export type RootsOfUnity = {
|
||||||
|
roots: (bits: number) => bigint[];
|
||||||
|
brp(bits: number): bigint[];
|
||||||
|
inverse(bits: number): bigint[];
|
||||||
|
omega: (bits: number) => bigint;
|
||||||
|
clear: () => void;
|
||||||
|
};
|
||||||
|
/** We limit roots up to 2**31, which is a lot: 2-billion polynomimal should be rare. */
|
||||||
|
export declare function rootsOfUnity(field: IField<bigint>, generator?: bigint): RootsOfUnity;
|
||||||
|
export type Polynomial<T> = MutableArrayLike<T>;
|
||||||
|
/**
|
||||||
|
* Maps great to Field<bigint>, but not to Group (EC points):
|
||||||
|
* - inv from scalar field
|
||||||
|
* - we need multiplyUnsafe here, instead of multiply for speed
|
||||||
|
* - multiplyUnsafe is safe in the context: we do mul(rootsOfUnity), which are public and sparse
|
||||||
|
*/
|
||||||
|
export type FFTOpts<T, R> = {
|
||||||
|
add: (a: T, b: T) => T;
|
||||||
|
sub: (a: T, b: T) => T;
|
||||||
|
mul: (a: T, scalar: R) => T;
|
||||||
|
inv: (a: R) => R;
|
||||||
|
};
|
||||||
|
export type FFTCoreOpts<R> = {
|
||||||
|
N: number;
|
||||||
|
roots: Polynomial<R>;
|
||||||
|
dit: boolean;
|
||||||
|
invertButterflies?: boolean;
|
||||||
|
skipStages?: number;
|
||||||
|
brp?: boolean;
|
||||||
|
};
|
||||||
|
export type FFTCoreLoop<T> = <P extends Polynomial<T>>(values: P) => P;
|
||||||
|
/**
|
||||||
|
* Constructs different flavors of FFT. radix2 implementation of low level mutating API. Flavors:
|
||||||
|
*
|
||||||
|
* - DIT (Decimation-in-Time): Bottom-Up (leaves -> root), Cool-Turkey
|
||||||
|
* - DIF (Decimation-in-Frequency): Top-Down (root -> leaves), Gentleman–Sande
|
||||||
|
*
|
||||||
|
* DIT takes brp input, returns natural output.
|
||||||
|
* DIF takes natural input, returns brp output.
|
||||||
|
*
|
||||||
|
* The output is actually identical. Time / frequence distinction is not meaningful
|
||||||
|
* for Polynomial multiplication in fields.
|
||||||
|
* Which means if protocol supports/needs brp output/inputs, then we can skip this step.
|
||||||
|
*
|
||||||
|
* Cyclic NTT: Rq = Zq[x]/(x^n-1). butterfly_DIT+loop_DIT OR butterfly_DIF+loop_DIT, roots are omega
|
||||||
|
* Negacyclic NTT: Rq = Zq[x]/(x^n+1). butterfly_DIT+loop_DIF, at least for mlkem / mldsa
|
||||||
|
*/
|
||||||
|
export declare const FFTCore: <T, R>(F: FFTOpts<T, R>, coreOpts: FFTCoreOpts<R>) => FFTCoreLoop<T>;
|
||||||
|
export type FFTMethods<T> = {
|
||||||
|
direct<P extends Polynomial<T>>(values: P, brpInput?: boolean, brpOutput?: boolean): P;
|
||||||
|
inverse<P extends Polynomial<T>>(values: P, brpInput?: boolean, brpOutput?: boolean): P;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* NTT aka FFT over finite field (NOT over complex numbers).
|
||||||
|
* Naming mirrors other libraries.
|
||||||
|
*/
|
||||||
|
export declare function FFT<T>(roots: RootsOfUnity, opts: FFTOpts<T, bigint>): FFTMethods<T>;
|
||||||
|
export type CreatePolyFn<P extends Polynomial<T>, T> = (len: number, elm?: T) => P;
|
||||||
|
export type PolyFn<P extends Polynomial<T>, T> = {
|
||||||
|
roots: RootsOfUnity;
|
||||||
|
create: CreatePolyFn<P, T>;
|
||||||
|
length?: number;
|
||||||
|
degree: (a: P) => number;
|
||||||
|
extend: (a: P, len: number) => P;
|
||||||
|
add: (a: P, b: P) => P;
|
||||||
|
sub: (a: P, b: P) => P;
|
||||||
|
mul: (a: P, b: P | T) => P;
|
||||||
|
dot: (a: P, b: P) => P;
|
||||||
|
convolve: (a: P, b: P) => P;
|
||||||
|
shift: (p: P, factor: bigint) => P;
|
||||||
|
clone: (a: P) => P;
|
||||||
|
eval: (a: P, basis: P) => T;
|
||||||
|
monomial: {
|
||||||
|
basis: (x: T, n: number) => P;
|
||||||
|
eval: (a: P, x: T) => T;
|
||||||
|
};
|
||||||
|
lagrange: {
|
||||||
|
basis: (x: T, n: number, brp?: boolean) => P;
|
||||||
|
eval: (a: P, x: T, brp?: boolean) => T;
|
||||||
|
};
|
||||||
|
vanishing: (roots: P) => P;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Poly wants a cracker.
|
||||||
|
*
|
||||||
|
* Polynomials are functions like `y=f(x)`, which means when we multiply two polynomials, result is
|
||||||
|
* function `f3(x) = f1(x) * f2(x)`, we don't multiply values. Key takeaways:
|
||||||
|
*
|
||||||
|
* - **Polynomial** is an array of coefficients: `f(x) = sum(coeff[i] * basis[i](x))`
|
||||||
|
* - **Basis** is array of functions
|
||||||
|
* - **Monominal** is Polynomial where `basis[i](x) == x**i` (powers)
|
||||||
|
* - **Array size** is domain size
|
||||||
|
* - **Lattice** is matrix (Polynomial of Polynomials)
|
||||||
|
*/
|
||||||
|
export declare function poly<T>(field: IField<T>, roots: RootsOfUnity, create?: undefined, fft?: FFTMethods<T>, length?: number): PolyFn<T[], T>;
|
||||||
|
export declare function poly<T, P extends Polynomial<T>>(field: IField<T>, roots: RootsOfUnity, create: CreatePolyFn<P, T>, fft?: FFTMethods<T>, length?: number): PolyFn<P, T>;
|
||||||
|
//# sourceMappingURL=fft.d.ts.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"fft.d.ts","sourceRoot":"","sources":["../src/abstract/fft.ts"],"names":[],"mappings":"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"}
|
||||||
+438
@@ -0,0 +1,438 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.FFTCore = void 0;
|
||||||
|
exports.isPowerOfTwo = isPowerOfTwo;
|
||||||
|
exports.nextPowerOfTwo = nextPowerOfTwo;
|
||||||
|
exports.reverseBits = reverseBits;
|
||||||
|
exports.log2 = log2;
|
||||||
|
exports.bitReversalInplace = bitReversalInplace;
|
||||||
|
exports.bitReversalPermutation = bitReversalPermutation;
|
||||||
|
exports.rootsOfUnity = rootsOfUnity;
|
||||||
|
exports.FFT = FFT;
|
||||||
|
exports.poly = poly;
|
||||||
|
function checkU32(n) {
|
||||||
|
// 0xff_ff_ff_ff
|
||||||
|
if (!Number.isSafeInteger(n) || n < 0 || n > 0xffffffff)
|
||||||
|
throw new Error('wrong u32 integer:' + n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
/** Checks if integer is in form of `1 << X` */
|
||||||
|
function isPowerOfTwo(x) {
|
||||||
|
checkU32(x);
|
||||||
|
return (x & (x - 1)) === 0 && x !== 0;
|
||||||
|
}
|
||||||
|
function nextPowerOfTwo(n) {
|
||||||
|
checkU32(n);
|
||||||
|
if (n <= 1)
|
||||||
|
return 1;
|
||||||
|
return (1 << (log2(n - 1) + 1)) >>> 0;
|
||||||
|
}
|
||||||
|
function reverseBits(n, bits) {
|
||||||
|
checkU32(n);
|
||||||
|
let reversed = 0;
|
||||||
|
for (let i = 0; i < bits; i++, n >>>= 1)
|
||||||
|
reversed = (reversed << 1) | (n & 1);
|
||||||
|
return reversed;
|
||||||
|
}
|
||||||
|
/** Similar to `bitLen(x)-1` but much faster for small integers, like indices */
|
||||||
|
function log2(n) {
|
||||||
|
checkU32(n);
|
||||||
|
return 31 - Math.clz32(n);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Moves lowest bit to highest position, which at first step splits
|
||||||
|
* array on even and odd indices, then it applied again to each part,
|
||||||
|
* which is core of fft
|
||||||
|
*/
|
||||||
|
function bitReversalInplace(values) {
|
||||||
|
const n = values.length;
|
||||||
|
if (n < 2 || !isPowerOfTwo(n))
|
||||||
|
throw new Error('n must be a power of 2 and greater than 1. Got ' + n);
|
||||||
|
const bits = log2(n);
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
const j = reverseBits(i, bits);
|
||||||
|
if (i < j) {
|
||||||
|
const tmp = values[i];
|
||||||
|
values[i] = values[j];
|
||||||
|
values[j] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
function bitReversalPermutation(values) {
|
||||||
|
return bitReversalInplace(values.slice());
|
||||||
|
}
|
||||||
|
const _1n = /** @__PURE__ */ BigInt(1);
|
||||||
|
function findGenerator(field) {
|
||||||
|
let G = BigInt(2);
|
||||||
|
for (; field.eql(field.pow(G, field.ORDER >> _1n), field.ONE); G++)
|
||||||
|
;
|
||||||
|
return G;
|
||||||
|
}
|
||||||
|
/** We limit roots up to 2**31, which is a lot: 2-billion polynomimal should be rare. */
|
||||||
|
function rootsOfUnity(field, generator) {
|
||||||
|
// Factor field.ORDER-1 as oddFactor * 2^powerOfTwo
|
||||||
|
let oddFactor = field.ORDER - _1n;
|
||||||
|
let powerOfTwo = 0;
|
||||||
|
for (; (oddFactor & _1n) !== _1n; powerOfTwo++, oddFactor >>= _1n)
|
||||||
|
;
|
||||||
|
// Find non quadratic residue
|
||||||
|
let G = generator !== undefined ? BigInt(generator) : findGenerator(field);
|
||||||
|
// Powers of generator
|
||||||
|
const omegas = new Array(powerOfTwo + 1);
|
||||||
|
omegas[powerOfTwo] = field.pow(G, oddFactor);
|
||||||
|
for (let i = powerOfTwo; i > 0; i--)
|
||||||
|
omegas[i - 1] = field.sqr(omegas[i]);
|
||||||
|
// Compute all roots of unity for powers up to maxPower
|
||||||
|
const rootsCache = [];
|
||||||
|
const checkBits = (bits) => {
|
||||||
|
checkU32(bits);
|
||||||
|
if (bits > 31 || bits > powerOfTwo)
|
||||||
|
throw new Error('rootsOfUnity: wrong bits ' + bits + ' powerOfTwo=' + powerOfTwo);
|
||||||
|
return bits;
|
||||||
|
};
|
||||||
|
const precomputeRoots = (maxPower) => {
|
||||||
|
checkBits(maxPower);
|
||||||
|
for (let power = maxPower; power >= 0; power--) {
|
||||||
|
if (rootsCache[power])
|
||||||
|
continue; // Skip if we've already computed roots for this power
|
||||||
|
const rootsAtPower = [];
|
||||||
|
for (let j = 0, cur = field.ONE; j < 2 ** power; j++, cur = field.mul(cur, omegas[power]))
|
||||||
|
rootsAtPower.push(cur);
|
||||||
|
rootsCache[power] = rootsAtPower;
|
||||||
|
}
|
||||||
|
return rootsCache[maxPower];
|
||||||
|
};
|
||||||
|
const brpCache = new Map();
|
||||||
|
const inverseCache = new Map();
|
||||||
|
// NOTE: we use bits instead of power, because power = 2**bits,
|
||||||
|
// but power is not neccesary isPowerOfTwo(power)!
|
||||||
|
return {
|
||||||
|
roots: (bits) => {
|
||||||
|
const b = checkBits(bits);
|
||||||
|
return precomputeRoots(b);
|
||||||
|
},
|
||||||
|
brp(bits) {
|
||||||
|
const b = checkBits(bits);
|
||||||
|
if (brpCache.has(b))
|
||||||
|
return brpCache.get(b);
|
||||||
|
else {
|
||||||
|
const res = bitReversalPermutation(this.roots(b));
|
||||||
|
brpCache.set(b, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inverse(bits) {
|
||||||
|
const b = checkBits(bits);
|
||||||
|
if (inverseCache.has(b))
|
||||||
|
return inverseCache.get(b);
|
||||||
|
else {
|
||||||
|
const res = field.invertBatch(this.roots(b));
|
||||||
|
inverseCache.set(b, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
omega: (bits) => omegas[checkBits(bits)],
|
||||||
|
clear: () => {
|
||||||
|
rootsCache.splice(0, rootsCache.length);
|
||||||
|
brpCache.clear();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Constructs different flavors of FFT. radix2 implementation of low level mutating API. Flavors:
|
||||||
|
*
|
||||||
|
* - DIT (Decimation-in-Time): Bottom-Up (leaves -> root), Cool-Turkey
|
||||||
|
* - DIF (Decimation-in-Frequency): Top-Down (root -> leaves), Gentleman–Sande
|
||||||
|
*
|
||||||
|
* DIT takes brp input, returns natural output.
|
||||||
|
* DIF takes natural input, returns brp output.
|
||||||
|
*
|
||||||
|
* The output is actually identical. Time / frequence distinction is not meaningful
|
||||||
|
* for Polynomial multiplication in fields.
|
||||||
|
* Which means if protocol supports/needs brp output/inputs, then we can skip this step.
|
||||||
|
*
|
||||||
|
* Cyclic NTT: Rq = Zq[x]/(x^n-1). butterfly_DIT+loop_DIT OR butterfly_DIF+loop_DIT, roots are omega
|
||||||
|
* Negacyclic NTT: Rq = Zq[x]/(x^n+1). butterfly_DIT+loop_DIF, at least for mlkem / mldsa
|
||||||
|
*/
|
||||||
|
const FFTCore = (F, coreOpts) => {
|
||||||
|
const { N, roots, dit, invertButterflies = false, skipStages = 0, brp = true } = coreOpts;
|
||||||
|
const bits = log2(N);
|
||||||
|
if (!isPowerOfTwo(N))
|
||||||
|
throw new Error('FFT: Polynomial size should be power of two');
|
||||||
|
const isDit = dit !== invertButterflies;
|
||||||
|
isDit;
|
||||||
|
return (values) => {
|
||||||
|
if (values.length !== N)
|
||||||
|
throw new Error('FFT: wrong Polynomial length');
|
||||||
|
if (dit && brp)
|
||||||
|
bitReversalInplace(values);
|
||||||
|
for (let i = 0, g = 1; i < bits - skipStages; i++) {
|
||||||
|
// For each stage s (sub-FFT length m = 2^s)
|
||||||
|
const s = dit ? i + 1 + skipStages : bits - i;
|
||||||
|
const m = 1 << s;
|
||||||
|
const m2 = m >> 1;
|
||||||
|
const stride = N >> s;
|
||||||
|
// Loop over each subarray of length m
|
||||||
|
for (let k = 0; k < N; k += m) {
|
||||||
|
// Loop over each butterfly within the subarray
|
||||||
|
for (let j = 0, grp = g++; j < m2; j++) {
|
||||||
|
const rootPos = invertButterflies ? (dit ? N - grp : grp) : j * stride;
|
||||||
|
const i0 = k + j;
|
||||||
|
const i1 = k + j + m2;
|
||||||
|
const omega = roots[rootPos];
|
||||||
|
const b = values[i1];
|
||||||
|
const a = values[i0];
|
||||||
|
// Inlining gives us 10% perf in kyber vs functions
|
||||||
|
if (isDit) {
|
||||||
|
const t = F.mul(b, omega); // Standard DIT butterfly
|
||||||
|
values[i0] = F.add(a, t);
|
||||||
|
values[i1] = F.sub(a, t);
|
||||||
|
}
|
||||||
|
else if (invertButterflies) {
|
||||||
|
values[i0] = F.add(b, a); // DIT loop + inverted butterflies (Kyber decode)
|
||||||
|
values[i1] = F.mul(F.sub(b, a), omega);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
values[i0] = F.add(a, b); // Standard DIF butterfly
|
||||||
|
values[i1] = F.mul(F.sub(a, b), omega);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dit && brp)
|
||||||
|
bitReversalInplace(values);
|
||||||
|
return values;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
exports.FFTCore = FFTCore;
|
||||||
|
/**
|
||||||
|
* NTT aka FFT over finite field (NOT over complex numbers).
|
||||||
|
* Naming mirrors other libraries.
|
||||||
|
*/
|
||||||
|
function FFT(roots, opts) {
|
||||||
|
const getLoop = (N, roots, brpInput = false, brpOutput = false) => {
|
||||||
|
if (brpInput && brpOutput) {
|
||||||
|
// we cannot optimize this case, but lets support it anyway
|
||||||
|
return (values) => (0, exports.FFTCore)(opts, { N, roots, dit: false, brp: false })(bitReversalInplace(values));
|
||||||
|
}
|
||||||
|
if (brpInput)
|
||||||
|
return (0, exports.FFTCore)(opts, { N, roots, dit: true, brp: false });
|
||||||
|
if (brpOutput)
|
||||||
|
return (0, exports.FFTCore)(opts, { N, roots, dit: false, brp: false });
|
||||||
|
return (0, exports.FFTCore)(opts, { N, roots, dit: true, brp: true }); // all natural
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
direct(values, brpInput = false, brpOutput = false) {
|
||||||
|
const N = values.length;
|
||||||
|
if (!isPowerOfTwo(N))
|
||||||
|
throw new Error('FFT: Polynomial size should be power of two');
|
||||||
|
const bits = log2(N);
|
||||||
|
return getLoop(N, roots.roots(bits), brpInput, brpOutput)(values.slice());
|
||||||
|
},
|
||||||
|
inverse(values, brpInput = false, brpOutput = false) {
|
||||||
|
const N = values.length;
|
||||||
|
const bits = log2(N);
|
||||||
|
const res = getLoop(N, roots.inverse(bits), brpInput, brpOutput)(values.slice());
|
||||||
|
const ivm = opts.inv(BigInt(values.length)); // scale
|
||||||
|
// we can get brp output if we use dif instead of dit!
|
||||||
|
for (let i = 0; i < res.length; i++)
|
||||||
|
res[i] = opts.mul(res[i], ivm);
|
||||||
|
// Allows to re-use non-inverted roots, but is VERY fragile
|
||||||
|
// return [res[0]].concat(res.slice(1).reverse());
|
||||||
|
// inverse calculated as pow(-1), which transforms into ω^{-kn} (-> reverses indices)
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function poly(field, roots, create, fft, length) {
|
||||||
|
const F = field;
|
||||||
|
const _create = create ||
|
||||||
|
((len, elm) => new Array(len).fill(elm ?? F.ZERO));
|
||||||
|
const isPoly = (x) => Array.isArray(x) || ArrayBuffer.isView(x);
|
||||||
|
const checkLength = (...lst) => {
|
||||||
|
if (!lst.length)
|
||||||
|
return 0;
|
||||||
|
for (const i of lst)
|
||||||
|
if (!isPoly(i))
|
||||||
|
throw new Error('poly: not polynomial: ' + i);
|
||||||
|
const L = lst[0].length;
|
||||||
|
for (let i = 1; i < lst.length; i++)
|
||||||
|
if (lst[i].length !== L)
|
||||||
|
throw new Error(`poly: mismatched lengths ${L} vs ${lst[i].length}`);
|
||||||
|
if (length !== undefined && L !== length)
|
||||||
|
throw new Error(`poly: expected fixed length ${length}, got ${L}`);
|
||||||
|
return L;
|
||||||
|
};
|
||||||
|
function findOmegaIndex(x, n, brp = false) {
|
||||||
|
const bits = log2(n);
|
||||||
|
const omega = brp ? roots.brp(bits) : roots.roots(bits);
|
||||||
|
for (let i = 0; i < n; i++)
|
||||||
|
if (F.eql(x, omega[i]))
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// TODO: mutating versions for mlkem/mldsa
|
||||||
|
return {
|
||||||
|
roots,
|
||||||
|
create: _create,
|
||||||
|
length,
|
||||||
|
extend: (a, len) => {
|
||||||
|
checkLength(a);
|
||||||
|
const out = _create(len, F.ZERO);
|
||||||
|
for (let i = 0; i < a.length; i++)
|
||||||
|
out[i] = a[i];
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
degree: (a) => {
|
||||||
|
checkLength(a);
|
||||||
|
for (let i = a.length - 1; i >= 0; i--)
|
||||||
|
if (!F.is0(a[i]))
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
add: (a, b) => {
|
||||||
|
const len = checkLength(a, b);
|
||||||
|
const out = _create(len);
|
||||||
|
for (let i = 0; i < len; i++)
|
||||||
|
out[i] = F.add(a[i], b[i]);
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
sub: (a, b) => {
|
||||||
|
const len = checkLength(a, b);
|
||||||
|
const out = _create(len);
|
||||||
|
for (let i = 0; i < len; i++)
|
||||||
|
out[i] = F.sub(a[i], b[i]);
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
dot: (a, b) => {
|
||||||
|
const len = checkLength(a, b);
|
||||||
|
const out = _create(len);
|
||||||
|
for (let i = 0; i < len; i++)
|
||||||
|
out[i] = F.mul(a[i], b[i]);
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
mul: (a, b) => {
|
||||||
|
if (isPoly(b)) {
|
||||||
|
const len = checkLength(a, b);
|
||||||
|
if (fft) {
|
||||||
|
const A = fft.direct(a, false, true);
|
||||||
|
const B = fft.direct(b, false, true);
|
||||||
|
for (let i = 0; i < A.length; i++)
|
||||||
|
A[i] = F.mul(A[i], B[i]);
|
||||||
|
return fft.inverse(A, true, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// NOTE: this is quadratic and mostly for compat tests with FFT
|
||||||
|
const res = _create(len);
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
for (let j = 0; j < len; j++) {
|
||||||
|
const k = (i + j) % len; // wrap mod length
|
||||||
|
res[k] = F.add(res[k], F.mul(a[i], b[j]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const out = _create(checkLength(a));
|
||||||
|
for (let i = 0; i < out.length; i++)
|
||||||
|
out[i] = F.mul(a[i], b);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
convolve(a, b) {
|
||||||
|
const len = nextPowerOfTwo(a.length + b.length - 1);
|
||||||
|
return this.mul(this.extend(a, len), this.extend(b, len));
|
||||||
|
},
|
||||||
|
shift(p, factor) {
|
||||||
|
const out = _create(checkLength(p));
|
||||||
|
out[0] = p[0];
|
||||||
|
for (let i = 1, power = F.ONE; i < p.length; i++) {
|
||||||
|
power = F.mul(power, factor);
|
||||||
|
out[i] = F.mul(p[i], power);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
clone: (a) => {
|
||||||
|
checkLength(a);
|
||||||
|
const out = _create(a.length);
|
||||||
|
for (let i = 0; i < a.length; i++)
|
||||||
|
out[i] = a[i];
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
eval: (a, basis) => {
|
||||||
|
checkLength(a);
|
||||||
|
let acc = F.ZERO;
|
||||||
|
for (let i = 0; i < a.length; i++)
|
||||||
|
acc = F.add(acc, F.mul(a[i], basis[i]));
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
monomial: {
|
||||||
|
basis: (x, n) => {
|
||||||
|
const out = _create(n);
|
||||||
|
let pow = F.ONE;
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
out[i] = pow;
|
||||||
|
pow = F.mul(pow, x);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
eval: (a, x) => {
|
||||||
|
checkLength(a);
|
||||||
|
// Same as eval(a, monomialBasis(x, a.length)), but it is faster this way
|
||||||
|
let acc = F.ZERO;
|
||||||
|
for (let i = a.length - 1; i >= 0; i--)
|
||||||
|
acc = F.add(F.mul(acc, x), a[i]);
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lagrange: {
|
||||||
|
basis: (x, n, brp = false, weights) => {
|
||||||
|
const bits = log2(n);
|
||||||
|
const cache = weights || brp ? roots.brp(bits) : roots.roots(bits); // [ω⁰, ω¹, ..., ωⁿ⁻¹]
|
||||||
|
const out = _create(n);
|
||||||
|
// Fast Kronecker-δ shortcut
|
||||||
|
const idx = findOmegaIndex(x, n, brp);
|
||||||
|
if (idx !== -1) {
|
||||||
|
out[idx] = F.ONE;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
const tm = F.pow(x, BigInt(n));
|
||||||
|
const c = F.mul(F.sub(tm, F.ONE), F.inv(BigInt(n))); // c = (xⁿ - 1)/n
|
||||||
|
const denom = _create(n);
|
||||||
|
for (let i = 0; i < n; i++)
|
||||||
|
denom[i] = F.sub(x, cache[i]);
|
||||||
|
const inv = F.invertBatch(denom);
|
||||||
|
for (let i = 0; i < n; i++)
|
||||||
|
out[i] = F.mul(c, F.mul(cache[i], inv[i]));
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
eval(a, x, brp = false) {
|
||||||
|
checkLength(a);
|
||||||
|
const idx = findOmegaIndex(x, a.length, brp);
|
||||||
|
if (idx !== -1)
|
||||||
|
return a[idx]; // fast path
|
||||||
|
const L = this.basis(x, a.length, brp); // Lᵢ(x)
|
||||||
|
let acc = F.ZERO;
|
||||||
|
for (let i = 0; i < a.length; i++)
|
||||||
|
if (!F.is0(a[i]))
|
||||||
|
acc = F.add(acc, F.mul(a[i], L[i]));
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
vanishing(roots) {
|
||||||
|
checkLength(roots);
|
||||||
|
const out = _create(roots.length + 1, F.ZERO);
|
||||||
|
out[0] = F.ONE;
|
||||||
|
for (const r of roots) {
|
||||||
|
const neg = F.neg(r);
|
||||||
|
for (let j = out.length - 1; j > 0; j--)
|
||||||
|
out[j] = F.add(F.mul(out[j], neg), out[j - 1]);
|
||||||
|
out[0] = F.mul(out[0], neg);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=fft.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+102
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* hash-to-curve from RFC 9380.
|
||||||
|
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
|
||||||
|
* https://www.rfc-editor.org/rfc/rfc9380
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
import type { CHash } from '../utils.ts';
|
||||||
|
import type { AffinePoint, Group, GroupConstructor } from './curve.ts';
|
||||||
|
import { type IField } from './modular.ts';
|
||||||
|
export type UnicodeOrBytes = string | Uint8Array;
|
||||||
|
/**
|
||||||
|
* * `DST` is a domain separation tag, defined in section 2.2.5
|
||||||
|
* * `p` characteristic of F, where F is a finite field of characteristic p and order q = p^m
|
||||||
|
* * `m` is extension degree (1 for prime fields)
|
||||||
|
* * `k` is the target security target in bits (e.g. 128), from section 5.1
|
||||||
|
* * `expand` is `xmd` (SHA2, SHA3, BLAKE) or `xof` (SHAKE, BLAKE-XOF)
|
||||||
|
* * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
|
||||||
|
*/
|
||||||
|
export type H2COpts = {
|
||||||
|
DST: UnicodeOrBytes;
|
||||||
|
expand: 'xmd' | 'xof';
|
||||||
|
hash: CHash;
|
||||||
|
p: bigint;
|
||||||
|
m: number;
|
||||||
|
k: number;
|
||||||
|
};
|
||||||
|
export type H2CHashOpts = {
|
||||||
|
expand: 'xmd' | 'xof';
|
||||||
|
hash: CHash;
|
||||||
|
};
|
||||||
|
export type Opts = H2COpts;
|
||||||
|
/**
|
||||||
|
* Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
|
||||||
|
* [RFC 9380 5.3.1](https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1).
|
||||||
|
*/
|
||||||
|
export declare function expand_message_xmd(msg: Uint8Array, DST: UnicodeOrBytes, lenInBytes: number, H: CHash): Uint8Array;
|
||||||
|
/**
|
||||||
|
* Produces a uniformly random byte string using an extendable-output function (XOF) H.
|
||||||
|
* 1. The collision resistance of H MUST be at least k bits.
|
||||||
|
* 2. H MUST be an XOF that has been proved indifferentiable from
|
||||||
|
* a random oracle under a reasonable cryptographic assumption.
|
||||||
|
* [RFC 9380 5.3.2](https://www.rfc-editor.org/rfc/rfc9380#section-5.3.2).
|
||||||
|
*/
|
||||||
|
export declare function expand_message_xof(msg: Uint8Array, DST: UnicodeOrBytes, lenInBytes: number, k: number, H: CHash): Uint8Array;
|
||||||
|
/**
|
||||||
|
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
|
||||||
|
* [RFC 9380 5.2](https://www.rfc-editor.org/rfc/rfc9380#section-5.2).
|
||||||
|
* @param msg a byte string containing the message to hash
|
||||||
|
* @param count the number of elements of F to output
|
||||||
|
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
|
||||||
|
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
||||||
|
*/
|
||||||
|
export declare function hash_to_field(msg: Uint8Array, count: number, options: H2COpts): bigint[][];
|
||||||
|
export type XY<T> = (x: T, y: T) => {
|
||||||
|
x: T;
|
||||||
|
y: T;
|
||||||
|
};
|
||||||
|
export type XYRatio<T> = [T[], T[], T[], T[]];
|
||||||
|
export declare function isogenyMap<T, F extends IField<T>>(field: F, map: XYRatio<T>): XY<T>;
|
||||||
|
/** Point interface, which curves must implement to work correctly with the module. */
|
||||||
|
export interface H2CPoint<T> extends Group<H2CPoint<T>> {
|
||||||
|
add(rhs: H2CPoint<T>): H2CPoint<T>;
|
||||||
|
toAffine(iz?: bigint): AffinePoint<T>;
|
||||||
|
clearCofactor(): H2CPoint<T>;
|
||||||
|
assertValidity(): void;
|
||||||
|
}
|
||||||
|
export interface H2CPointConstructor<T> extends GroupConstructor<H2CPoint<T>> {
|
||||||
|
fromAffine(ap: AffinePoint<T>): H2CPoint<T>;
|
||||||
|
}
|
||||||
|
export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
|
||||||
|
export type htfBasicOpts = {
|
||||||
|
DST: UnicodeOrBytes;
|
||||||
|
};
|
||||||
|
export type H2CMethod<T> = (msg: Uint8Array, options?: htfBasicOpts) => H2CPoint<T>;
|
||||||
|
export type HTFMethod<T> = H2CMethod<T>;
|
||||||
|
export type MapMethod<T> = (scalars: bigint[]) => H2CPoint<T>;
|
||||||
|
export type H2CHasherBase<T> = {
|
||||||
|
hashToCurve: H2CMethod<T>;
|
||||||
|
hashToScalar: (msg: Uint8Array, options: htfBasicOpts) => bigint;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* RFC 9380 methods, with cofactor clearing. See https://www.rfc-editor.org/rfc/rfc9380#section-3.
|
||||||
|
*
|
||||||
|
* * hashToCurve: `map(hash(input))`, encodes RANDOM bytes to curve (WITH hashing)
|
||||||
|
* * encodeToCurve: `map(hash(input))`, encodes NON-UNIFORM bytes to curve (WITH hashing)
|
||||||
|
* * mapToCurve: `map(scalars)`, encodes NON-UNIFORM scalars to curve (NO hashing)
|
||||||
|
*/
|
||||||
|
export type H2CHasher<T> = H2CHasherBase<T> & {
|
||||||
|
encodeToCurve: H2CMethod<T>;
|
||||||
|
mapToCurve: MapMethod<T>;
|
||||||
|
defaults: H2COpts & {
|
||||||
|
encodeDST?: UnicodeOrBytes;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export type Hasher<T> = H2CHasher<T>;
|
||||||
|
export declare const _DST_scalar: Uint8Array;
|
||||||
|
/** Creates hash-to-curve methods from EC Point and mapToCurve function. See {@link H2CHasher}. */
|
||||||
|
export declare function createHasher<T>(Point: H2CPointConstructor<T>, mapToCurve: MapToCurve<T>, defaults: H2COpts & {
|
||||||
|
encodeDST?: UnicodeOrBytes;
|
||||||
|
}): H2CHasher<T>;
|
||||||
|
//# sourceMappingURL=hash-to-curve.d.ts.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"hash-to-curve.d.ts","sourceRoot":"","sources":["../src/abstract/hash-to-curve.ts"],"names":[],"mappings":"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"}
|
||||||
+211
@@ -0,0 +1,211 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports._DST_scalar = void 0;
|
||||||
|
exports.expand_message_xmd = expand_message_xmd;
|
||||||
|
exports.expand_message_xof = expand_message_xof;
|
||||||
|
exports.hash_to_field = hash_to_field;
|
||||||
|
exports.isogenyMap = isogenyMap;
|
||||||
|
exports.createHasher = createHasher;
|
||||||
|
const utils_ts_1 = require("../utils.js");
|
||||||
|
const modular_ts_1 = require("./modular.js");
|
||||||
|
// Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
|
||||||
|
const os2ip = utils_ts_1.bytesToNumberBE;
|
||||||
|
// Integer to Octet Stream (numberToBytesBE)
|
||||||
|
function i2osp(value, length) {
|
||||||
|
anum(value);
|
||||||
|
anum(length);
|
||||||
|
if (value < 0 || value >= 1 << (8 * length))
|
||||||
|
throw new Error('invalid I2OSP input: ' + value);
|
||||||
|
const res = Array.from({ length }).fill(0);
|
||||||
|
for (let i = length - 1; i >= 0; i--) {
|
||||||
|
res[i] = value & 0xff;
|
||||||
|
value >>>= 8;
|
||||||
|
}
|
||||||
|
return new Uint8Array(res);
|
||||||
|
}
|
||||||
|
function strxor(a, b) {
|
||||||
|
const arr = new Uint8Array(a.length);
|
||||||
|
for (let i = 0; i < a.length; i++) {
|
||||||
|
arr[i] = a[i] ^ b[i];
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
function anum(item) {
|
||||||
|
if (!Number.isSafeInteger(item))
|
||||||
|
throw new Error('number expected');
|
||||||
|
}
|
||||||
|
function normDST(DST) {
|
||||||
|
if (!(0, utils_ts_1.isBytes)(DST) && typeof DST !== 'string')
|
||||||
|
throw new Error('DST must be Uint8Array or string');
|
||||||
|
return typeof DST === 'string' ? (0, utils_ts_1.utf8ToBytes)(DST) : DST;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
|
||||||
|
* [RFC 9380 5.3.1](https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1).
|
||||||
|
*/
|
||||||
|
function expand_message_xmd(msg, DST, lenInBytes, H) {
|
||||||
|
(0, utils_ts_1.abytes)(msg);
|
||||||
|
anum(lenInBytes);
|
||||||
|
DST = normDST(DST);
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
|
||||||
|
if (DST.length > 255)
|
||||||
|
DST = H((0, utils_ts_1.concatBytes)((0, utils_ts_1.utf8ToBytes)('H2C-OVERSIZE-DST-'), DST));
|
||||||
|
const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
|
||||||
|
const ell = Math.ceil(lenInBytes / b_in_bytes);
|
||||||
|
if (lenInBytes > 65535 || ell > 255)
|
||||||
|
throw new Error('expand_message_xmd: invalid lenInBytes');
|
||||||
|
const DST_prime = (0, utils_ts_1.concatBytes)(DST, i2osp(DST.length, 1));
|
||||||
|
const Z_pad = i2osp(0, r_in_bytes);
|
||||||
|
const l_i_b_str = i2osp(lenInBytes, 2); // len_in_bytes_str
|
||||||
|
const b = new Array(ell);
|
||||||
|
const b_0 = H((0, utils_ts_1.concatBytes)(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));
|
||||||
|
b[0] = H((0, utils_ts_1.concatBytes)(b_0, i2osp(1, 1), DST_prime));
|
||||||
|
for (let i = 1; i <= ell; i++) {
|
||||||
|
const args = [strxor(b_0, b[i - 1]), i2osp(i + 1, 1), DST_prime];
|
||||||
|
b[i] = H((0, utils_ts_1.concatBytes)(...args));
|
||||||
|
}
|
||||||
|
const pseudo_random_bytes = (0, utils_ts_1.concatBytes)(...b);
|
||||||
|
return pseudo_random_bytes.slice(0, lenInBytes);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Produces a uniformly random byte string using an extendable-output function (XOF) H.
|
||||||
|
* 1. The collision resistance of H MUST be at least k bits.
|
||||||
|
* 2. H MUST be an XOF that has been proved indifferentiable from
|
||||||
|
* a random oracle under a reasonable cryptographic assumption.
|
||||||
|
* [RFC 9380 5.3.2](https://www.rfc-editor.org/rfc/rfc9380#section-5.3.2).
|
||||||
|
*/
|
||||||
|
function expand_message_xof(msg, DST, lenInBytes, k, H) {
|
||||||
|
(0, utils_ts_1.abytes)(msg);
|
||||||
|
anum(lenInBytes);
|
||||||
|
DST = normDST(DST);
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
|
||||||
|
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
|
||||||
|
if (DST.length > 255) {
|
||||||
|
const dkLen = Math.ceil((2 * k) / 8);
|
||||||
|
DST = H.create({ dkLen }).update((0, utils_ts_1.utf8ToBytes)('H2C-OVERSIZE-DST-')).update(DST).digest();
|
||||||
|
}
|
||||||
|
if (lenInBytes > 65535 || DST.length > 255)
|
||||||
|
throw new Error('expand_message_xof: invalid lenInBytes');
|
||||||
|
return (H.create({ dkLen: lenInBytes })
|
||||||
|
.update(msg)
|
||||||
|
.update(i2osp(lenInBytes, 2))
|
||||||
|
// 2. DST_prime = DST || I2OSP(len(DST), 1)
|
||||||
|
.update(DST)
|
||||||
|
.update(i2osp(DST.length, 1))
|
||||||
|
.digest());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
|
||||||
|
* [RFC 9380 5.2](https://www.rfc-editor.org/rfc/rfc9380#section-5.2).
|
||||||
|
* @param msg a byte string containing the message to hash
|
||||||
|
* @param count the number of elements of F to output
|
||||||
|
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
|
||||||
|
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
||||||
|
*/
|
||||||
|
function hash_to_field(msg, count, options) {
|
||||||
|
(0, utils_ts_1._validateObject)(options, {
|
||||||
|
p: 'bigint',
|
||||||
|
m: 'number',
|
||||||
|
k: 'number',
|
||||||
|
hash: 'function',
|
||||||
|
});
|
||||||
|
const { p, k, m, hash, expand, DST } = options;
|
||||||
|
if (!(0, utils_ts_1.isHash)(options.hash))
|
||||||
|
throw new Error('expected valid hash');
|
||||||
|
(0, utils_ts_1.abytes)(msg);
|
||||||
|
anum(count);
|
||||||
|
const log2p = p.toString(2).length;
|
||||||
|
const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
|
||||||
|
const len_in_bytes = count * m * L;
|
||||||
|
let prb; // pseudo_random_bytes
|
||||||
|
if (expand === 'xmd') {
|
||||||
|
prb = expand_message_xmd(msg, DST, len_in_bytes, hash);
|
||||||
|
}
|
||||||
|
else if (expand === 'xof') {
|
||||||
|
prb = expand_message_xof(msg, DST, len_in_bytes, k, hash);
|
||||||
|
}
|
||||||
|
else if (expand === '_internal_pass') {
|
||||||
|
// for internal tests only
|
||||||
|
prb = msg;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error('expand must be "xmd" or "xof"');
|
||||||
|
}
|
||||||
|
const u = new Array(count);
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const e = new Array(m);
|
||||||
|
for (let j = 0; j < m; j++) {
|
||||||
|
const elm_offset = L * (j + i * m);
|
||||||
|
const tv = prb.subarray(elm_offset, elm_offset + L);
|
||||||
|
e[j] = (0, modular_ts_1.mod)(os2ip(tv), p);
|
||||||
|
}
|
||||||
|
u[i] = e;
|
||||||
|
}
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
function isogenyMap(field, map) {
|
||||||
|
// Make same order as in spec
|
||||||
|
const coeff = map.map((i) => Array.from(i).reverse());
|
||||||
|
return (x, y) => {
|
||||||
|
const [xn, xd, yn, yd] = coeff.map((val) => val.reduce((acc, i) => field.add(field.mul(acc, x), i)));
|
||||||
|
// 6.6.3
|
||||||
|
// Exceptional cases of iso_map are inputs that cause the denominator of
|
||||||
|
// either rational function to evaluate to zero; such cases MUST return
|
||||||
|
// the identity point on E.
|
||||||
|
const [xd_inv, yd_inv] = (0, modular_ts_1.FpInvertBatch)(field, [xd, yd], true);
|
||||||
|
x = field.mul(xn, xd_inv); // xNum / xDen
|
||||||
|
y = field.mul(y, field.mul(yn, yd_inv)); // y * (yNum / yDev)
|
||||||
|
return { x, y };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
exports._DST_scalar = (0, utils_ts_1.utf8ToBytes)('HashToScalar-');
|
||||||
|
/** Creates hash-to-curve methods from EC Point and mapToCurve function. See {@link H2CHasher}. */
|
||||||
|
function createHasher(Point, mapToCurve, defaults) {
|
||||||
|
if (typeof mapToCurve !== 'function')
|
||||||
|
throw new Error('mapToCurve() must be defined');
|
||||||
|
function map(num) {
|
||||||
|
return Point.fromAffine(mapToCurve(num));
|
||||||
|
}
|
||||||
|
function clear(initial) {
|
||||||
|
const P = initial.clearCofactor();
|
||||||
|
if (P.equals(Point.ZERO))
|
||||||
|
return Point.ZERO; // zero will throw in assert
|
||||||
|
P.assertValidity();
|
||||||
|
return P;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
defaults,
|
||||||
|
hashToCurve(msg, options) {
|
||||||
|
const opts = Object.assign({}, defaults, options);
|
||||||
|
const u = hash_to_field(msg, 2, opts);
|
||||||
|
const u0 = map(u[0]);
|
||||||
|
const u1 = map(u[1]);
|
||||||
|
return clear(u0.add(u1));
|
||||||
|
},
|
||||||
|
encodeToCurve(msg, options) {
|
||||||
|
const optsDst = defaults.encodeDST ? { DST: defaults.encodeDST } : {};
|
||||||
|
const opts = Object.assign({}, defaults, optsDst, options);
|
||||||
|
const u = hash_to_field(msg, 1, opts);
|
||||||
|
const u0 = map(u[0]);
|
||||||
|
return clear(u0);
|
||||||
|
},
|
||||||
|
/** See {@link H2CHasher} */
|
||||||
|
mapToCurve(scalars) {
|
||||||
|
if (!Array.isArray(scalars))
|
||||||
|
throw new Error('expected array of bigints');
|
||||||
|
for (const i of scalars)
|
||||||
|
if (typeof i !== 'bigint')
|
||||||
|
throw new Error('expected array of bigints');
|
||||||
|
return clear(map(scalars));
|
||||||
|
},
|
||||||
|
// hash_to_scalar can produce 0: https://www.rfc-editor.org/errata/eid8393
|
||||||
|
// RFC 9380, draft-irtf-cfrg-bbs-signatures-08
|
||||||
|
hashToScalar(msg, options) {
|
||||||
|
// @ts-ignore
|
||||||
|
const N = Point.Fn.ORDER;
|
||||||
|
const opts = Object.assign({}, defaults, { p: N, m: 1, DST: exports._DST_scalar }, options);
|
||||||
|
return hash_to_field(msg, 1, opts)[0][0];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=hash-to-curve.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+171
@@ -0,0 +1,171 @@
|
|||||||
|
export declare function mod(a: bigint, b: bigint): bigint;
|
||||||
|
/**
|
||||||
|
* Efficiently raise num to power and do modular division.
|
||||||
|
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
||||||
|
* @example
|
||||||
|
* pow(2n, 6n, 11n) // 64n % 11n == 9n
|
||||||
|
*/
|
||||||
|
export declare function pow(num: bigint, power: bigint, modulo: bigint): bigint;
|
||||||
|
/** Does `x^(2^power)` mod p. `pow2(30, 4)` == `30^(2^4)` */
|
||||||
|
export declare function pow2(x: bigint, power: bigint, modulo: bigint): bigint;
|
||||||
|
/**
|
||||||
|
* Inverses number over modulo.
|
||||||
|
* Implemented using [Euclidean GCD](https://brilliant.org/wiki/extended-euclidean-algorithm/).
|
||||||
|
*/
|
||||||
|
export declare function invert(number: bigint, modulo: bigint): bigint;
|
||||||
|
/**
|
||||||
|
* Tonelli-Shanks square root search algorithm.
|
||||||
|
* 1. https://eprint.iacr.org/2012/685.pdf (page 12)
|
||||||
|
* 2. Square Roots from 1; 24, 51, 10 to Dan Shanks
|
||||||
|
* @param P field order
|
||||||
|
* @returns function that takes field Fp (created from P) and number n
|
||||||
|
*/
|
||||||
|
export declare function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T;
|
||||||
|
/**
|
||||||
|
* Square root for a finite field. Will try optimized versions first:
|
||||||
|
*
|
||||||
|
* 1. P ≡ 3 (mod 4)
|
||||||
|
* 2. P ≡ 5 (mod 8)
|
||||||
|
* 3. P ≡ 9 (mod 16)
|
||||||
|
* 4. Tonelli-Shanks algorithm
|
||||||
|
*
|
||||||
|
* Different algorithms can give different roots, it is up to user to decide which one they want.
|
||||||
|
* For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
|
||||||
|
*/
|
||||||
|
export declare function FpSqrt(P: bigint): <T>(Fp: IField<T>, n: T) => T;
|
||||||
|
export declare const isNegativeLE: (num: bigint, modulo: bigint) => boolean;
|
||||||
|
/** Field is not always over prime: for example, Fp2 has ORDER(q)=p^m. */
|
||||||
|
export interface IField<T> {
|
||||||
|
ORDER: bigint;
|
||||||
|
isLE: boolean;
|
||||||
|
BYTES: number;
|
||||||
|
BITS: number;
|
||||||
|
MASK: bigint;
|
||||||
|
ZERO: T;
|
||||||
|
ONE: T;
|
||||||
|
create: (num: T) => T;
|
||||||
|
isValid: (num: T) => boolean;
|
||||||
|
is0: (num: T) => boolean;
|
||||||
|
isValidNot0: (num: T) => boolean;
|
||||||
|
neg(num: T): T;
|
||||||
|
inv(num: T): T;
|
||||||
|
sqrt(num: T): T;
|
||||||
|
sqr(num: T): T;
|
||||||
|
eql(lhs: T, rhs: T): boolean;
|
||||||
|
add(lhs: T, rhs: T): T;
|
||||||
|
sub(lhs: T, rhs: T): T;
|
||||||
|
mul(lhs: T, rhs: T | bigint): T;
|
||||||
|
pow(lhs: T, power: bigint): T;
|
||||||
|
div(lhs: T, rhs: T | bigint): T;
|
||||||
|
addN(lhs: T, rhs: T): T;
|
||||||
|
subN(lhs: T, rhs: T): T;
|
||||||
|
mulN(lhs: T, rhs: T | bigint): T;
|
||||||
|
sqrN(num: T): T;
|
||||||
|
isOdd?(num: T): boolean;
|
||||||
|
allowedLengths?: number[];
|
||||||
|
invertBatch: (lst: T[]) => T[];
|
||||||
|
toBytes(num: T): Uint8Array;
|
||||||
|
fromBytes(bytes: Uint8Array, skipValidation?: boolean): T;
|
||||||
|
cmov(a: T, b: T, c: boolean): T;
|
||||||
|
}
|
||||||
|
export declare function validateField<T>(field: IField<T>): IField<T>;
|
||||||
|
/**
|
||||||
|
* Same as `pow` but for Fp: non-constant-time.
|
||||||
|
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
||||||
|
*/
|
||||||
|
export declare function FpPow<T>(Fp: IField<T>, num: T, power: bigint): T;
|
||||||
|
/**
|
||||||
|
* Efficiently invert an array of Field elements.
|
||||||
|
* Exception-free. Will return `undefined` for 0 elements.
|
||||||
|
* @param passZero map 0 to 0 (instead of undefined)
|
||||||
|
*/
|
||||||
|
export declare function FpInvertBatch<T>(Fp: IField<T>, nums: T[], passZero?: boolean): T[];
|
||||||
|
export declare function FpDiv<T>(Fp: IField<T>, lhs: T, rhs: T | bigint): T;
|
||||||
|
/**
|
||||||
|
* Legendre symbol.
|
||||||
|
* Legendre constant is used to calculate Legendre symbol (a | p)
|
||||||
|
* which denotes the value of a^((p-1)/2) (mod p).
|
||||||
|
*
|
||||||
|
* * (a | p) ≡ 1 if a is a square (mod p), quadratic residue
|
||||||
|
* * (a | p) ≡ -1 if a is not a square (mod p), quadratic non residue
|
||||||
|
* * (a | p) ≡ 0 if a ≡ 0 (mod p)
|
||||||
|
*/
|
||||||
|
export declare function FpLegendre<T>(Fp: IField<T>, n: T): -1 | 0 | 1;
|
||||||
|
export declare function FpIsSquare<T>(Fp: IField<T>, n: T): boolean;
|
||||||
|
export type NLength = {
|
||||||
|
nByteLength: number;
|
||||||
|
nBitLength: number;
|
||||||
|
};
|
||||||
|
export declare function nLength(n: bigint, nBitLength?: number): NLength;
|
||||||
|
type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
|
||||||
|
type SqrtFn = (n: bigint) => bigint;
|
||||||
|
type FieldOpts = Partial<{
|
||||||
|
sqrt: SqrtFn;
|
||||||
|
isLE: boolean;
|
||||||
|
BITS: number;
|
||||||
|
modFromBytes: boolean;
|
||||||
|
allowedLengths?: readonly number[];
|
||||||
|
}>;
|
||||||
|
/**
|
||||||
|
* Creates a finite field. Major performance optimizations:
|
||||||
|
* * 1. Denormalized operations like mulN instead of mul.
|
||||||
|
* * 2. Identical object shape: never add or remove keys.
|
||||||
|
* * 3. `Object.freeze`.
|
||||||
|
* Fragile: always run a benchmark on a change.
|
||||||
|
* Security note: operations don't check 'isValid' for all elements for performance reasons,
|
||||||
|
* it is caller responsibility to check this.
|
||||||
|
* This is low-level code, please make sure you know what you're doing.
|
||||||
|
*
|
||||||
|
* Note about field properties:
|
||||||
|
* * CHARACTERISTIC p = prime number, number of elements in main subgroup.
|
||||||
|
* * ORDER q = similar to cofactor in curves, may be composite `q = p^m`.
|
||||||
|
*
|
||||||
|
* @param ORDER field order, probably prime, or could be composite
|
||||||
|
* @param bitLen how many bits the field consumes
|
||||||
|
* @param isLE (default: false) if encoding / decoding should be in little-endian
|
||||||
|
* @param redef optional faster redefinitions of sqrt and other methods
|
||||||
|
*/
|
||||||
|
export declare function Field(ORDER: bigint, bitLenOrOpts?: number | FieldOpts, // TODO: use opts only in v2?
|
||||||
|
isLE?: boolean, opts?: {
|
||||||
|
sqrt?: SqrtFn;
|
||||||
|
}): Readonly<FpField>;
|
||||||
|
export declare function FpSqrtOdd<T>(Fp: IField<T>, elm: T): T;
|
||||||
|
export declare function FpSqrtEven<T>(Fp: IField<T>, elm: T): T;
|
||||||
|
/**
|
||||||
|
* "Constant-time" private key generation utility.
|
||||||
|
* Same as mapKeyToField, but accepts less bytes (40 instead of 48 for 32-byte field).
|
||||||
|
* Which makes it slightly more biased, less secure.
|
||||||
|
* @deprecated use `mapKeyToField` instead
|
||||||
|
*/
|
||||||
|
export declare function hashToPrivateScalar(hash: string | Uint8Array, groupOrder: bigint, isLE?: boolean): bigint;
|
||||||
|
/**
|
||||||
|
* Returns total number of bytes consumed by the field element.
|
||||||
|
* For example, 32 bytes for usual 256-bit weierstrass curve.
|
||||||
|
* @param fieldOrder number of field elements, usually CURVE.n
|
||||||
|
* @returns byte length of field
|
||||||
|
*/
|
||||||
|
export declare function getFieldBytesLength(fieldOrder: bigint): number;
|
||||||
|
/**
|
||||||
|
* Returns minimal amount of bytes that can be safely reduced
|
||||||
|
* by field order.
|
||||||
|
* Should be 2^-128 for 128-bit curve such as P256.
|
||||||
|
* @param fieldOrder number of field elements, usually CURVE.n
|
||||||
|
* @returns byte length of target hash
|
||||||
|
*/
|
||||||
|
export declare function getMinHashLength(fieldOrder: bigint): number;
|
||||||
|
/**
|
||||||
|
* "Constant-time" private key generation utility.
|
||||||
|
* Can take (n + n/2) or more bytes of uniform input e.g. from CSPRNG or KDF
|
||||||
|
* and convert them into private scalar, with the modulo bias being negligible.
|
||||||
|
* Needs at least 48 bytes of input for 32-byte private key.
|
||||||
|
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
||||||
|
* FIPS 186-5, A.2 https://csrc.nist.gov/publications/detail/fips/186/5/final
|
||||||
|
* RFC 9380, https://www.rfc-editor.org/rfc/rfc9380#section-5
|
||||||
|
* @param hash hash output from SHA3 or a similar function
|
||||||
|
* @param groupOrder size of subgroup - (e.g. secp256k1.CURVE.n)
|
||||||
|
* @param isLE interpret hash bytes as LE num
|
||||||
|
* @returns valid private scalar
|
||||||
|
*/
|
||||||
|
export declare function mapHashToField(key: Uint8Array, fieldOrder: bigint, isLE?: boolean): Uint8Array;
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=modular.d.ts.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"modular.d.ts","sourceRoot":"","sources":["../src/abstract/modular.ts"],"names":[],"mappings":"AA0BA,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAGhD;AACD;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,4DAA4D;AAC5D,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAOrE;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAoB7D;AAqDD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAgEtE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAS/D;AAGD,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,EAAE,QAAQ,MAAM,KAAG,OACzB,CAAC;AAEnC,yEAAyE;AACzE,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,CAAC,CAAC;IACR,GAAG,EAAE,CAAC,CAAC;IAEP,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;IACtB,OAAO,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC;IAC7B,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC;IACzB,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC;IACjC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACf,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACf,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IAChB,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IAEf,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IAC7B,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACvB,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACvB,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC;IAChC,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC;IAC9B,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC;IAEhC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC;IACjC,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IAMhB,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAE1B,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;IAC/B,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,UAAU,CAAC;IAC5B,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,cAAc,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC;IAE1D,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC;CACjC;AAOD,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAgB5D;AAID;;;GAGG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CAYhE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,QAAQ,UAAQ,GAAG,CAAC,EAAE,CAiBhF;AAGD,wBAAgB,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,CAAC,CAElE;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAU7D;AAGD,wBAAgB,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,CAG1D;AAED,MAAM,MAAM,OAAO,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAM/D;AAED,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AACxE,KAAK,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;AACpC,KAAK,SAAS,GAAG,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC,CAAC,CAAC;AACH;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,KAAK,CACnB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,6BAA6B;AAChE,IAAI,UAAQ,EACZ,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GAC3B,QAAQ,CAAC,OAAO,CAAC,CA6FnB;AAgBD,wBAAgB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAIrD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAItD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,GAAG,UAAU,EACzB,UAAU,EAAE,MAAM,EAClB,IAAI,UAAQ,GACX,MAAM,CAUR;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAI9D;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,UAAQ,GAAG,UAAU,CAW5F"}
|
||||||
+554
@@ -0,0 +1,554 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.isNegativeLE = void 0;
|
||||||
|
exports.mod = mod;
|
||||||
|
exports.pow = pow;
|
||||||
|
exports.pow2 = pow2;
|
||||||
|
exports.invert = invert;
|
||||||
|
exports.tonelliShanks = tonelliShanks;
|
||||||
|
exports.FpSqrt = FpSqrt;
|
||||||
|
exports.validateField = validateField;
|
||||||
|
exports.FpPow = FpPow;
|
||||||
|
exports.FpInvertBatch = FpInvertBatch;
|
||||||
|
exports.FpDiv = FpDiv;
|
||||||
|
exports.FpLegendre = FpLegendre;
|
||||||
|
exports.FpIsSquare = FpIsSquare;
|
||||||
|
exports.nLength = nLength;
|
||||||
|
exports.Field = Field;
|
||||||
|
exports.FpSqrtOdd = FpSqrtOdd;
|
||||||
|
exports.FpSqrtEven = FpSqrtEven;
|
||||||
|
exports.hashToPrivateScalar = hashToPrivateScalar;
|
||||||
|
exports.getFieldBytesLength = getFieldBytesLength;
|
||||||
|
exports.getMinHashLength = getMinHashLength;
|
||||||
|
exports.mapHashToField = mapHashToField;
|
||||||
|
/**
|
||||||
|
* Utils for modular division and fields.
|
||||||
|
* Field over 11 is a finite (Galois) field is integer number operations `mod 11`.
|
||||||
|
* There is no division: it is replaced by modular multiplicative inverse.
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
const utils_ts_1 = require("../utils.js");
|
||||||
|
// prettier-ignore
|
||||||
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3);
|
||||||
|
// prettier-ignore
|
||||||
|
const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5), _7n = /* @__PURE__ */ BigInt(7);
|
||||||
|
// prettier-ignore
|
||||||
|
const _8n = /* @__PURE__ */ BigInt(8), _9n = /* @__PURE__ */ BigInt(9), _16n = /* @__PURE__ */ BigInt(16);
|
||||||
|
// Calculates a modulo b
|
||||||
|
function mod(a, b) {
|
||||||
|
const result = a % b;
|
||||||
|
return result >= _0n ? result : b + result;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Efficiently raise num to power and do modular division.
|
||||||
|
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
||||||
|
* @example
|
||||||
|
* pow(2n, 6n, 11n) // 64n % 11n == 9n
|
||||||
|
*/
|
||||||
|
function pow(num, power, modulo) {
|
||||||
|
return FpPow(Field(modulo), num, power);
|
||||||
|
}
|
||||||
|
/** Does `x^(2^power)` mod p. `pow2(30, 4)` == `30^(2^4)` */
|
||||||
|
function pow2(x, power, modulo) {
|
||||||
|
let res = x;
|
||||||
|
while (power-- > _0n) {
|
||||||
|
res *= res;
|
||||||
|
res %= modulo;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Inverses number over modulo.
|
||||||
|
* Implemented using [Euclidean GCD](https://brilliant.org/wiki/extended-euclidean-algorithm/).
|
||||||
|
*/
|
||||||
|
function invert(number, modulo) {
|
||||||
|
if (number === _0n)
|
||||||
|
throw new Error('invert: expected non-zero number');
|
||||||
|
if (modulo <= _0n)
|
||||||
|
throw new Error('invert: expected positive modulus, got ' + modulo);
|
||||||
|
// Fermat's little theorem "CT-like" version inv(n) = n^(m-2) mod m is 30x slower.
|
||||||
|
let a = mod(number, modulo);
|
||||||
|
let b = modulo;
|
||||||
|
// prettier-ignore
|
||||||
|
let x = _0n, y = _1n, u = _1n, v = _0n;
|
||||||
|
while (a !== _0n) {
|
||||||
|
// JIT applies optimization if those two lines follow each other
|
||||||
|
const q = b / a;
|
||||||
|
const r = b % a;
|
||||||
|
const m = x - u * q;
|
||||||
|
const n = y - v * q;
|
||||||
|
// prettier-ignore
|
||||||
|
b = a, a = r, x = u, y = v, u = m, v = n;
|
||||||
|
}
|
||||||
|
const gcd = b;
|
||||||
|
if (gcd !== _1n)
|
||||||
|
throw new Error('invert: does not exist');
|
||||||
|
return mod(x, modulo);
|
||||||
|
}
|
||||||
|
function assertIsSquare(Fp, root, n) {
|
||||||
|
if (!Fp.eql(Fp.sqr(root), n))
|
||||||
|
throw new Error('Cannot find square root');
|
||||||
|
}
|
||||||
|
// Not all roots are possible! Example which will throw:
|
||||||
|
// const NUM =
|
||||||
|
// n = 72057594037927816n;
|
||||||
|
// Fp = Field(BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'));
|
||||||
|
function sqrt3mod4(Fp, n) {
|
||||||
|
const p1div4 = (Fp.ORDER + _1n) / _4n;
|
||||||
|
const root = Fp.pow(n, p1div4);
|
||||||
|
assertIsSquare(Fp, root, n);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
function sqrt5mod8(Fp, n) {
|
||||||
|
const p5div8 = (Fp.ORDER - _5n) / _8n;
|
||||||
|
const n2 = Fp.mul(n, _2n);
|
||||||
|
const v = Fp.pow(n2, p5div8);
|
||||||
|
const nv = Fp.mul(n, v);
|
||||||
|
const i = Fp.mul(Fp.mul(nv, _2n), v);
|
||||||
|
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
|
||||||
|
assertIsSquare(Fp, root, n);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
// Based on RFC9380, Kong algorithm
|
||||||
|
// prettier-ignore
|
||||||
|
function sqrt9mod16(P) {
|
||||||
|
const Fp_ = Field(P);
|
||||||
|
const tn = tonelliShanks(P);
|
||||||
|
const c1 = tn(Fp_, Fp_.neg(Fp_.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
|
||||||
|
const c2 = tn(Fp_, c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
|
||||||
|
const c3 = tn(Fp_, Fp_.neg(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
|
||||||
|
const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
|
||||||
|
return (Fp, n) => {
|
||||||
|
let tv1 = Fp.pow(n, c4); // 1. tv1 = x^c4
|
||||||
|
let tv2 = Fp.mul(tv1, c1); // 2. tv2 = c1 * tv1
|
||||||
|
const tv3 = Fp.mul(tv1, c2); // 3. tv3 = c2 * tv1
|
||||||
|
const tv4 = Fp.mul(tv1, c3); // 4. tv4 = c3 * tv1
|
||||||
|
const e1 = Fp.eql(Fp.sqr(tv2), n); // 5. e1 = (tv2^2) == x
|
||||||
|
const e2 = Fp.eql(Fp.sqr(tv3), n); // 6. e2 = (tv3^2) == x
|
||||||
|
tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
|
||||||
|
tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
|
||||||
|
const e3 = Fp.eql(Fp.sqr(tv2), n); // 9. e3 = (tv2^2) == x
|
||||||
|
const root = Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select sqrt from tv1 & tv2
|
||||||
|
assertIsSquare(Fp, root, n);
|
||||||
|
return root;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Tonelli-Shanks square root search algorithm.
|
||||||
|
* 1. https://eprint.iacr.org/2012/685.pdf (page 12)
|
||||||
|
* 2. Square Roots from 1; 24, 51, 10 to Dan Shanks
|
||||||
|
* @param P field order
|
||||||
|
* @returns function that takes field Fp (created from P) and number n
|
||||||
|
*/
|
||||||
|
function tonelliShanks(P) {
|
||||||
|
// Initialization (precomputation).
|
||||||
|
// Caching initialization could boost perf by 7%.
|
||||||
|
if (P < _3n)
|
||||||
|
throw new Error('sqrt is not defined for small field');
|
||||||
|
// Factor P - 1 = Q * 2^S, where Q is odd
|
||||||
|
let Q = P - _1n;
|
||||||
|
let S = 0;
|
||||||
|
while (Q % _2n === _0n) {
|
||||||
|
Q /= _2n;
|
||||||
|
S++;
|
||||||
|
}
|
||||||
|
// Find the first quadratic non-residue Z >= 2
|
||||||
|
let Z = _2n;
|
||||||
|
const _Fp = Field(P);
|
||||||
|
while (FpLegendre(_Fp, Z) === 1) {
|
||||||
|
// Basic primality test for P. After x iterations, chance of
|
||||||
|
// not finding quadratic non-residue is 2^x, so 2^1000.
|
||||||
|
if (Z++ > 1000)
|
||||||
|
throw new Error('Cannot find square root: probably non-prime P');
|
||||||
|
}
|
||||||
|
// Fast-path; usually done before Z, but we do "primality test".
|
||||||
|
if (S === 1)
|
||||||
|
return sqrt3mod4;
|
||||||
|
// Slow-path
|
||||||
|
// TODO: test on Fp2 and others
|
||||||
|
let cc = _Fp.pow(Z, Q); // c = z^Q
|
||||||
|
const Q1div2 = (Q + _1n) / _2n;
|
||||||
|
return function tonelliSlow(Fp, n) {
|
||||||
|
if (Fp.is0(n))
|
||||||
|
return n;
|
||||||
|
// Check if n is a quadratic residue using Legendre symbol
|
||||||
|
if (FpLegendre(Fp, n) !== 1)
|
||||||
|
throw new Error('Cannot find square root');
|
||||||
|
// Initialize variables for the main loop
|
||||||
|
let M = S;
|
||||||
|
let c = Fp.mul(Fp.ONE, cc); // c = z^Q, move cc from field _Fp into field Fp
|
||||||
|
let t = Fp.pow(n, Q); // t = n^Q, first guess at the fudge factor
|
||||||
|
let R = Fp.pow(n, Q1div2); // R = n^((Q+1)/2), first guess at the square root
|
||||||
|
// Main loop
|
||||||
|
// while t != 1
|
||||||
|
while (!Fp.eql(t, Fp.ONE)) {
|
||||||
|
if (Fp.is0(t))
|
||||||
|
return Fp.ZERO; // if t=0 return R=0
|
||||||
|
let i = 1;
|
||||||
|
// Find the smallest i >= 1 such that t^(2^i) ≡ 1 (mod P)
|
||||||
|
let t_tmp = Fp.sqr(t); // t^(2^1)
|
||||||
|
while (!Fp.eql(t_tmp, Fp.ONE)) {
|
||||||
|
i++;
|
||||||
|
t_tmp = Fp.sqr(t_tmp); // t^(2^2)...
|
||||||
|
if (i === M)
|
||||||
|
throw new Error('Cannot find square root');
|
||||||
|
}
|
||||||
|
// Calculate the exponent for b: 2^(M - i - 1)
|
||||||
|
const exponent = _1n << BigInt(M - i - 1); // bigint is important
|
||||||
|
const b = Fp.pow(c, exponent); // b = 2^(M - i - 1)
|
||||||
|
// Update variables
|
||||||
|
M = i;
|
||||||
|
c = Fp.sqr(b); // c = b^2
|
||||||
|
t = Fp.mul(t, c); // t = (t * b^2)
|
||||||
|
R = Fp.mul(R, b); // R = R*b
|
||||||
|
}
|
||||||
|
return R;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Square root for a finite field. Will try optimized versions first:
|
||||||
|
*
|
||||||
|
* 1. P ≡ 3 (mod 4)
|
||||||
|
* 2. P ≡ 5 (mod 8)
|
||||||
|
* 3. P ≡ 9 (mod 16)
|
||||||
|
* 4. Tonelli-Shanks algorithm
|
||||||
|
*
|
||||||
|
* Different algorithms can give different roots, it is up to user to decide which one they want.
|
||||||
|
* For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
|
||||||
|
*/
|
||||||
|
function FpSqrt(P) {
|
||||||
|
// P ≡ 3 (mod 4) => √n = n^((P+1)/4)
|
||||||
|
if (P % _4n === _3n)
|
||||||
|
return sqrt3mod4;
|
||||||
|
// P ≡ 5 (mod 8) => Atkin algorithm, page 10 of https://eprint.iacr.org/2012/685.pdf
|
||||||
|
if (P % _8n === _5n)
|
||||||
|
return sqrt5mod8;
|
||||||
|
// P ≡ 9 (mod 16) => Kong algorithm, page 11 of https://eprint.iacr.org/2012/685.pdf (algorithm 4)
|
||||||
|
if (P % _16n === _9n)
|
||||||
|
return sqrt9mod16(P);
|
||||||
|
// Tonelli-Shanks algorithm
|
||||||
|
return tonelliShanks(P);
|
||||||
|
}
|
||||||
|
// Little-endian check for first LE bit (last BE bit);
|
||||||
|
const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;
|
||||||
|
exports.isNegativeLE = isNegativeLE;
|
||||||
|
// prettier-ignore
|
||||||
|
const FIELD_FIELDS = [
|
||||||
|
'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
|
||||||
|
'eql', 'add', 'sub', 'mul', 'pow', 'div',
|
||||||
|
'addN', 'subN', 'mulN', 'sqrN'
|
||||||
|
];
|
||||||
|
function validateField(field) {
|
||||||
|
const initial = {
|
||||||
|
ORDER: 'bigint',
|
||||||
|
MASK: 'bigint',
|
||||||
|
BYTES: 'number',
|
||||||
|
BITS: 'number',
|
||||||
|
};
|
||||||
|
const opts = FIELD_FIELDS.reduce((map, val) => {
|
||||||
|
map[val] = 'function';
|
||||||
|
return map;
|
||||||
|
}, initial);
|
||||||
|
(0, utils_ts_1._validateObject)(field, opts);
|
||||||
|
// const max = 16384;
|
||||||
|
// if (field.BYTES < 1 || field.BYTES > max) throw new Error('invalid field');
|
||||||
|
// if (field.BITS < 1 || field.BITS > 8 * max) throw new Error('invalid field');
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
// Generic field functions
|
||||||
|
/**
|
||||||
|
* Same as `pow` but for Fp: non-constant-time.
|
||||||
|
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
||||||
|
*/
|
||||||
|
function FpPow(Fp, num, power) {
|
||||||
|
if (power < _0n)
|
||||||
|
throw new Error('invalid exponent, negatives unsupported');
|
||||||
|
if (power === _0n)
|
||||||
|
return Fp.ONE;
|
||||||
|
if (power === _1n)
|
||||||
|
return num;
|
||||||
|
let p = Fp.ONE;
|
||||||
|
let d = num;
|
||||||
|
while (power > _0n) {
|
||||||
|
if (power & _1n)
|
||||||
|
p = Fp.mul(p, d);
|
||||||
|
d = Fp.sqr(d);
|
||||||
|
power >>= _1n;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Efficiently invert an array of Field elements.
|
||||||
|
* Exception-free. Will return `undefined` for 0 elements.
|
||||||
|
* @param passZero map 0 to 0 (instead of undefined)
|
||||||
|
*/
|
||||||
|
function FpInvertBatch(Fp, nums, passZero = false) {
|
||||||
|
const inverted = new Array(nums.length).fill(passZero ? Fp.ZERO : undefined);
|
||||||
|
// Walk from first to last, multiply them by each other MOD p
|
||||||
|
const multipliedAcc = nums.reduce((acc, num, i) => {
|
||||||
|
if (Fp.is0(num))
|
||||||
|
return acc;
|
||||||
|
inverted[i] = acc;
|
||||||
|
return Fp.mul(acc, num);
|
||||||
|
}, Fp.ONE);
|
||||||
|
// Invert last element
|
||||||
|
const invertedAcc = Fp.inv(multipliedAcc);
|
||||||
|
// Walk from last to first, multiply them by inverted each other MOD p
|
||||||
|
nums.reduceRight((acc, num, i) => {
|
||||||
|
if (Fp.is0(num))
|
||||||
|
return acc;
|
||||||
|
inverted[i] = Fp.mul(acc, inverted[i]);
|
||||||
|
return Fp.mul(acc, num);
|
||||||
|
}, invertedAcc);
|
||||||
|
return inverted;
|
||||||
|
}
|
||||||
|
// TODO: remove
|
||||||
|
function FpDiv(Fp, lhs, rhs) {
|
||||||
|
return Fp.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, Fp.ORDER) : Fp.inv(rhs));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Legendre symbol.
|
||||||
|
* Legendre constant is used to calculate Legendre symbol (a | p)
|
||||||
|
* which denotes the value of a^((p-1)/2) (mod p).
|
||||||
|
*
|
||||||
|
* * (a | p) ≡ 1 if a is a square (mod p), quadratic residue
|
||||||
|
* * (a | p) ≡ -1 if a is not a square (mod p), quadratic non residue
|
||||||
|
* * (a | p) ≡ 0 if a ≡ 0 (mod p)
|
||||||
|
*/
|
||||||
|
function FpLegendre(Fp, n) {
|
||||||
|
// We can use 3rd argument as optional cache of this value
|
||||||
|
// but seems unneeded for now. The operation is very fast.
|
||||||
|
const p1mod2 = (Fp.ORDER - _1n) / _2n;
|
||||||
|
const powered = Fp.pow(n, p1mod2);
|
||||||
|
const yes = Fp.eql(powered, Fp.ONE);
|
||||||
|
const zero = Fp.eql(powered, Fp.ZERO);
|
||||||
|
const no = Fp.eql(powered, Fp.neg(Fp.ONE));
|
||||||
|
if (!yes && !zero && !no)
|
||||||
|
throw new Error('invalid Legendre symbol result');
|
||||||
|
return yes ? 1 : zero ? 0 : -1;
|
||||||
|
}
|
||||||
|
// This function returns True whenever the value x is a square in the field F.
|
||||||
|
function FpIsSquare(Fp, n) {
|
||||||
|
const l = FpLegendre(Fp, n);
|
||||||
|
return l === 1;
|
||||||
|
}
|
||||||
|
// CURVE.n lengths
|
||||||
|
function nLength(n, nBitLength) {
|
||||||
|
// Bit size, byte size of CURVE.n
|
||||||
|
if (nBitLength !== undefined)
|
||||||
|
(0, utils_ts_1.anumber)(nBitLength);
|
||||||
|
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
||||||
|
const nByteLength = Math.ceil(_nBitLength / 8);
|
||||||
|
return { nBitLength: _nBitLength, nByteLength };
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Creates a finite field. Major performance optimizations:
|
||||||
|
* * 1. Denormalized operations like mulN instead of mul.
|
||||||
|
* * 2. Identical object shape: never add or remove keys.
|
||||||
|
* * 3. `Object.freeze`.
|
||||||
|
* Fragile: always run a benchmark on a change.
|
||||||
|
* Security note: operations don't check 'isValid' for all elements for performance reasons,
|
||||||
|
* it is caller responsibility to check this.
|
||||||
|
* This is low-level code, please make sure you know what you're doing.
|
||||||
|
*
|
||||||
|
* Note about field properties:
|
||||||
|
* * CHARACTERISTIC p = prime number, number of elements in main subgroup.
|
||||||
|
* * ORDER q = similar to cofactor in curves, may be composite `q = p^m`.
|
||||||
|
*
|
||||||
|
* @param ORDER field order, probably prime, or could be composite
|
||||||
|
* @param bitLen how many bits the field consumes
|
||||||
|
* @param isLE (default: false) if encoding / decoding should be in little-endian
|
||||||
|
* @param redef optional faster redefinitions of sqrt and other methods
|
||||||
|
*/
|
||||||
|
function Field(ORDER, bitLenOrOpts, // TODO: use opts only in v2?
|
||||||
|
isLE = false, opts = {}) {
|
||||||
|
if (ORDER <= _0n)
|
||||||
|
throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
|
||||||
|
let _nbitLength = undefined;
|
||||||
|
let _sqrt = undefined;
|
||||||
|
let modFromBytes = false;
|
||||||
|
let allowedLengths = undefined;
|
||||||
|
if (typeof bitLenOrOpts === 'object' && bitLenOrOpts != null) {
|
||||||
|
if (opts.sqrt || isLE)
|
||||||
|
throw new Error('cannot specify opts in two arguments');
|
||||||
|
const _opts = bitLenOrOpts;
|
||||||
|
if (_opts.BITS)
|
||||||
|
_nbitLength = _opts.BITS;
|
||||||
|
if (_opts.sqrt)
|
||||||
|
_sqrt = _opts.sqrt;
|
||||||
|
if (typeof _opts.isLE === 'boolean')
|
||||||
|
isLE = _opts.isLE;
|
||||||
|
if (typeof _opts.modFromBytes === 'boolean')
|
||||||
|
modFromBytes = _opts.modFromBytes;
|
||||||
|
allowedLengths = _opts.allowedLengths;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (typeof bitLenOrOpts === 'number')
|
||||||
|
_nbitLength = bitLenOrOpts;
|
||||||
|
if (opts.sqrt)
|
||||||
|
_sqrt = opts.sqrt;
|
||||||
|
}
|
||||||
|
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, _nbitLength);
|
||||||
|
if (BYTES > 2048)
|
||||||
|
throw new Error('invalid field: expected ORDER of <= 2048 bytes');
|
||||||
|
let sqrtP; // cached sqrtP
|
||||||
|
const f = Object.freeze({
|
||||||
|
ORDER,
|
||||||
|
isLE,
|
||||||
|
BITS,
|
||||||
|
BYTES,
|
||||||
|
MASK: (0, utils_ts_1.bitMask)(BITS),
|
||||||
|
ZERO: _0n,
|
||||||
|
ONE: _1n,
|
||||||
|
allowedLengths: allowedLengths,
|
||||||
|
create: (num) => mod(num, ORDER),
|
||||||
|
isValid: (num) => {
|
||||||
|
if (typeof num !== 'bigint')
|
||||||
|
throw new Error('invalid field element: expected bigint, got ' + typeof num);
|
||||||
|
return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
|
||||||
|
},
|
||||||
|
is0: (num) => num === _0n,
|
||||||
|
// is valid and invertible
|
||||||
|
isValidNot0: (num) => !f.is0(num) && f.isValid(num),
|
||||||
|
isOdd: (num) => (num & _1n) === _1n,
|
||||||
|
neg: (num) => mod(-num, ORDER),
|
||||||
|
eql: (lhs, rhs) => lhs === rhs,
|
||||||
|
sqr: (num) => mod(num * num, ORDER),
|
||||||
|
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
|
||||||
|
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
|
||||||
|
mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
|
||||||
|
pow: (num, power) => FpPow(f, num, power),
|
||||||
|
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
|
||||||
|
// Same as above, but doesn't normalize
|
||||||
|
sqrN: (num) => num * num,
|
||||||
|
addN: (lhs, rhs) => lhs + rhs,
|
||||||
|
subN: (lhs, rhs) => lhs - rhs,
|
||||||
|
mulN: (lhs, rhs) => lhs * rhs,
|
||||||
|
inv: (num) => invert(num, ORDER),
|
||||||
|
sqrt: _sqrt ||
|
||||||
|
((n) => {
|
||||||
|
if (!sqrtP)
|
||||||
|
sqrtP = FpSqrt(ORDER);
|
||||||
|
return sqrtP(f, n);
|
||||||
|
}),
|
||||||
|
toBytes: (num) => (isLE ? (0, utils_ts_1.numberToBytesLE)(num, BYTES) : (0, utils_ts_1.numberToBytesBE)(num, BYTES)),
|
||||||
|
fromBytes: (bytes, skipValidation = true) => {
|
||||||
|
if (allowedLengths) {
|
||||||
|
if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
|
||||||
|
throw new Error('Field.fromBytes: expected ' + allowedLengths + ' bytes, got ' + bytes.length);
|
||||||
|
}
|
||||||
|
const padded = new Uint8Array(BYTES);
|
||||||
|
// isLE add 0 to right, !isLE to the left.
|
||||||
|
padded.set(bytes, isLE ? 0 : padded.length - bytes.length);
|
||||||
|
bytes = padded;
|
||||||
|
}
|
||||||
|
if (bytes.length !== BYTES)
|
||||||
|
throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
|
||||||
|
let scalar = isLE ? (0, utils_ts_1.bytesToNumberLE)(bytes) : (0, utils_ts_1.bytesToNumberBE)(bytes);
|
||||||
|
if (modFromBytes)
|
||||||
|
scalar = mod(scalar, ORDER);
|
||||||
|
if (!skipValidation)
|
||||||
|
if (!f.isValid(scalar))
|
||||||
|
throw new Error('invalid field element: outside of range 0..ORDER');
|
||||||
|
// NOTE: we don't validate scalar here, please use isValid. This done such way because some
|
||||||
|
// protocol may allow non-reduced scalar that reduced later or changed some other way.
|
||||||
|
return scalar;
|
||||||
|
},
|
||||||
|
// TODO: we don't need it here, move out to separate fn
|
||||||
|
invertBatch: (lst) => FpInvertBatch(f, lst),
|
||||||
|
// We can't move this out because Fp6, Fp12 implement it
|
||||||
|
// and it's unclear what to return in there.
|
||||||
|
cmov: (a, b, c) => (c ? b : a),
|
||||||
|
});
|
||||||
|
return Object.freeze(f);
|
||||||
|
}
|
||||||
|
// Generic random scalar, we can do same for other fields if via Fp2.mul(Fp2.ONE, Fp2.random)?
|
||||||
|
// This allows unsafe methods like ignore bias or zero. These unsafe, but often used in different protocols (if deterministic RNG).
|
||||||
|
// which mean we cannot force this via opts.
|
||||||
|
// Not sure what to do with randomBytes, we can accept it inside opts if wanted.
|
||||||
|
// Probably need to export getMinHashLength somewhere?
|
||||||
|
// random(bytes?: Uint8Array, unsafeAllowZero = false, unsafeAllowBias = false) {
|
||||||
|
// const LEN = !unsafeAllowBias ? getMinHashLength(ORDER) : BYTES;
|
||||||
|
// if (bytes === undefined) bytes = randomBytes(LEN); // _opts.randomBytes?
|
||||||
|
// const num = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
||||||
|
// // `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
|
||||||
|
// const reduced = unsafeAllowZero ? mod(num, ORDER) : mod(num, ORDER - _1n) + _1n;
|
||||||
|
// return reduced;
|
||||||
|
// },
|
||||||
|
function FpSqrtOdd(Fp, elm) {
|
||||||
|
if (!Fp.isOdd)
|
||||||
|
throw new Error("Field doesn't have isOdd");
|
||||||
|
const root = Fp.sqrt(elm);
|
||||||
|
return Fp.isOdd(root) ? root : Fp.neg(root);
|
||||||
|
}
|
||||||
|
function FpSqrtEven(Fp, elm) {
|
||||||
|
if (!Fp.isOdd)
|
||||||
|
throw new Error("Field doesn't have isOdd");
|
||||||
|
const root = Fp.sqrt(elm);
|
||||||
|
return Fp.isOdd(root) ? Fp.neg(root) : root;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* "Constant-time" private key generation utility.
|
||||||
|
* Same as mapKeyToField, but accepts less bytes (40 instead of 48 for 32-byte field).
|
||||||
|
* Which makes it slightly more biased, less secure.
|
||||||
|
* @deprecated use `mapKeyToField` instead
|
||||||
|
*/
|
||||||
|
function hashToPrivateScalar(hash, groupOrder, isLE = false) {
|
||||||
|
hash = (0, utils_ts_1.ensureBytes)('privateHash', hash);
|
||||||
|
const hashLen = hash.length;
|
||||||
|
const minLen = nLength(groupOrder).nByteLength + 8;
|
||||||
|
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
||||||
|
throw new Error('hashToPrivateScalar: expected ' + minLen + '-1024 bytes of input, got ' + hashLen);
|
||||||
|
const num = isLE ? (0, utils_ts_1.bytesToNumberLE)(hash) : (0, utils_ts_1.bytesToNumberBE)(hash);
|
||||||
|
return mod(num, groupOrder - _1n) + _1n;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns total number of bytes consumed by the field element.
|
||||||
|
* For example, 32 bytes for usual 256-bit weierstrass curve.
|
||||||
|
* @param fieldOrder number of field elements, usually CURVE.n
|
||||||
|
* @returns byte length of field
|
||||||
|
*/
|
||||||
|
function getFieldBytesLength(fieldOrder) {
|
||||||
|
if (typeof fieldOrder !== 'bigint')
|
||||||
|
throw new Error('field order must be bigint');
|
||||||
|
const bitLength = fieldOrder.toString(2).length;
|
||||||
|
return Math.ceil(bitLength / 8);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns minimal amount of bytes that can be safely reduced
|
||||||
|
* by field order.
|
||||||
|
* Should be 2^-128 for 128-bit curve such as P256.
|
||||||
|
* @param fieldOrder number of field elements, usually CURVE.n
|
||||||
|
* @returns byte length of target hash
|
||||||
|
*/
|
||||||
|
function getMinHashLength(fieldOrder) {
|
||||||
|
const length = getFieldBytesLength(fieldOrder);
|
||||||
|
return length + Math.ceil(length / 2);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* "Constant-time" private key generation utility.
|
||||||
|
* Can take (n + n/2) or more bytes of uniform input e.g. from CSPRNG or KDF
|
||||||
|
* and convert them into private scalar, with the modulo bias being negligible.
|
||||||
|
* Needs at least 48 bytes of input for 32-byte private key.
|
||||||
|
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
||||||
|
* FIPS 186-5, A.2 https://csrc.nist.gov/publications/detail/fips/186/5/final
|
||||||
|
* RFC 9380, https://www.rfc-editor.org/rfc/rfc9380#section-5
|
||||||
|
* @param hash hash output from SHA3 or a similar function
|
||||||
|
* @param groupOrder size of subgroup - (e.g. secp256k1.CURVE.n)
|
||||||
|
* @param isLE interpret hash bytes as LE num
|
||||||
|
* @returns valid private scalar
|
||||||
|
*/
|
||||||
|
function mapHashToField(key, fieldOrder, isLE = false) {
|
||||||
|
const len = key.length;
|
||||||
|
const fieldLen = getFieldBytesLength(fieldOrder);
|
||||||
|
const minLen = getMinHashLength(fieldOrder);
|
||||||
|
// No small numbers: need to understand bias story. No huge numbers: easier to detect JS timings.
|
||||||
|
if (len < 16 || len < minLen || len > 1024)
|
||||||
|
throw new Error('expected ' + minLen + '-1024 bytes of input, got ' + len);
|
||||||
|
const num = isLE ? (0, utils_ts_1.bytesToNumberLE)(key) : (0, utils_ts_1.bytesToNumberBE)(key);
|
||||||
|
// `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
|
||||||
|
const reduced = mod(num, fieldOrder - _1n) + _1n;
|
||||||
|
return isLE ? (0, utils_ts_1.numberToBytesLE)(reduced, fieldLen) : (0, utils_ts_1.numberToBytesBE)(reduced, fieldLen);
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=modular.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+30
@@ -0,0 +1,30 @@
|
|||||||
|
import type { CurveLengths } from './curve.ts';
|
||||||
|
type Hex = string | Uint8Array;
|
||||||
|
export type CurveType = {
|
||||||
|
P: bigint;
|
||||||
|
type: 'x25519' | 'x448';
|
||||||
|
adjustScalarBytes: (bytes: Uint8Array) => Uint8Array;
|
||||||
|
powPminus2: (x: bigint) => bigint;
|
||||||
|
randomBytes?: (bytesLength?: number) => Uint8Array;
|
||||||
|
};
|
||||||
|
export type MontgomeryECDH = {
|
||||||
|
scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
|
||||||
|
scalarMultBase: (scalar: Hex) => Uint8Array;
|
||||||
|
getSharedSecret: (secretKeyA: Hex, publicKeyB: Hex) => Uint8Array;
|
||||||
|
getPublicKey: (secretKey: Hex) => Uint8Array;
|
||||||
|
utils: {
|
||||||
|
randomSecretKey: () => Uint8Array;
|
||||||
|
/** @deprecated use `randomSecretKey` */
|
||||||
|
randomPrivateKey: () => Uint8Array;
|
||||||
|
};
|
||||||
|
GuBytes: Uint8Array;
|
||||||
|
lengths: CurveLengths;
|
||||||
|
keygen: (seed?: Uint8Array) => {
|
||||||
|
secretKey: Uint8Array;
|
||||||
|
publicKey: Uint8Array;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export type CurveFn = MontgomeryECDH;
|
||||||
|
export declare function montgomery(curveDef: CurveType): MontgomeryECDH;
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=montgomery.d.ts.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"montgomery.d.ts","sourceRoot":"","sources":["../src/abstract/montgomery.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM/C,KAAK,GAAG,GAAG,MAAM,GAAG,UAAU,CAAC;AAE/B,MAAM,MAAM,SAAS,GAAG;IACtB,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACxB,iBAAiB,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,UAAU,CAAC;IACrD,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAClC,WAAW,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,MAAM,KAAK,UAAU,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,KAAK,UAAU,CAAC;IAChD,cAAc,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,UAAU,CAAC;IAC5C,eAAe,EAAE,CAAC,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,KAAK,UAAU,CAAC;IAClE,YAAY,EAAE,CAAC,SAAS,EAAE,GAAG,KAAK,UAAU,CAAC;IAC7C,KAAK,EAAE;QACL,eAAe,EAAE,MAAM,UAAU,CAAC;QAClC,wCAAwC;QACxC,gBAAgB,EAAE,MAAM,UAAU,CAAC;KACpC,CAAC;IACF,OAAO,EAAE,UAAU,CAAC;IACpB,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,UAAU,KAAK;QAAE,SAAS,EAAE,UAAU,CAAC;QAAC,SAAS,EAAE,UAAU,CAAA;KAAE,CAAC;CACjF,CAAC;AACF,MAAM,MAAM,OAAO,GAAG,cAAc,CAAC;AAUrC,wBAAgB,UAAU,CAAC,QAAQ,EAAE,SAAS,GAAG,cAAc,CAyI9D"}
|
||||||
+160
@@ -0,0 +1,160 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.montgomery = montgomery;
|
||||||
|
/**
|
||||||
|
* Montgomery curve methods. It's not really whole montgomery curve,
|
||||||
|
* just bunch of very specific methods for X25519 / X448 from
|
||||||
|
* [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748)
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
const utils_ts_1 = require("../utils.js");
|
||||||
|
const modular_ts_1 = require("./modular.js");
|
||||||
|
const _0n = BigInt(0);
|
||||||
|
const _1n = BigInt(1);
|
||||||
|
const _2n = BigInt(2);
|
||||||
|
function validateOpts(curve) {
|
||||||
|
(0, utils_ts_1._validateObject)(curve, {
|
||||||
|
adjustScalarBytes: 'function',
|
||||||
|
powPminus2: 'function',
|
||||||
|
});
|
||||||
|
return Object.freeze({ ...curve });
|
||||||
|
}
|
||||||
|
function montgomery(curveDef) {
|
||||||
|
const CURVE = validateOpts(curveDef);
|
||||||
|
const { P, type, adjustScalarBytes, powPminus2, randomBytes: rand } = CURVE;
|
||||||
|
const is25519 = type === 'x25519';
|
||||||
|
if (!is25519 && type !== 'x448')
|
||||||
|
throw new Error('invalid type');
|
||||||
|
const randomBytes_ = rand || utils_ts_1.randomBytes;
|
||||||
|
const montgomeryBits = is25519 ? 255 : 448;
|
||||||
|
const fieldLen = is25519 ? 32 : 56;
|
||||||
|
const Gu = is25519 ? BigInt(9) : BigInt(5);
|
||||||
|
// RFC 7748 #5:
|
||||||
|
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519 and
|
||||||
|
// (156326 - 2) / 4 = 39081 for curve448/X448
|
||||||
|
// const a = is25519 ? 156326n : 486662n;
|
||||||
|
const a24 = is25519 ? BigInt(121665) : BigInt(39081);
|
||||||
|
// RFC: x25519 "the resulting integer is of the form 2^254 plus
|
||||||
|
// eight times a value between 0 and 2^251 - 1 (inclusive)"
|
||||||
|
// x448: "2^447 plus four times a value between 0 and 2^445 - 1 (inclusive)"
|
||||||
|
const minScalar = is25519 ? _2n ** BigInt(254) : _2n ** BigInt(447);
|
||||||
|
const maxAdded = is25519
|
||||||
|
? BigInt(8) * _2n ** BigInt(251) - _1n
|
||||||
|
: BigInt(4) * _2n ** BigInt(445) - _1n;
|
||||||
|
const maxScalar = minScalar + maxAdded + _1n; // (inclusive)
|
||||||
|
const modP = (n) => (0, modular_ts_1.mod)(n, P);
|
||||||
|
const GuBytes = encodeU(Gu);
|
||||||
|
function encodeU(u) {
|
||||||
|
return (0, utils_ts_1.numberToBytesLE)(modP(u), fieldLen);
|
||||||
|
}
|
||||||
|
function decodeU(u) {
|
||||||
|
const _u = (0, utils_ts_1.ensureBytes)('u coordinate', u, fieldLen);
|
||||||
|
// RFC: When receiving such an array, implementations of X25519
|
||||||
|
// (but not X448) MUST mask the most significant bit in the final byte.
|
||||||
|
if (is25519)
|
||||||
|
_u[31] &= 127; // 0b0111_1111
|
||||||
|
// RFC: Implementations MUST accept non-canonical values and process them as
|
||||||
|
// if they had been reduced modulo the field prime. The non-canonical
|
||||||
|
// values are 2^255 - 19 through 2^255 - 1 for X25519 and 2^448 - 2^224
|
||||||
|
// - 1 through 2^448 - 1 for X448.
|
||||||
|
return modP((0, utils_ts_1.bytesToNumberLE)(_u));
|
||||||
|
}
|
||||||
|
function decodeScalar(scalar) {
|
||||||
|
return (0, utils_ts_1.bytesToNumberLE)(adjustScalarBytes((0, utils_ts_1.ensureBytes)('scalar', scalar, fieldLen)));
|
||||||
|
}
|
||||||
|
function scalarMult(scalar, u) {
|
||||||
|
const pu = montgomeryLadder(decodeU(u), decodeScalar(scalar));
|
||||||
|
// Some public keys are useless, of low-order. Curve author doesn't think
|
||||||
|
// it needs to be validated, but we do it nonetheless.
|
||||||
|
// https://cr.yp.to/ecdh.html#validate
|
||||||
|
if (pu === _0n)
|
||||||
|
throw new Error('invalid private or public key received');
|
||||||
|
return encodeU(pu);
|
||||||
|
}
|
||||||
|
// Computes public key from private. By doing scalar multiplication of base point.
|
||||||
|
function scalarMultBase(scalar) {
|
||||||
|
return scalarMult(scalar, GuBytes);
|
||||||
|
}
|
||||||
|
// cswap from RFC7748 "example code"
|
||||||
|
function cswap(swap, x_2, x_3) {
|
||||||
|
// dummy = mask(swap) AND (x_2 XOR x_3)
|
||||||
|
// Where mask(swap) is the all-1 or all-0 word of the same length as x_2
|
||||||
|
// and x_3, computed, e.g., as mask(swap) = 0 - swap.
|
||||||
|
const dummy = modP(swap * (x_2 - x_3));
|
||||||
|
x_2 = modP(x_2 - dummy); // x_2 = x_2 XOR dummy
|
||||||
|
x_3 = modP(x_3 + dummy); // x_3 = x_3 XOR dummy
|
||||||
|
return { x_2, x_3 };
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Montgomery x-only multiplication ladder.
|
||||||
|
* @param pointU u coordinate (x) on Montgomery Curve 25519
|
||||||
|
* @param scalar by which the point would be multiplied
|
||||||
|
* @returns new Point on Montgomery curve
|
||||||
|
*/
|
||||||
|
function montgomeryLadder(u, scalar) {
|
||||||
|
(0, utils_ts_1.aInRange)('u', u, _0n, P);
|
||||||
|
(0, utils_ts_1.aInRange)('scalar', scalar, minScalar, maxScalar);
|
||||||
|
const k = scalar;
|
||||||
|
const x_1 = u;
|
||||||
|
let x_2 = _1n;
|
||||||
|
let z_2 = _0n;
|
||||||
|
let x_3 = u;
|
||||||
|
let z_3 = _1n;
|
||||||
|
let swap = _0n;
|
||||||
|
for (let t = BigInt(montgomeryBits - 1); t >= _0n; t--) {
|
||||||
|
const k_t = (k >> t) & _1n;
|
||||||
|
swap ^= k_t;
|
||||||
|
({ x_2, x_3 } = cswap(swap, x_2, x_3));
|
||||||
|
({ x_2: z_2, x_3: z_3 } = cswap(swap, z_2, z_3));
|
||||||
|
swap = k_t;
|
||||||
|
const A = x_2 + z_2;
|
||||||
|
const AA = modP(A * A);
|
||||||
|
const B = x_2 - z_2;
|
||||||
|
const BB = modP(B * B);
|
||||||
|
const E = AA - BB;
|
||||||
|
const C = x_3 + z_3;
|
||||||
|
const D = x_3 - z_3;
|
||||||
|
const DA = modP(D * A);
|
||||||
|
const CB = modP(C * B);
|
||||||
|
const dacb = DA + CB;
|
||||||
|
const da_cb = DA - CB;
|
||||||
|
x_3 = modP(dacb * dacb);
|
||||||
|
z_3 = modP(x_1 * modP(da_cb * da_cb));
|
||||||
|
x_2 = modP(AA * BB);
|
||||||
|
z_2 = modP(E * (AA + modP(a24 * E)));
|
||||||
|
}
|
||||||
|
({ x_2, x_3 } = cswap(swap, x_2, x_3));
|
||||||
|
({ x_2: z_2, x_3: z_3 } = cswap(swap, z_2, z_3));
|
||||||
|
const z2 = powPminus2(z_2); // `Fp.pow(x, P - _2n)` is much slower equivalent
|
||||||
|
return modP(x_2 * z2); // Return x_2 * (z_2^(p - 2))
|
||||||
|
}
|
||||||
|
const lengths = {
|
||||||
|
secretKey: fieldLen,
|
||||||
|
publicKey: fieldLen,
|
||||||
|
seed: fieldLen,
|
||||||
|
};
|
||||||
|
const randomSecretKey = (seed = randomBytes_(fieldLen)) => {
|
||||||
|
(0, utils_ts_1.abytes)(seed, lengths.seed);
|
||||||
|
return seed;
|
||||||
|
};
|
||||||
|
function keygen(seed) {
|
||||||
|
const secretKey = randomSecretKey(seed);
|
||||||
|
return { secretKey, publicKey: scalarMultBase(secretKey) };
|
||||||
|
}
|
||||||
|
const utils = {
|
||||||
|
randomSecretKey,
|
||||||
|
randomPrivateKey: randomSecretKey,
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
keygen,
|
||||||
|
getSharedSecret: (secretKey, publicKey) => scalarMult(secretKey, publicKey),
|
||||||
|
getPublicKey: (secretKey) => scalarMultBase(secretKey),
|
||||||
|
scalarMult,
|
||||||
|
scalarMultBase,
|
||||||
|
utils,
|
||||||
|
GuBytes: GuBytes.slice(),
|
||||||
|
lengths,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=montgomery.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+68
@@ -0,0 +1,68 @@
|
|||||||
|
import { type IField } from './modular.ts';
|
||||||
|
export type PoseidonBasicOpts = {
|
||||||
|
Fp: IField<bigint>;
|
||||||
|
t: number;
|
||||||
|
roundsFull: number;
|
||||||
|
roundsPartial: number;
|
||||||
|
isSboxInverse?: boolean;
|
||||||
|
};
|
||||||
|
export type PoseidonGrainOpts = PoseidonBasicOpts & {
|
||||||
|
sboxPower?: number;
|
||||||
|
};
|
||||||
|
type PoseidonConstants = {
|
||||||
|
mds: bigint[][];
|
||||||
|
roundConstants: bigint[][];
|
||||||
|
};
|
||||||
|
export declare function grainGenConstants(opts: PoseidonGrainOpts, skipMDS?: number): PoseidonConstants;
|
||||||
|
export type PoseidonOpts = PoseidonBasicOpts & PoseidonConstants & {
|
||||||
|
sboxPower?: number;
|
||||||
|
reversePartialPowIdx?: boolean;
|
||||||
|
};
|
||||||
|
export declare function validateOpts(opts: PoseidonOpts): Readonly<{
|
||||||
|
rounds: number;
|
||||||
|
sboxFn: (n: bigint) => bigint;
|
||||||
|
roundConstants: bigint[][];
|
||||||
|
mds: bigint[][];
|
||||||
|
Fp: IField<bigint>;
|
||||||
|
t: number;
|
||||||
|
roundsFull: number;
|
||||||
|
roundsPartial: number;
|
||||||
|
sboxPower?: number;
|
||||||
|
reversePartialPowIdx?: boolean;
|
||||||
|
}>;
|
||||||
|
export declare function splitConstants(rc: bigint[], t: number): bigint[][];
|
||||||
|
export type PoseidonFn = {
|
||||||
|
(values: bigint[]): bigint[];
|
||||||
|
roundConstants: bigint[][];
|
||||||
|
};
|
||||||
|
/** Poseidon NTT-friendly hash. */
|
||||||
|
export declare function poseidon(opts: PoseidonOpts): PoseidonFn;
|
||||||
|
export declare class PoseidonSponge {
|
||||||
|
private Fp;
|
||||||
|
readonly rate: number;
|
||||||
|
readonly capacity: number;
|
||||||
|
readonly hash: PoseidonFn;
|
||||||
|
private state;
|
||||||
|
private pos;
|
||||||
|
private isAbsorbing;
|
||||||
|
constructor(Fp: IField<bigint>, rate: number, capacity: number, hash: PoseidonFn);
|
||||||
|
private process;
|
||||||
|
absorb(input: bigint[]): void;
|
||||||
|
squeeze(count: number): bigint[];
|
||||||
|
clean(): void;
|
||||||
|
clone(): PoseidonSponge;
|
||||||
|
}
|
||||||
|
export type PoseidonSpongeOpts = Omit<PoseidonOpts, 't'> & {
|
||||||
|
rate: number;
|
||||||
|
capacity: number;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The method is not defined in spec, but nevertheless used often.
|
||||||
|
* Check carefully for compatibility: there are many edge cases, like absorbing an empty array.
|
||||||
|
* We cross-test against:
|
||||||
|
* - https://github.com/ProvableHQ/snarkVM/tree/staging/algorithms
|
||||||
|
* - https://github.com/arkworks-rs/crypto-primitives/tree/main
|
||||||
|
*/
|
||||||
|
export declare function poseidonSponge(opts: PoseidonSpongeOpts): () => PoseidonSponge;
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=poseidon.d.ts.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"poseidon.d.ts","sourceRoot":"","sources":["../src/abstract/poseidon.ts"],"names":[],"mappings":"AAUA,OAAO,EAAwB,KAAK,MAAM,EAAiB,MAAM,cAAc,CAAC;AAyBhF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACnB,CAAC,EAAE,MAAM,CAAC;IACV,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AA0DF,MAAM,MAAM,iBAAiB,GAAG,iBAAiB,GAAG;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,KAAK,iBAAiB,GAAG;IAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC;IAAC,cAAc,EAAE,MAAM,EAAE,EAAE,CAAA;CAAE,CAAC;AAIzE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,GAAE,MAAU,GAAG,iBAAiB,CAuBjG;AAED,MAAM,MAAM,YAAY,GAAG,iBAAiB,GAC1C,iBAAiB,GAAG;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEJ,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,CAAC;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,cAAc,EAAE,MAAM,EAAE,EAAE,CAAC;IAC3B,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACnB,CAAC,EAAE,MAAM,CAAC;IACV,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC,CAwCD;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CAalE;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IAE7B,cAAc,EAAE,MAAM,EAAE,EAAE,CAAC;CAC5B,CAAC;AACF,kCAAkC;AAClC,wBAAgB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,UAAU,CAmCvD;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,EAAE,CAAiB;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,WAAW,CAAQ;gBAEf,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU;IAQhF,OAAO,CAAC,OAAO;IAGf,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAgB7B,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAahC,KAAK,IAAI,IAAI;IAKb,KAAK,IAAI,cAAc;CAMxB;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,YAAY,EAAE,GAAG,CAAC,GAAG;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,cAAc,CAW7E"}
|
||||||
+305
@@ -0,0 +1,305 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.PoseidonSponge = void 0;
|
||||||
|
exports.grainGenConstants = grainGenConstants;
|
||||||
|
exports.validateOpts = validateOpts;
|
||||||
|
exports.splitConstants = splitConstants;
|
||||||
|
exports.poseidon = poseidon;
|
||||||
|
exports.poseidonSponge = poseidonSponge;
|
||||||
|
/**
|
||||||
|
* Implements [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash.
|
||||||
|
*
|
||||||
|
* There are many poseidon variants with different constants.
|
||||||
|
* We don't provide them: you should construct them manually.
|
||||||
|
* Check out [micro-starknet](https://github.com/paulmillr/micro-starknet) package for a proper example.
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
const utils_ts_1 = require("../utils.js");
|
||||||
|
const modular_ts_1 = require("./modular.js");
|
||||||
|
// Grain LFSR (Linear-Feedback Shift Register): https://eprint.iacr.org/2009/109.pdf
|
||||||
|
function grainLFSR(state) {
|
||||||
|
let pos = 0;
|
||||||
|
if (state.length !== 80)
|
||||||
|
throw new Error('grainLFRS: wrong state length, should be 80 bits');
|
||||||
|
const getBit = () => {
|
||||||
|
const r = (offset) => state[(pos + offset) % 80];
|
||||||
|
const bit = r(62) ^ r(51) ^ r(38) ^ r(23) ^ r(13) ^ r(0);
|
||||||
|
state[pos] = bit;
|
||||||
|
pos = ++pos % 80;
|
||||||
|
return !!bit;
|
||||||
|
};
|
||||||
|
for (let i = 0; i < 160; i++)
|
||||||
|
getBit();
|
||||||
|
return () => {
|
||||||
|
// https://en.wikipedia.org/wiki/Shrinking_generator
|
||||||
|
while (true) {
|
||||||
|
const b1 = getBit();
|
||||||
|
const b2 = getBit();
|
||||||
|
if (!b1)
|
||||||
|
continue;
|
||||||
|
return b2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function assertValidPosOpts(opts) {
|
||||||
|
const { Fp, roundsFull } = opts;
|
||||||
|
(0, modular_ts_1.validateField)(Fp);
|
||||||
|
(0, utils_ts_1._validateObject)(opts, {
|
||||||
|
t: 'number',
|
||||||
|
roundsFull: 'number',
|
||||||
|
roundsPartial: 'number',
|
||||||
|
}, {
|
||||||
|
isSboxInverse: 'boolean',
|
||||||
|
});
|
||||||
|
for (const i of ['t', 'roundsFull', 'roundsPartial']) {
|
||||||
|
if (!Number.isSafeInteger(opts[i]) || opts[i] < 1)
|
||||||
|
throw new Error('invalid number ' + i);
|
||||||
|
}
|
||||||
|
if (roundsFull & 1)
|
||||||
|
throw new Error('roundsFull is not even' + roundsFull);
|
||||||
|
}
|
||||||
|
function poseidonGrain(opts) {
|
||||||
|
assertValidPosOpts(opts);
|
||||||
|
const { Fp } = opts;
|
||||||
|
const state = Array(80).fill(1);
|
||||||
|
let pos = 0;
|
||||||
|
const writeBits = (value, bitCount) => {
|
||||||
|
for (let i = bitCount - 1; i >= 0; i--)
|
||||||
|
state[pos++] = Number((0, utils_ts_1.bitGet)(value, i));
|
||||||
|
};
|
||||||
|
const _0n = BigInt(0);
|
||||||
|
const _1n = BigInt(1);
|
||||||
|
writeBits(_1n, 2); // prime field
|
||||||
|
writeBits(opts.isSboxInverse ? _1n : _0n, 4); // b2..b5
|
||||||
|
writeBits(BigInt(Fp.BITS), 12); // b6..b17
|
||||||
|
writeBits(BigInt(opts.t), 12); // b18..b29
|
||||||
|
writeBits(BigInt(opts.roundsFull), 10); // b30..b39
|
||||||
|
writeBits(BigInt(opts.roundsPartial), 10); // b40..b49
|
||||||
|
const getBit = grainLFSR(state);
|
||||||
|
return (count, reject) => {
|
||||||
|
const res = [];
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
while (true) {
|
||||||
|
let num = _0n;
|
||||||
|
for (let i = 0; i < Fp.BITS; i++) {
|
||||||
|
num <<= _1n;
|
||||||
|
if (getBit())
|
||||||
|
num |= _1n;
|
||||||
|
}
|
||||||
|
if (reject && num >= Fp.ORDER)
|
||||||
|
continue; // rejection sampling
|
||||||
|
res.push(Fp.create(num));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// NOTE: this is not standard but used often for constant generation for poseidon
|
||||||
|
// (grain LFRS-like structure)
|
||||||
|
function grainGenConstants(opts, skipMDS = 0) {
|
||||||
|
const { Fp, t, roundsFull, roundsPartial } = opts;
|
||||||
|
const rounds = roundsFull + roundsPartial;
|
||||||
|
const sample = poseidonGrain(opts);
|
||||||
|
const roundConstants = [];
|
||||||
|
for (let r = 0; r < rounds; r++)
|
||||||
|
roundConstants.push(sample(t, true));
|
||||||
|
if (skipMDS > 0)
|
||||||
|
for (let i = 0; i < skipMDS; i++)
|
||||||
|
sample(2 * t, false);
|
||||||
|
const xs = sample(t, false);
|
||||||
|
const ys = sample(t, false);
|
||||||
|
// Construct MDS Matrix M[i][j] = 1 / (xs[i] + ys[j])
|
||||||
|
const mds = [];
|
||||||
|
for (let i = 0; i < t; i++) {
|
||||||
|
const row = [];
|
||||||
|
for (let j = 0; j < t; j++) {
|
||||||
|
const xy = Fp.add(xs[i], ys[j]);
|
||||||
|
if (Fp.is0(xy))
|
||||||
|
throw new Error(`Error generating MDS matrix: xs[${i}] + ys[${j}] resulted in zero.`);
|
||||||
|
row.push(xy);
|
||||||
|
}
|
||||||
|
mds.push((0, modular_ts_1.FpInvertBatch)(Fp, row));
|
||||||
|
}
|
||||||
|
return { roundConstants, mds };
|
||||||
|
}
|
||||||
|
function validateOpts(opts) {
|
||||||
|
assertValidPosOpts(opts);
|
||||||
|
const { Fp, mds, reversePartialPowIdx: rev, roundConstants: rc } = opts;
|
||||||
|
const { roundsFull, roundsPartial, sboxPower, t } = opts;
|
||||||
|
// MDS is TxT matrix
|
||||||
|
if (!Array.isArray(mds) || mds.length !== t)
|
||||||
|
throw new Error('Poseidon: invalid MDS matrix');
|
||||||
|
const _mds = mds.map((mdsRow) => {
|
||||||
|
if (!Array.isArray(mdsRow) || mdsRow.length !== t)
|
||||||
|
throw new Error('invalid MDS matrix row: ' + mdsRow);
|
||||||
|
return mdsRow.map((i) => {
|
||||||
|
if (typeof i !== 'bigint')
|
||||||
|
throw new Error('invalid MDS matrix bigint: ' + i);
|
||||||
|
return Fp.create(i);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (rev !== undefined && typeof rev !== 'boolean')
|
||||||
|
throw new Error('invalid param reversePartialPowIdx=' + rev);
|
||||||
|
if (roundsFull & 1)
|
||||||
|
throw new Error('roundsFull is not even' + roundsFull);
|
||||||
|
const rounds = roundsFull + roundsPartial;
|
||||||
|
if (!Array.isArray(rc) || rc.length !== rounds)
|
||||||
|
throw new Error('Poseidon: invalid round constants');
|
||||||
|
const roundConstants = rc.map((rc) => {
|
||||||
|
if (!Array.isArray(rc) || rc.length !== t)
|
||||||
|
throw new Error('invalid round constants');
|
||||||
|
return rc.map((i) => {
|
||||||
|
if (typeof i !== 'bigint' || !Fp.isValid(i))
|
||||||
|
throw new Error('invalid round constant');
|
||||||
|
return Fp.create(i);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (!sboxPower || ![3, 5, 7, 17].includes(sboxPower))
|
||||||
|
throw new Error('invalid sboxPower');
|
||||||
|
const _sboxPower = BigInt(sboxPower);
|
||||||
|
let sboxFn = (n) => (0, modular_ts_1.FpPow)(Fp, n, _sboxPower);
|
||||||
|
// Unwrapped sbox power for common cases (195->142μs)
|
||||||
|
if (sboxPower === 3)
|
||||||
|
sboxFn = (n) => Fp.mul(Fp.sqrN(n), n);
|
||||||
|
else if (sboxPower === 5)
|
||||||
|
sboxFn = (n) => Fp.mul(Fp.sqrN(Fp.sqrN(n)), n);
|
||||||
|
return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds: _mds });
|
||||||
|
}
|
||||||
|
function splitConstants(rc, t) {
|
||||||
|
if (typeof t !== 'number')
|
||||||
|
throw new Error('poseidonSplitConstants: invalid t');
|
||||||
|
if (!Array.isArray(rc) || rc.length % t)
|
||||||
|
throw new Error('poseidonSplitConstants: invalid rc');
|
||||||
|
const res = [];
|
||||||
|
let tmp = [];
|
||||||
|
for (let i = 0; i < rc.length; i++) {
|
||||||
|
tmp.push(rc[i]);
|
||||||
|
if (tmp.length === t) {
|
||||||
|
res.push(tmp);
|
||||||
|
tmp = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/** Poseidon NTT-friendly hash. */
|
||||||
|
function poseidon(opts) {
|
||||||
|
const _opts = validateOpts(opts);
|
||||||
|
const { Fp, mds, roundConstants, rounds: totalRounds, roundsPartial, sboxFn, t } = _opts;
|
||||||
|
const halfRoundsFull = _opts.roundsFull / 2;
|
||||||
|
const partialIdx = _opts.reversePartialPowIdx ? t - 1 : 0;
|
||||||
|
const poseidonRound = (values, isFull, idx) => {
|
||||||
|
values = values.map((i, j) => Fp.add(i, roundConstants[idx][j]));
|
||||||
|
if (isFull)
|
||||||
|
values = values.map((i) => sboxFn(i));
|
||||||
|
else
|
||||||
|
values[partialIdx] = sboxFn(values[partialIdx]);
|
||||||
|
// Matrix multiplication
|
||||||
|
values = mds.map((i) => i.reduce((acc, i, j) => Fp.add(acc, Fp.mulN(i, values[j])), Fp.ZERO));
|
||||||
|
return values;
|
||||||
|
};
|
||||||
|
const poseidonHash = function poseidonHash(values) {
|
||||||
|
if (!Array.isArray(values) || values.length !== t)
|
||||||
|
throw new Error('invalid values, expected array of bigints with length ' + t);
|
||||||
|
values = values.map((i) => {
|
||||||
|
if (typeof i !== 'bigint')
|
||||||
|
throw new Error('invalid bigint=' + i);
|
||||||
|
return Fp.create(i);
|
||||||
|
});
|
||||||
|
let lastRound = 0;
|
||||||
|
// Apply r_f/2 full rounds.
|
||||||
|
for (let i = 0; i < halfRoundsFull; i++)
|
||||||
|
values = poseidonRound(values, true, lastRound++);
|
||||||
|
// Apply r_p partial rounds.
|
||||||
|
for (let i = 0; i < roundsPartial; i++)
|
||||||
|
values = poseidonRound(values, false, lastRound++);
|
||||||
|
// Apply r_f/2 full rounds.
|
||||||
|
for (let i = 0; i < halfRoundsFull; i++)
|
||||||
|
values = poseidonRound(values, true, lastRound++);
|
||||||
|
if (lastRound !== totalRounds)
|
||||||
|
throw new Error('invalid number of rounds');
|
||||||
|
return values;
|
||||||
|
};
|
||||||
|
// For verification in tests
|
||||||
|
poseidonHash.roundConstants = roundConstants;
|
||||||
|
return poseidonHash;
|
||||||
|
}
|
||||||
|
class PoseidonSponge {
|
||||||
|
constructor(Fp, rate, capacity, hash) {
|
||||||
|
this.pos = 0;
|
||||||
|
this.isAbsorbing = true;
|
||||||
|
this.Fp = Fp;
|
||||||
|
this.hash = hash;
|
||||||
|
this.rate = rate;
|
||||||
|
this.capacity = capacity;
|
||||||
|
this.state = new Array(rate + capacity);
|
||||||
|
this.clean();
|
||||||
|
}
|
||||||
|
process() {
|
||||||
|
this.state = this.hash(this.state);
|
||||||
|
}
|
||||||
|
absorb(input) {
|
||||||
|
for (const i of input)
|
||||||
|
if (typeof i !== 'bigint' || !this.Fp.isValid(i))
|
||||||
|
throw new Error('invalid input: ' + i);
|
||||||
|
for (let i = 0; i < input.length;) {
|
||||||
|
if (!this.isAbsorbing || this.pos === this.rate) {
|
||||||
|
this.process();
|
||||||
|
this.pos = 0;
|
||||||
|
this.isAbsorbing = true;
|
||||||
|
}
|
||||||
|
const chunk = Math.min(this.rate - this.pos, input.length - i);
|
||||||
|
for (let j = 0; j < chunk; j++) {
|
||||||
|
const idx = this.capacity + this.pos++;
|
||||||
|
this.state[idx] = this.Fp.add(this.state[idx], input[i++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
squeeze(count) {
|
||||||
|
const res = [];
|
||||||
|
while (res.length < count) {
|
||||||
|
if (this.isAbsorbing || this.pos === this.rate) {
|
||||||
|
this.process();
|
||||||
|
this.pos = 0;
|
||||||
|
this.isAbsorbing = false;
|
||||||
|
}
|
||||||
|
const chunk = Math.min(this.rate - this.pos, count - res.length);
|
||||||
|
for (let i = 0; i < chunk; i++)
|
||||||
|
res.push(this.state[this.capacity + this.pos++]);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
clean() {
|
||||||
|
this.state.fill(this.Fp.ZERO);
|
||||||
|
this.isAbsorbing = true;
|
||||||
|
this.pos = 0;
|
||||||
|
}
|
||||||
|
clone() {
|
||||||
|
const c = new PoseidonSponge(this.Fp, this.rate, this.capacity, this.hash);
|
||||||
|
c.pos = this.pos;
|
||||||
|
c.state = [...this.state];
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.PoseidonSponge = PoseidonSponge;
|
||||||
|
/**
|
||||||
|
* The method is not defined in spec, but nevertheless used often.
|
||||||
|
* Check carefully for compatibility: there are many edge cases, like absorbing an empty array.
|
||||||
|
* We cross-test against:
|
||||||
|
* - https://github.com/ProvableHQ/snarkVM/tree/staging/algorithms
|
||||||
|
* - https://github.com/arkworks-rs/crypto-primitives/tree/main
|
||||||
|
*/
|
||||||
|
function poseidonSponge(opts) {
|
||||||
|
for (const i of ['rate', 'capacity']) {
|
||||||
|
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
|
||||||
|
throw new Error('invalid number ' + i);
|
||||||
|
}
|
||||||
|
const { rate, capacity } = opts;
|
||||||
|
const t = opts.rate + opts.capacity;
|
||||||
|
// Re-use hash instance between multiple instances
|
||||||
|
const hash = poseidon({ ...opts, t });
|
||||||
|
const { Fp } = opts;
|
||||||
|
return () => new PoseidonSponge(Fp, rate, capacity, hash);
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=poseidon.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+95
@@ -0,0 +1,95 @@
|
|||||||
|
import * as mod from './modular.ts';
|
||||||
|
import type { WeierstrassPoint, WeierstrassPointCons } from './weierstrass.ts';
|
||||||
|
export type BigintTuple = [bigint, bigint];
|
||||||
|
export type Fp = bigint;
|
||||||
|
export type Fp2 = {
|
||||||
|
c0: bigint;
|
||||||
|
c1: bigint;
|
||||||
|
};
|
||||||
|
export type BigintSix = [bigint, bigint, bigint, bigint, bigint, bigint];
|
||||||
|
export type Fp6 = {
|
||||||
|
c0: Fp2;
|
||||||
|
c1: Fp2;
|
||||||
|
c2: Fp2;
|
||||||
|
};
|
||||||
|
export type Fp12 = {
|
||||||
|
c0: Fp6;
|
||||||
|
c1: Fp6;
|
||||||
|
};
|
||||||
|
export type BigintTwelve = [
|
||||||
|
bigint,
|
||||||
|
bigint,
|
||||||
|
bigint,
|
||||||
|
bigint,
|
||||||
|
bigint,
|
||||||
|
bigint,
|
||||||
|
bigint,
|
||||||
|
bigint,
|
||||||
|
bigint,
|
||||||
|
bigint,
|
||||||
|
bigint,
|
||||||
|
bigint
|
||||||
|
];
|
||||||
|
export type Fp2Bls = mod.IField<Fp2> & {
|
||||||
|
Fp: mod.IField<Fp>;
|
||||||
|
frobeniusMap(num: Fp2, power: number): Fp2;
|
||||||
|
fromBigTuple(num: BigintTuple): Fp2;
|
||||||
|
mulByB: (num: Fp2) => Fp2;
|
||||||
|
mulByNonresidue: (num: Fp2) => Fp2;
|
||||||
|
reim: (num: Fp2) => {
|
||||||
|
re: Fp;
|
||||||
|
im: Fp;
|
||||||
|
};
|
||||||
|
Fp4Square: (a: Fp2, b: Fp2) => {
|
||||||
|
first: Fp2;
|
||||||
|
second: Fp2;
|
||||||
|
};
|
||||||
|
NONRESIDUE: Fp2;
|
||||||
|
};
|
||||||
|
export type Fp6Bls = mod.IField<Fp6> & {
|
||||||
|
Fp2: Fp2Bls;
|
||||||
|
frobeniusMap(num: Fp6, power: number): Fp6;
|
||||||
|
fromBigSix: (tuple: BigintSix) => Fp6;
|
||||||
|
mul1(num: Fp6, b1: Fp2): Fp6;
|
||||||
|
mul01(num: Fp6, b0: Fp2, b1: Fp2): Fp6;
|
||||||
|
mulByFp2(lhs: Fp6, rhs: Fp2): Fp6;
|
||||||
|
mulByNonresidue: (num: Fp6) => Fp6;
|
||||||
|
};
|
||||||
|
export type Fp12Bls = mod.IField<Fp12> & {
|
||||||
|
Fp6: Fp6Bls;
|
||||||
|
frobeniusMap(num: Fp12, power: number): Fp12;
|
||||||
|
fromBigTwelve: (t: BigintTwelve) => Fp12;
|
||||||
|
mul014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
||||||
|
mul034(num: Fp12, o0: Fp2, o3: Fp2, o4: Fp2): Fp12;
|
||||||
|
mulByFp2(lhs: Fp12, rhs: Fp2): Fp12;
|
||||||
|
conjugate(num: Fp12): Fp12;
|
||||||
|
finalExponentiate(num: Fp12): Fp12;
|
||||||
|
_cyclotomicSquare(num: Fp12): Fp12;
|
||||||
|
_cyclotomicExp(num: Fp12, n: bigint): Fp12;
|
||||||
|
};
|
||||||
|
export declare function psiFrobenius(Fp: mod.IField<Fp>, Fp2: Fp2Bls, base: Fp2): {
|
||||||
|
psi: (x: Fp2, y: Fp2) => [Fp2, Fp2];
|
||||||
|
psi2: (x: Fp2, y: Fp2) => [Fp2, Fp2];
|
||||||
|
G2psi: (c: WeierstrassPointCons<Fp2>, P: WeierstrassPoint<Fp2>) => WeierstrassPoint<Fp2>;
|
||||||
|
G2psi2: (c: WeierstrassPointCons<Fp2>, P: WeierstrassPoint<Fp2>) => WeierstrassPoint<Fp2>;
|
||||||
|
PSI_X: Fp2;
|
||||||
|
PSI_Y: Fp2;
|
||||||
|
PSI2_X: Fp2;
|
||||||
|
PSI2_Y: Fp2;
|
||||||
|
};
|
||||||
|
export type Tower12Opts = {
|
||||||
|
ORDER: bigint;
|
||||||
|
X_LEN: number;
|
||||||
|
NONRESIDUE?: Fp;
|
||||||
|
FP2_NONRESIDUE: BigintTuple;
|
||||||
|
Fp2sqrt?: (num: Fp2) => Fp2;
|
||||||
|
Fp2mulByB: (num: Fp2) => Fp2;
|
||||||
|
Fp12finalExponentiate: (num: Fp12) => Fp12;
|
||||||
|
};
|
||||||
|
export declare function tower12(opts: Tower12Opts): {
|
||||||
|
Fp: Readonly<mod.IField<bigint> & Required<Pick<mod.IField<bigint>, 'isOdd'>>>;
|
||||||
|
Fp2: Fp2Bls;
|
||||||
|
Fp6: Fp6Bls;
|
||||||
|
Fp12: Fp12Bls;
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=tower.d.ts.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"tower.d.ts","sourceRoot":"","sources":["../src/abstract/tower.ts"],"names":[],"mappings":"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"}
|
||||||
+718
@@ -0,0 +1,718 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.psiFrobenius = psiFrobenius;
|
||||||
|
exports.tower12 = tower12;
|
||||||
|
/**
|
||||||
|
* Towered extension fields.
|
||||||
|
* Rather than implementing a massive 12th-degree extension directly, it is more efficient
|
||||||
|
* to build it up from smaller extensions: a tower of extensions.
|
||||||
|
*
|
||||||
|
* For BLS12-381, the Fp12 field is implemented as a quadratic (degree two) extension,
|
||||||
|
* on top of a cubic (degree three) extension, on top of a quadratic extension of Fp.
|
||||||
|
*
|
||||||
|
* For more info: "Pairings for beginners" by Costello, section 7.3.
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
const utils_ts_1 = require("../utils.js");
|
||||||
|
const mod = require("./modular.js");
|
||||||
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
||||||
|
// prettier-ignore
|
||||||
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
||||||
|
function calcFrobeniusCoefficients(Fp, nonResidue, modulus, degree, num = 1, divisor) {
|
||||||
|
const _divisor = BigInt(divisor === undefined ? degree : divisor);
|
||||||
|
const towerModulus = modulus ** BigInt(degree);
|
||||||
|
const res = [];
|
||||||
|
for (let i = 0; i < num; i++) {
|
||||||
|
const a = BigInt(i + 1);
|
||||||
|
const powers = [];
|
||||||
|
for (let j = 0, qPower = _1n; j < degree; j++) {
|
||||||
|
const power = ((a * qPower - a) / _divisor) % towerModulus;
|
||||||
|
powers.push(Fp.pow(nonResidue, power));
|
||||||
|
qPower *= modulus;
|
||||||
|
}
|
||||||
|
res.push(powers);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// This works same at least for bls12-381, bn254 and bls12-377
|
||||||
|
function psiFrobenius(Fp, Fp2, base) {
|
||||||
|
// GLV endomorphism Ψ(P)
|
||||||
|
const PSI_X = Fp2.pow(base, (Fp.ORDER - _1n) / _3n); // u^((p-1)/3)
|
||||||
|
const PSI_Y = Fp2.pow(base, (Fp.ORDER - _1n) / _2n); // u^((p-1)/2)
|
||||||
|
function psi(x, y) {
|
||||||
|
// This x10 faster than previous version in bls12-381
|
||||||
|
const x2 = Fp2.mul(Fp2.frobeniusMap(x, 1), PSI_X);
|
||||||
|
const y2 = Fp2.mul(Fp2.frobeniusMap(y, 1), PSI_Y);
|
||||||
|
return [x2, y2];
|
||||||
|
}
|
||||||
|
// Ψ²(P) endomorphism (psi2(x) = psi(psi(x)))
|
||||||
|
const PSI2_X = Fp2.pow(base, (Fp.ORDER ** _2n - _1n) / _3n); // u^((p^2 - 1)/3)
|
||||||
|
// This equals -1, which causes y to be Fp2.neg(y).
|
||||||
|
// But not sure if there are case when this is not true?
|
||||||
|
const PSI2_Y = Fp2.pow(base, (Fp.ORDER ** _2n - _1n) / _2n); // u^((p^2 - 1)/3)
|
||||||
|
if (!Fp2.eql(PSI2_Y, Fp2.neg(Fp2.ONE)))
|
||||||
|
throw new Error('psiFrobenius: PSI2_Y!==-1');
|
||||||
|
function psi2(x, y) {
|
||||||
|
return [Fp2.mul(x, PSI2_X), Fp2.neg(y)];
|
||||||
|
}
|
||||||
|
// Map points
|
||||||
|
const mapAffine = (fn) => (c, P) => {
|
||||||
|
const affine = P.toAffine();
|
||||||
|
const p = fn(affine.x, affine.y);
|
||||||
|
return c.fromAffine({ x: p[0], y: p[1] });
|
||||||
|
};
|
||||||
|
const G2psi = mapAffine(psi);
|
||||||
|
const G2psi2 = mapAffine(psi2);
|
||||||
|
return { psi, psi2, G2psi, G2psi2, PSI_X, PSI_Y, PSI2_X, PSI2_Y };
|
||||||
|
}
|
||||||
|
const Fp2fromBigTuple = (Fp, tuple) => {
|
||||||
|
if (tuple.length !== 2)
|
||||||
|
throw new Error('invalid tuple');
|
||||||
|
const fps = tuple.map((n) => Fp.create(n));
|
||||||
|
return { c0: fps[0], c1: fps[1] };
|
||||||
|
};
|
||||||
|
class _Field2 {
|
||||||
|
constructor(Fp, opts = {}) {
|
||||||
|
this.MASK = _1n;
|
||||||
|
const ORDER = Fp.ORDER;
|
||||||
|
const FP2_ORDER = ORDER * ORDER;
|
||||||
|
this.Fp = Fp;
|
||||||
|
this.ORDER = FP2_ORDER;
|
||||||
|
this.BITS = (0, utils_ts_1.bitLen)(FP2_ORDER);
|
||||||
|
this.BYTES = Math.ceil((0, utils_ts_1.bitLen)(FP2_ORDER) / 8);
|
||||||
|
this.isLE = Fp.isLE;
|
||||||
|
this.ZERO = { c0: Fp.ZERO, c1: Fp.ZERO };
|
||||||
|
this.ONE = { c0: Fp.ONE, c1: Fp.ZERO };
|
||||||
|
this.Fp_NONRESIDUE = Fp.create(opts.NONRESIDUE || BigInt(-1));
|
||||||
|
this.Fp_div2 = Fp.div(Fp.ONE, _2n); // 1/2
|
||||||
|
this.NONRESIDUE = Fp2fromBigTuple(Fp, opts.FP2_NONRESIDUE);
|
||||||
|
// const Fp2Nonresidue = Fp2fromBigTuple(opts.FP2_NONRESIDUE);
|
||||||
|
this.FROBENIUS_COEFFICIENTS = calcFrobeniusCoefficients(Fp, this.Fp_NONRESIDUE, Fp.ORDER, 2)[0];
|
||||||
|
this.mulByB = opts.Fp2mulByB;
|
||||||
|
Object.seal(this);
|
||||||
|
}
|
||||||
|
fromBigTuple(tuple) {
|
||||||
|
return Fp2fromBigTuple(this.Fp, tuple);
|
||||||
|
}
|
||||||
|
create(num) {
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
isValid({ c0, c1 }) {
|
||||||
|
function isValidC(num, ORDER) {
|
||||||
|
return typeof num === 'bigint' && _0n <= num && num < ORDER;
|
||||||
|
}
|
||||||
|
return isValidC(c0, this.ORDER) && isValidC(c1, this.ORDER);
|
||||||
|
}
|
||||||
|
is0({ c0, c1 }) {
|
||||||
|
return this.Fp.is0(c0) && this.Fp.is0(c1);
|
||||||
|
}
|
||||||
|
isValidNot0(num) {
|
||||||
|
return !this.is0(num) && this.isValid(num);
|
||||||
|
}
|
||||||
|
eql({ c0, c1 }, { c0: r0, c1: r1 }) {
|
||||||
|
return this.Fp.eql(c0, r0) && this.Fp.eql(c1, r1);
|
||||||
|
}
|
||||||
|
neg({ c0, c1 }) {
|
||||||
|
return { c0: this.Fp.neg(c0), c1: this.Fp.neg(c1) };
|
||||||
|
}
|
||||||
|
pow(num, power) {
|
||||||
|
return mod.FpPow(this, num, power);
|
||||||
|
}
|
||||||
|
invertBatch(nums) {
|
||||||
|
return mod.FpInvertBatch(this, nums);
|
||||||
|
}
|
||||||
|
// Normalized
|
||||||
|
add(f1, f2) {
|
||||||
|
const { c0, c1 } = f1;
|
||||||
|
const { c0: r0, c1: r1 } = f2;
|
||||||
|
return {
|
||||||
|
c0: this.Fp.add(c0, r0),
|
||||||
|
c1: this.Fp.add(c1, r1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
sub({ c0, c1 }, { c0: r0, c1: r1 }) {
|
||||||
|
return {
|
||||||
|
c0: this.Fp.sub(c0, r0),
|
||||||
|
c1: this.Fp.sub(c1, r1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
mul({ c0, c1 }, rhs) {
|
||||||
|
const { Fp } = this;
|
||||||
|
if (typeof rhs === 'bigint')
|
||||||
|
return { c0: Fp.mul(c0, rhs), c1: Fp.mul(c1, rhs) };
|
||||||
|
// (a+bi)(c+di) = (ac−bd) + (ad+bc)i
|
||||||
|
const { c0: r0, c1: r1 } = rhs;
|
||||||
|
let t1 = Fp.mul(c0, r0); // c0 * o0
|
||||||
|
let t2 = Fp.mul(c1, r1); // c1 * o1
|
||||||
|
// (T1 - T2) + ((c0 + c1) * (r0 + r1) - (T1 + T2))*i
|
||||||
|
const o0 = Fp.sub(t1, t2);
|
||||||
|
const o1 = Fp.sub(Fp.mul(Fp.add(c0, c1), Fp.add(r0, r1)), Fp.add(t1, t2));
|
||||||
|
return { c0: o0, c1: o1 };
|
||||||
|
}
|
||||||
|
sqr({ c0, c1 }) {
|
||||||
|
const { Fp } = this;
|
||||||
|
const a = Fp.add(c0, c1);
|
||||||
|
const b = Fp.sub(c0, c1);
|
||||||
|
const c = Fp.add(c0, c0);
|
||||||
|
return { c0: Fp.mul(a, b), c1: Fp.mul(c, c1) };
|
||||||
|
}
|
||||||
|
// NonNormalized stuff
|
||||||
|
addN(a, b) {
|
||||||
|
return this.add(a, b);
|
||||||
|
}
|
||||||
|
subN(a, b) {
|
||||||
|
return this.sub(a, b);
|
||||||
|
}
|
||||||
|
mulN(a, b) {
|
||||||
|
return this.mul(a, b);
|
||||||
|
}
|
||||||
|
sqrN(a) {
|
||||||
|
return this.sqr(a);
|
||||||
|
}
|
||||||
|
// Why inversion for bigint inside Fp instead of Fp2? it is even used in that context?
|
||||||
|
div(lhs, rhs) {
|
||||||
|
const { Fp } = this;
|
||||||
|
// @ts-ignore
|
||||||
|
return this.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : this.inv(rhs));
|
||||||
|
}
|
||||||
|
inv({ c0: a, c1: b }) {
|
||||||
|
// We wish to find the multiplicative inverse of a nonzero
|
||||||
|
// element a + bu in Fp2. We leverage an identity
|
||||||
|
//
|
||||||
|
// (a + bu)(a - bu) = a² + b²
|
||||||
|
//
|
||||||
|
// which holds because u² = -1. This can be rewritten as
|
||||||
|
//
|
||||||
|
// (a + bu)(a - bu)/(a² + b²) = 1
|
||||||
|
//
|
||||||
|
// because a² + b² = 0 has no nonzero solutions for (a, b).
|
||||||
|
// This gives that (a - bu)/(a² + b²) is the inverse
|
||||||
|
// of (a + bu). Importantly, this can be computing using
|
||||||
|
// only a single inversion in Fp.
|
||||||
|
const { Fp } = this;
|
||||||
|
const factor = Fp.inv(Fp.create(a * a + b * b));
|
||||||
|
return { c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) };
|
||||||
|
}
|
||||||
|
sqrt(num) {
|
||||||
|
// This is generic for all quadratic extensions (Fp2)
|
||||||
|
const { Fp } = this;
|
||||||
|
const Fp2 = this;
|
||||||
|
const { c0, c1 } = num;
|
||||||
|
if (Fp.is0(c1)) {
|
||||||
|
// if c0 is quadratic residue
|
||||||
|
if (mod.FpLegendre(Fp, c0) === 1)
|
||||||
|
return Fp2.create({ c0: Fp.sqrt(c0), c1: Fp.ZERO });
|
||||||
|
else
|
||||||
|
return Fp2.create({ c0: Fp.ZERO, c1: Fp.sqrt(Fp.div(c0, this.Fp_NONRESIDUE)) });
|
||||||
|
}
|
||||||
|
const a = Fp.sqrt(Fp.sub(Fp.sqr(c0), Fp.mul(Fp.sqr(c1), this.Fp_NONRESIDUE)));
|
||||||
|
let d = Fp.mul(Fp.add(a, c0), this.Fp_div2);
|
||||||
|
const legendre = mod.FpLegendre(Fp, d);
|
||||||
|
// -1, Quadratic non residue
|
||||||
|
if (legendre === -1)
|
||||||
|
d = Fp.sub(d, a);
|
||||||
|
const a0 = Fp.sqrt(d);
|
||||||
|
const candidateSqrt = Fp2.create({ c0: a0, c1: Fp.div(Fp.mul(c1, this.Fp_div2), a0) });
|
||||||
|
if (!Fp2.eql(Fp2.sqr(candidateSqrt), num))
|
||||||
|
throw new Error('Cannot find square root');
|
||||||
|
// Normalize root: at this point candidateSqrt ** 2 = num, but also -candidateSqrt ** 2 = num
|
||||||
|
const x1 = candidateSqrt;
|
||||||
|
const x2 = Fp2.neg(x1);
|
||||||
|
const { re: re1, im: im1 } = Fp2.reim(x1);
|
||||||
|
const { re: re2, im: im2 } = Fp2.reim(x2);
|
||||||
|
if (im1 > im2 || (im1 === im2 && re1 > re2))
|
||||||
|
return x1;
|
||||||
|
return x2;
|
||||||
|
}
|
||||||
|
// Same as sgn0_m_eq_2 in RFC 9380
|
||||||
|
isOdd(x) {
|
||||||
|
const { re: x0, im: x1 } = this.reim(x);
|
||||||
|
const sign_0 = x0 % _2n;
|
||||||
|
const zero_0 = x0 === _0n;
|
||||||
|
const sign_1 = x1 % _2n;
|
||||||
|
return BigInt(sign_0 || (zero_0 && sign_1)) == _1n;
|
||||||
|
}
|
||||||
|
// Bytes util
|
||||||
|
fromBytes(b) {
|
||||||
|
const { Fp } = this;
|
||||||
|
if (b.length !== this.BYTES)
|
||||||
|
throw new Error('fromBytes invalid length=' + b.length);
|
||||||
|
return { c0: Fp.fromBytes(b.subarray(0, Fp.BYTES)), c1: Fp.fromBytes(b.subarray(Fp.BYTES)) };
|
||||||
|
}
|
||||||
|
toBytes({ c0, c1 }) {
|
||||||
|
return (0, utils_ts_1.concatBytes)(this.Fp.toBytes(c0), this.Fp.toBytes(c1));
|
||||||
|
}
|
||||||
|
cmov({ c0, c1 }, { c0: r0, c1: r1 }, c) {
|
||||||
|
return {
|
||||||
|
c0: this.Fp.cmov(c0, r0, c),
|
||||||
|
c1: this.Fp.cmov(c1, r1, c),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
reim({ c0, c1 }) {
|
||||||
|
return { re: c0, im: c1 };
|
||||||
|
}
|
||||||
|
Fp4Square(a, b) {
|
||||||
|
const Fp2 = this;
|
||||||
|
const a2 = Fp2.sqr(a);
|
||||||
|
const b2 = Fp2.sqr(b);
|
||||||
|
return {
|
||||||
|
first: Fp2.add(Fp2.mulByNonresidue(b2), a2), // b² * Nonresidue + a²
|
||||||
|
second: Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(a, b)), a2), b2), // (a + b)² - a² - b²
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// multiply by u + 1
|
||||||
|
mulByNonresidue({ c0, c1 }) {
|
||||||
|
return this.mul({ c0, c1 }, this.NONRESIDUE);
|
||||||
|
}
|
||||||
|
frobeniusMap({ c0, c1 }, power) {
|
||||||
|
return {
|
||||||
|
c0,
|
||||||
|
c1: this.Fp.mul(c1, this.FROBENIUS_COEFFICIENTS[power % 2]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class _Field6 {
|
||||||
|
constructor(Fp2) {
|
||||||
|
this.MASK = _1n;
|
||||||
|
this.Fp2 = Fp2;
|
||||||
|
this.ORDER = Fp2.ORDER; // TODO: unused, but need to verify
|
||||||
|
this.BITS = 3 * Fp2.BITS;
|
||||||
|
this.BYTES = 3 * Fp2.BYTES;
|
||||||
|
this.isLE = Fp2.isLE;
|
||||||
|
this.ZERO = { c0: Fp2.ZERO, c1: Fp2.ZERO, c2: Fp2.ZERO };
|
||||||
|
this.ONE = { c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO };
|
||||||
|
const { Fp } = Fp2;
|
||||||
|
const frob = calcFrobeniusCoefficients(Fp2, Fp2.NONRESIDUE, Fp.ORDER, 6, 2, 3);
|
||||||
|
this.FROBENIUS_COEFFICIENTS_1 = frob[0];
|
||||||
|
this.FROBENIUS_COEFFICIENTS_2 = frob[1];
|
||||||
|
Object.seal(this);
|
||||||
|
}
|
||||||
|
add({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
return {
|
||||||
|
c0: Fp2.add(c0, r0),
|
||||||
|
c1: Fp2.add(c1, r1),
|
||||||
|
c2: Fp2.add(c2, r2),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
sub({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
return {
|
||||||
|
c0: Fp2.sub(c0, r0),
|
||||||
|
c1: Fp2.sub(c1, r1),
|
||||||
|
c2: Fp2.sub(c2, r2),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
mul({ c0, c1, c2 }, rhs) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
if (typeof rhs === 'bigint') {
|
||||||
|
return {
|
||||||
|
c0: Fp2.mul(c0, rhs),
|
||||||
|
c1: Fp2.mul(c1, rhs),
|
||||||
|
c2: Fp2.mul(c2, rhs),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const { c0: r0, c1: r1, c2: r2 } = rhs;
|
||||||
|
const t0 = Fp2.mul(c0, r0); // c0 * o0
|
||||||
|
const t1 = Fp2.mul(c1, r1); // c1 * o1
|
||||||
|
const t2 = Fp2.mul(c2, r2); // c2 * o2
|
||||||
|
return {
|
||||||
|
// t0 + (c1 + c2) * (r1 * r2) - (T1 + T2) * (u + 1)
|
||||||
|
c0: Fp2.add(t0, Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), Fp2.add(r1, r2)), Fp2.add(t1, t2)))),
|
||||||
|
// (c0 + c1) * (r0 + r1) - (T0 + T1) + T2 * (u + 1)
|
||||||
|
c1: Fp2.add(Fp2.sub(Fp2.mul(Fp2.add(c0, c1), Fp2.add(r0, r1)), Fp2.add(t0, t1)), Fp2.mulByNonresidue(t2)),
|
||||||
|
// T1 + (c0 + c2) * (r0 + r2) - T0 + T2
|
||||||
|
c2: Fp2.sub(Fp2.add(t1, Fp2.mul(Fp2.add(c0, c2), Fp2.add(r0, r2))), Fp2.add(t0, t2)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
sqr({ c0, c1, c2 }) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
let t0 = Fp2.sqr(c0); // c0²
|
||||||
|
let t1 = Fp2.mul(Fp2.mul(c0, c1), _2n); // 2 * c0 * c1
|
||||||
|
let t3 = Fp2.mul(Fp2.mul(c1, c2), _2n); // 2 * c1 * c2
|
||||||
|
let t4 = Fp2.sqr(c2); // c2²
|
||||||
|
return {
|
||||||
|
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
|
||||||
|
c1: Fp2.add(Fp2.mulByNonresidue(t4), t1), // T4 * (u + 1) + T1
|
||||||
|
// T1 + (c0 - c1 + c2)² + T3 - T0 - T4
|
||||||
|
c2: Fp2.sub(Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.sqr(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0), t4),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
addN(a, b) {
|
||||||
|
return this.add(a, b);
|
||||||
|
}
|
||||||
|
subN(a, b) {
|
||||||
|
return this.sub(a, b);
|
||||||
|
}
|
||||||
|
mulN(a, b) {
|
||||||
|
return this.mul(a, b);
|
||||||
|
}
|
||||||
|
sqrN(a) {
|
||||||
|
return this.sqr(a);
|
||||||
|
}
|
||||||
|
create(num) {
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
isValid({ c0, c1, c2 }) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
return Fp2.isValid(c0) && Fp2.isValid(c1) && Fp2.isValid(c2);
|
||||||
|
}
|
||||||
|
is0({ c0, c1, c2 }) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
return Fp2.is0(c0) && Fp2.is0(c1) && Fp2.is0(c2);
|
||||||
|
}
|
||||||
|
isValidNot0(num) {
|
||||||
|
return !this.is0(num) && this.isValid(num);
|
||||||
|
}
|
||||||
|
neg({ c0, c1, c2 }) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
return { c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) };
|
||||||
|
}
|
||||||
|
eql({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
return Fp2.eql(c0, r0) && Fp2.eql(c1, r1) && Fp2.eql(c2, r2);
|
||||||
|
}
|
||||||
|
sqrt(_) {
|
||||||
|
return (0, utils_ts_1.notImplemented)();
|
||||||
|
}
|
||||||
|
// Do we need division by bigint at all? Should be done via order:
|
||||||
|
div(lhs, rhs) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
const { Fp } = Fp2;
|
||||||
|
return this.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : this.inv(rhs));
|
||||||
|
}
|
||||||
|
pow(num, power) {
|
||||||
|
return mod.FpPow(this, num, power);
|
||||||
|
}
|
||||||
|
invertBatch(nums) {
|
||||||
|
return mod.FpInvertBatch(this, nums);
|
||||||
|
}
|
||||||
|
inv({ c0, c1, c2 }) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
let t0 = Fp2.sub(Fp2.sqr(c0), Fp2.mulByNonresidue(Fp2.mul(c2, c1))); // c0² - c2 * c1 * (u + 1)
|
||||||
|
let t1 = Fp2.sub(Fp2.mulByNonresidue(Fp2.sqr(c2)), Fp2.mul(c0, c1)); // c2² * (u + 1) - c0 * c1
|
||||||
|
let t2 = Fp2.sub(Fp2.sqr(c1), Fp2.mul(c0, c2)); // c1² - c0 * c2
|
||||||
|
// 1/(((c2 * T1 + c1 * T2) * v) + c0 * T0)
|
||||||
|
let t4 = Fp2.inv(Fp2.add(Fp2.mulByNonresidue(Fp2.add(Fp2.mul(c2, t1), Fp2.mul(c1, t2))), Fp2.mul(c0, t0)));
|
||||||
|
return { c0: Fp2.mul(t4, t0), c1: Fp2.mul(t4, t1), c2: Fp2.mul(t4, t2) };
|
||||||
|
}
|
||||||
|
// Bytes utils
|
||||||
|
fromBytes(b) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
if (b.length !== this.BYTES)
|
||||||
|
throw new Error('fromBytes invalid length=' + b.length);
|
||||||
|
const B2 = Fp2.BYTES;
|
||||||
|
return {
|
||||||
|
c0: Fp2.fromBytes(b.subarray(0, B2)),
|
||||||
|
c1: Fp2.fromBytes(b.subarray(B2, B2 * 2)),
|
||||||
|
c2: Fp2.fromBytes(b.subarray(2 * B2)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
toBytes({ c0, c1, c2 }) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
return (0, utils_ts_1.concatBytes)(Fp2.toBytes(c0), Fp2.toBytes(c1), Fp2.toBytes(c2));
|
||||||
|
}
|
||||||
|
cmov({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }, c) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
return {
|
||||||
|
c0: Fp2.cmov(c0, r0, c),
|
||||||
|
c1: Fp2.cmov(c1, r1, c),
|
||||||
|
c2: Fp2.cmov(c2, r2, c),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fromBigSix(t) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
if (!Array.isArray(t) || t.length !== 6)
|
||||||
|
throw new Error('invalid Fp6 usage');
|
||||||
|
return {
|
||||||
|
c0: Fp2.fromBigTuple(t.slice(0, 2)),
|
||||||
|
c1: Fp2.fromBigTuple(t.slice(2, 4)),
|
||||||
|
c2: Fp2.fromBigTuple(t.slice(4, 6)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
frobeniusMap({ c0, c1, c2 }, power) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
return {
|
||||||
|
c0: Fp2.frobeniusMap(c0, power),
|
||||||
|
c1: Fp2.mul(Fp2.frobeniusMap(c1, power), this.FROBENIUS_COEFFICIENTS_1[power % 6]),
|
||||||
|
c2: Fp2.mul(Fp2.frobeniusMap(c2, power), this.FROBENIUS_COEFFICIENTS_2[power % 6]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
mulByFp2({ c0, c1, c2 }, rhs) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
return {
|
||||||
|
c0: Fp2.mul(c0, rhs),
|
||||||
|
c1: Fp2.mul(c1, rhs),
|
||||||
|
c2: Fp2.mul(c2, rhs),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
mulByNonresidue({ c0, c1, c2 }) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
return { c0: Fp2.mulByNonresidue(c2), c1: c0, c2: c1 };
|
||||||
|
}
|
||||||
|
// Sparse multiplication
|
||||||
|
mul1({ c0, c1, c2 }, b1) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
return {
|
||||||
|
c0: Fp2.mulByNonresidue(Fp2.mul(c2, b1)),
|
||||||
|
c1: Fp2.mul(c0, b1),
|
||||||
|
c2: Fp2.mul(c1, b1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Sparse multiplication
|
||||||
|
mul01({ c0, c1, c2 }, b0, b1) {
|
||||||
|
const { Fp2 } = this;
|
||||||
|
let t0 = Fp2.mul(c0, b0); // c0 * b0
|
||||||
|
let t1 = Fp2.mul(c1, b1); // c1 * b1
|
||||||
|
return {
|
||||||
|
// ((c1 + c2) * b1 - T1) * (u + 1) + T0
|
||||||
|
c0: Fp2.add(Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), b1), t1)), t0),
|
||||||
|
// (b0 + b1) * (c0 + c1) - T0 - T1
|
||||||
|
c1: Fp2.sub(Fp2.sub(Fp2.mul(Fp2.add(b0, b1), Fp2.add(c0, c1)), t0), t1),
|
||||||
|
// (c0 + c2) * b0 - T0 + T1
|
||||||
|
c2: Fp2.add(Fp2.sub(Fp2.mul(Fp2.add(c0, c2), b0), t0), t1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class _Field12 {
|
||||||
|
constructor(Fp6, opts) {
|
||||||
|
this.MASK = _1n;
|
||||||
|
const { Fp2 } = Fp6;
|
||||||
|
const { Fp } = Fp2;
|
||||||
|
this.Fp6 = Fp6;
|
||||||
|
this.ORDER = Fp2.ORDER; // TODO: verify if it's unuesd
|
||||||
|
this.BITS = 2 * Fp6.BITS;
|
||||||
|
this.BYTES = 2 * Fp6.BYTES;
|
||||||
|
this.isLE = Fp6.isLE;
|
||||||
|
this.ZERO = { c0: Fp6.ZERO, c1: Fp6.ZERO };
|
||||||
|
this.ONE = { c0: Fp6.ONE, c1: Fp6.ZERO };
|
||||||
|
this.FROBENIUS_COEFFICIENTS = calcFrobeniusCoefficients(Fp2, Fp2.NONRESIDUE, Fp.ORDER, 12, 1, 6)[0];
|
||||||
|
this.X_LEN = opts.X_LEN;
|
||||||
|
this.finalExponentiate = opts.Fp12finalExponentiate;
|
||||||
|
}
|
||||||
|
create(num) {
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
isValid({ c0, c1 }) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
return Fp6.isValid(c0) && Fp6.isValid(c1);
|
||||||
|
}
|
||||||
|
is0({ c0, c1 }) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
return Fp6.is0(c0) && Fp6.is0(c1);
|
||||||
|
}
|
||||||
|
isValidNot0(num) {
|
||||||
|
return !this.is0(num) && this.isValid(num);
|
||||||
|
}
|
||||||
|
neg({ c0, c1 }) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
return { c0: Fp6.neg(c0), c1: Fp6.neg(c1) };
|
||||||
|
}
|
||||||
|
eql({ c0, c1 }, { c0: r0, c1: r1 }) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
return Fp6.eql(c0, r0) && Fp6.eql(c1, r1);
|
||||||
|
}
|
||||||
|
sqrt(_) {
|
||||||
|
(0, utils_ts_1.notImplemented)();
|
||||||
|
}
|
||||||
|
inv({ c0, c1 }) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
let t = Fp6.inv(Fp6.sub(Fp6.sqr(c0), Fp6.mulByNonresidue(Fp6.sqr(c1)))); // 1 / (c0² - c1² * v)
|
||||||
|
return { c0: Fp6.mul(c0, t), c1: Fp6.neg(Fp6.mul(c1, t)) }; // ((C0 * T) * T) + (-C1 * T) * w
|
||||||
|
}
|
||||||
|
div(lhs, rhs) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
const { Fp2 } = Fp6;
|
||||||
|
const { Fp } = Fp2;
|
||||||
|
return this.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : this.inv(rhs));
|
||||||
|
}
|
||||||
|
pow(num, power) {
|
||||||
|
return mod.FpPow(this, num, power);
|
||||||
|
}
|
||||||
|
invertBatch(nums) {
|
||||||
|
return mod.FpInvertBatch(this, nums);
|
||||||
|
}
|
||||||
|
// Normalized
|
||||||
|
add({ c0, c1 }, { c0: r0, c1: r1 }) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
return {
|
||||||
|
c0: Fp6.add(c0, r0),
|
||||||
|
c1: Fp6.add(c1, r1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
sub({ c0, c1 }, { c0: r0, c1: r1 }) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
return {
|
||||||
|
c0: Fp6.sub(c0, r0),
|
||||||
|
c1: Fp6.sub(c1, r1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
mul({ c0, c1 }, rhs) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
if (typeof rhs === 'bigint')
|
||||||
|
return { c0: Fp6.mul(c0, rhs), c1: Fp6.mul(c1, rhs) };
|
||||||
|
let { c0: r0, c1: r1 } = rhs;
|
||||||
|
let t1 = Fp6.mul(c0, r0); // c0 * r0
|
||||||
|
let t2 = Fp6.mul(c1, r1); // c1 * r1
|
||||||
|
return {
|
||||||
|
c0: Fp6.add(t1, Fp6.mulByNonresidue(t2)), // T1 + T2 * v
|
||||||
|
// (c0 + c1) * (r0 + r1) - (T1 + T2)
|
||||||
|
c1: Fp6.sub(Fp6.mul(Fp6.add(c0, c1), Fp6.add(r0, r1)), Fp6.add(t1, t2)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
sqr({ c0, c1 }) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
let ab = Fp6.mul(c0, c1); // c0 * c1
|
||||||
|
return {
|
||||||
|
// (c1 * v + c0) * (c0 + c1) - AB - AB * v
|
||||||
|
c0: Fp6.sub(Fp6.sub(Fp6.mul(Fp6.add(Fp6.mulByNonresidue(c1), c0), Fp6.add(c0, c1)), ab), Fp6.mulByNonresidue(ab)),
|
||||||
|
c1: Fp6.add(ab, ab),
|
||||||
|
}; // AB + AB
|
||||||
|
}
|
||||||
|
// NonNormalized stuff
|
||||||
|
addN(a, b) {
|
||||||
|
return this.add(a, b);
|
||||||
|
}
|
||||||
|
subN(a, b) {
|
||||||
|
return this.sub(a, b);
|
||||||
|
}
|
||||||
|
mulN(a, b) {
|
||||||
|
return this.mul(a, b);
|
||||||
|
}
|
||||||
|
sqrN(a) {
|
||||||
|
return this.sqr(a);
|
||||||
|
}
|
||||||
|
// Bytes utils
|
||||||
|
fromBytes(b) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
if (b.length !== this.BYTES)
|
||||||
|
throw new Error('fromBytes invalid length=' + b.length);
|
||||||
|
return {
|
||||||
|
c0: Fp6.fromBytes(b.subarray(0, Fp6.BYTES)),
|
||||||
|
c1: Fp6.fromBytes(b.subarray(Fp6.BYTES)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
toBytes({ c0, c1 }) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
return (0, utils_ts_1.concatBytes)(Fp6.toBytes(c0), Fp6.toBytes(c1));
|
||||||
|
}
|
||||||
|
cmov({ c0, c1 }, { c0: r0, c1: r1 }, c) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
return {
|
||||||
|
c0: Fp6.cmov(c0, r0, c),
|
||||||
|
c1: Fp6.cmov(c1, r1, c),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Utils
|
||||||
|
// toString() {
|
||||||
|
// return '' + 'Fp12(' + this.c0 + this.c1 + '* w');
|
||||||
|
// },
|
||||||
|
// fromTuple(c: [Fp6, Fp6]) {
|
||||||
|
// return new Fp12(...c);
|
||||||
|
// }
|
||||||
|
fromBigTwelve(t) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
return {
|
||||||
|
c0: Fp6.fromBigSix(t.slice(0, 6)),
|
||||||
|
c1: Fp6.fromBigSix(t.slice(6, 12)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Raises to q**i -th power
|
||||||
|
frobeniusMap(lhs, power) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
const { Fp2 } = Fp6;
|
||||||
|
const { c0, c1, c2 } = Fp6.frobeniusMap(lhs.c1, power);
|
||||||
|
const coeff = this.FROBENIUS_COEFFICIENTS[power % 12];
|
||||||
|
return {
|
||||||
|
c0: Fp6.frobeniusMap(lhs.c0, power),
|
||||||
|
c1: Fp6.create({
|
||||||
|
c0: Fp2.mul(c0, coeff),
|
||||||
|
c1: Fp2.mul(c1, coeff),
|
||||||
|
c2: Fp2.mul(c2, coeff),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
mulByFp2({ c0, c1 }, rhs) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
return {
|
||||||
|
c0: Fp6.mulByFp2(c0, rhs),
|
||||||
|
c1: Fp6.mulByFp2(c1, rhs),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
conjugate({ c0, c1 }) {
|
||||||
|
return { c0, c1: this.Fp6.neg(c1) };
|
||||||
|
}
|
||||||
|
// Sparse multiplication
|
||||||
|
mul014({ c0, c1 }, o0, o1, o4) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
const { Fp2 } = Fp6;
|
||||||
|
let t0 = Fp6.mul01(c0, o0, o1);
|
||||||
|
let t1 = Fp6.mul1(c1, o4);
|
||||||
|
return {
|
||||||
|
c0: Fp6.add(Fp6.mulByNonresidue(t1), t0), // T1 * v + T0
|
||||||
|
// (c1 + c0) * [o0, o1+o4] - T0 - T1
|
||||||
|
c1: Fp6.sub(Fp6.sub(Fp6.mul01(Fp6.add(c1, c0), o0, Fp2.add(o1, o4)), t0), t1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
mul034({ c0, c1 }, o0, o3, o4) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
const { Fp2 } = Fp6;
|
||||||
|
const a = Fp6.create({
|
||||||
|
c0: Fp2.mul(c0.c0, o0),
|
||||||
|
c1: Fp2.mul(c0.c1, o0),
|
||||||
|
c2: Fp2.mul(c0.c2, o0),
|
||||||
|
});
|
||||||
|
const b = Fp6.mul01(c1, o3, o4);
|
||||||
|
const e = Fp6.mul01(Fp6.add(c0, c1), Fp2.add(o0, o3), o4);
|
||||||
|
return {
|
||||||
|
c0: Fp6.add(Fp6.mulByNonresidue(b), a),
|
||||||
|
c1: Fp6.sub(e, Fp6.add(a, b)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// A cyclotomic group is a subgroup of Fp^n defined by
|
||||||
|
// GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) = 1}
|
||||||
|
// The result of any pairing is in a cyclotomic subgroup
|
||||||
|
// https://eprint.iacr.org/2009/565.pdf
|
||||||
|
// https://eprint.iacr.org/2010/354.pdf
|
||||||
|
_cyclotomicSquare({ c0, c1 }) {
|
||||||
|
const { Fp6 } = this;
|
||||||
|
const { Fp2 } = Fp6;
|
||||||
|
const { c0: c0c0, c1: c0c1, c2: c0c2 } = c0;
|
||||||
|
const { c0: c1c0, c1: c1c1, c2: c1c2 } = c1;
|
||||||
|
const { first: t3, second: t4 } = Fp2.Fp4Square(c0c0, c1c1);
|
||||||
|
const { first: t5, second: t6 } = Fp2.Fp4Square(c1c0, c0c2);
|
||||||
|
const { first: t7, second: t8 } = Fp2.Fp4Square(c0c1, c1c2);
|
||||||
|
const t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
|
||||||
|
return {
|
||||||
|
c0: Fp6.create({
|
||||||
|
c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n), t3), // 2 * (T3 - c0c0) + T3
|
||||||
|
c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n), t5), // 2 * (T5 - c0c1) + T5
|
||||||
|
c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), _2n), t7),
|
||||||
|
}), // 2 * (T7 - c0c2) + T7
|
||||||
|
c1: Fp6.create({
|
||||||
|
c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n), t9), // 2 * (T9 + c1c0) + T9
|
||||||
|
c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n), t4), // 2 * (T4 + c1c1) + T4
|
||||||
|
c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), _2n), t6),
|
||||||
|
}),
|
||||||
|
}; // 2 * (T6 + c1c2) + T6
|
||||||
|
}
|
||||||
|
// https://eprint.iacr.org/2009/565.pdf
|
||||||
|
_cyclotomicExp(num, n) {
|
||||||
|
let z = this.ONE;
|
||||||
|
for (let i = this.X_LEN - 1; i >= 0; i--) {
|
||||||
|
z = this._cyclotomicSquare(z);
|
||||||
|
if ((0, utils_ts_1.bitGet)(n, i))
|
||||||
|
z = this.mul(z, num);
|
||||||
|
}
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function tower12(opts) {
|
||||||
|
const Fp = mod.Field(opts.ORDER);
|
||||||
|
const Fp2 = new _Field2(Fp, opts);
|
||||||
|
const Fp6 = new _Field6(Fp2);
|
||||||
|
const Fp12 = new _Field12(Fp6, opts);
|
||||||
|
return { Fp, Fp2, Fp6, Fp12 };
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=tower.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+78
@@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* Deprecated module: moved from curves/abstract/utils.js to curves/utils.js
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
import * as u from '../utils.ts';
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export type Hex = u.Hex;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export type PrivKey = u.PrivKey;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export type CHash = u.CHash;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export type FHash = u.FHash;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const abytes: typeof u.abytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const anumber: typeof u.anumber;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const bytesToHex: typeof u.bytesToHex;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const bytesToUtf8: typeof u.bytesToUtf8;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const concatBytes: typeof u.concatBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const hexToBytes: typeof u.hexToBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const isBytes: typeof u.isBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const randomBytes: typeof u.randomBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const utf8ToBytes: typeof u.utf8ToBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const abool: typeof u.abool;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const numberToHexUnpadded: typeof u.numberToHexUnpadded;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const hexToNumber: typeof u.hexToNumber;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const bytesToNumberBE: typeof u.bytesToNumberBE;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const bytesToNumberLE: typeof u.bytesToNumberLE;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const numberToBytesBE: typeof u.numberToBytesBE;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const numberToBytesLE: typeof u.numberToBytesLE;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const numberToVarBytesBE: typeof u.numberToVarBytesBE;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const ensureBytes: typeof u.ensureBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const equalBytes: typeof u.equalBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const copyBytes: typeof u.copyBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const asciiToBytes: typeof u.asciiToBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const inRange: typeof u.inRange;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const aInRange: typeof u.aInRange;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const bitLen: typeof u.bitLen;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const bitGet: typeof u.bitGet;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const bitSet: typeof u.bitSet;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const bitMask: typeof u.bitMask;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const createHmacDrbg: typeof u.createHmacDrbg;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const notImplemented: typeof u.notImplemented;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const memoized: typeof u.memoized;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const validateObject: typeof u.validateObject;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
export declare const isHash: typeof u.isHash;
|
||||||
|
//# sourceMappingURL=utils.d.ts.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/abstract/utils.ts"],"names":[],"mappings":"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"}
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.isHash = exports.validateObject = exports.memoized = exports.notImplemented = exports.createHmacDrbg = exports.bitMask = exports.bitSet = exports.bitGet = exports.bitLen = exports.aInRange = exports.inRange = exports.asciiToBytes = exports.copyBytes = exports.equalBytes = exports.ensureBytes = exports.numberToVarBytesBE = exports.numberToBytesLE = exports.numberToBytesBE = exports.bytesToNumberLE = exports.bytesToNumberBE = exports.hexToNumber = exports.numberToHexUnpadded = exports.abool = exports.utf8ToBytes = exports.randomBytes = exports.isBytes = exports.hexToBytes = exports.concatBytes = exports.bytesToUtf8 = exports.bytesToHex = exports.anumber = exports.abytes = void 0;
|
||||||
|
/**
|
||||||
|
* Deprecated module: moved from curves/abstract/utils.js to curves/utils.js
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
const u = require("../utils.js");
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.abytes = u.abytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.anumber = u.anumber;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.bytesToHex = u.bytesToHex;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.bytesToUtf8 = u.bytesToUtf8;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.concatBytes = u.concatBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.hexToBytes = u.hexToBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.isBytes = u.isBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.randomBytes = u.randomBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.utf8ToBytes = u.utf8ToBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.abool = u.abool;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.numberToHexUnpadded = u.numberToHexUnpadded;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.hexToNumber = u.hexToNumber;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.bytesToNumberBE = u.bytesToNumberBE;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.bytesToNumberLE = u.bytesToNumberLE;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.numberToBytesBE = u.numberToBytesBE;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.numberToBytesLE = u.numberToBytesLE;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.numberToVarBytesBE = u.numberToVarBytesBE;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.ensureBytes = u.ensureBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.equalBytes = u.equalBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.copyBytes = u.copyBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.asciiToBytes = u.asciiToBytes;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.inRange = u.inRange;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.aInRange = u.aInRange;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.bitLen = u.bitLen;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.bitGet = u.bitGet;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.bitSet = u.bitSet;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.bitMask = u.bitMask;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.createHmacDrbg = u.createHmacDrbg;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.notImplemented = u.notImplemented;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.memoized = u.memoized;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.validateObject = u.validateObject;
|
||||||
|
/** @deprecated moved to `@noble/curves/utils.js` */
|
||||||
|
exports.isHash = u.isHash;
|
||||||
|
//# sourceMappingURL=utils.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/abstract/utils.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,iCAAiC;AAWjC,oDAAoD;AACvC,QAAA,MAAM,GAAoB,CAAC,CAAC,MAAM,CAAC;AAChD,oDAAoD;AACvC,QAAA,OAAO,GAAqB,CAAC,CAAC,OAAO,CAAC;AACnD,oDAAoD;AACvC,QAAA,UAAU,GAAwB,CAAC,CAAC,UAAU,CAAC;AAC5D,oDAAoD;AACvC,QAAA,WAAW,GAAyB,CAAC,CAAC,WAAW,CAAC;AAC/D,oDAAoD;AACvC,QAAA,WAAW,GAAyB,CAAC,CAAC,WAAW,CAAC;AAC/D,oDAAoD;AACvC,QAAA,UAAU,GAAwB,CAAC,CAAC,UAAU,CAAC;AAC5D,oDAAoD;AACvC,QAAA,OAAO,GAAqB,CAAC,CAAC,OAAO,CAAC;AACnD,oDAAoD;AACvC,QAAA,WAAW,GAAyB,CAAC,CAAC,WAAW,CAAC;AAC/D,oDAAoD;AACvC,QAAA,WAAW,GAAyB,CAAC,CAAC,WAAW,CAAC;AAE/D,oDAAoD;AACvC,QAAA,KAAK,GAAmB,CAAC,CAAC,KAAK,CAAC;AAC7C,oDAAoD;AACvC,QAAA,mBAAmB,GAAiC,CAAC,CAAC,mBAAmB,CAAC;AACvF,oDAAoD;AACvC,QAAA,WAAW,GAAyB,CAAC,CAAC,WAAW,CAAC;AAC/D,oDAAoD;AACvC,QAAA,eAAe,GAA6B,CAAC,CAAC,eAAe,CAAC;AAC3E,oDAAoD;AACvC,QAAA,eAAe,GAA6B,CAAC,CAAC,eAAe,CAAC;AAC3E,oDAAoD;AACvC,QAAA,eAAe,GAA6B,CAAC,CAAC,eAAe,CAAC;AAC3E,oDAAoD;AACvC,QAAA,eAAe,GAA6B,CAAC,CAAC,eAAe,CAAC;AAC3E,oDAAoD;AACvC,QAAA,kBAAkB,GAAgC,CAAC,CAAC,kBAAkB,CAAC;AACpF,oDAAoD;AACvC,QAAA,WAAW,GAAyB,CAAC,CAAC,WAAW,CAAC;AAC/D,oDAAoD;AACvC,QAAA,UAAU,GAAwB,CAAC,CAAC,UAAU,CAAC;AAC5D,oDAAoD;AACvC,QAAA,SAAS,GAAuB,CAAC,CAAC,SAAS,CAAC;AACzD,oDAAoD;AACvC,QAAA,YAAY,GAA0B,CAAC,CAAC,YAAY,CAAC;AAClE,oDAAoD;AACvC,QAAA,OAAO,GAAqB,CAAC,CAAC,OAAO,CAAC;AACnD,oDAAoD;AACvC,QAAA,QAAQ,GAAsB,CAAC,CAAC,QAAQ,CAAC;AACtD,oDAAoD;AACvC,QAAA,MAAM,GAAoB,CAAC,CAAC,MAAM,CAAC;AAChD,oDAAoD;AACvC,QAAA,MAAM,GAAoB,CAAC,CAAC,MAAM,CAAC;AAChD,oDAAoD;AACvC,QAAA,MAAM,GAAoB,CAAC,CAAC,MAAM,CAAC;AAChD,oDAAoD;AACvC,QAAA,OAAO,GAAqB,CAAC,CAAC,OAAO,CAAC;AACnD,oDAAoD;AACvC,QAAA,cAAc,GAA4B,CAAC,CAAC,cAAc,CAAC;AACxE,oDAAoD;AACvC,QAAA,cAAc,GAA4B,CAAC,CAAC,cAAc,CAAC;AACxE,oDAAoD;AACvC,QAAA,QAAQ,GAAsB,CAAC,CAAC,QAAQ,CAAC;AACtD,oDAAoD;AACvC,QAAA,cAAc,GAA4B,CAAC,CAAC,cAAc,CAAC;AACxE,oDAAoD;AACvC,QAAA,MAAM,GAAoB,CAAC,CAAC,MAAM,CAAC"}
|
||||||
+416
@@ -0,0 +1,416 @@
|
|||||||
|
import { type CHash, type Hex, type PrivKey } from '../utils.ts';
|
||||||
|
import { type AffinePoint, type BasicCurve, type CurveLengths, type CurvePoint, type CurvePointCons } from './curve.ts';
|
||||||
|
import { type IField, type NLength } from './modular.ts';
|
||||||
|
export type { AffinePoint };
|
||||||
|
export type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
|
||||||
|
type EndoBasis = [[bigint, bigint], [bigint, bigint]];
|
||||||
|
/**
|
||||||
|
* When Weierstrass curve has `a=0`, it becomes Koblitz curve.
|
||||||
|
* Koblitz curves allow using **efficiently-computable GLV endomorphism ψ**.
|
||||||
|
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
|
||||||
|
* For precomputed wNAF it trades off 1/2 init time & 1/3 ram for 20% perf hit.
|
||||||
|
*
|
||||||
|
* Endomorphism consists of beta, lambda and splitScalar:
|
||||||
|
*
|
||||||
|
* 1. GLV endomorphism ψ transforms a point: `P = (x, y) ↦ ψ(P) = (β·x mod p, y)`
|
||||||
|
* 2. GLV scalar decomposition transforms a scalar: `k ≡ k₁ + k₂·λ (mod n)`
|
||||||
|
* 3. Then these are combined: `k·P = k₁·P + k₂·ψ(P)`
|
||||||
|
* 4. Two 128-bit point-by-scalar multiplications + one point addition is faster than
|
||||||
|
* one 256-bit multiplication.
|
||||||
|
*
|
||||||
|
* where
|
||||||
|
* * beta: β ∈ Fₚ with β³ = 1, β ≠ 1
|
||||||
|
* * lambda: λ ∈ Fₙ with λ³ = 1, λ ≠ 1
|
||||||
|
* * splitScalar decomposes k ↦ k₁, k₂, by using reduced basis vectors.
|
||||||
|
* Gauss lattice reduction calculates them from initial basis vectors `(n, 0), (-λ, 0)`
|
||||||
|
*
|
||||||
|
* Check out `test/misc/endomorphism.js` and
|
||||||
|
* [gist](https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066).
|
||||||
|
*/
|
||||||
|
export type EndomorphismOpts = {
|
||||||
|
beta: bigint;
|
||||||
|
basises?: EndoBasis;
|
||||||
|
splitScalar?: (k: bigint) => {
|
||||||
|
k1neg: boolean;
|
||||||
|
k1: bigint;
|
||||||
|
k2neg: boolean;
|
||||||
|
k2: bigint;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export type ScalarEndoParts = {
|
||||||
|
k1neg: boolean;
|
||||||
|
k1: bigint;
|
||||||
|
k2neg: boolean;
|
||||||
|
k2: bigint;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Splits scalar for GLV endomorphism.
|
||||||
|
*/
|
||||||
|
export declare function _splitEndoScalar(k: bigint, basis: EndoBasis, n: bigint): ScalarEndoParts;
|
||||||
|
export type ECDSASigFormat = 'compact' | 'recovered' | 'der';
|
||||||
|
export type ECDSARecoverOpts = {
|
||||||
|
prehash?: boolean;
|
||||||
|
};
|
||||||
|
export type ECDSAVerifyOpts = {
|
||||||
|
prehash?: boolean;
|
||||||
|
lowS?: boolean;
|
||||||
|
format?: ECDSASigFormat;
|
||||||
|
};
|
||||||
|
export type ECDSASignOpts = {
|
||||||
|
prehash?: boolean;
|
||||||
|
lowS?: boolean;
|
||||||
|
format?: ECDSASigFormat;
|
||||||
|
extraEntropy?: Uint8Array | boolean;
|
||||||
|
};
|
||||||
|
/** Instance methods for 3D XYZ projective points. */
|
||||||
|
export interface WeierstrassPoint<T> extends CurvePoint<T, WeierstrassPoint<T>> {
|
||||||
|
/** projective X coordinate. Different from affine x. */
|
||||||
|
readonly X: T;
|
||||||
|
/** projective Y coordinate. Different from affine y. */
|
||||||
|
readonly Y: T;
|
||||||
|
/** projective z coordinate */
|
||||||
|
readonly Z: T;
|
||||||
|
/** affine x coordinate. Different from projective X. */
|
||||||
|
get x(): T;
|
||||||
|
/** affine y coordinate. Different from projective Y. */
|
||||||
|
get y(): T;
|
||||||
|
/** Encodes point using IEEE P1363 (DER) encoding. First byte is 2/3/4. Default = isCompressed. */
|
||||||
|
toBytes(isCompressed?: boolean): Uint8Array;
|
||||||
|
toHex(isCompressed?: boolean): string;
|
||||||
|
/** @deprecated use `.X` */
|
||||||
|
readonly px: T;
|
||||||
|
/** @deprecated use `.Y` */
|
||||||
|
readonly py: T;
|
||||||
|
/** @deprecated use `.Z` */
|
||||||
|
readonly pz: T;
|
||||||
|
/** @deprecated use `toBytes` */
|
||||||
|
toRawBytes(isCompressed?: boolean): Uint8Array;
|
||||||
|
/** @deprecated use `multiplyUnsafe` */
|
||||||
|
multiplyAndAddUnsafe(Q: WeierstrassPoint<T>, a: bigint, b: bigint): WeierstrassPoint<T> | undefined;
|
||||||
|
/** @deprecated use `p.y % 2n === 0n` */
|
||||||
|
hasEvenY(): boolean;
|
||||||
|
/** @deprecated use `p.precompute(windowSize)` */
|
||||||
|
_setWindowSize(windowSize: number): void;
|
||||||
|
}
|
||||||
|
/** Static methods for 3D XYZ projective points. */
|
||||||
|
export interface WeierstrassPointCons<T> extends CurvePointCons<WeierstrassPoint<T>> {
|
||||||
|
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
||||||
|
new (X: T, Y: T, Z: T): WeierstrassPoint<T>;
|
||||||
|
CURVE(): WeierstrassOpts<T>;
|
||||||
|
/** @deprecated use `Point.BASE.multiply(Point.Fn.fromBytes(privateKey))` */
|
||||||
|
fromPrivateKey(privateKey: PrivKey): WeierstrassPoint<T>;
|
||||||
|
/** @deprecated use `import { normalizeZ } from '@noble/curves/abstract/curve.js';` */
|
||||||
|
normalizeZ(points: WeierstrassPoint<T>[]): WeierstrassPoint<T>[];
|
||||||
|
/** @deprecated use `import { pippenger } from '@noble/curves/abstract/curve.js';` */
|
||||||
|
msm(points: WeierstrassPoint<T>[], scalars: bigint[]): WeierstrassPoint<T>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Weierstrass curve options.
|
||||||
|
*
|
||||||
|
* * p: prime characteristic (order) of finite field, in which arithmetics is done
|
||||||
|
* * n: order of prime subgroup a.k.a total amount of valid curve points
|
||||||
|
* * h: cofactor, usually 1. h*n is group order; n is subgroup order
|
||||||
|
* * a: formula param, must be in field of p
|
||||||
|
* * b: formula param, must be in field of p
|
||||||
|
* * Gx: x coordinate of generator point a.k.a. base point
|
||||||
|
* * Gy: y coordinate of generator point
|
||||||
|
*/
|
||||||
|
export type WeierstrassOpts<T> = Readonly<{
|
||||||
|
p: bigint;
|
||||||
|
n: bigint;
|
||||||
|
h: bigint;
|
||||||
|
a: T;
|
||||||
|
b: T;
|
||||||
|
Gx: T;
|
||||||
|
Gy: T;
|
||||||
|
}>;
|
||||||
|
export type WeierstrassExtraOpts<T> = Partial<{
|
||||||
|
Fp: IField<T>;
|
||||||
|
Fn: IField<bigint>;
|
||||||
|
allowInfinityPoint: boolean;
|
||||||
|
endo: EndomorphismOpts;
|
||||||
|
isTorsionFree: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
|
||||||
|
clearCofactor: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
|
||||||
|
fromBytes: (bytes: Uint8Array) => AffinePoint<T>;
|
||||||
|
toBytes: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>, isCompressed: boolean) => Uint8Array;
|
||||||
|
}>;
|
||||||
|
/**
|
||||||
|
* Options for ECDSA signatures over a Weierstrass curve.
|
||||||
|
*
|
||||||
|
* * lowS: (default: true) whether produced / verified signatures occupy low half of ecdsaOpts.p. Prevents malleability.
|
||||||
|
* * hmac: (default: noble-hashes hmac) function, would be used to init hmac-drbg for k generation.
|
||||||
|
* * randomBytes: (default: webcrypto os-level CSPRNG) custom method for fetching secure randomness.
|
||||||
|
* * bits2int, bits2int_modN: used in sigs, sometimes overridden by curves
|
||||||
|
*/
|
||||||
|
export type ECDSAOpts = Partial<{
|
||||||
|
lowS: boolean;
|
||||||
|
hmac: HmacFnSync;
|
||||||
|
randomBytes: (bytesLength?: number) => Uint8Array;
|
||||||
|
bits2int: (bytes: Uint8Array) => bigint;
|
||||||
|
bits2int_modN: (bytes: Uint8Array) => bigint;
|
||||||
|
}>;
|
||||||
|
/**
|
||||||
|
* Elliptic Curve Diffie-Hellman interface.
|
||||||
|
* Provides keygen, secret-to-public conversion, calculating shared secrets.
|
||||||
|
*/
|
||||||
|
export interface ECDH {
|
||||||
|
keygen: (seed?: Uint8Array) => {
|
||||||
|
secretKey: Uint8Array;
|
||||||
|
publicKey: Uint8Array;
|
||||||
|
};
|
||||||
|
getPublicKey: (secretKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
||||||
|
getSharedSecret: (secretKeyA: PrivKey, publicKeyB: Hex, isCompressed?: boolean) => Uint8Array;
|
||||||
|
Point: WeierstrassPointCons<bigint>;
|
||||||
|
utils: {
|
||||||
|
isValidSecretKey: (secretKey: PrivKey) => boolean;
|
||||||
|
isValidPublicKey: (publicKey: Uint8Array, isCompressed?: boolean) => boolean;
|
||||||
|
randomSecretKey: (seed?: Uint8Array) => Uint8Array;
|
||||||
|
/** @deprecated use `randomSecretKey` */
|
||||||
|
randomPrivateKey: (seed?: Uint8Array) => Uint8Array;
|
||||||
|
/** @deprecated use `isValidSecretKey` */
|
||||||
|
isValidPrivateKey: (secretKey: PrivKey) => boolean;
|
||||||
|
/** @deprecated use `Point.Fn.fromBytes()` */
|
||||||
|
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
||||||
|
/** @deprecated use `point.precompute()` */
|
||||||
|
precompute: (windowSize?: number, point?: WeierstrassPoint<bigint>) => WeierstrassPoint<bigint>;
|
||||||
|
};
|
||||||
|
lengths: CurveLengths;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* ECDSA interface.
|
||||||
|
* Only supported for prime fields, not Fp2 (extension fields).
|
||||||
|
*/
|
||||||
|
export interface ECDSA extends ECDH {
|
||||||
|
sign: (message: Hex, secretKey: PrivKey, opts?: ECDSASignOpts) => ECDSASigRecovered;
|
||||||
|
verify: (signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array, opts?: ECDSAVerifyOpts) => boolean;
|
||||||
|
recoverPublicKey(signature: Uint8Array, message: Uint8Array, opts?: ECDSARecoverOpts): Uint8Array;
|
||||||
|
Signature: ECDSASignatureCons;
|
||||||
|
}
|
||||||
|
export declare class DERErr extends Error {
|
||||||
|
constructor(m?: string);
|
||||||
|
}
|
||||||
|
export type IDER = {
|
||||||
|
Err: typeof DERErr;
|
||||||
|
_tlv: {
|
||||||
|
encode: (tag: number, data: string) => string;
|
||||||
|
decode(tag: number, data: Uint8Array): {
|
||||||
|
v: Uint8Array;
|
||||||
|
l: Uint8Array;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
_int: {
|
||||||
|
encode(num: bigint): string;
|
||||||
|
decode(data: Uint8Array): bigint;
|
||||||
|
};
|
||||||
|
toSig(hex: string | Uint8Array): {
|
||||||
|
r: bigint;
|
||||||
|
s: bigint;
|
||||||
|
};
|
||||||
|
hexFromSig(sig: {
|
||||||
|
r: bigint;
|
||||||
|
s: bigint;
|
||||||
|
}): string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* ASN.1 DER encoding utilities. ASN is very complex & fragile. Format:
|
||||||
|
*
|
||||||
|
* [0x30 (SEQUENCE), bytelength, 0x02 (INTEGER), intLength, R, 0x02 (INTEGER), intLength, S]
|
||||||
|
*
|
||||||
|
* Docs: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/, https://luca.ntop.org/Teaching/Appunti/asn1.html
|
||||||
|
*/
|
||||||
|
export declare const DER: IDER;
|
||||||
|
export declare function _normFnElement(Fn: IField<bigint>, key: PrivKey): bigint;
|
||||||
|
/**
|
||||||
|
* Creates weierstrass Point constructor, based on specified curve options.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
```js
|
||||||
|
const opts = {
|
||||||
|
p: BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'),
|
||||||
|
n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
|
||||||
|
h: BigInt(1),
|
||||||
|
a: BigInt('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc'),
|
||||||
|
b: BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b'),
|
||||||
|
Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
|
||||||
|
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
||||||
|
};
|
||||||
|
const p256_Point = weierstrass(opts);
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
export declare function weierstrassN<T>(params: WeierstrassOpts<T>, extraOpts?: WeierstrassExtraOpts<T>): WeierstrassPointCons<T>;
|
||||||
|
/** Methods of ECDSA signature instance. */
|
||||||
|
export interface ECDSASignature {
|
||||||
|
readonly r: bigint;
|
||||||
|
readonly s: bigint;
|
||||||
|
readonly recovery?: number;
|
||||||
|
addRecoveryBit(recovery: number): ECDSASigRecovered;
|
||||||
|
hasHighS(): boolean;
|
||||||
|
toBytes(format?: string): Uint8Array;
|
||||||
|
toHex(format?: string): string;
|
||||||
|
/** @deprecated */
|
||||||
|
assertValidity(): void;
|
||||||
|
/** @deprecated */
|
||||||
|
normalizeS(): ECDSASignature;
|
||||||
|
/** @deprecated use standalone method `curve.recoverPublicKey(sig.toBytes('recovered'), msg)` */
|
||||||
|
recoverPublicKey(msgHash: Hex): WeierstrassPoint<bigint>;
|
||||||
|
/** @deprecated use `.toBytes('compact')` */
|
||||||
|
toCompactRawBytes(): Uint8Array;
|
||||||
|
/** @deprecated use `.toBytes('compact')` */
|
||||||
|
toCompactHex(): string;
|
||||||
|
/** @deprecated use `.toBytes('der')` */
|
||||||
|
toDERRawBytes(): Uint8Array;
|
||||||
|
/** @deprecated use `.toBytes('der')` */
|
||||||
|
toDERHex(): string;
|
||||||
|
}
|
||||||
|
export type ECDSASigRecovered = ECDSASignature & {
|
||||||
|
readonly recovery: number;
|
||||||
|
};
|
||||||
|
/** Methods of ECDSA signature constructor. */
|
||||||
|
export type ECDSASignatureCons = {
|
||||||
|
new (r: bigint, s: bigint, recovery?: number): ECDSASignature;
|
||||||
|
fromBytes(bytes: Uint8Array, format?: ECDSASigFormat): ECDSASignature;
|
||||||
|
fromHex(hex: string, format?: ECDSASigFormat): ECDSASignature;
|
||||||
|
/** @deprecated use `.fromBytes(bytes, 'compact')` */
|
||||||
|
fromCompact(hex: Hex): ECDSASignature;
|
||||||
|
/** @deprecated use `.fromBytes(bytes, 'der')` */
|
||||||
|
fromDER(hex: Hex): ECDSASignature;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
|
||||||
|
* TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
|
||||||
|
* b = True and y = sqrt(u / v) if (u / v) is square in F, and
|
||||||
|
* b = False and y = sqrt(Z * (u / v)) otherwise.
|
||||||
|
* @param Fp
|
||||||
|
* @param Z
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export declare function SWUFpSqrtRatio<T>(Fp: IField<T>, Z: T): (u: T, v: T) => {
|
||||||
|
isValid: boolean;
|
||||||
|
value: T;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Simplified Shallue-van de Woestijne-Ulas Method
|
||||||
|
* https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
|
||||||
|
*/
|
||||||
|
export declare function mapToCurveSimpleSWU<T>(Fp: IField<T>, opts: {
|
||||||
|
A: T;
|
||||||
|
B: T;
|
||||||
|
Z: T;
|
||||||
|
}): (u: T) => {
|
||||||
|
x: T;
|
||||||
|
y: T;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
|
||||||
|
* This helper ensures no signature functionality is present. Less code, smaller bundle size.
|
||||||
|
*/
|
||||||
|
export declare function ecdh(Point: WeierstrassPointCons<bigint>, ecdhOpts?: {
|
||||||
|
randomBytes?: (bytesLength?: number) => Uint8Array;
|
||||||
|
}): ECDH;
|
||||||
|
/**
|
||||||
|
* Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
|
||||||
|
* We need `hash` for 2 features:
|
||||||
|
* 1. Message prehash-ing. NOT used if `sign` / `verify` are called with `prehash: false`
|
||||||
|
* 2. k generation in `sign`, using HMAC-drbg(hash)
|
||||||
|
*
|
||||||
|
* ECDSAOpts are only rarely needed.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```js
|
||||||
|
* const p256_Point = weierstrass(...);
|
||||||
|
* const p256_sha256 = ecdsa(p256_Point, sha256);
|
||||||
|
* const p256_sha224 = ecdsa(p256_Point, sha224);
|
||||||
|
* const p256_sha224_r = ecdsa(p256_Point, sha224, { randomBytes: (length) => { ... } });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export declare function ecdsa(Point: WeierstrassPointCons<bigint>, hash: CHash, ecdsaOpts?: ECDSAOpts): ECDSA;
|
||||||
|
/** @deprecated use ECDSASignature */
|
||||||
|
export type SignatureType = ECDSASignature;
|
||||||
|
/** @deprecated use ECDSASigRecovered */
|
||||||
|
export type RecoveredSignatureType = ECDSASigRecovered;
|
||||||
|
/** @deprecated switch to Uint8Array signatures in format 'compact' */
|
||||||
|
export type SignatureLike = {
|
||||||
|
r: bigint;
|
||||||
|
s: bigint;
|
||||||
|
};
|
||||||
|
export type ECDSAExtraEntropy = Hex | boolean;
|
||||||
|
/** @deprecated use `ECDSAExtraEntropy` */
|
||||||
|
export type Entropy = Hex | boolean;
|
||||||
|
export type BasicWCurve<T> = BasicCurve<T> & {
|
||||||
|
a: T;
|
||||||
|
b: T;
|
||||||
|
allowedPrivateKeyLengths?: readonly number[];
|
||||||
|
wrapPrivateKey?: boolean;
|
||||||
|
endo?: EndomorphismOpts;
|
||||||
|
isTorsionFree?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
|
||||||
|
clearCofactor?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
|
||||||
|
};
|
||||||
|
/** @deprecated use ECDSASignOpts */
|
||||||
|
export type SignOpts = ECDSASignOpts;
|
||||||
|
/** @deprecated use ECDSASignOpts */
|
||||||
|
export type VerOpts = ECDSAVerifyOpts;
|
||||||
|
/** @deprecated use WeierstrassPoint */
|
||||||
|
export type ProjPointType<T> = WeierstrassPoint<T>;
|
||||||
|
/** @deprecated use WeierstrassPointCons */
|
||||||
|
export type ProjConstructor<T> = WeierstrassPointCons<T>;
|
||||||
|
/** @deprecated use ECDSASignatureCons */
|
||||||
|
export type SignatureConstructor = ECDSASignatureCons;
|
||||||
|
export type CurvePointsType<T> = BasicWCurve<T> & {
|
||||||
|
fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
|
||||||
|
toBytes?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>, isCompressed: boolean) => Uint8Array;
|
||||||
|
};
|
||||||
|
export type CurvePointsTypeWithLength<T> = Readonly<CurvePointsType<T> & Partial<NLength>>;
|
||||||
|
export type CurvePointsRes<T> = {
|
||||||
|
Point: WeierstrassPointCons<T>;
|
||||||
|
/** @deprecated use `Point.CURVE()` */
|
||||||
|
CURVE: CurvePointsType<T>;
|
||||||
|
/** @deprecated use `Point` */
|
||||||
|
ProjectivePoint: WeierstrassPointCons<T>;
|
||||||
|
/** @deprecated use `Point.Fn.fromBytes(privateKey)` */
|
||||||
|
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
||||||
|
/** @deprecated */
|
||||||
|
weierstrassEquation: (x: T) => T;
|
||||||
|
/** @deprecated use `Point.Fn.isValidNot0(num)` */
|
||||||
|
isWithinCurveOrder: (num: bigint) => boolean;
|
||||||
|
};
|
||||||
|
/** @deprecated use `Uint8Array` */
|
||||||
|
export type PubKey = Hex | WeierstrassPoint<bigint>;
|
||||||
|
export type CurveType = BasicWCurve<bigint> & {
|
||||||
|
hash: CHash;
|
||||||
|
hmac?: HmacFnSync;
|
||||||
|
randomBytes?: (bytesLength?: number) => Uint8Array;
|
||||||
|
lowS?: boolean;
|
||||||
|
bits2int?: (bytes: Uint8Array) => bigint;
|
||||||
|
bits2int_modN?: (bytes: Uint8Array) => bigint;
|
||||||
|
};
|
||||||
|
export type CurveFn = {
|
||||||
|
/** @deprecated use `Point.CURVE()` */
|
||||||
|
CURVE: CurvePointsType<bigint>;
|
||||||
|
keygen: ECDSA['keygen'];
|
||||||
|
getPublicKey: ECDSA['getPublicKey'];
|
||||||
|
getSharedSecret: ECDSA['getSharedSecret'];
|
||||||
|
sign: ECDSA['sign'];
|
||||||
|
verify: ECDSA['verify'];
|
||||||
|
Point: WeierstrassPointCons<bigint>;
|
||||||
|
/** @deprecated use `Point` */
|
||||||
|
ProjectivePoint: WeierstrassPointCons<bigint>;
|
||||||
|
Signature: ECDSASignatureCons;
|
||||||
|
utils: ECDSA['utils'];
|
||||||
|
lengths: ECDSA['lengths'];
|
||||||
|
};
|
||||||
|
/** @deprecated use `weierstrass` in newer releases */
|
||||||
|
export declare function weierstrassPoints<T>(c: CurvePointsTypeWithLength<T>): CurvePointsRes<T>;
|
||||||
|
export type WsPointComposed<T> = {
|
||||||
|
CURVE: WeierstrassOpts<T>;
|
||||||
|
curveOpts: WeierstrassExtraOpts<T>;
|
||||||
|
};
|
||||||
|
export type WsComposed = {
|
||||||
|
/** @deprecated use `Point.CURVE()` */
|
||||||
|
CURVE: WeierstrassOpts<bigint>;
|
||||||
|
hash: CHash;
|
||||||
|
curveOpts: WeierstrassExtraOpts<bigint>;
|
||||||
|
ecdsaOpts: ECDSAOpts;
|
||||||
|
};
|
||||||
|
export declare function _legacyHelperEquat<T>(Fp: IField<T>, a: T, b: T): (x: T) => T;
|
||||||
|
export declare function weierstrass(c: CurveType): CurveFn;
|
||||||
|
//# sourceMappingURL=weierstrass.d.ts.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+1427
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+16
@@ -0,0 +1,16 @@
|
|||||||
|
import { type CurveFn } from './abstract/bls.ts';
|
||||||
|
import { type IField } from './abstract/modular.ts';
|
||||||
|
export declare const bls12_381_Fr: IField<bigint>;
|
||||||
|
/**
|
||||||
|
* bls12-381 pairing-friendly curve.
|
||||||
|
* @example
|
||||||
|
* import { bls12_381 as bls } from '@noble/curves/bls12-381';
|
||||||
|
* // G1 keys, G2 signatures
|
||||||
|
* const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
|
||||||
|
* const message = '64726e3da8';
|
||||||
|
* const publicKey = bls.getPublicKey(privateKey);
|
||||||
|
* const signature = bls.sign(message, privateKey);
|
||||||
|
* const isValid = bls.verify(signature, message, publicKey);
|
||||||
|
*/
|
||||||
|
export declare const bls12_381: CurveFn;
|
||||||
|
//# sourceMappingURL=bls12-381.d.ts.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"bls12-381.d.ts","sourceRoot":"","sources":["src/bls12-381.ts"],"names":[],"mappings":"AAgFA,OAAO,EAAO,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAS,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAmE3D,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,CAGtC,CAAC;AAuTH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,EAAE,OA8HtB,CAAC"}
|
||||||
+708
@@ -0,0 +1,708 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.bls12_381 = exports.bls12_381_Fr = void 0;
|
||||||
|
/**
|
||||||
|
* bls12-381 is pairing-friendly Barreto-Lynn-Scott elliptic curve construction allowing to:
|
||||||
|
|
||||||
|
* Construct zk-SNARKs at the ~120-bit security, as per [Barbulescu-Duquesne 2017](https://hal.science/hal-01534101/file/main.pdf)
|
||||||
|
* Efficiently verify N aggregate signatures with 1 pairing and N ec additions:
|
||||||
|
the Boneh-Lynn-Shacham signature scheme is orders of magnitude more efficient than Schnorr
|
||||||
|
|
||||||
|
BLS can mean 2 different things:
|
||||||
|
|
||||||
|
* Barreto-Lynn-Scott: BLS12, a Pairing Friendly Elliptic Curve
|
||||||
|
* Boneh-Lynn-Shacham: A Signature Scheme.
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
1. BLS Relies on expensive bilinear pairing
|
||||||
|
2. Secret Keys: 32 bytes
|
||||||
|
3. Public Keys: 48 OR 96 bytes - big-endian x coordinate of point on G1 OR G2 curve
|
||||||
|
4. Signatures: 96 OR 48 bytes - big-endian x coordinate of point on G2 OR G1 curve
|
||||||
|
5. The 12 stands for the Embedding degree.
|
||||||
|
|
||||||
|
Modes of operation:
|
||||||
|
|
||||||
|
* Long signatures: 48-byte keys + 96-byte sigs (G1 keys + G2 sigs).
|
||||||
|
* Short signatures: 96-byte keys + 48-byte sigs (G2 keys + G1 sigs).
|
||||||
|
|
||||||
|
### Formulas
|
||||||
|
|
||||||
|
- `P = pk x G` - public keys
|
||||||
|
- `S = pk x H(m)` - signing, uses hash-to-curve on m
|
||||||
|
- `e(P, H(m)) == e(G, S)` - verification using pairings
|
||||||
|
- `e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))` - signature aggregation
|
||||||
|
|
||||||
|
### Curves
|
||||||
|
|
||||||
|
G1 is ordinary elliptic curve. G2 is extension field curve, think "over complex numbers".
|
||||||
|
|
||||||
|
- G1: y² = x³ + 4
|
||||||
|
- G2: y² = x³ + 4(u + 1) where u = √−1; r-order subgroup of E'(Fp²), M-type twist
|
||||||
|
|
||||||
|
### Towers
|
||||||
|
|
||||||
|
Pairing G1 + G2 produces element in Fp₁₂, 12-degree polynomial.
|
||||||
|
Fp₁₂ is usually implemented using tower of lower-degree polynomials for speed.
|
||||||
|
|
||||||
|
- Fp₁₂ = Fp₆² => Fp₂³
|
||||||
|
- Fp(u) / (u² - β) where β = -1
|
||||||
|
- Fp₂(v) / (v³ - ξ) where ξ = u + 1
|
||||||
|
- Fp₆(w) / (w² - γ) where γ = v
|
||||||
|
- Fp²[u] = Fp/u²+1
|
||||||
|
- Fp⁶[v] = Fp²/v³-1-u
|
||||||
|
- Fp¹²[w] = Fp⁶/w²-v
|
||||||
|
|
||||||
|
### Params
|
||||||
|
|
||||||
|
* Embedding degree (k): 12
|
||||||
|
* Seed is sometimes named x or t
|
||||||
|
* t = -15132376222941642752
|
||||||
|
* p = (t-1)² * (t⁴-t²+1)/3 + t
|
||||||
|
* r = t⁴-t²+1
|
||||||
|
* Ate loop size: X
|
||||||
|
|
||||||
|
To verify curve parameters, see
|
||||||
|
[pairing-friendly-curves spec](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11).
|
||||||
|
Basic math is done over finite fields over p.
|
||||||
|
More complicated math is done over polynominal extension fields.
|
||||||
|
|
||||||
|
### Compatibility and notes
|
||||||
|
1. It is compatible with Algorand, Chia, Dfinity, Ethereum, Filecoin, ZEC.
|
||||||
|
Filecoin uses little endian byte arrays for secret keys - make sure to reverse byte order.
|
||||||
|
2. Make sure to correctly select mode: "long signature" or "short signature".
|
||||||
|
3. Compatible with specs:
|
||||||
|
RFC 9380,
|
||||||
|
[cfrg-pairing-friendly-curves-11](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11),
|
||||||
|
[cfrg-bls-signature-05](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/).
|
||||||
|
|
||||||
|
*
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
const sha2_js_1 = require("@noble/hashes/sha2.js");
|
||||||
|
const bls_ts_1 = require("./abstract/bls.js");
|
||||||
|
const modular_ts_1 = require("./abstract/modular.js");
|
||||||
|
const utils_ts_1 = require("./utils.js");
|
||||||
|
// Types
|
||||||
|
const hash_to_curve_ts_1 = require("./abstract/hash-to-curve.js");
|
||||||
|
const tower_ts_1 = require("./abstract/tower.js");
|
||||||
|
const weierstrass_ts_1 = require("./abstract/weierstrass.js");
|
||||||
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
||||||
|
// prettier-ignore
|
||||||
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
||||||
|
// To verify math:
|
||||||
|
// https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11
|
||||||
|
// The BLS parameter x (seed) for BLS12-381. NOTE: it is negative!
|
||||||
|
// x = -2^63 - 2^62 - 2^60 - 2^57 - 2^48 - 2^16
|
||||||
|
const BLS_X = BigInt('0xd201000000010000');
|
||||||
|
// t = x (called differently in different places)
|
||||||
|
// const t = -BLS_X;
|
||||||
|
const BLS_X_LEN = (0, utils_ts_1.bitLen)(BLS_X);
|
||||||
|
// a=0, b=4
|
||||||
|
// P is characteristic of field Fp, in which curve calculations are done.
|
||||||
|
// p = (t-1)² * (t⁴-t²+1)/3 + t
|
||||||
|
// bls12_381_Fp = (t-1n)**2n * (t**4n - t**2n + 1n) / 3n + t
|
||||||
|
// r*h is curve order, amount of points on curve,
|
||||||
|
// where r is order of prime subgroup and h is cofactor.
|
||||||
|
// r = t⁴-t²+1
|
||||||
|
// r = (t**4n - t**2n + 1n)
|
||||||
|
// cofactor h of G1: (t - 1)²/3
|
||||||
|
// cofactorG1 = (t-1n)**2n/3n
|
||||||
|
// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
|
||||||
|
// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
|
||||||
|
const bls12_381_CURVE_G1 = {
|
||||||
|
p: BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'),
|
||||||
|
n: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'),
|
||||||
|
h: BigInt('0x396c8c005555e1568c00aaab0000aaab'),
|
||||||
|
a: _0n,
|
||||||
|
b: _4n,
|
||||||
|
Gx: BigInt('0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'),
|
||||||
|
Gy: BigInt('0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'),
|
||||||
|
};
|
||||||
|
// CURVE FIELDS
|
||||||
|
exports.bls12_381_Fr = (0, modular_ts_1.Field)(bls12_381_CURVE_G1.n, {
|
||||||
|
modFromBytes: true,
|
||||||
|
isLE: true,
|
||||||
|
});
|
||||||
|
const { Fp, Fp2, Fp6, Fp12 } = (0, tower_ts_1.tower12)({
|
||||||
|
ORDER: bls12_381_CURVE_G1.p,
|
||||||
|
X_LEN: BLS_X_LEN,
|
||||||
|
// Finite extension field over irreducible polynominal.
|
||||||
|
// Fp(u) / (u² - β) where β = -1
|
||||||
|
FP2_NONRESIDUE: [_1n, _1n],
|
||||||
|
Fp2mulByB: ({ c0, c1 }) => {
|
||||||
|
const t0 = Fp.mul(c0, _4n); // 4 * c0
|
||||||
|
const t1 = Fp.mul(c1, _4n); // 4 * c1
|
||||||
|
// (T0-T1) + (T0+T1)*i
|
||||||
|
return { c0: Fp.sub(t0, t1), c1: Fp.add(t0, t1) };
|
||||||
|
},
|
||||||
|
Fp12finalExponentiate: (num) => {
|
||||||
|
const x = BLS_X;
|
||||||
|
// this^(q⁶) / this
|
||||||
|
const t0 = Fp12.div(Fp12.frobeniusMap(num, 6), num);
|
||||||
|
// t0^(q²) * t0
|
||||||
|
const t1 = Fp12.mul(Fp12.frobeniusMap(t0, 2), t0);
|
||||||
|
const t2 = Fp12.conjugate(Fp12._cyclotomicExp(t1, x));
|
||||||
|
const t3 = Fp12.mul(Fp12.conjugate(Fp12._cyclotomicSquare(t1)), t2);
|
||||||
|
const t4 = Fp12.conjugate(Fp12._cyclotomicExp(t3, x));
|
||||||
|
const t5 = Fp12.conjugate(Fp12._cyclotomicExp(t4, x));
|
||||||
|
const t6 = Fp12.mul(Fp12.conjugate(Fp12._cyclotomicExp(t5, x)), Fp12._cyclotomicSquare(t2));
|
||||||
|
const t7 = Fp12.conjugate(Fp12._cyclotomicExp(t6, x));
|
||||||
|
const t2_t5_pow_q2 = Fp12.frobeniusMap(Fp12.mul(t2, t5), 2);
|
||||||
|
const t4_t1_pow_q3 = Fp12.frobeniusMap(Fp12.mul(t4, t1), 3);
|
||||||
|
const t6_t1c_pow_q1 = Fp12.frobeniusMap(Fp12.mul(t6, Fp12.conjugate(t1)), 1);
|
||||||
|
const t7_t3c_t1 = Fp12.mul(Fp12.mul(t7, Fp12.conjugate(t3)), t1);
|
||||||
|
// (t2 * t5)^(q²) * (t4 * t1)^(q³) * (t6 * t1.conj)^(q^1) * t7 * t3.conj * t1
|
||||||
|
return Fp12.mul(Fp12.mul(Fp12.mul(t2_t5_pow_q2, t4_t1_pow_q3), t6_t1c_pow_q1), t7_t3c_t1);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// GLV endomorphism Ψ(P), for fast cofactor clearing
|
||||||
|
const { G2psi, G2psi2 } = (0, tower_ts_1.psiFrobenius)(Fp, Fp2, Fp2.div(Fp2.ONE, Fp2.NONRESIDUE)); // 1/(u+1)
|
||||||
|
/**
|
||||||
|
* Default hash_to_field / hash-to-curve for BLS.
|
||||||
|
* m: 1 for G1, 2 for G2
|
||||||
|
* k: target security level in bits
|
||||||
|
* hash: any function, e.g. BBS+ uses BLAKE2: see [github](https://github.com/hyperledger/aries-framework-go/issues/2247).
|
||||||
|
* Parameter values come from [section 8.8.2 of RFC 9380](https://www.rfc-editor.org/rfc/rfc9380#section-8.8.2).
|
||||||
|
*/
|
||||||
|
const htfDefaults = Object.freeze({
|
||||||
|
DST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
|
||||||
|
encodeDST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
|
||||||
|
p: Fp.ORDER,
|
||||||
|
m: 2,
|
||||||
|
k: 128,
|
||||||
|
expand: 'xmd',
|
||||||
|
hash: sha2_js_1.sha256,
|
||||||
|
});
|
||||||
|
// a=0, b=4
|
||||||
|
// cofactor h of G2
|
||||||
|
// (t^8 - 4t^7 + 5t^6 - 4t^4 + 6t^3 - 4t^2 - 4t + 13)/9
|
||||||
|
// cofactorG2 = (t**8n - 4n*t**7n + 5n*t**6n - 4n*t**4n + 6n*t**3n - 4n*t**2n - 4n*t+13n)/9n
|
||||||
|
// x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160
|
||||||
|
// y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
|
||||||
|
const bls12_381_CURVE_G2 = {
|
||||||
|
p: Fp2.ORDER,
|
||||||
|
n: bls12_381_CURVE_G1.n,
|
||||||
|
h: BigInt('0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5'),
|
||||||
|
a: Fp2.ZERO,
|
||||||
|
b: Fp2.fromBigTuple([_4n, _4n]),
|
||||||
|
Gx: Fp2.fromBigTuple([
|
||||||
|
BigInt('0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8'),
|
||||||
|
BigInt('0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e'),
|
||||||
|
]),
|
||||||
|
Gy: Fp2.fromBigTuple([
|
||||||
|
BigInt('0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801'),
|
||||||
|
BigInt('0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be'),
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
// Encoding utils
|
||||||
|
// Compressed point of infinity
|
||||||
|
// Set compressed & point-at-infinity bits
|
||||||
|
const COMPZERO = setMask(Fp.toBytes(_0n), { infinity: true, compressed: true });
|
||||||
|
function parseMask(bytes) {
|
||||||
|
// Copy, so we can remove mask data. It will be removed also later, when Fp.create will call modulo.
|
||||||
|
bytes = bytes.slice();
|
||||||
|
const mask = bytes[0] & 224;
|
||||||
|
const compressed = !!((mask >> 7) & 1); // compression bit (0b1000_0000)
|
||||||
|
const infinity = !!((mask >> 6) & 1); // point at infinity bit (0b0100_0000)
|
||||||
|
const sort = !!((mask >> 5) & 1); // sort bit (0b0010_0000)
|
||||||
|
bytes[0] &= 31; // clear mask (zero first 3 bits)
|
||||||
|
return { compressed, infinity, sort, value: bytes };
|
||||||
|
}
|
||||||
|
function setMask(bytes, mask) {
|
||||||
|
if (bytes[0] & 224)
|
||||||
|
throw new Error('setMask: non-empty mask');
|
||||||
|
if (mask.compressed)
|
||||||
|
bytes[0] |= 128;
|
||||||
|
if (mask.infinity)
|
||||||
|
bytes[0] |= 64;
|
||||||
|
if (mask.sort)
|
||||||
|
bytes[0] |= 32;
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
function pointG1ToBytes(_c, point, isComp) {
|
||||||
|
const { BYTES: L, ORDER: P } = Fp;
|
||||||
|
const is0 = point.is0();
|
||||||
|
const { x, y } = point.toAffine();
|
||||||
|
if (isComp) {
|
||||||
|
if (is0)
|
||||||
|
return COMPZERO.slice();
|
||||||
|
const sort = Boolean((y * _2n) / P);
|
||||||
|
return setMask((0, utils_ts_1.numberToBytesBE)(x, L), { compressed: true, sort });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (is0) {
|
||||||
|
return (0, utils_ts_1.concatBytes)(Uint8Array.of(0x40), new Uint8Array(2 * L - 1));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (0, utils_ts_1.concatBytes)((0, utils_ts_1.numberToBytesBE)(x, L), (0, utils_ts_1.numberToBytesBE)(y, L));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function signatureG1ToBytes(point) {
|
||||||
|
point.assertValidity();
|
||||||
|
const { BYTES: L, ORDER: P } = Fp;
|
||||||
|
const { x, y } = point.toAffine();
|
||||||
|
if (point.is0())
|
||||||
|
return COMPZERO.slice();
|
||||||
|
const sort = Boolean((y * _2n) / P);
|
||||||
|
return setMask((0, utils_ts_1.numberToBytesBE)(x, L), { compressed: true, sort });
|
||||||
|
}
|
||||||
|
function pointG1FromBytes(bytes) {
|
||||||
|
const { compressed, infinity, sort, value } = parseMask(bytes);
|
||||||
|
const { BYTES: L, ORDER: P } = Fp;
|
||||||
|
if (value.length === 48 && compressed) {
|
||||||
|
const compressedValue = (0, utils_ts_1.bytesToNumberBE)(value);
|
||||||
|
// Zero
|
||||||
|
const x = Fp.create(compressedValue & (0, utils_ts_1.bitMask)(Fp.BITS));
|
||||||
|
if (infinity) {
|
||||||
|
if (x !== _0n)
|
||||||
|
throw new Error('invalid G1 point: non-empty, at infinity, with compression');
|
||||||
|
return { x: _0n, y: _0n };
|
||||||
|
}
|
||||||
|
const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381_CURVE_G1.b)); // y² = x³ + b
|
||||||
|
let y = Fp.sqrt(right);
|
||||||
|
if (!y)
|
||||||
|
throw new Error('invalid G1 point: compressed point');
|
||||||
|
if ((y * _2n) / P !== BigInt(sort))
|
||||||
|
y = Fp.neg(y);
|
||||||
|
return { x: Fp.create(x), y: Fp.create(y) };
|
||||||
|
}
|
||||||
|
else if (value.length === 96 && !compressed) {
|
||||||
|
// Check if the infinity flag is set
|
||||||
|
const x = (0, utils_ts_1.bytesToNumberBE)(value.subarray(0, L));
|
||||||
|
const y = (0, utils_ts_1.bytesToNumberBE)(value.subarray(L));
|
||||||
|
if (infinity) {
|
||||||
|
if (x !== _0n || y !== _0n)
|
||||||
|
throw new Error('G1: non-empty point at infinity');
|
||||||
|
return exports.bls12_381.G1.Point.ZERO.toAffine();
|
||||||
|
}
|
||||||
|
return { x: Fp.create(x), y: Fp.create(y) };
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error('invalid G1 point: expected 48/96 bytes');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function signatureG1FromBytes(hex) {
|
||||||
|
const { infinity, sort, value } = parseMask((0, utils_ts_1.ensureBytes)('signatureHex', hex, 48));
|
||||||
|
const P = Fp.ORDER;
|
||||||
|
const Point = exports.bls12_381.G1.Point;
|
||||||
|
const compressedValue = (0, utils_ts_1.bytesToNumberBE)(value);
|
||||||
|
// Zero
|
||||||
|
if (infinity)
|
||||||
|
return Point.ZERO;
|
||||||
|
const x = Fp.create(compressedValue & (0, utils_ts_1.bitMask)(Fp.BITS));
|
||||||
|
const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381_CURVE_G1.b)); // y² = x³ + b
|
||||||
|
let y = Fp.sqrt(right);
|
||||||
|
if (!y)
|
||||||
|
throw new Error('invalid G1 point: compressed');
|
||||||
|
const aflag = BigInt(sort);
|
||||||
|
if ((y * _2n) / P !== aflag)
|
||||||
|
y = Fp.neg(y);
|
||||||
|
const point = Point.fromAffine({ x, y });
|
||||||
|
point.assertValidity();
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
function pointG2ToBytes(_c, point, isComp) {
|
||||||
|
const { BYTES: L, ORDER: P } = Fp;
|
||||||
|
const is0 = point.is0();
|
||||||
|
const { x, y } = point.toAffine();
|
||||||
|
if (isComp) {
|
||||||
|
if (is0)
|
||||||
|
return (0, utils_ts_1.concatBytes)(COMPZERO, (0, utils_ts_1.numberToBytesBE)(_0n, L));
|
||||||
|
const flag = Boolean(y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P);
|
||||||
|
return (0, utils_ts_1.concatBytes)(setMask((0, utils_ts_1.numberToBytesBE)(x.c1, L), { compressed: true, sort: flag }), (0, utils_ts_1.numberToBytesBE)(x.c0, L));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (is0)
|
||||||
|
return (0, utils_ts_1.concatBytes)(Uint8Array.of(0x40), new Uint8Array(4 * L - 1));
|
||||||
|
const { re: x0, im: x1 } = Fp2.reim(x);
|
||||||
|
const { re: y0, im: y1 } = Fp2.reim(y);
|
||||||
|
return (0, utils_ts_1.concatBytes)((0, utils_ts_1.numberToBytesBE)(x1, L), (0, utils_ts_1.numberToBytesBE)(x0, L), (0, utils_ts_1.numberToBytesBE)(y1, L), (0, utils_ts_1.numberToBytesBE)(y0, L));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function signatureG2ToBytes(point) {
|
||||||
|
point.assertValidity();
|
||||||
|
const { BYTES: L } = Fp;
|
||||||
|
if (point.is0())
|
||||||
|
return (0, utils_ts_1.concatBytes)(COMPZERO, (0, utils_ts_1.numberToBytesBE)(_0n, L));
|
||||||
|
const { x, y } = point.toAffine();
|
||||||
|
const { re: x0, im: x1 } = Fp2.reim(x);
|
||||||
|
const { re: y0, im: y1 } = Fp2.reim(y);
|
||||||
|
const tmp = y1 > _0n ? y1 * _2n : y0 * _2n;
|
||||||
|
const sort = Boolean((tmp / Fp.ORDER) & _1n);
|
||||||
|
const z2 = x0;
|
||||||
|
return (0, utils_ts_1.concatBytes)(setMask((0, utils_ts_1.numberToBytesBE)(x1, L), { sort, compressed: true }), (0, utils_ts_1.numberToBytesBE)(z2, L));
|
||||||
|
}
|
||||||
|
function pointG2FromBytes(bytes) {
|
||||||
|
const { BYTES: L, ORDER: P } = Fp;
|
||||||
|
const { compressed, infinity, sort, value } = parseMask(bytes);
|
||||||
|
if ((!compressed && !infinity && sort) || // 00100000
|
||||||
|
(!compressed && infinity && sort) || // 01100000
|
||||||
|
(sort && infinity && compressed) // 11100000
|
||||||
|
) {
|
||||||
|
throw new Error('invalid encoding flag: ' + (bytes[0] & 224));
|
||||||
|
}
|
||||||
|
const slc = (b, from, to) => (0, utils_ts_1.bytesToNumberBE)(b.slice(from, to));
|
||||||
|
if (value.length === 96 && compressed) {
|
||||||
|
if (infinity) {
|
||||||
|
// check that all bytes are 0
|
||||||
|
if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
|
||||||
|
throw new Error('invalid G2 point: compressed');
|
||||||
|
}
|
||||||
|
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
||||||
|
}
|
||||||
|
const x_1 = slc(value, 0, L);
|
||||||
|
const x_0 = slc(value, L, 2 * L);
|
||||||
|
const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
|
||||||
|
const right = Fp2.add(Fp2.pow(x, _3n), bls12_381_CURVE_G2.b); // y² = x³ + 4 * (u+1) = x³ + b
|
||||||
|
let y = Fp2.sqrt(right);
|
||||||
|
const Y_bit = y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P ? _1n : _0n;
|
||||||
|
y = sort && Y_bit > 0 ? y : Fp2.neg(y);
|
||||||
|
return { x, y };
|
||||||
|
}
|
||||||
|
else if (value.length === 192 && !compressed) {
|
||||||
|
if (infinity) {
|
||||||
|
if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
|
||||||
|
throw new Error('invalid G2 point: uncompressed');
|
||||||
|
}
|
||||||
|
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
||||||
|
}
|
||||||
|
const x1 = slc(value, 0 * L, 1 * L);
|
||||||
|
const x0 = slc(value, 1 * L, 2 * L);
|
||||||
|
const y1 = slc(value, 2 * L, 3 * L);
|
||||||
|
const y0 = slc(value, 3 * L, 4 * L);
|
||||||
|
return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error('invalid G2 point: expected 96/192 bytes');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function signatureG2FromBytes(hex) {
|
||||||
|
const { ORDER: P } = Fp;
|
||||||
|
// TODO: Optimize, it's very slow because of sqrt.
|
||||||
|
const { infinity, sort, value } = parseMask((0, utils_ts_1.ensureBytes)('signatureHex', hex));
|
||||||
|
const Point = exports.bls12_381.G2.Point;
|
||||||
|
const half = value.length / 2;
|
||||||
|
if (half !== 48 && half !== 96)
|
||||||
|
throw new Error('invalid compressed signature length, expected 96/192 bytes');
|
||||||
|
const z1 = (0, utils_ts_1.bytesToNumberBE)(value.slice(0, half));
|
||||||
|
const z2 = (0, utils_ts_1.bytesToNumberBE)(value.slice(half));
|
||||||
|
// Indicates the infinity point
|
||||||
|
if (infinity)
|
||||||
|
return Point.ZERO;
|
||||||
|
const x1 = Fp.create(z1 & (0, utils_ts_1.bitMask)(Fp.BITS));
|
||||||
|
const x2 = Fp.create(z2);
|
||||||
|
const x = Fp2.create({ c0: x2, c1: x1 });
|
||||||
|
const y2 = Fp2.add(Fp2.pow(x, _3n), bls12_381_CURVE_G2.b); // y² = x³ + 4
|
||||||
|
// The slow part
|
||||||
|
let y = Fp2.sqrt(y2);
|
||||||
|
if (!y)
|
||||||
|
throw new Error('Failed to find a square root');
|
||||||
|
// Choose the y whose leftmost bit of the imaginary part is equal to the a_flag1
|
||||||
|
// If y1 happens to be zero, then use the bit of y0
|
||||||
|
const { re: y0, im: y1 } = Fp2.reim(y);
|
||||||
|
const aflag1 = BigInt(sort);
|
||||||
|
const isGreater = y1 > _0n && (y1 * _2n) / P !== aflag1;
|
||||||
|
const is0 = y1 === _0n && (y0 * _2n) / P !== aflag1;
|
||||||
|
if (isGreater || is0)
|
||||||
|
y = Fp2.neg(y);
|
||||||
|
const point = Point.fromAffine({ x, y });
|
||||||
|
point.assertValidity();
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* bls12-381 pairing-friendly curve.
|
||||||
|
* @example
|
||||||
|
* import { bls12_381 as bls } from '@noble/curves/bls12-381';
|
||||||
|
* // G1 keys, G2 signatures
|
||||||
|
* const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
|
||||||
|
* const message = '64726e3da8';
|
||||||
|
* const publicKey = bls.getPublicKey(privateKey);
|
||||||
|
* const signature = bls.sign(message, privateKey);
|
||||||
|
* const isValid = bls.verify(signature, message, publicKey);
|
||||||
|
*/
|
||||||
|
exports.bls12_381 = (0, bls_ts_1.bls)({
|
||||||
|
// Fields
|
||||||
|
fields: {
|
||||||
|
Fp,
|
||||||
|
Fp2,
|
||||||
|
Fp6,
|
||||||
|
Fp12,
|
||||||
|
Fr: exports.bls12_381_Fr,
|
||||||
|
},
|
||||||
|
// G1: y² = x³ + 4
|
||||||
|
G1: {
|
||||||
|
...bls12_381_CURVE_G1,
|
||||||
|
Fp,
|
||||||
|
htfDefaults: { ...htfDefaults, m: 1, DST: 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_' },
|
||||||
|
wrapPrivateKey: true,
|
||||||
|
allowInfinityPoint: true,
|
||||||
|
// Checks is the point resides in prime-order subgroup.
|
||||||
|
// point.isTorsionFree() should return true for valid points
|
||||||
|
// It returns false for shitty points.
|
||||||
|
// https://eprint.iacr.org/2021/1130.pdf
|
||||||
|
isTorsionFree: (c, point) => {
|
||||||
|
// GLV endomorphism ψ(P)
|
||||||
|
const beta = BigInt('0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe');
|
||||||
|
const phi = new c(Fp.mul(point.X, beta), point.Y, point.Z);
|
||||||
|
// TODO: unroll
|
||||||
|
const xP = point.multiplyUnsafe(BLS_X).negate(); // [x]P
|
||||||
|
const u2P = xP.multiplyUnsafe(BLS_X); // [u2]P
|
||||||
|
return u2P.equals(phi);
|
||||||
|
},
|
||||||
|
// Clear cofactor of G1
|
||||||
|
// https://eprint.iacr.org/2019/403
|
||||||
|
clearCofactor: (_c, point) => {
|
||||||
|
// return this.multiplyUnsafe(CURVE.h);
|
||||||
|
return point.multiplyUnsafe(BLS_X).add(point); // x*P + P
|
||||||
|
},
|
||||||
|
mapToCurve: mapToG1,
|
||||||
|
fromBytes: pointG1FromBytes,
|
||||||
|
toBytes: pointG1ToBytes,
|
||||||
|
ShortSignature: {
|
||||||
|
fromBytes(bytes) {
|
||||||
|
(0, utils_ts_1.abytes)(bytes);
|
||||||
|
return signatureG1FromBytes(bytes);
|
||||||
|
},
|
||||||
|
fromHex(hex) {
|
||||||
|
return signatureG1FromBytes(hex);
|
||||||
|
},
|
||||||
|
toBytes(point) {
|
||||||
|
return signatureG1ToBytes(point);
|
||||||
|
},
|
||||||
|
toRawBytes(point) {
|
||||||
|
return signatureG1ToBytes(point);
|
||||||
|
},
|
||||||
|
toHex(point) {
|
||||||
|
return (0, utils_ts_1.bytesToHex)(signatureG1ToBytes(point));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
G2: {
|
||||||
|
...bls12_381_CURVE_G2,
|
||||||
|
Fp: Fp2,
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc9380#name-clearing-the-cofactor
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc9380#name-cofactor-clearing-for-bls12
|
||||||
|
hEff: BigInt('0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551'),
|
||||||
|
htfDefaults: { ...htfDefaults },
|
||||||
|
wrapPrivateKey: true,
|
||||||
|
allowInfinityPoint: true,
|
||||||
|
mapToCurve: mapToG2,
|
||||||
|
// Checks is the point resides in prime-order subgroup.
|
||||||
|
// point.isTorsionFree() should return true for valid points
|
||||||
|
// It returns false for shitty points.
|
||||||
|
// https://eprint.iacr.org/2021/1130.pdf
|
||||||
|
// Older version: https://eprint.iacr.org/2019/814.pdf
|
||||||
|
isTorsionFree: (c, P) => {
|
||||||
|
return P.multiplyUnsafe(BLS_X).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
|
||||||
|
},
|
||||||
|
// Maps the point into the prime-order subgroup G2.
|
||||||
|
// clear_cofactor_bls12381_g2 from RFC 9380.
|
||||||
|
// https://eprint.iacr.org/2017/419.pdf
|
||||||
|
// prettier-ignore
|
||||||
|
clearCofactor: (c, P) => {
|
||||||
|
const x = BLS_X;
|
||||||
|
let t1 = P.multiplyUnsafe(x).negate(); // [-x]P
|
||||||
|
let t2 = G2psi(c, P); // Ψ(P)
|
||||||
|
let t3 = P.double(); // 2P
|
||||||
|
t3 = G2psi2(c, t3); // Ψ²(2P)
|
||||||
|
t3 = t3.subtract(t2); // Ψ²(2P) - Ψ(P)
|
||||||
|
t2 = t1.add(t2); // [-x]P + Ψ(P)
|
||||||
|
t2 = t2.multiplyUnsafe(x).negate(); // [x²]P - [x]Ψ(P)
|
||||||
|
t3 = t3.add(t2); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P)
|
||||||
|
t3 = t3.subtract(t1); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P
|
||||||
|
const Q = t3.subtract(P); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P - 1P
|
||||||
|
return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
|
||||||
|
},
|
||||||
|
fromBytes: pointG2FromBytes,
|
||||||
|
toBytes: pointG2ToBytes,
|
||||||
|
Signature: {
|
||||||
|
fromBytes(bytes) {
|
||||||
|
(0, utils_ts_1.abytes)(bytes);
|
||||||
|
return signatureG2FromBytes(bytes);
|
||||||
|
},
|
||||||
|
fromHex(hex) {
|
||||||
|
return signatureG2FromBytes(hex);
|
||||||
|
},
|
||||||
|
toBytes(point) {
|
||||||
|
return signatureG2ToBytes(point);
|
||||||
|
},
|
||||||
|
toRawBytes(point) {
|
||||||
|
return signatureG2ToBytes(point);
|
||||||
|
},
|
||||||
|
toHex(point) {
|
||||||
|
return (0, utils_ts_1.bytesToHex)(signatureG2ToBytes(point));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
ateLoopSize: BLS_X, // The BLS parameter x for BLS12-381
|
||||||
|
r: bls12_381_CURVE_G1.n, // order; z⁴ − z² + 1; CURVE.n from other curves
|
||||||
|
xNegative: true,
|
||||||
|
twistType: 'multiplicative',
|
||||||
|
},
|
||||||
|
htfDefaults,
|
||||||
|
hash: sha2_js_1.sha256,
|
||||||
|
});
|
||||||
|
// 3-isogeny map from E' to E https://www.rfc-editor.org/rfc/rfc9380#appendix-E.3
|
||||||
|
const isogenyMapG2 = (0, hash_to_curve_ts_1.isogenyMap)(Fp2, [
|
||||||
|
// xNum
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97d6',
|
||||||
|
'0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97d6',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'0x0',
|
||||||
|
'0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71a',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71e',
|
||||||
|
'0x8ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38d',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'0x171d6541fa38ccfaed6dea691f5fb614cb14b4e7f4e810aa22d6108f142b85757098e38d0f671c7188e2aaaaaaaa5ed1',
|
||||||
|
'0x0',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
// xDen
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'0x0',
|
||||||
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa63',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'0xc',
|
||||||
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa9f',
|
||||||
|
],
|
||||||
|
['0x1', '0x0'], // LAST 1
|
||||||
|
],
|
||||||
|
// yNum
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'0x1530477c7ab4113b59a4c18b076d11930f7da5d4a07f649bf54439d87d27e500fc8c25ebf8c92f6812cfc71c71c6d706',
|
||||||
|
'0x1530477c7ab4113b59a4c18b076d11930f7da5d4a07f649bf54439d87d27e500fc8c25ebf8c92f6812cfc71c71c6d706',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'0x0',
|
||||||
|
'0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97be',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71c',
|
||||||
|
'0x8ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38f',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'0x124c9ad43b6cf79bfbf7043de3811ad0761b0f37a1e26286b0e977c69aa274524e79097a56dc4bd9e1b371c71c718b10',
|
||||||
|
'0x0',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
// yDen
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8fb',
|
||||||
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8fb',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'0x0',
|
||||||
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa9d3',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'0x12',
|
||||||
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa99',
|
||||||
|
],
|
||||||
|
['0x1', '0x0'], // LAST 1
|
||||||
|
],
|
||||||
|
].map((i) => i.map((pair) => Fp2.fromBigTuple(pair.map(BigInt)))));
|
||||||
|
// 11-isogeny map from E' to E
|
||||||
|
const isogenyMapG1 = (0, hash_to_curve_ts_1.isogenyMap)(Fp, [
|
||||||
|
// xNum
|
||||||
|
[
|
||||||
|
'0x11a05f2b1e833340b809101dd99815856b303e88a2d7005ff2627b56cdb4e2c85610c2d5f2e62d6eaeac1662734649b7',
|
||||||
|
'0x17294ed3e943ab2f0588bab22147a81c7c17e75b2f6a8417f565e33c70d1e86b4838f2a6f318c356e834eef1b3cb83bb',
|
||||||
|
'0xd54005db97678ec1d1048c5d10a9a1bce032473295983e56878e501ec68e25c958c3e3d2a09729fe0179f9dac9edcb0',
|
||||||
|
'0x1778e7166fcc6db74e0609d307e55412d7f5e4656a8dbf25f1b33289f1b330835336e25ce3107193c5b388641d9b6861',
|
||||||
|
'0xe99726a3199f4436642b4b3e4118e5499db995a1257fb3f086eeb65982fac18985a286f301e77c451154ce9ac8895d9',
|
||||||
|
'0x1630c3250d7313ff01d1201bf7a74ab5db3cb17dd952799b9ed3ab9097e68f90a0870d2dcae73d19cd13c1c66f652983',
|
||||||
|
'0xd6ed6553fe44d296a3726c38ae652bfb11586264f0f8ce19008e218f9c86b2a8da25128c1052ecaddd7f225a139ed84',
|
||||||
|
'0x17b81e7701abdbe2e8743884d1117e53356de5ab275b4db1a682c62ef0f2753339b7c8f8c8f475af9ccb5618e3f0c88e',
|
||||||
|
'0x80d3cf1f9a78fc47b90b33563be990dc43b756ce79f5574a2c596c928c5d1de4fa295f296b74e956d71986a8497e317',
|
||||||
|
'0x169b1f8e1bcfa7c42e0c37515d138f22dd2ecb803a0c5c99676314baf4bb1b7fa3190b2edc0327797f241067be390c9e',
|
||||||
|
'0x10321da079ce07e272d8ec09d2565b0dfa7dccdde6787f96d50af36003b14866f69b771f8c285decca67df3f1605fb7b',
|
||||||
|
'0x6e08c248e260e70bd1e962381edee3d31d79d7e22c837bc23c0bf1bc24c6b68c24b1b80b64d391fa9c8ba2e8ba2d229',
|
||||||
|
],
|
||||||
|
// xDen
|
||||||
|
[
|
||||||
|
'0x8ca8d548cff19ae18b2e62f4bd3fa6f01d5ef4ba35b48ba9c9588617fc8ac62b558d681be343df8993cf9fa40d21b1c',
|
||||||
|
'0x12561a5deb559c4348b4711298e536367041e8ca0cf0800c0126c2588c48bf5713daa8846cb026e9e5c8276ec82b3bff',
|
||||||
|
'0xb2962fe57a3225e8137e629bff2991f6f89416f5a718cd1fca64e00b11aceacd6a3d0967c94fedcfcc239ba5cb83e19',
|
||||||
|
'0x3425581a58ae2fec83aafef7c40eb545b08243f16b1655154cca8abc28d6fd04976d5243eecf5c4130de8938dc62cd8',
|
||||||
|
'0x13a8e162022914a80a6f1d5f43e7a07dffdfc759a12062bb8d6b44e833b306da9bd29ba81f35781d539d395b3532a21e',
|
||||||
|
'0xe7355f8e4e667b955390f7f0506c6e9395735e9ce9cad4d0a43bcef24b8982f7400d24bc4228f11c02df9a29f6304a5',
|
||||||
|
'0x772caacf16936190f3e0c63e0596721570f5799af53a1894e2e073062aede9cea73b3538f0de06cec2574496ee84a3a',
|
||||||
|
'0x14a7ac2a9d64a8b230b3f5b074cf01996e7f63c21bca68a81996e1cdf9822c580fa5b9489d11e2d311f7d99bbdcc5a5e',
|
||||||
|
'0xa10ecf6ada54f825e920b3dafc7a3cce07f8d1d7161366b74100da67f39883503826692abba43704776ec3a79a1d641',
|
||||||
|
'0x95fc13ab9e92ad4476d6e3eb3a56680f682b4ee96f7d03776df533978f31c1593174e4b4b7865002d6384d168ecdd0a',
|
||||||
|
'0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
||||||
|
],
|
||||||
|
// yNum
|
||||||
|
[
|
||||||
|
'0x90d97c81ba24ee0259d1f094980dcfa11ad138e48a869522b52af6c956543d3cd0c7aee9b3ba3c2be9845719707bb33',
|
||||||
|
'0x134996a104ee5811d51036d776fb46831223e96c254f383d0f906343eb67ad34d6c56711962fa8bfe097e75a2e41c696',
|
||||||
|
'0xcc786baa966e66f4a384c86a3b49942552e2d658a31ce2c344be4b91400da7d26d521628b00523b8dfe240c72de1f6',
|
||||||
|
'0x1f86376e8981c217898751ad8746757d42aa7b90eeb791c09e4a3ec03251cf9de405aba9ec61deca6355c77b0e5f4cb',
|
||||||
|
'0x8cc03fdefe0ff135caf4fe2a21529c4195536fbe3ce50b879833fd221351adc2ee7f8dc099040a841b6daecf2e8fedb',
|
||||||
|
'0x16603fca40634b6a2211e11db8f0a6a074a7d0d4afadb7bd76505c3d3ad5544e203f6326c95a807299b23ab13633a5f0',
|
||||||
|
'0x4ab0b9bcfac1bbcb2c977d027796b3ce75bb8ca2be184cb5231413c4d634f3747a87ac2460f415ec961f8855fe9d6f2',
|
||||||
|
'0x987c8d5333ab86fde9926bd2ca6c674170a05bfe3bdd81ffd038da6c26c842642f64550fedfe935a15e4ca31870fb29',
|
||||||
|
'0x9fc4018bd96684be88c9e221e4da1bb8f3abd16679dc26c1e8b6e6a1f20cabe69d65201c78607a360370e577bdba587',
|
||||||
|
'0xe1bba7a1186bdb5223abde7ada14a23c42a0ca7915af6fe06985e7ed1e4d43b9b3f7055dd4eba6f2bafaaebca731c30',
|
||||||
|
'0x19713e47937cd1be0dfd0b8f1d43fb93cd2fcbcb6caf493fd1183e416389e61031bf3a5cce3fbafce813711ad011c132',
|
||||||
|
'0x18b46a908f36f6deb918c143fed2edcc523559b8aaf0c2462e6bfe7f911f643249d9cdf41b44d606ce07c8a4d0074d8e',
|
||||||
|
'0xb182cac101b9399d155096004f53f447aa7b12a3426b08ec02710e807b4633f06c851c1919211f20d4c04f00b971ef8',
|
||||||
|
'0x245a394ad1eca9b72fc00ae7be315dc757b3b080d4c158013e6632d3c40659cc6cf90ad1c232a6442d9d3f5db980133',
|
||||||
|
'0x5c129645e44cf1102a159f748c4a3fc5e673d81d7e86568d9ab0f5d396a7ce46ba1049b6579afb7866b1e715475224b',
|
||||||
|
'0x15e6be4e990f03ce4ea50b3b42df2eb5cb181d8f84965a3957add4fa95af01b2b665027efec01c7704b456be69c8b604',
|
||||||
|
],
|
||||||
|
// yDen
|
||||||
|
[
|
||||||
|
'0x16112c4c3a9c98b252181140fad0eae9601a6de578980be6eec3232b5be72e7a07f3688ef60c206d01479253b03663c1',
|
||||||
|
'0x1962d75c2381201e1a0cbd6c43c348b885c84ff731c4d59ca4a10356f453e01f78a4260763529e3532f6102c2e49a03d',
|
||||||
|
'0x58df3306640da276faaae7d6e8eb15778c4855551ae7f310c35a5dd279cd2eca6757cd636f96f891e2538b53dbf67f2',
|
||||||
|
'0x16b7d288798e5395f20d23bf89edb4d1d115c5dbddbcd30e123da489e726af41727364f2c28297ada8d26d98445f5416',
|
||||||
|
'0xbe0e079545f43e4b00cc912f8228ddcc6d19c9f0f69bbb0542eda0fc9dec916a20b15dc0fd2ededda39142311a5001d',
|
||||||
|
'0x8d9e5297186db2d9fb266eaac783182b70152c65550d881c5ecd87b6f0f5a6449f38db9dfa9cce202c6477faaf9b7ac',
|
||||||
|
'0x166007c08a99db2fc3ba8734ace9824b5eecfdfa8d0cf8ef5dd365bc400a0051d5fa9c01a58b1fb93d1a1399126a775c',
|
||||||
|
'0x16a3ef08be3ea7ea03bcddfabba6ff6ee5a4375efa1f4fd7feb34fd206357132b920f5b00801dee460ee415a15812ed9',
|
||||||
|
'0x1866c8ed336c61231a1be54fd1d74cc4f9fb0ce4c6af5920abc5750c4bf39b4852cfe2f7bb9248836b233d9d55535d4a',
|
||||||
|
'0x167a55cda70a6e1cea820597d94a84903216f763e13d87bb5308592e7ea7d4fbc7385ea3d529b35e346ef48bb8913f55',
|
||||||
|
'0x4d2f259eea405bd48f010a01ad2911d9c6dd039bb61a6290e591b36e636a5c871a5c29f4f83060400f8b49cba8f6aa8',
|
||||||
|
'0xaccbb67481d033ff5852c1e48c50c477f94ff8aefce42d28c0f9a88cea7913516f968986f7ebbea9684b529e2561092',
|
||||||
|
'0xad6b9514c767fe3c3613144b45f1496543346d98adf02267d5ceef9a00d9b8693000763e3b90ac11e99b138573345cc',
|
||||||
|
'0x2660400eb2e4f3b628bdd0d53cd76f2bf565b94e72927c1cb748df27942480e420517bd8714cc80d1fadc1326ed06f7',
|
||||||
|
'0xe0fa1d816ddc03e6b24255e0d7819c171c40f65e273b853324efcd6356caa205ca2f570f13497804415473a1d634b8f',
|
||||||
|
'0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
||||||
|
],
|
||||||
|
].map((i) => i.map((j) => BigInt(j))));
|
||||||
|
// Optimized SWU Map - Fp to G1
|
||||||
|
const G1_SWU = (0, weierstrass_ts_1.mapToCurveSimpleSWU)(Fp, {
|
||||||
|
A: Fp.create(BigInt('0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d')),
|
||||||
|
B: Fp.create(BigInt('0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0')),
|
||||||
|
Z: Fp.create(BigInt(11)),
|
||||||
|
});
|
||||||
|
// SWU Map - Fp2 to G2': y² = x³ + 240i * x + 1012 + 1012i
|
||||||
|
const G2_SWU = (0, weierstrass_ts_1.mapToCurveSimpleSWU)(Fp2, {
|
||||||
|
A: Fp2.create({ c0: Fp.create(_0n), c1: Fp.create(BigInt(240)) }), // A' = 240 * I
|
||||||
|
B: Fp2.create({ c0: Fp.create(BigInt(1012)), c1: Fp.create(BigInt(1012)) }), // B' = 1012 * (1 + I)
|
||||||
|
Z: Fp2.create({ c0: Fp.create(BigInt(-2)), c1: Fp.create(BigInt(-1)) }), // Z: -(2 + I)
|
||||||
|
});
|
||||||
|
function mapToG1(scalars) {
|
||||||
|
const { x, y } = G1_SWU(Fp.create(scalars[0]));
|
||||||
|
return isogenyMapG1(x, y);
|
||||||
|
}
|
||||||
|
function mapToG2(scalars) {
|
||||||
|
const { x, y } = G2_SWU(Fp2.fromBigTuple(scalars));
|
||||||
|
return isogenyMapG2(x, y);
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=bls12-381.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+18
@@ -0,0 +1,18 @@
|
|||||||
|
import { type CurveFn as BLSCurveFn, type PostPrecomputeFn } from './abstract/bls.ts';
|
||||||
|
import { type IField } from './abstract/modular.ts';
|
||||||
|
import { type CurveFn } from './abstract/weierstrass.ts';
|
||||||
|
export declare const bn254_Fr: IField<bigint>;
|
||||||
|
export declare const _postPrecompute: PostPrecomputeFn;
|
||||||
|
/**
|
||||||
|
* bn254 (a.k.a. alt_bn128) pairing-friendly curve.
|
||||||
|
* Contains G1 / G2 operations and pairings.
|
||||||
|
*/
|
||||||
|
export declare const bn254: BLSCurveFn;
|
||||||
|
/**
|
||||||
|
* bn254 weierstrass curve with ECDSA.
|
||||||
|
* This is very rare and probably not used anywhere.
|
||||||
|
* Instead, you should use G1 / G2, defined above.
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
export declare const bn254_weierstrass: CurveFn;
|
||||||
|
//# sourceMappingURL=bn254.d.ts.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"bn254.d.ts","sourceRoot":"","sources":["src/bn254.ts"],"names":[],"mappings":"AAyDA,OAAO,EAEL,KAAK,OAAO,IAAI,UAAU,EAC1B,KAAK,gBAAgB,EAEtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAS,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,EAAE,KAAK,OAAO,EAAqC,MAAM,2BAA2B,CAAC;AAsB5F,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,CAA2B,CAAC;AAsDhE,eAAO,MAAM,eAAe,EAAE,gBAY7B,CAAC;AAmBF;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,UAgDlB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,OAS9B,CAAC"}
|
||||||
+218
@@ -0,0 +1,218 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.bn254_weierstrass = exports.bn254 = exports._postPrecompute = exports.bn254_Fr = void 0;
|
||||||
|
/**
|
||||||
|
* bn254, previously known as alt_bn_128, when it had 128-bit security.
|
||||||
|
|
||||||
|
Barbulescu-Duquesne 2017 shown it's weaker: just about 100 bits,
|
||||||
|
so the naming has been adjusted to its prime bit count:
|
||||||
|
https://hal.science/hal-01534101/file/main.pdf.
|
||||||
|
Compatible with EIP-196 and EIP-197.
|
||||||
|
|
||||||
|
There are huge compatibility issues in the ecosystem:
|
||||||
|
|
||||||
|
1. Different libraries call it in different ways: "bn254", "bn256", "alt_bn128", "bn128".
|
||||||
|
2. libff has bn128, but it's a different curve with different G2:
|
||||||
|
https://github.com/scipr-lab/libff/blob/a44f482e18b8ac04d034c193bd9d7df7817ad73f/libff/algebra/curves/bn128/bn128_init.cpp#L166-L169
|
||||||
|
3. halo2curves bn256 is also incompatible and returns different outputs
|
||||||
|
|
||||||
|
We don't implement Point methods toHex / toBytes.
|
||||||
|
To work around this limitation, has to initialize points on their own from BigInts.
|
||||||
|
Reason it's not implemented is because [there is no standard](https://github.com/privacy-scaling-explorations/halo2curves/issues/109).
|
||||||
|
Points of divergence:
|
||||||
|
|
||||||
|
- Endianness: LE vs BE (byte-swapped)
|
||||||
|
- Flags as first hex bits (similar to BLS) vs no-flags
|
||||||
|
- Imaginary part last in G2 vs first (c0, c1 vs c1, c0)
|
||||||
|
|
||||||
|
The goal of our implementation is to support "Ethereum" variant of the curve,
|
||||||
|
because it at least has specs:
|
||||||
|
|
||||||
|
- EIP196 (https://eips.ethereum.org/EIPS/eip-196) describes bn254 ECADD and ECMUL opcodes for EVM
|
||||||
|
- EIP197 (https://eips.ethereum.org/EIPS/eip-197) describes bn254 pairings
|
||||||
|
- It's hard: EIPs don't have proper tests. EIP-197 returns boolean output instead of Fp12
|
||||||
|
- The existing implementations are bad. Some are deprecated:
|
||||||
|
- https://github.com/paritytech/bn (old version)
|
||||||
|
- https://github.com/ewasm/ethereum-bn128.rs (uses paritytech/bn)
|
||||||
|
- https://github.com/zcash-hackworks/bn
|
||||||
|
- https://github.com/arkworks-rs/curves/blob/master/bn254/src/lib.rs
|
||||||
|
- Python implementations use different towers and produce different Fp12 outputs:
|
||||||
|
- https://github.com/ethereum/py_pairing
|
||||||
|
- https://github.com/ethereum/execution-specs/blob/master/src/ethereum/crypto/alt_bn128.py
|
||||||
|
- Points are encoded differently in different implementations
|
||||||
|
|
||||||
|
### Params
|
||||||
|
Seed (X): 4965661367192848881
|
||||||
|
Fr: (36x⁴+36x³+18x²+6x+1)
|
||||||
|
Fp: (36x⁴+36x³+24x²+6x+1)
|
||||||
|
(E / Fp ): Y² = X³+3
|
||||||
|
(Et / Fp²): Y² = X³+3/(u+9) (D-type twist)
|
||||||
|
Ate loop size: 6x+2
|
||||||
|
|
||||||
|
### Towers
|
||||||
|
- Fp²[u] = Fp/u²+1
|
||||||
|
- Fp⁶[v] = Fp²/v³-9-u
|
||||||
|
- Fp¹²[w] = Fp⁶/w²-v
|
||||||
|
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
const sha2_js_1 = require("@noble/hashes/sha2.js");
|
||||||
|
const bls_ts_1 = require("./abstract/bls.js");
|
||||||
|
const modular_ts_1 = require("./abstract/modular.js");
|
||||||
|
const tower_ts_1 = require("./abstract/tower.js");
|
||||||
|
const weierstrass_ts_1 = require("./abstract/weierstrass.js");
|
||||||
|
const utils_ts_1 = require("./utils.js");
|
||||||
|
// prettier-ignore
|
||||||
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
||||||
|
const _6n = BigInt(6);
|
||||||
|
const BN_X = BigInt('4965661367192848881');
|
||||||
|
const BN_X_LEN = (0, utils_ts_1.bitLen)(BN_X);
|
||||||
|
const SIX_X_SQUARED = _6n * BN_X ** _2n;
|
||||||
|
const bn254_G1_CURVE = {
|
||||||
|
p: BigInt('0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47'),
|
||||||
|
n: BigInt('0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001'),
|
||||||
|
h: _1n,
|
||||||
|
a: _0n,
|
||||||
|
b: _3n,
|
||||||
|
Gx: _1n,
|
||||||
|
Gy: BigInt(2),
|
||||||
|
};
|
||||||
|
// r == n
|
||||||
|
// Finite field over r. It's for convenience and is not used in the code below.
|
||||||
|
exports.bn254_Fr = (0, modular_ts_1.Field)(bn254_G1_CURVE.n);
|
||||||
|
// Fp2.div(Fp2.mul(Fp2.ONE, _3n), Fp2.NONRESIDUE)
|
||||||
|
const Fp2B = {
|
||||||
|
c0: BigInt('19485874751759354771024239261021720505790618469301721065564631296452457478373'),
|
||||||
|
c1: BigInt('266929791119991161246907387137283842545076965332900288569378510910307636690'),
|
||||||
|
};
|
||||||
|
const { Fp, Fp2, Fp6, Fp12 } = (0, tower_ts_1.tower12)({
|
||||||
|
ORDER: bn254_G1_CURVE.p,
|
||||||
|
X_LEN: BN_X_LEN,
|
||||||
|
FP2_NONRESIDUE: [BigInt(9), _1n],
|
||||||
|
Fp2mulByB: (num) => Fp2.mul(num, Fp2B),
|
||||||
|
Fp12finalExponentiate: (num) => {
|
||||||
|
const powMinusX = (num) => Fp12.conjugate(Fp12._cyclotomicExp(num, BN_X));
|
||||||
|
const r0 = Fp12.mul(Fp12.conjugate(num), Fp12.inv(num));
|
||||||
|
const r = Fp12.mul(Fp12.frobeniusMap(r0, 2), r0);
|
||||||
|
const y1 = Fp12._cyclotomicSquare(powMinusX(r));
|
||||||
|
const y2 = Fp12.mul(Fp12._cyclotomicSquare(y1), y1);
|
||||||
|
const y4 = powMinusX(y2);
|
||||||
|
const y6 = powMinusX(Fp12._cyclotomicSquare(y4));
|
||||||
|
const y8 = Fp12.mul(Fp12.mul(Fp12.conjugate(y6), y4), Fp12.conjugate(y2));
|
||||||
|
const y9 = Fp12.mul(y8, y1);
|
||||||
|
return Fp12.mul(Fp12.frobeniusMap(Fp12.mul(Fp12.conjugate(r), y9), 3), Fp12.mul(Fp12.frobeniusMap(y8, 2), Fp12.mul(Fp12.frobeniusMap(y9, 1), Fp12.mul(Fp12.mul(y8, y4), r))));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// END OF CURVE FIELDS
|
||||||
|
const { G2psi, psi } = (0, tower_ts_1.psiFrobenius)(Fp, Fp2, Fp2.NONRESIDUE);
|
||||||
|
/*
|
||||||
|
No hashToCurve for now (and signatures):
|
||||||
|
|
||||||
|
- RFC 9380 doesn't mention bn254 and doesn't provide test vectors
|
||||||
|
- Overall seems like nobody is using BLS signatures on top of bn254
|
||||||
|
- Seems like it can utilize SVDW, which is not implemented yet
|
||||||
|
*/
|
||||||
|
const htfDefaults = Object.freeze({
|
||||||
|
// DST: a domain separation tag defined in section 2.2.5
|
||||||
|
DST: 'BN254G2_XMD:SHA-256_SVDW_RO_',
|
||||||
|
encodeDST: 'BN254G2_XMD:SHA-256_SVDW_RO_',
|
||||||
|
p: Fp.ORDER,
|
||||||
|
m: 2,
|
||||||
|
k: 128,
|
||||||
|
expand: 'xmd',
|
||||||
|
hash: sha2_js_1.sha256,
|
||||||
|
});
|
||||||
|
const _postPrecompute = (Rx, Ry, Rz, Qx, Qy, pointAdd) => {
|
||||||
|
const q = psi(Qx, Qy);
|
||||||
|
({ Rx, Ry, Rz } = pointAdd(Rx, Ry, Rz, q[0], q[1]));
|
||||||
|
const q2 = psi(q[0], q[1]);
|
||||||
|
pointAdd(Rx, Ry, Rz, q2[0], Fp2.neg(q2[1]));
|
||||||
|
};
|
||||||
|
exports._postPrecompute = _postPrecompute;
|
||||||
|
// cofactor: (36 * X^4) + (36 * X^3) + (30 * X^2) + 6*X + 1
|
||||||
|
const bn254_G2_CURVE = {
|
||||||
|
p: Fp2.ORDER,
|
||||||
|
n: bn254_G1_CURVE.n,
|
||||||
|
h: BigInt('0x30644e72e131a029b85045b68181585e06ceecda572a2489345f2299c0f9fa8d'),
|
||||||
|
a: Fp2.ZERO,
|
||||||
|
b: Fp2B,
|
||||||
|
Gx: Fp2.fromBigTuple([
|
||||||
|
BigInt('10857046999023057135944570762232829481370756359578518086990519993285655852781'),
|
||||||
|
BigInt('11559732032986387107991004021392285783925812861821192530917403151452391805634'),
|
||||||
|
]),
|
||||||
|
Gy: Fp2.fromBigTuple([
|
||||||
|
BigInt('8495653923123431417604973247489272438418190587263600148770280649306958101930'),
|
||||||
|
BigInt('4082367875863433681332203403145435568316851327593401208105741076214120093531'),
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* bn254 (a.k.a. alt_bn128) pairing-friendly curve.
|
||||||
|
* Contains G1 / G2 operations and pairings.
|
||||||
|
*/
|
||||||
|
exports.bn254 = (0, bls_ts_1.bls)({
|
||||||
|
// Fields
|
||||||
|
fields: { Fp, Fp2, Fp6, Fp12, Fr: exports.bn254_Fr },
|
||||||
|
G1: {
|
||||||
|
...bn254_G1_CURVE,
|
||||||
|
Fp,
|
||||||
|
htfDefaults: { ...htfDefaults, m: 1, DST: 'BN254G2_XMD:SHA-256_SVDW_RO_' },
|
||||||
|
wrapPrivateKey: true,
|
||||||
|
allowInfinityPoint: true,
|
||||||
|
mapToCurve: utils_ts_1.notImplemented,
|
||||||
|
fromBytes: utils_ts_1.notImplemented,
|
||||||
|
toBytes: utils_ts_1.notImplemented,
|
||||||
|
ShortSignature: {
|
||||||
|
fromBytes: utils_ts_1.notImplemented,
|
||||||
|
fromHex: utils_ts_1.notImplemented,
|
||||||
|
toBytes: utils_ts_1.notImplemented,
|
||||||
|
toRawBytes: utils_ts_1.notImplemented,
|
||||||
|
toHex: utils_ts_1.notImplemented,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
G2: {
|
||||||
|
...bn254_G2_CURVE,
|
||||||
|
Fp: Fp2,
|
||||||
|
hEff: BigInt('21888242871839275222246405745257275088844257914179612981679871602714643921549'),
|
||||||
|
htfDefaults: { ...htfDefaults },
|
||||||
|
wrapPrivateKey: true,
|
||||||
|
allowInfinityPoint: true,
|
||||||
|
isTorsionFree: (c, P) => P.multiplyUnsafe(SIX_X_SQUARED).equals(G2psi(c, P)), // [p]P = [6X^2]P
|
||||||
|
mapToCurve: utils_ts_1.notImplemented,
|
||||||
|
fromBytes: utils_ts_1.notImplemented,
|
||||||
|
toBytes: utils_ts_1.notImplemented,
|
||||||
|
Signature: {
|
||||||
|
fromBytes: utils_ts_1.notImplemented,
|
||||||
|
fromHex: utils_ts_1.notImplemented,
|
||||||
|
toBytes: utils_ts_1.notImplemented,
|
||||||
|
toRawBytes: utils_ts_1.notImplemented,
|
||||||
|
toHex: utils_ts_1.notImplemented,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
ateLoopSize: BN_X * _6n + _2n,
|
||||||
|
r: exports.bn254_Fr.ORDER,
|
||||||
|
xNegative: false,
|
||||||
|
twistType: 'divisive',
|
||||||
|
},
|
||||||
|
htfDefaults,
|
||||||
|
hash: sha2_js_1.sha256,
|
||||||
|
postPrecompute: exports._postPrecompute,
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* bn254 weierstrass curve with ECDSA.
|
||||||
|
* This is very rare and probably not used anywhere.
|
||||||
|
* Instead, you should use G1 / G2, defined above.
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
exports.bn254_weierstrass = (0, weierstrass_ts_1.weierstrass)({
|
||||||
|
a: BigInt(0),
|
||||||
|
b: BigInt(3),
|
||||||
|
Fp,
|
||||||
|
n: BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617'),
|
||||||
|
Gx: BigInt(1),
|
||||||
|
Gy: BigInt(2),
|
||||||
|
h: BigInt(1),
|
||||||
|
hash: sha2_js_1.sha256,
|
||||||
|
});
|
||||||
|
//# sourceMappingURL=bn254.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+106
@@ -0,0 +1,106 @@
|
|||||||
|
import { type AffinePoint } from './abstract/curve.ts';
|
||||||
|
import { PrimeEdwardsPoint, type CurveFn, type EdwardsPoint } from './abstract/edwards.ts';
|
||||||
|
import { type H2CHasher, type H2CHasherBase, type H2CMethod, type htfBasicOpts } from './abstract/hash-to-curve.ts';
|
||||||
|
import { type IField } from './abstract/modular.ts';
|
||||||
|
import { type MontgomeryECDH as XCurveFn } from './abstract/montgomery.ts';
|
||||||
|
import { type Hex } from './utils.ts';
|
||||||
|
/**
|
||||||
|
* ed25519 curve with EdDSA signatures.
|
||||||
|
* @example
|
||||||
|
* import { ed25519 } from '@noble/curves/ed25519';
|
||||||
|
* const { secretKey, publicKey } = ed25519.keygen();
|
||||||
|
* const msg = new TextEncoder().encode('hello');
|
||||||
|
* const sig = ed25519.sign(msg, priv);
|
||||||
|
* ed25519.verify(sig, msg, pub); // Default mode: follows ZIP215
|
||||||
|
* ed25519.verify(sig, msg, pub, { zip215: false }); // RFC8032 / FIPS 186-5
|
||||||
|
*/
|
||||||
|
export declare const ed25519: CurveFn;
|
||||||
|
/** Context of ed25519. Uses context for domain separation. */
|
||||||
|
export declare const ed25519ctx: CurveFn;
|
||||||
|
/** Prehashed version of ed25519. Accepts already-hashed messages in sign() and verify(). */
|
||||||
|
export declare const ed25519ph: CurveFn;
|
||||||
|
/**
|
||||||
|
* ECDH using curve25519 aka x25519.
|
||||||
|
* @example
|
||||||
|
* import { x25519 } from '@noble/curves/ed25519';
|
||||||
|
* const priv = 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4';
|
||||||
|
* const pub = 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c';
|
||||||
|
* x25519.getSharedSecret(priv, pub) === x25519.scalarMult(priv, pub); // aliases
|
||||||
|
* x25519.getPublicKey(priv) === x25519.scalarMultBase(priv);
|
||||||
|
* x25519.getPublicKey(x25519.utils.randomSecretKey());
|
||||||
|
*/
|
||||||
|
export declare const x25519: XCurveFn;
|
||||||
|
/** Hashing to ed25519 points / field. RFC 9380 methods. */
|
||||||
|
export declare const ed25519_hasher: H2CHasher<bigint>;
|
||||||
|
type ExtendedPoint = EdwardsPoint;
|
||||||
|
/**
|
||||||
|
* Wrapper over Edwards Point for ristretto255.
|
||||||
|
*
|
||||||
|
* Each ed25519/ExtendedPoint has 8 different equivalent points. This can be
|
||||||
|
* a source of bugs for protocols like ring signatures. Ristretto was created to solve this.
|
||||||
|
* Ristretto point operates in X:Y:Z:T extended coordinates like ExtendedPoint,
|
||||||
|
* but it should work in its own namespace: do not combine those two.
|
||||||
|
* See [RFC9496](https://www.rfc-editor.org/rfc/rfc9496).
|
||||||
|
*/
|
||||||
|
declare class _RistrettoPoint extends PrimeEdwardsPoint<_RistrettoPoint> {
|
||||||
|
static BASE: _RistrettoPoint;
|
||||||
|
static ZERO: _RistrettoPoint;
|
||||||
|
static Fp: IField<bigint>;
|
||||||
|
static Fn: IField<bigint>;
|
||||||
|
constructor(ep: ExtendedPoint);
|
||||||
|
static fromAffine(ap: AffinePoint<bigint>): _RistrettoPoint;
|
||||||
|
protected assertSame(other: _RistrettoPoint): void;
|
||||||
|
protected init(ep: EdwardsPoint): _RistrettoPoint;
|
||||||
|
/** @deprecated use `import { ristretto255_hasher } from '@noble/curves/ed25519.js';` */
|
||||||
|
static hashToCurve(hex: Hex): _RistrettoPoint;
|
||||||
|
static fromBytes(bytes: Uint8Array): _RistrettoPoint;
|
||||||
|
/**
|
||||||
|
* Converts ristretto-encoded string to ristretto point.
|
||||||
|
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-decode).
|
||||||
|
* @param hex Ristretto-encoded 32 bytes. Not every 32-byte string is valid ristretto encoding
|
||||||
|
*/
|
||||||
|
static fromHex(hex: Hex): _RistrettoPoint;
|
||||||
|
static msm(points: _RistrettoPoint[], scalars: bigint[]): _RistrettoPoint;
|
||||||
|
/**
|
||||||
|
* Encodes ristretto point to Uint8Array.
|
||||||
|
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-encode).
|
||||||
|
*/
|
||||||
|
toBytes(): Uint8Array;
|
||||||
|
/**
|
||||||
|
* Compares two Ristretto points.
|
||||||
|
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-equals).
|
||||||
|
*/
|
||||||
|
equals(other: _RistrettoPoint): boolean;
|
||||||
|
is0(): boolean;
|
||||||
|
}
|
||||||
|
export declare const ristretto255: {
|
||||||
|
Point: typeof _RistrettoPoint;
|
||||||
|
};
|
||||||
|
/** Hashing to ristretto255 points / field. RFC 9380 methods. */
|
||||||
|
export declare const ristretto255_hasher: H2CHasherBase<bigint>;
|
||||||
|
/**
|
||||||
|
* Weird / bogus points, useful for debugging.
|
||||||
|
* All 8 ed25519 points of 8-torsion subgroup can be generated from the point
|
||||||
|
* T = `26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05`.
|
||||||
|
* ⟨T⟩ = { O, T, 2T, 3T, 4T, 5T, 6T, 7T }
|
||||||
|
*/
|
||||||
|
export declare const ED25519_TORSION_SUBGROUP: string[];
|
||||||
|
/** @deprecated use `ed25519.utils.toMontgomery` */
|
||||||
|
export declare function edwardsToMontgomeryPub(edwardsPub: Hex): Uint8Array;
|
||||||
|
/** @deprecated use `ed25519.utils.toMontgomery` */
|
||||||
|
export declare const edwardsToMontgomery: typeof edwardsToMontgomeryPub;
|
||||||
|
/** @deprecated use `ed25519.utils.toMontgomerySecret` */
|
||||||
|
export declare function edwardsToMontgomeryPriv(edwardsPriv: Uint8Array): Uint8Array;
|
||||||
|
/** @deprecated use `ristretto255.Point` */
|
||||||
|
export declare const RistrettoPoint: typeof _RistrettoPoint;
|
||||||
|
/** @deprecated use `import { ed25519_hasher } from '@noble/curves/ed25519.js';` */
|
||||||
|
export declare const hashToCurve: H2CMethod<bigint>;
|
||||||
|
/** @deprecated use `import { ed25519_hasher } from '@noble/curves/ed25519.js';` */
|
||||||
|
export declare const encodeToCurve: H2CMethod<bigint>;
|
||||||
|
type RistHasher = (msg: Uint8Array, options: htfBasicOpts) => _RistrettoPoint;
|
||||||
|
/** @deprecated use `import { ristretto255_hasher } from '@noble/curves/ed25519.js';` */
|
||||||
|
export declare const hashToRistretto255: RistHasher;
|
||||||
|
/** @deprecated use `import { ristretto255_hasher } from '@noble/curves/ed25519.js';` */
|
||||||
|
export declare const hash_to_ristretto255: RistHasher;
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=ed25519.d.ts.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"ed25519.d.ts","sourceRoot":"","sources":["src/ed25519.ts"],"names":[],"mappings":"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"}
|
||||||
+472
@@ -0,0 +1,472 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.hash_to_ristretto255 = exports.hashToRistretto255 = exports.encodeToCurve = exports.hashToCurve = exports.RistrettoPoint = exports.edwardsToMontgomery = exports.ED25519_TORSION_SUBGROUP = exports.ristretto255_hasher = exports.ristretto255 = exports.ed25519_hasher = exports.x25519 = exports.ed25519ph = exports.ed25519ctx = exports.ed25519 = void 0;
|
||||||
|
exports.edwardsToMontgomeryPub = edwardsToMontgomeryPub;
|
||||||
|
exports.edwardsToMontgomeryPriv = edwardsToMontgomeryPriv;
|
||||||
|
/**
|
||||||
|
* ed25519 Twisted Edwards curve with following addons:
|
||||||
|
* - X25519 ECDH
|
||||||
|
* - Ristretto cofactor elimination
|
||||||
|
* - Elligator hash-to-group / point indistinguishability
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
const sha2_js_1 = require("@noble/hashes/sha2.js");
|
||||||
|
const utils_js_1 = require("@noble/hashes/utils.js");
|
||||||
|
const curve_ts_1 = require("./abstract/curve.js");
|
||||||
|
const edwards_ts_1 = require("./abstract/edwards.js");
|
||||||
|
const hash_to_curve_ts_1 = require("./abstract/hash-to-curve.js");
|
||||||
|
const modular_ts_1 = require("./abstract/modular.js");
|
||||||
|
const montgomery_ts_1 = require("./abstract/montgomery.js");
|
||||||
|
const utils_ts_1 = require("./utils.js");
|
||||||
|
// prettier-ignore
|
||||||
|
const _0n = /* @__PURE__ */ BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
||||||
|
// prettier-ignore
|
||||||
|
const _5n = BigInt(5), _8n = BigInt(8);
|
||||||
|
// P = 2n**255n-19n
|
||||||
|
const ed25519_CURVE_p = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed');
|
||||||
|
// N = 2n**252n + 27742317777372353535851937790883648493n
|
||||||
|
// a = Fp.create(BigInt(-1))
|
||||||
|
// d = -121665/121666 a.k.a. Fp.neg(121665 * Fp.inv(121666))
|
||||||
|
const ed25519_CURVE = /* @__PURE__ */ (() => ({
|
||||||
|
p: ed25519_CURVE_p,
|
||||||
|
n: BigInt('0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed'),
|
||||||
|
h: _8n,
|
||||||
|
a: BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec'),
|
||||||
|
d: BigInt('0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3'),
|
||||||
|
Gx: BigInt('0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a'),
|
||||||
|
Gy: BigInt('0x6666666666666666666666666666666666666666666666666666666666666658'),
|
||||||
|
}))();
|
||||||
|
function ed25519_pow_2_252_3(x) {
|
||||||
|
// prettier-ignore
|
||||||
|
const _10n = BigInt(10), _20n = BigInt(20), _40n = BigInt(40), _80n = BigInt(80);
|
||||||
|
const P = ed25519_CURVE_p;
|
||||||
|
const x2 = (x * x) % P;
|
||||||
|
const b2 = (x2 * x) % P; // x^3, 11
|
||||||
|
const b4 = ((0, modular_ts_1.pow2)(b2, _2n, P) * b2) % P; // x^15, 1111
|
||||||
|
const b5 = ((0, modular_ts_1.pow2)(b4, _1n, P) * x) % P; // x^31
|
||||||
|
const b10 = ((0, modular_ts_1.pow2)(b5, _5n, P) * b5) % P;
|
||||||
|
const b20 = ((0, modular_ts_1.pow2)(b10, _10n, P) * b10) % P;
|
||||||
|
const b40 = ((0, modular_ts_1.pow2)(b20, _20n, P) * b20) % P;
|
||||||
|
const b80 = ((0, modular_ts_1.pow2)(b40, _40n, P) * b40) % P;
|
||||||
|
const b160 = ((0, modular_ts_1.pow2)(b80, _80n, P) * b80) % P;
|
||||||
|
const b240 = ((0, modular_ts_1.pow2)(b160, _80n, P) * b80) % P;
|
||||||
|
const b250 = ((0, modular_ts_1.pow2)(b240, _10n, P) * b10) % P;
|
||||||
|
const pow_p_5_8 = ((0, modular_ts_1.pow2)(b250, _2n, P) * x) % P;
|
||||||
|
// ^ To pow to (p+3)/8, multiply it by x.
|
||||||
|
return { pow_p_5_8, b2 };
|
||||||
|
}
|
||||||
|
function adjustScalarBytes(bytes) {
|
||||||
|
// Section 5: For X25519, in order to decode 32 random bytes as an integer scalar,
|
||||||
|
// set the three least significant bits of the first byte
|
||||||
|
bytes[0] &= 248; // 0b1111_1000
|
||||||
|
// and the most significant bit of the last to zero,
|
||||||
|
bytes[31] &= 127; // 0b0111_1111
|
||||||
|
// set the second most significant bit of the last byte to 1
|
||||||
|
bytes[31] |= 64; // 0b0100_0000
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
// √(-1) aka √(a) aka 2^((p-1)/4)
|
||||||
|
// Fp.sqrt(Fp.neg(1))
|
||||||
|
const ED25519_SQRT_M1 = /* @__PURE__ */ BigInt('19681161376707505956807079304988542015446066515923890162744021073123829784752');
|
||||||
|
// sqrt(u/v)
|
||||||
|
function uvRatio(u, v) {
|
||||||
|
const P = ed25519_CURVE_p;
|
||||||
|
const v3 = (0, modular_ts_1.mod)(v * v * v, P); // v³
|
||||||
|
const v7 = (0, modular_ts_1.mod)(v3 * v3 * v, P); // v⁷
|
||||||
|
// (p+3)/8 and (p-5)/8
|
||||||
|
const pow = ed25519_pow_2_252_3(u * v7).pow_p_5_8;
|
||||||
|
let x = (0, modular_ts_1.mod)(u * v3 * pow, P); // (uv³)(uv⁷)^(p-5)/8
|
||||||
|
const vx2 = (0, modular_ts_1.mod)(v * x * x, P); // vx²
|
||||||
|
const root1 = x; // First root candidate
|
||||||
|
const root2 = (0, modular_ts_1.mod)(x * ED25519_SQRT_M1, P); // Second root candidate
|
||||||
|
const useRoot1 = vx2 === u; // If vx² = u (mod p), x is a square root
|
||||||
|
const useRoot2 = vx2 === (0, modular_ts_1.mod)(-u, P); // If vx² = -u, set x <-- x * 2^((p-1)/4)
|
||||||
|
const noRoot = vx2 === (0, modular_ts_1.mod)(-u * ED25519_SQRT_M1, P); // There is no valid root, vx² = -u√(-1)
|
||||||
|
if (useRoot1)
|
||||||
|
x = root1;
|
||||||
|
if (useRoot2 || noRoot)
|
||||||
|
x = root2; // We return root2 anyway, for const-time
|
||||||
|
if ((0, modular_ts_1.isNegativeLE)(x, P))
|
||||||
|
x = (0, modular_ts_1.mod)(-x, P);
|
||||||
|
return { isValid: useRoot1 || useRoot2, value: x };
|
||||||
|
}
|
||||||
|
const Fp = /* @__PURE__ */ (() => (0, modular_ts_1.Field)(ed25519_CURVE.p, { isLE: true }))();
|
||||||
|
const Fn = /* @__PURE__ */ (() => (0, modular_ts_1.Field)(ed25519_CURVE.n, { isLE: true }))();
|
||||||
|
const ed25519Defaults = /* @__PURE__ */ (() => ({
|
||||||
|
...ed25519_CURVE,
|
||||||
|
Fp,
|
||||||
|
hash: sha2_js_1.sha512,
|
||||||
|
adjustScalarBytes,
|
||||||
|
// dom2
|
||||||
|
// Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3.
|
||||||
|
// Constant-time, u/√v
|
||||||
|
uvRatio,
|
||||||
|
}))();
|
||||||
|
/**
|
||||||
|
* ed25519 curve with EdDSA signatures.
|
||||||
|
* @example
|
||||||
|
* import { ed25519 } from '@noble/curves/ed25519';
|
||||||
|
* const { secretKey, publicKey } = ed25519.keygen();
|
||||||
|
* const msg = new TextEncoder().encode('hello');
|
||||||
|
* const sig = ed25519.sign(msg, priv);
|
||||||
|
* ed25519.verify(sig, msg, pub); // Default mode: follows ZIP215
|
||||||
|
* ed25519.verify(sig, msg, pub, { zip215: false }); // RFC8032 / FIPS 186-5
|
||||||
|
*/
|
||||||
|
exports.ed25519 = (() => (0, edwards_ts_1.twistedEdwards)(ed25519Defaults))();
|
||||||
|
function ed25519_domain(data, ctx, phflag) {
|
||||||
|
if (ctx.length > 255)
|
||||||
|
throw new Error('Context is too big');
|
||||||
|
return (0, utils_js_1.concatBytes)((0, utils_js_1.utf8ToBytes)('SigEd25519 no Ed25519 collisions'), new Uint8Array([phflag ? 1 : 0, ctx.length]), ctx, data);
|
||||||
|
}
|
||||||
|
/** Context of ed25519. Uses context for domain separation. */
|
||||||
|
exports.ed25519ctx = (() => (0, edwards_ts_1.twistedEdwards)({
|
||||||
|
...ed25519Defaults,
|
||||||
|
domain: ed25519_domain,
|
||||||
|
}))();
|
||||||
|
/** Prehashed version of ed25519. Accepts already-hashed messages in sign() and verify(). */
|
||||||
|
exports.ed25519ph = (() => (0, edwards_ts_1.twistedEdwards)(Object.assign({}, ed25519Defaults, {
|
||||||
|
domain: ed25519_domain,
|
||||||
|
prehash: sha2_js_1.sha512,
|
||||||
|
})))();
|
||||||
|
/**
|
||||||
|
* ECDH using curve25519 aka x25519.
|
||||||
|
* @example
|
||||||
|
* import { x25519 } from '@noble/curves/ed25519';
|
||||||
|
* const priv = 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4';
|
||||||
|
* const pub = 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c';
|
||||||
|
* x25519.getSharedSecret(priv, pub) === x25519.scalarMult(priv, pub); // aliases
|
||||||
|
* x25519.getPublicKey(priv) === x25519.scalarMultBase(priv);
|
||||||
|
* x25519.getPublicKey(x25519.utils.randomSecretKey());
|
||||||
|
*/
|
||||||
|
exports.x25519 = (() => {
|
||||||
|
const P = Fp.ORDER;
|
||||||
|
return (0, montgomery_ts_1.montgomery)({
|
||||||
|
P,
|
||||||
|
type: 'x25519',
|
||||||
|
powPminus2: (x) => {
|
||||||
|
// x^(p-2) aka x^(2^255-21)
|
||||||
|
const { pow_p_5_8, b2 } = ed25519_pow_2_252_3(x);
|
||||||
|
return (0, modular_ts_1.mod)((0, modular_ts_1.pow2)(pow_p_5_8, _3n, P) * b2, P);
|
||||||
|
},
|
||||||
|
adjustScalarBytes,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
// Hash To Curve Elligator2 Map (NOTE: different from ristretto255 elligator)
|
||||||
|
// NOTE: very important part is usage of FpSqrtEven for ELL2_C1_EDWARDS, since
|
||||||
|
// SageMath returns different root first and everything falls apart
|
||||||
|
const ELL2_C1 = /* @__PURE__ */ (() => (ed25519_CURVE_p + _3n) / _8n)(); // 1. c1 = (q + 3) / 8 # Integer arithmetic
|
||||||
|
const ELL2_C2 = /* @__PURE__ */ (() => Fp.pow(_2n, ELL2_C1))(); // 2. c2 = 2^c1
|
||||||
|
const ELL2_C3 = /* @__PURE__ */ (() => Fp.sqrt(Fp.neg(Fp.ONE)))(); // 3. c3 = sqrt(-1)
|
||||||
|
// prettier-ignore
|
||||||
|
function map_to_curve_elligator2_curve25519(u) {
|
||||||
|
const ELL2_C4 = (ed25519_CURVE_p - _5n) / _8n; // 4. c4 = (q - 5) / 8 # Integer arithmetic
|
||||||
|
const ELL2_J = BigInt(486662);
|
||||||
|
let tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
||||||
|
tv1 = Fp.mul(tv1, _2n); // 2. tv1 = 2 * tv1
|
||||||
|
let xd = Fp.add(tv1, Fp.ONE); // 3. xd = tv1 + 1 # Nonzero: -1 is square (mod p), tv1 is not
|
||||||
|
let x1n = Fp.neg(ELL2_J); // 4. x1n = -J # x1 = x1n / xd = -J / (1 + 2 * u^2)
|
||||||
|
let tv2 = Fp.sqr(xd); // 5. tv2 = xd^2
|
||||||
|
let gxd = Fp.mul(tv2, xd); // 6. gxd = tv2 * xd # gxd = xd^3
|
||||||
|
let gx1 = Fp.mul(tv1, ELL2_J); // 7. gx1 = J * tv1 # x1n + J * xd
|
||||||
|
gx1 = Fp.mul(gx1, x1n); // 8. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
|
||||||
|
gx1 = Fp.add(gx1, tv2); // 9. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
|
||||||
|
gx1 = Fp.mul(gx1, x1n); // 10. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
|
||||||
|
let tv3 = Fp.sqr(gxd); // 11. tv3 = gxd^2
|
||||||
|
tv2 = Fp.sqr(tv3); // 12. tv2 = tv3^2 # gxd^4
|
||||||
|
tv3 = Fp.mul(tv3, gxd); // 13. tv3 = tv3 * gxd # gxd^3
|
||||||
|
tv3 = Fp.mul(tv3, gx1); // 14. tv3 = tv3 * gx1 # gx1 * gxd^3
|
||||||
|
tv2 = Fp.mul(tv2, tv3); // 15. tv2 = tv2 * tv3 # gx1 * gxd^7
|
||||||
|
let y11 = Fp.pow(tv2, ELL2_C4); // 16. y11 = tv2^c4 # (gx1 * gxd^7)^((p - 5) / 8)
|
||||||
|
y11 = Fp.mul(y11, tv3); // 17. y11 = y11 * tv3 # gx1*gxd^3*(gx1*gxd^7)^((p-5)/8)
|
||||||
|
let y12 = Fp.mul(y11, ELL2_C3); // 18. y12 = y11 * c3
|
||||||
|
tv2 = Fp.sqr(y11); // 19. tv2 = y11^2
|
||||||
|
tv2 = Fp.mul(tv2, gxd); // 20. tv2 = tv2 * gxd
|
||||||
|
let e1 = Fp.eql(tv2, gx1); // 21. e1 = tv2 == gx1
|
||||||
|
let y1 = Fp.cmov(y12, y11, e1); // 22. y1 = CMOV(y12, y11, e1) # If g(x1) is square, this is its sqrt
|
||||||
|
let x2n = Fp.mul(x1n, tv1); // 23. x2n = x1n * tv1 # x2 = x2n / xd = 2 * u^2 * x1n / xd
|
||||||
|
let y21 = Fp.mul(y11, u); // 24. y21 = y11 * u
|
||||||
|
y21 = Fp.mul(y21, ELL2_C2); // 25. y21 = y21 * c2
|
||||||
|
let y22 = Fp.mul(y21, ELL2_C3); // 26. y22 = y21 * c3
|
||||||
|
let gx2 = Fp.mul(gx1, tv1); // 27. gx2 = gx1 * tv1 # g(x2) = gx2 / gxd = 2 * u^2 * g(x1)
|
||||||
|
tv2 = Fp.sqr(y21); // 28. tv2 = y21^2
|
||||||
|
tv2 = Fp.mul(tv2, gxd); // 29. tv2 = tv2 * gxd
|
||||||
|
let e2 = Fp.eql(tv2, gx2); // 30. e2 = tv2 == gx2
|
||||||
|
let y2 = Fp.cmov(y22, y21, e2); // 31. y2 = CMOV(y22, y21, e2) # If g(x2) is square, this is its sqrt
|
||||||
|
tv2 = Fp.sqr(y1); // 32. tv2 = y1^2
|
||||||
|
tv2 = Fp.mul(tv2, gxd); // 33. tv2 = tv2 * gxd
|
||||||
|
let e3 = Fp.eql(tv2, gx1); // 34. e3 = tv2 == gx1
|
||||||
|
let xn = Fp.cmov(x2n, x1n, e3); // 35. xn = CMOV(x2n, x1n, e3) # If e3, x = x1, else x = x2
|
||||||
|
let y = Fp.cmov(y2, y1, e3); // 36. y = CMOV(y2, y1, e3) # If e3, y = y1, else y = y2
|
||||||
|
let e4 = Fp.isOdd(y); // 37. e4 = sgn0(y) == 1 # Fix sign of y
|
||||||
|
y = Fp.cmov(y, Fp.neg(y), e3 !== e4); // 38. y = CMOV(y, -y, e3 XOR e4)
|
||||||
|
return { xMn: xn, xMd: xd, yMn: y, yMd: _1n }; // 39. return (xn, xd, y, 1)
|
||||||
|
}
|
||||||
|
const ELL2_C1_EDWARDS = /* @__PURE__ */ (() => (0, modular_ts_1.FpSqrtEven)(Fp, Fp.neg(BigInt(486664))))(); // sgn0(c1) MUST equal 0
|
||||||
|
function map_to_curve_elligator2_edwards25519(u) {
|
||||||
|
const { xMn, xMd, yMn, yMd } = map_to_curve_elligator2_curve25519(u); // 1. (xMn, xMd, yMn, yMd) =
|
||||||
|
// map_to_curve_elligator2_curve25519(u)
|
||||||
|
let xn = Fp.mul(xMn, yMd); // 2. xn = xMn * yMd
|
||||||
|
xn = Fp.mul(xn, ELL2_C1_EDWARDS); // 3. xn = xn * c1
|
||||||
|
let xd = Fp.mul(xMd, yMn); // 4. xd = xMd * yMn # xn / xd = c1 * xM / yM
|
||||||
|
let yn = Fp.sub(xMn, xMd); // 5. yn = xMn - xMd
|
||||||
|
let yd = Fp.add(xMn, xMd); // 6. yd = xMn + xMd # (n / d - 1) / (n / d + 1) = (n - d) / (n + d)
|
||||||
|
let tv1 = Fp.mul(xd, yd); // 7. tv1 = xd * yd
|
||||||
|
let e = Fp.eql(tv1, Fp.ZERO); // 8. e = tv1 == 0
|
||||||
|
xn = Fp.cmov(xn, Fp.ZERO, e); // 9. xn = CMOV(xn, 0, e)
|
||||||
|
xd = Fp.cmov(xd, Fp.ONE, e); // 10. xd = CMOV(xd, 1, e)
|
||||||
|
yn = Fp.cmov(yn, Fp.ONE, e); // 11. yn = CMOV(yn, 1, e)
|
||||||
|
yd = Fp.cmov(yd, Fp.ONE, e); // 12. yd = CMOV(yd, 1, e)
|
||||||
|
const [xd_inv, yd_inv] = (0, modular_ts_1.FpInvertBatch)(Fp, [xd, yd], true); // batch division
|
||||||
|
return { x: Fp.mul(xn, xd_inv), y: Fp.mul(yn, yd_inv) }; // 13. return (xn, xd, yn, yd)
|
||||||
|
}
|
||||||
|
/** Hashing to ed25519 points / field. RFC 9380 methods. */
|
||||||
|
exports.ed25519_hasher = (() => (0, hash_to_curve_ts_1.createHasher)(exports.ed25519.Point, (scalars) => map_to_curve_elligator2_edwards25519(scalars[0]), {
|
||||||
|
DST: 'edwards25519_XMD:SHA-512_ELL2_RO_',
|
||||||
|
encodeDST: 'edwards25519_XMD:SHA-512_ELL2_NU_',
|
||||||
|
p: ed25519_CURVE_p,
|
||||||
|
m: 1,
|
||||||
|
k: 128,
|
||||||
|
expand: 'xmd',
|
||||||
|
hash: sha2_js_1.sha512,
|
||||||
|
}))();
|
||||||
|
// √(-1) aka √(a) aka 2^((p-1)/4)
|
||||||
|
const SQRT_M1 = ED25519_SQRT_M1;
|
||||||
|
// √(ad - 1)
|
||||||
|
const SQRT_AD_MINUS_ONE = /* @__PURE__ */ BigInt('25063068953384623474111414158702152701244531502492656460079210482610430750235');
|
||||||
|
// 1 / √(a-d)
|
||||||
|
const INVSQRT_A_MINUS_D = /* @__PURE__ */ BigInt('54469307008909316920995813868745141605393597292927456921205312896311721017578');
|
||||||
|
// 1-d²
|
||||||
|
const ONE_MINUS_D_SQ = /* @__PURE__ */ BigInt('1159843021668779879193775521855586647937357759715417654439879720876111806838');
|
||||||
|
// (d-1)²
|
||||||
|
const D_MINUS_ONE_SQ = /* @__PURE__ */ BigInt('40440834346308536858101042469323190826248399146238708352240133220865137265952');
|
||||||
|
// Calculates 1/√(number)
|
||||||
|
const invertSqrt = (number) => uvRatio(_1n, number);
|
||||||
|
const MAX_255B = /* @__PURE__ */ BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||||
|
const bytes255ToNumberLE = (bytes) => exports.ed25519.Point.Fp.create((0, utils_ts_1.bytesToNumberLE)(bytes) & MAX_255B);
|
||||||
|
/**
|
||||||
|
* Computes Elligator map for Ristretto255.
|
||||||
|
* Described in [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#appendix-B) and on
|
||||||
|
* the [website](https://ristretto.group/formulas/elligator.html).
|
||||||
|
*/
|
||||||
|
function calcElligatorRistrettoMap(r0) {
|
||||||
|
const { d } = ed25519_CURVE;
|
||||||
|
const P = ed25519_CURVE_p;
|
||||||
|
const mod = (n) => Fp.create(n);
|
||||||
|
const r = mod(SQRT_M1 * r0 * r0); // 1
|
||||||
|
const Ns = mod((r + _1n) * ONE_MINUS_D_SQ); // 2
|
||||||
|
let c = BigInt(-1); // 3
|
||||||
|
const D = mod((c - d * r) * mod(r + d)); // 4
|
||||||
|
let { isValid: Ns_D_is_sq, value: s } = uvRatio(Ns, D); // 5
|
||||||
|
let s_ = mod(s * r0); // 6
|
||||||
|
if (!(0, modular_ts_1.isNegativeLE)(s_, P))
|
||||||
|
s_ = mod(-s_);
|
||||||
|
if (!Ns_D_is_sq)
|
||||||
|
s = s_; // 7
|
||||||
|
if (!Ns_D_is_sq)
|
||||||
|
c = r; // 8
|
||||||
|
const Nt = mod(c * (r - _1n) * D_MINUS_ONE_SQ - D); // 9
|
||||||
|
const s2 = s * s;
|
||||||
|
const W0 = mod((s + s) * D); // 10
|
||||||
|
const W1 = mod(Nt * SQRT_AD_MINUS_ONE); // 11
|
||||||
|
const W2 = mod(_1n - s2); // 12
|
||||||
|
const W3 = mod(_1n + s2); // 13
|
||||||
|
return new exports.ed25519.Point(mod(W0 * W3), mod(W2 * W1), mod(W1 * W3), mod(W0 * W2));
|
||||||
|
}
|
||||||
|
function ristretto255_map(bytes) {
|
||||||
|
(0, utils_js_1.abytes)(bytes, 64);
|
||||||
|
const r1 = bytes255ToNumberLE(bytes.subarray(0, 32));
|
||||||
|
const R1 = calcElligatorRistrettoMap(r1);
|
||||||
|
const r2 = bytes255ToNumberLE(bytes.subarray(32, 64));
|
||||||
|
const R2 = calcElligatorRistrettoMap(r2);
|
||||||
|
return new _RistrettoPoint(R1.add(R2));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Wrapper over Edwards Point for ristretto255.
|
||||||
|
*
|
||||||
|
* Each ed25519/ExtendedPoint has 8 different equivalent points. This can be
|
||||||
|
* a source of bugs for protocols like ring signatures. Ristretto was created to solve this.
|
||||||
|
* Ristretto point operates in X:Y:Z:T extended coordinates like ExtendedPoint,
|
||||||
|
* but it should work in its own namespace: do not combine those two.
|
||||||
|
* See [RFC9496](https://www.rfc-editor.org/rfc/rfc9496).
|
||||||
|
*/
|
||||||
|
class _RistrettoPoint extends edwards_ts_1.PrimeEdwardsPoint {
|
||||||
|
constructor(ep) {
|
||||||
|
super(ep);
|
||||||
|
}
|
||||||
|
static fromAffine(ap) {
|
||||||
|
return new _RistrettoPoint(exports.ed25519.Point.fromAffine(ap));
|
||||||
|
}
|
||||||
|
assertSame(other) {
|
||||||
|
if (!(other instanceof _RistrettoPoint))
|
||||||
|
throw new Error('RistrettoPoint expected');
|
||||||
|
}
|
||||||
|
init(ep) {
|
||||||
|
return new _RistrettoPoint(ep);
|
||||||
|
}
|
||||||
|
/** @deprecated use `import { ristretto255_hasher } from '@noble/curves/ed25519.js';` */
|
||||||
|
static hashToCurve(hex) {
|
||||||
|
return ristretto255_map((0, utils_ts_1.ensureBytes)('ristrettoHash', hex, 64));
|
||||||
|
}
|
||||||
|
static fromBytes(bytes) {
|
||||||
|
(0, utils_js_1.abytes)(bytes, 32);
|
||||||
|
const { a, d } = ed25519_CURVE;
|
||||||
|
const P = ed25519_CURVE_p;
|
||||||
|
const mod = (n) => Fp.create(n);
|
||||||
|
const s = bytes255ToNumberLE(bytes);
|
||||||
|
// 1. Check that s_bytes is the canonical encoding of a field element, or else abort.
|
||||||
|
// 3. Check that s is non-negative, or else abort
|
||||||
|
if (!(0, utils_ts_1.equalBytes)(Fp.toBytes(s), bytes) || (0, modular_ts_1.isNegativeLE)(s, P))
|
||||||
|
throw new Error('invalid ristretto255 encoding 1');
|
||||||
|
const s2 = mod(s * s);
|
||||||
|
const u1 = mod(_1n + a * s2); // 4 (a is -1)
|
||||||
|
const u2 = mod(_1n - a * s2); // 5
|
||||||
|
const u1_2 = mod(u1 * u1);
|
||||||
|
const u2_2 = mod(u2 * u2);
|
||||||
|
const v = mod(a * d * u1_2 - u2_2); // 6
|
||||||
|
const { isValid, value: I } = invertSqrt(mod(v * u2_2)); // 7
|
||||||
|
const Dx = mod(I * u2); // 8
|
||||||
|
const Dy = mod(I * Dx * v); // 9
|
||||||
|
let x = mod((s + s) * Dx); // 10
|
||||||
|
if ((0, modular_ts_1.isNegativeLE)(x, P))
|
||||||
|
x = mod(-x); // 10
|
||||||
|
const y = mod(u1 * Dy); // 11
|
||||||
|
const t = mod(x * y); // 12
|
||||||
|
if (!isValid || (0, modular_ts_1.isNegativeLE)(t, P) || y === _0n)
|
||||||
|
throw new Error('invalid ristretto255 encoding 2');
|
||||||
|
return new _RistrettoPoint(new exports.ed25519.Point(x, y, _1n, t));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Converts ristretto-encoded string to ristretto point.
|
||||||
|
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-decode).
|
||||||
|
* @param hex Ristretto-encoded 32 bytes. Not every 32-byte string is valid ristretto encoding
|
||||||
|
*/
|
||||||
|
static fromHex(hex) {
|
||||||
|
return _RistrettoPoint.fromBytes((0, utils_ts_1.ensureBytes)('ristrettoHex', hex, 32));
|
||||||
|
}
|
||||||
|
static msm(points, scalars) {
|
||||||
|
return (0, curve_ts_1.pippenger)(_RistrettoPoint, exports.ed25519.Point.Fn, points, scalars);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Encodes ristretto point to Uint8Array.
|
||||||
|
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-encode).
|
||||||
|
*/
|
||||||
|
toBytes() {
|
||||||
|
let { X, Y, Z, T } = this.ep;
|
||||||
|
const P = ed25519_CURVE_p;
|
||||||
|
const mod = (n) => Fp.create(n);
|
||||||
|
const u1 = mod(mod(Z + Y) * mod(Z - Y)); // 1
|
||||||
|
const u2 = mod(X * Y); // 2
|
||||||
|
// Square root always exists
|
||||||
|
const u2sq = mod(u2 * u2);
|
||||||
|
const { value: invsqrt } = invertSqrt(mod(u1 * u2sq)); // 3
|
||||||
|
const D1 = mod(invsqrt * u1); // 4
|
||||||
|
const D2 = mod(invsqrt * u2); // 5
|
||||||
|
const zInv = mod(D1 * D2 * T); // 6
|
||||||
|
let D; // 7
|
||||||
|
if ((0, modular_ts_1.isNegativeLE)(T * zInv, P)) {
|
||||||
|
let _x = mod(Y * SQRT_M1);
|
||||||
|
let _y = mod(X * SQRT_M1);
|
||||||
|
X = _x;
|
||||||
|
Y = _y;
|
||||||
|
D = mod(D1 * INVSQRT_A_MINUS_D);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
D = D2; // 8
|
||||||
|
}
|
||||||
|
if ((0, modular_ts_1.isNegativeLE)(X * zInv, P))
|
||||||
|
Y = mod(-Y); // 9
|
||||||
|
let s = mod((Z - Y) * D); // 10 (check footer's note, no sqrt(-a))
|
||||||
|
if ((0, modular_ts_1.isNegativeLE)(s, P))
|
||||||
|
s = mod(-s);
|
||||||
|
return Fp.toBytes(s); // 11
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Compares two Ristretto points.
|
||||||
|
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-equals).
|
||||||
|
*/
|
||||||
|
equals(other) {
|
||||||
|
this.assertSame(other);
|
||||||
|
const { X: X1, Y: Y1 } = this.ep;
|
||||||
|
const { X: X2, Y: Y2 } = other.ep;
|
||||||
|
const mod = (n) => Fp.create(n);
|
||||||
|
// (x1 * y2 == y1 * x2) | (y1 * y2 == x1 * x2)
|
||||||
|
const one = mod(X1 * Y2) === mod(Y1 * X2);
|
||||||
|
const two = mod(Y1 * Y2) === mod(X1 * X2);
|
||||||
|
return one || two;
|
||||||
|
}
|
||||||
|
is0() {
|
||||||
|
return this.equals(_RistrettoPoint.ZERO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Do NOT change syntax: the following gymnastics is done,
|
||||||
|
// because typescript strips comments, which makes bundlers disable tree-shaking.
|
||||||
|
// prettier-ignore
|
||||||
|
_RistrettoPoint.BASE =
|
||||||
|
/* @__PURE__ */ (() => new _RistrettoPoint(exports.ed25519.Point.BASE))();
|
||||||
|
// prettier-ignore
|
||||||
|
_RistrettoPoint.ZERO =
|
||||||
|
/* @__PURE__ */ (() => new _RistrettoPoint(exports.ed25519.Point.ZERO))();
|
||||||
|
// prettier-ignore
|
||||||
|
_RistrettoPoint.Fp =
|
||||||
|
/* @__PURE__ */ (() => Fp)();
|
||||||
|
// prettier-ignore
|
||||||
|
_RistrettoPoint.Fn =
|
||||||
|
/* @__PURE__ */ (() => Fn)();
|
||||||
|
exports.ristretto255 = { Point: _RistrettoPoint };
|
||||||
|
/** Hashing to ristretto255 points / field. RFC 9380 methods. */
|
||||||
|
exports.ristretto255_hasher = {
|
||||||
|
hashToCurve(msg, options) {
|
||||||
|
const DST = options?.DST || 'ristretto255_XMD:SHA-512_R255MAP_RO_';
|
||||||
|
const xmd = (0, hash_to_curve_ts_1.expand_message_xmd)(msg, DST, 64, sha2_js_1.sha512);
|
||||||
|
return ristretto255_map(xmd);
|
||||||
|
},
|
||||||
|
hashToScalar(msg, options = { DST: hash_to_curve_ts_1._DST_scalar }) {
|
||||||
|
const xmd = (0, hash_to_curve_ts_1.expand_message_xmd)(msg, options.DST, 64, sha2_js_1.sha512);
|
||||||
|
return Fn.create((0, utils_ts_1.bytesToNumberLE)(xmd));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// export const ristretto255_oprf: OPRF = createORPF({
|
||||||
|
// name: 'ristretto255-SHA512',
|
||||||
|
// Point: RistrettoPoint,
|
||||||
|
// hash: sha512,
|
||||||
|
// hashToGroup: ristretto255_hasher.hashToCurve,
|
||||||
|
// hashToScalar: ristretto255_hasher.hashToScalar,
|
||||||
|
// });
|
||||||
|
/**
|
||||||
|
* Weird / bogus points, useful for debugging.
|
||||||
|
* All 8 ed25519 points of 8-torsion subgroup can be generated from the point
|
||||||
|
* T = `26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05`.
|
||||||
|
* ⟨T⟩ = { O, T, 2T, 3T, 4T, 5T, 6T, 7T }
|
||||||
|
*/
|
||||||
|
exports.ED25519_TORSION_SUBGROUP = [
|
||||||
|
'0100000000000000000000000000000000000000000000000000000000000000',
|
||||||
|
'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000080',
|
||||||
|
'26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05',
|
||||||
|
'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||||
|
'26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85',
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000000',
|
||||||
|
'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa',
|
||||||
|
];
|
||||||
|
/** @deprecated use `ed25519.utils.toMontgomery` */
|
||||||
|
function edwardsToMontgomeryPub(edwardsPub) {
|
||||||
|
return exports.ed25519.utils.toMontgomery((0, utils_ts_1.ensureBytes)('pub', edwardsPub));
|
||||||
|
}
|
||||||
|
/** @deprecated use `ed25519.utils.toMontgomery` */
|
||||||
|
exports.edwardsToMontgomery = edwardsToMontgomeryPub;
|
||||||
|
/** @deprecated use `ed25519.utils.toMontgomerySecret` */
|
||||||
|
function edwardsToMontgomeryPriv(edwardsPriv) {
|
||||||
|
return exports.ed25519.utils.toMontgomerySecret((0, utils_ts_1.ensureBytes)('pub', edwardsPriv));
|
||||||
|
}
|
||||||
|
/** @deprecated use `ristretto255.Point` */
|
||||||
|
exports.RistrettoPoint = _RistrettoPoint;
|
||||||
|
/** @deprecated use `import { ed25519_hasher } from '@noble/curves/ed25519.js';` */
|
||||||
|
exports.hashToCurve = (() => exports.ed25519_hasher.hashToCurve)();
|
||||||
|
/** @deprecated use `import { ed25519_hasher } from '@noble/curves/ed25519.js';` */
|
||||||
|
exports.encodeToCurve = (() => exports.ed25519_hasher.encodeToCurve)();
|
||||||
|
/** @deprecated use `import { ristretto255_hasher } from '@noble/curves/ed25519.js';` */
|
||||||
|
exports.hashToRistretto255 = (() => exports.ristretto255_hasher.hashToCurve)();
|
||||||
|
/** @deprecated use `import { ristretto255_hasher } from '@noble/curves/ed25519.js';` */
|
||||||
|
exports.hash_to_ristretto255 = (() => exports.ristretto255_hasher.hashToCurve)();
|
||||||
|
//# sourceMappingURL=ed25519.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+100
@@ -0,0 +1,100 @@
|
|||||||
|
import type { AffinePoint } from './abstract/curve.ts';
|
||||||
|
import { PrimeEdwardsPoint, type CurveFn, type EdwardsPoint, type EdwardsPointCons } from './abstract/edwards.ts';
|
||||||
|
import { type H2CHasher, type H2CHasherBase, type H2CMethod, type htfBasicOpts } from './abstract/hash-to-curve.ts';
|
||||||
|
import { type IField } from './abstract/modular.ts';
|
||||||
|
import { type MontgomeryECDH as XCurveFn } from './abstract/montgomery.ts';
|
||||||
|
import { type Hex } from './utils.ts';
|
||||||
|
/**
|
||||||
|
* ed448 EdDSA curve and methods.
|
||||||
|
* @example
|
||||||
|
* import { ed448 } from '@noble/curves/ed448';
|
||||||
|
* const { secretKey, publicKey } = ed448.keygen();
|
||||||
|
* const msg = new TextEncoder().encode('hello');
|
||||||
|
* const sig = ed448.sign(msg, secretKey);
|
||||||
|
* const isValid = ed448.verify(sig, msg, publicKey);
|
||||||
|
*/
|
||||||
|
export declare const ed448: CurveFn;
|
||||||
|
/** Prehashed version of ed448. Accepts already-hashed messages in sign() and verify(). */
|
||||||
|
export declare const ed448ph: CurveFn;
|
||||||
|
/**
|
||||||
|
* E448 curve, defined by NIST.
|
||||||
|
* E448 != edwards448 used in ed448.
|
||||||
|
* E448 is birationally equivalent to edwards448.
|
||||||
|
*/
|
||||||
|
export declare const E448: EdwardsPointCons;
|
||||||
|
/**
|
||||||
|
* ECDH using curve448 aka x448.
|
||||||
|
* x448 has 56-byte keys as per RFC 7748, while
|
||||||
|
* ed448 has 57-byte keys as per RFC 8032.
|
||||||
|
*/
|
||||||
|
export declare const x448: XCurveFn;
|
||||||
|
/** Hashing / encoding to ed448 points / field. RFC 9380 methods. */
|
||||||
|
export declare const ed448_hasher: H2CHasher<bigint>;
|
||||||
|
/**
|
||||||
|
* Each ed448/EdwardsPoint has 4 different equivalent points. This can be
|
||||||
|
* a source of bugs for protocols like ring signatures. Decaf was created to solve this.
|
||||||
|
* Decaf point operates in X:Y:Z:T extended coordinates like EdwardsPoint,
|
||||||
|
* but it should work in its own namespace: do not combine those two.
|
||||||
|
* See [RFC9496](https://www.rfc-editor.org/rfc/rfc9496).
|
||||||
|
*/
|
||||||
|
declare class _DecafPoint extends PrimeEdwardsPoint<_DecafPoint> {
|
||||||
|
static BASE: _DecafPoint;
|
||||||
|
static ZERO: _DecafPoint;
|
||||||
|
static Fp: IField<bigint>;
|
||||||
|
static Fn: IField<bigint>;
|
||||||
|
constructor(ep: EdwardsPoint);
|
||||||
|
static fromAffine(ap: AffinePoint<bigint>): _DecafPoint;
|
||||||
|
protected assertSame(other: _DecafPoint): void;
|
||||||
|
protected init(ep: EdwardsPoint): _DecafPoint;
|
||||||
|
/** @deprecated use `import { decaf448_hasher } from '@noble/curves/ed448.js';` */
|
||||||
|
static hashToCurve(hex: Hex): _DecafPoint;
|
||||||
|
static fromBytes(bytes: Uint8Array): _DecafPoint;
|
||||||
|
/**
|
||||||
|
* Converts decaf-encoded string to decaf point.
|
||||||
|
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-decode-2).
|
||||||
|
* @param hex Decaf-encoded 56 bytes. Not every 56-byte string is valid decaf encoding
|
||||||
|
*/
|
||||||
|
static fromHex(hex: Hex): _DecafPoint;
|
||||||
|
/** @deprecated use `import { pippenger } from '@noble/curves/abstract/curve.js';` */
|
||||||
|
static msm(points: _DecafPoint[], scalars: bigint[]): _DecafPoint;
|
||||||
|
/**
|
||||||
|
* Encodes decaf point to Uint8Array.
|
||||||
|
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-encode-2).
|
||||||
|
*/
|
||||||
|
toBytes(): Uint8Array;
|
||||||
|
/**
|
||||||
|
* Compare one point to another.
|
||||||
|
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-equals-2).
|
||||||
|
*/
|
||||||
|
equals(other: _DecafPoint): boolean;
|
||||||
|
is0(): boolean;
|
||||||
|
}
|
||||||
|
export declare const decaf448: {
|
||||||
|
Point: typeof _DecafPoint;
|
||||||
|
};
|
||||||
|
/** Hashing to decaf448 points / field. RFC 9380 methods. */
|
||||||
|
export declare const decaf448_hasher: H2CHasherBase<bigint>;
|
||||||
|
/**
|
||||||
|
* Weird / bogus points, useful for debugging.
|
||||||
|
* Unlike ed25519, there is no ed448 generator point which can produce full T subgroup.
|
||||||
|
* Instead, there is a Klein four-group, which spans over 2 independent 2-torsion points:
|
||||||
|
* (0, 1), (0, -1), (-1, 0), (1, 0).
|
||||||
|
*/
|
||||||
|
export declare const ED448_TORSION_SUBGROUP: string[];
|
||||||
|
type DcfHasher = (msg: Uint8Array, options: htfBasicOpts) => _DecafPoint;
|
||||||
|
/** @deprecated use `decaf448.Point` */
|
||||||
|
export declare const DecafPoint: typeof _DecafPoint;
|
||||||
|
/** @deprecated use `import { ed448_hasher } from '@noble/curves/ed448.js';` */
|
||||||
|
export declare const hashToCurve: H2CMethod<bigint>;
|
||||||
|
/** @deprecated use `import { ed448_hasher } from '@noble/curves/ed448.js';` */
|
||||||
|
export declare const encodeToCurve: H2CMethod<bigint>;
|
||||||
|
/** @deprecated use `import { decaf448_hasher } from '@noble/curves/ed448.js';` */
|
||||||
|
export declare const hashToDecaf448: DcfHasher;
|
||||||
|
/** @deprecated use `import { decaf448_hasher } from '@noble/curves/ed448.js';` */
|
||||||
|
export declare const hash_to_decaf448: DcfHasher;
|
||||||
|
/** @deprecated use `ed448.utils.toMontgomery` */
|
||||||
|
export declare function edwardsToMontgomeryPub(edwardsPub: string | Uint8Array): Uint8Array;
|
||||||
|
/** @deprecated use `ed448.utils.toMontgomery` */
|
||||||
|
export declare const edwardsToMontgomery: typeof edwardsToMontgomeryPub;
|
||||||
|
export {};
|
||||||
|
//# sourceMappingURL=ed448.d.ts.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"ed448.d.ts","sourceRoot":"","sources":["src/ed448.ts"],"names":[],"mappings":"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"}
|
||||||
+463
@@ -0,0 +1,463 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.edwardsToMontgomery = exports.hash_to_decaf448 = exports.hashToDecaf448 = exports.encodeToCurve = exports.hashToCurve = exports.DecafPoint = exports.ED448_TORSION_SUBGROUP = exports.decaf448_hasher = exports.decaf448 = exports.ed448_hasher = exports.x448 = exports.E448 = exports.ed448ph = exports.ed448 = void 0;
|
||||||
|
exports.edwardsToMontgomeryPub = edwardsToMontgomeryPub;
|
||||||
|
/**
|
||||||
|
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
|
||||||
|
* - X448 ECDH
|
||||||
|
* - Decaf cofactor elimination
|
||||||
|
* - Elligator hash-to-group / point indistinguishability
|
||||||
|
* Conforms to RFC 8032 https://www.rfc-editor.org/rfc/rfc8032.html#section-5.2
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
const sha3_js_1 = require("@noble/hashes/sha3.js");
|
||||||
|
const utils_js_1 = require("@noble/hashes/utils.js");
|
||||||
|
const curve_ts_1 = require("./abstract/curve.js");
|
||||||
|
const edwards_ts_1 = require("./abstract/edwards.js");
|
||||||
|
const hash_to_curve_ts_1 = require("./abstract/hash-to-curve.js");
|
||||||
|
const modular_ts_1 = require("./abstract/modular.js");
|
||||||
|
const montgomery_ts_1 = require("./abstract/montgomery.js");
|
||||||
|
const utils_ts_1 = require("./utils.js");
|
||||||
|
// edwards448 curve
|
||||||
|
// a = 1n
|
||||||
|
// d = Fp.neg(39081n)
|
||||||
|
// Finite field 2n**448n - 2n**224n - 1n
|
||||||
|
// Subgroup order
|
||||||
|
// 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
|
||||||
|
const ed448_CURVE = {
|
||||||
|
p: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff'),
|
||||||
|
n: BigInt('0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3'),
|
||||||
|
h: BigInt(4),
|
||||||
|
a: BigInt(1),
|
||||||
|
d: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffff6756'),
|
||||||
|
Gx: BigInt('0x4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e'),
|
||||||
|
Gy: BigInt('0x693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14'),
|
||||||
|
};
|
||||||
|
// E448 NIST curve is identical to edwards448, except for:
|
||||||
|
// d = 39082/39081
|
||||||
|
// Gx = 3/2
|
||||||
|
const E448_CURVE = Object.assign({}, ed448_CURVE, {
|
||||||
|
d: BigInt('0xd78b4bdc7f0daf19f24f38c29373a2ccad46157242a50f37809b1da3412a12e79ccc9c81264cfe9ad080997058fb61c4243cc32dbaa156b9'),
|
||||||
|
Gx: BigInt('0x79a70b2b70400553ae7c9df416c792c61128751ac92969240c25a07d728bdc93e21f7787ed6972249de732f38496cd11698713093e9c04fc'),
|
||||||
|
Gy: BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000001'),
|
||||||
|
});
|
||||||
|
const shake256_114 = /* @__PURE__ */ (0, utils_js_1.createHasher)(() => sha3_js_1.shake256.create({ dkLen: 114 }));
|
||||||
|
const shake256_64 = /* @__PURE__ */ (0, utils_js_1.createHasher)(() => sha3_js_1.shake256.create({ dkLen: 64 }));
|
||||||
|
// prettier-ignore
|
||||||
|
const _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4), _11n = BigInt(11);
|
||||||
|
// prettier-ignore
|
||||||
|
const _22n = BigInt(22), _44n = BigInt(44), _88n = BigInt(88), _223n = BigInt(223);
|
||||||
|
// powPminus3div4 calculates z = x^k mod p, where k = (p-3)/4.
|
||||||
|
// Used for efficient square root calculation.
|
||||||
|
// ((P-3)/4).toString(2) would produce bits [223x 1, 0, 222x 1]
|
||||||
|
function ed448_pow_Pminus3div4(x) {
|
||||||
|
const P = ed448_CURVE.p;
|
||||||
|
const b2 = (x * x * x) % P;
|
||||||
|
const b3 = (b2 * b2 * x) % P;
|
||||||
|
const b6 = ((0, modular_ts_1.pow2)(b3, _3n, P) * b3) % P;
|
||||||
|
const b9 = ((0, modular_ts_1.pow2)(b6, _3n, P) * b3) % P;
|
||||||
|
const b11 = ((0, modular_ts_1.pow2)(b9, _2n, P) * b2) % P;
|
||||||
|
const b22 = ((0, modular_ts_1.pow2)(b11, _11n, P) * b11) % P;
|
||||||
|
const b44 = ((0, modular_ts_1.pow2)(b22, _22n, P) * b22) % P;
|
||||||
|
const b88 = ((0, modular_ts_1.pow2)(b44, _44n, P) * b44) % P;
|
||||||
|
const b176 = ((0, modular_ts_1.pow2)(b88, _88n, P) * b88) % P;
|
||||||
|
const b220 = ((0, modular_ts_1.pow2)(b176, _44n, P) * b44) % P;
|
||||||
|
const b222 = ((0, modular_ts_1.pow2)(b220, _2n, P) * b2) % P;
|
||||||
|
const b223 = ((0, modular_ts_1.pow2)(b222, _1n, P) * x) % P;
|
||||||
|
return ((0, modular_ts_1.pow2)(b223, _223n, P) * b222) % P;
|
||||||
|
}
|
||||||
|
function adjustScalarBytes(bytes) {
|
||||||
|
// Section 5: Likewise, for X448, set the two least significant bits of the first byte to 0,
|
||||||
|
bytes[0] &= 252; // 0b11111100
|
||||||
|
// and the most significant bit of the last byte to 1.
|
||||||
|
bytes[55] |= 128; // 0b10000000
|
||||||
|
// NOTE: is NOOP for 56 bytes scalars (X25519/X448)
|
||||||
|
bytes[56] = 0; // Byte outside of group (456 buts vs 448 bits)
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
// Constant-time ratio of u to v. Allows to combine inversion and square root u/√v.
|
||||||
|
// Uses algo from RFC8032 5.1.3.
|
||||||
|
function uvRatio(u, v) {
|
||||||
|
const P = ed448_CURVE.p;
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc8032#section-5.2.3
|
||||||
|
// To compute the square root of (u/v), the first step is to compute the
|
||||||
|
// candidate root x = (u/v)^((p+1)/4). This can be done using the
|
||||||
|
// following trick, to use a single modular powering for both the
|
||||||
|
// inversion of v and the square root:
|
||||||
|
// x = (u/v)^((p+1)/4) = u³v(u⁵v³)^((p-3)/4) (mod p)
|
||||||
|
const u2v = (0, modular_ts_1.mod)(u * u * v, P); // u²v
|
||||||
|
const u3v = (0, modular_ts_1.mod)(u2v * u, P); // u³v
|
||||||
|
const u5v3 = (0, modular_ts_1.mod)(u3v * u2v * v, P); // u⁵v³
|
||||||
|
const root = ed448_pow_Pminus3div4(u5v3);
|
||||||
|
const x = (0, modular_ts_1.mod)(u3v * root, P);
|
||||||
|
// Verify that root is exists
|
||||||
|
const x2 = (0, modular_ts_1.mod)(x * x, P); // x²
|
||||||
|
// If vx² = u, the recovered x-coordinate is x. Otherwise, no
|
||||||
|
// square root exists, and the decoding fails.
|
||||||
|
return { isValid: (0, modular_ts_1.mod)(x2 * v, P) === u, value: x };
|
||||||
|
}
|
||||||
|
// Finite field 2n**448n - 2n**224n - 1n
|
||||||
|
// The value fits in 448 bits, but we use 456-bit (57-byte) elements because of bitflags.
|
||||||
|
// - ed25519 fits in 255 bits, allowing using last 1 byte for specifying bit flag of point negation.
|
||||||
|
// - ed448 fits in 448 bits. We can't use last 1 byte: we can only use a bit 224 in the middle.
|
||||||
|
const Fp = /* @__PURE__ */ (() => (0, modular_ts_1.Field)(ed448_CURVE.p, { BITS: 456, isLE: true }))();
|
||||||
|
const Fn = /* @__PURE__ */ (() => (0, modular_ts_1.Field)(ed448_CURVE.n, { BITS: 456, isLE: true }))();
|
||||||
|
// decaf448 uses 448-bit (56-byte) keys
|
||||||
|
const Fp448 = /* @__PURE__ */ (() => (0, modular_ts_1.Field)(ed448_CURVE.p, { BITS: 448, isLE: true }))();
|
||||||
|
const Fn448 = /* @__PURE__ */ (() => (0, modular_ts_1.Field)(ed448_CURVE.n, { BITS: 448, isLE: true }))();
|
||||||
|
// SHAKE256(dom4(phflag,context)||x, 114)
|
||||||
|
function dom4(data, ctx, phflag) {
|
||||||
|
if (ctx.length > 255)
|
||||||
|
throw new Error('context must be smaller than 255, got: ' + ctx.length);
|
||||||
|
return (0, utils_js_1.concatBytes)((0, utils_ts_1.asciiToBytes)('SigEd448'), new Uint8Array([phflag ? 1 : 0, ctx.length]), ctx, data);
|
||||||
|
}
|
||||||
|
// const ed448_eddsa_opts = { adjustScalarBytes, domain: dom4 };
|
||||||
|
// const ed448_Point = edwards(ed448_CURVE, { Fp, Fn, uvRatio });
|
||||||
|
const ED448_DEF = /* @__PURE__ */ (() => ({
|
||||||
|
...ed448_CURVE,
|
||||||
|
Fp,
|
||||||
|
Fn,
|
||||||
|
nBitLength: Fn.BITS,
|
||||||
|
hash: shake256_114,
|
||||||
|
adjustScalarBytes,
|
||||||
|
domain: dom4,
|
||||||
|
uvRatio,
|
||||||
|
}))();
|
||||||
|
/**
|
||||||
|
* ed448 EdDSA curve and methods.
|
||||||
|
* @example
|
||||||
|
* import { ed448 } from '@noble/curves/ed448';
|
||||||
|
* const { secretKey, publicKey } = ed448.keygen();
|
||||||
|
* const msg = new TextEncoder().encode('hello');
|
||||||
|
* const sig = ed448.sign(msg, secretKey);
|
||||||
|
* const isValid = ed448.verify(sig, msg, publicKey);
|
||||||
|
*/
|
||||||
|
exports.ed448 = (0, edwards_ts_1.twistedEdwards)(ED448_DEF);
|
||||||
|
// There is no ed448ctx, since ed448 supports ctx by default
|
||||||
|
/** Prehashed version of ed448. Accepts already-hashed messages in sign() and verify(). */
|
||||||
|
exports.ed448ph = (() => (0, edwards_ts_1.twistedEdwards)({
|
||||||
|
...ED448_DEF,
|
||||||
|
prehash: shake256_64,
|
||||||
|
}))();
|
||||||
|
/**
|
||||||
|
* E448 curve, defined by NIST.
|
||||||
|
* E448 != edwards448 used in ed448.
|
||||||
|
* E448 is birationally equivalent to edwards448.
|
||||||
|
*/
|
||||||
|
exports.E448 = (0, edwards_ts_1.edwards)(E448_CURVE);
|
||||||
|
/**
|
||||||
|
* ECDH using curve448 aka x448.
|
||||||
|
* x448 has 56-byte keys as per RFC 7748, while
|
||||||
|
* ed448 has 57-byte keys as per RFC 8032.
|
||||||
|
*/
|
||||||
|
exports.x448 = (() => {
|
||||||
|
const P = ed448_CURVE.p;
|
||||||
|
return (0, montgomery_ts_1.montgomery)({
|
||||||
|
P,
|
||||||
|
type: 'x448',
|
||||||
|
powPminus2: (x) => {
|
||||||
|
const Pminus3div4 = ed448_pow_Pminus3div4(x);
|
||||||
|
const Pminus3 = (0, modular_ts_1.pow2)(Pminus3div4, _2n, P);
|
||||||
|
return (0, modular_ts_1.mod)(Pminus3 * x, P); // Pminus3 * x = Pminus2
|
||||||
|
},
|
||||||
|
adjustScalarBytes,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
// Hash To Curve Elligator2 Map
|
||||||
|
const ELL2_C1 = /* @__PURE__ */ (() => (Fp.ORDER - BigInt(3)) / BigInt(4))(); // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
||||||
|
const ELL2_J = /* @__PURE__ */ BigInt(156326);
|
||||||
|
function map_to_curve_elligator2_curve448(u) {
|
||||||
|
let tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
||||||
|
let e1 = Fp.eql(tv1, Fp.ONE); // 2. e1 = tv1 == 1
|
||||||
|
tv1 = Fp.cmov(tv1, Fp.ZERO, e1); // 3. tv1 = CMOV(tv1, 0, e1) # If Z * u^2 == -1, set tv1 = 0
|
||||||
|
let xd = Fp.sub(Fp.ONE, tv1); // 4. xd = 1 - tv1
|
||||||
|
let x1n = Fp.neg(ELL2_J); // 5. x1n = -J
|
||||||
|
let tv2 = Fp.sqr(xd); // 6. tv2 = xd^2
|
||||||
|
let gxd = Fp.mul(tv2, xd); // 7. gxd = tv2 * xd # gxd = xd^3
|
||||||
|
let gx1 = Fp.mul(tv1, Fp.neg(ELL2_J)); // 8. gx1 = -J * tv1 # x1n + J * xd
|
||||||
|
gx1 = Fp.mul(gx1, x1n); // 9. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
|
||||||
|
gx1 = Fp.add(gx1, tv2); // 10. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
|
||||||
|
gx1 = Fp.mul(gx1, x1n); // 11. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
|
||||||
|
let tv3 = Fp.sqr(gxd); // 12. tv3 = gxd^2
|
||||||
|
tv2 = Fp.mul(gx1, gxd); // 13. tv2 = gx1 * gxd # gx1 * gxd
|
||||||
|
tv3 = Fp.mul(tv3, tv2); // 14. tv3 = tv3 * tv2 # gx1 * gxd^3
|
||||||
|
let y1 = Fp.pow(tv3, ELL2_C1); // 15. y1 = tv3^c1 # (gx1 * gxd^3)^((p - 3) / 4)
|
||||||
|
y1 = Fp.mul(y1, tv2); // 16. y1 = y1 * tv2 # gx1 * gxd * (gx1 * gxd^3)^((p - 3) / 4)
|
||||||
|
let x2n = Fp.mul(x1n, Fp.neg(tv1)); // 17. x2n = -tv1 * x1n # x2 = x2n / xd = -1 * u^2 * x1n / xd
|
||||||
|
let y2 = Fp.mul(y1, u); // 18. y2 = y1 * u
|
||||||
|
y2 = Fp.cmov(y2, Fp.ZERO, e1); // 19. y2 = CMOV(y2, 0, e1)
|
||||||
|
tv2 = Fp.sqr(y1); // 20. tv2 = y1^2
|
||||||
|
tv2 = Fp.mul(tv2, gxd); // 21. tv2 = tv2 * gxd
|
||||||
|
let e2 = Fp.eql(tv2, gx1); // 22. e2 = tv2 == gx1
|
||||||
|
let xn = Fp.cmov(x2n, x1n, e2); // 23. xn = CMOV(x2n, x1n, e2) # If e2, x = x1, else x = x2
|
||||||
|
let y = Fp.cmov(y2, y1, e2); // 24. y = CMOV(y2, y1, e2) # If e2, y = y1, else y = y2
|
||||||
|
let e3 = Fp.isOdd(y); // 25. e3 = sgn0(y) == 1 # Fix sign of y
|
||||||
|
y = Fp.cmov(y, Fp.neg(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
|
||||||
|
return { xn, xd, yn: y, yd: Fp.ONE }; // 27. return (xn, xd, y, 1)
|
||||||
|
}
|
||||||
|
function map_to_curve_elligator2_edwards448(u) {
|
||||||
|
let { xn, xd, yn, yd } = map_to_curve_elligator2_curve448(u); // 1. (xn, xd, yn, yd) = map_to_curve_elligator2_curve448(u)
|
||||||
|
let xn2 = Fp.sqr(xn); // 2. xn2 = xn^2
|
||||||
|
let xd2 = Fp.sqr(xd); // 3. xd2 = xd^2
|
||||||
|
let xd4 = Fp.sqr(xd2); // 4. xd4 = xd2^2
|
||||||
|
let yn2 = Fp.sqr(yn); // 5. yn2 = yn^2
|
||||||
|
let yd2 = Fp.sqr(yd); // 6. yd2 = yd^2
|
||||||
|
let xEn = Fp.sub(xn2, xd2); // 7. xEn = xn2 - xd2
|
||||||
|
let tv2 = Fp.sub(xEn, xd2); // 8. tv2 = xEn - xd2
|
||||||
|
xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
|
||||||
|
xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
|
||||||
|
xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
|
||||||
|
xEn = Fp.mul(xEn, _4n); // 12. xEn = xEn * 4
|
||||||
|
tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
|
||||||
|
tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
|
||||||
|
let tv3 = Fp.mul(yn2, _4n); // 15. tv3 = 4 * yn2
|
||||||
|
let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
|
||||||
|
tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
|
||||||
|
let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
|
||||||
|
tv2 = Fp.mul(tv2, xn); // 19. tv2 = tv2 * xn
|
||||||
|
let tv4 = Fp.mul(xn, xd4); // 20. tv4 = xn * xd4
|
||||||
|
let yEn = Fp.sub(tv3, yd2); // 21. yEn = tv3 - yd2
|
||||||
|
yEn = Fp.mul(yEn, tv4); // 22. yEn = yEn * tv4
|
||||||
|
yEn = Fp.sub(yEn, tv2); // 23. yEn = yEn - tv2
|
||||||
|
tv1 = Fp.add(xn2, xd2); // 24. tv1 = xn2 + xd2
|
||||||
|
tv1 = Fp.mul(tv1, xd2); // 25. tv1 = tv1 * xd2
|
||||||
|
tv1 = Fp.mul(tv1, xd); // 26. tv1 = tv1 * xd
|
||||||
|
tv1 = Fp.mul(tv1, yn2); // 27. tv1 = tv1 * yn2
|
||||||
|
tv1 = Fp.mul(tv1, BigInt(-2)); // 28. tv1 = -2 * tv1
|
||||||
|
let yEd = Fp.add(tv2, tv1); // 29. yEd = tv2 + tv1
|
||||||
|
tv4 = Fp.mul(tv4, yd2); // 30. tv4 = tv4 * yd2
|
||||||
|
yEd = Fp.add(yEd, tv4); // 31. yEd = yEd + tv4
|
||||||
|
tv1 = Fp.mul(xEd, yEd); // 32. tv1 = xEd * yEd
|
||||||
|
let e = Fp.eql(tv1, Fp.ZERO); // 33. e = tv1 == 0
|
||||||
|
xEn = Fp.cmov(xEn, Fp.ZERO, e); // 34. xEn = CMOV(xEn, 0, e)
|
||||||
|
xEd = Fp.cmov(xEd, Fp.ONE, e); // 35. xEd = CMOV(xEd, 1, e)
|
||||||
|
yEn = Fp.cmov(yEn, Fp.ONE, e); // 36. yEn = CMOV(yEn, 1, e)
|
||||||
|
yEd = Fp.cmov(yEd, Fp.ONE, e); // 37. yEd = CMOV(yEd, 1, e)
|
||||||
|
const inv = (0, modular_ts_1.FpInvertBatch)(Fp, [xEd, yEd], true); // batch division
|
||||||
|
return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
|
||||||
|
}
|
||||||
|
/** Hashing / encoding to ed448 points / field. RFC 9380 methods. */
|
||||||
|
exports.ed448_hasher = (() => (0, hash_to_curve_ts_1.createHasher)(exports.ed448.Point, (scalars) => map_to_curve_elligator2_edwards448(scalars[0]), {
|
||||||
|
DST: 'edwards448_XOF:SHAKE256_ELL2_RO_',
|
||||||
|
encodeDST: 'edwards448_XOF:SHAKE256_ELL2_NU_',
|
||||||
|
p: Fp.ORDER,
|
||||||
|
m: 1,
|
||||||
|
k: 224,
|
||||||
|
expand: 'xof',
|
||||||
|
hash: sha3_js_1.shake256,
|
||||||
|
}))();
|
||||||
|
// 1-d
|
||||||
|
const ONE_MINUS_D = /* @__PURE__ */ BigInt('39082');
|
||||||
|
// 1-2d
|
||||||
|
const ONE_MINUS_TWO_D = /* @__PURE__ */ BigInt('78163');
|
||||||
|
// √(-d)
|
||||||
|
const SQRT_MINUS_D = /* @__PURE__ */ BigInt('98944233647732219769177004876929019128417576295529901074099889598043702116001257856802131563896515373927712232092845883226922417596214');
|
||||||
|
// 1 / √(-d)
|
||||||
|
const INVSQRT_MINUS_D = /* @__PURE__ */ BigInt('315019913931389607337177038330951043522456072897266928557328499619017160722351061360252776265186336876723201881398623946864393857820716');
|
||||||
|
// Calculates 1/√(number)
|
||||||
|
const invertSqrt = (number) => uvRatio(_1n, number);
|
||||||
|
/**
|
||||||
|
* Elligator map for hash-to-curve of decaf448.
|
||||||
|
* Described in [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#appendix-C)
|
||||||
|
* and [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-element-derivation-2).
|
||||||
|
*/
|
||||||
|
function calcElligatorDecafMap(r0) {
|
||||||
|
const { d } = ed448_CURVE;
|
||||||
|
const P = Fp.ORDER;
|
||||||
|
const mod = (n) => Fp.create(n);
|
||||||
|
const r = mod(-(r0 * r0)); // 1
|
||||||
|
const u0 = mod(d * (r - _1n)); // 2
|
||||||
|
const u1 = mod((u0 + _1n) * (u0 - r)); // 3
|
||||||
|
const { isValid: was_square, value: v } = uvRatio(ONE_MINUS_TWO_D, mod((r + _1n) * u1)); // 4
|
||||||
|
let v_prime = v; // 5
|
||||||
|
if (!was_square)
|
||||||
|
v_prime = mod(r0 * v);
|
||||||
|
let sgn = _1n; // 6
|
||||||
|
if (!was_square)
|
||||||
|
sgn = mod(-_1n);
|
||||||
|
const s = mod(v_prime * (r + _1n)); // 7
|
||||||
|
let s_abs = s;
|
||||||
|
if ((0, modular_ts_1.isNegativeLE)(s, P))
|
||||||
|
s_abs = mod(-s);
|
||||||
|
const s2 = s * s;
|
||||||
|
const W0 = mod(s_abs * _2n); // 8
|
||||||
|
const W1 = mod(s2 + _1n); // 9
|
||||||
|
const W2 = mod(s2 - _1n); // 10
|
||||||
|
const W3 = mod(v_prime * s * (r - _1n) * ONE_MINUS_TWO_D + sgn); // 11
|
||||||
|
return new exports.ed448.Point(mod(W0 * W3), mod(W2 * W1), mod(W1 * W3), mod(W0 * W2));
|
||||||
|
}
|
||||||
|
function decaf448_map(bytes) {
|
||||||
|
(0, utils_js_1.abytes)(bytes, 112);
|
||||||
|
const skipValidation = true;
|
||||||
|
// Note: Similar to the field element decoding described in
|
||||||
|
// [RFC7748], and unlike the field element decoding described in
|
||||||
|
// Section 5.3.1, non-canonical values are accepted.
|
||||||
|
const r1 = Fp448.create(Fp448.fromBytes(bytes.subarray(0, 56), skipValidation));
|
||||||
|
const R1 = calcElligatorDecafMap(r1);
|
||||||
|
const r2 = Fp448.create(Fp448.fromBytes(bytes.subarray(56, 112), skipValidation));
|
||||||
|
const R2 = calcElligatorDecafMap(r2);
|
||||||
|
return new _DecafPoint(R1.add(R2));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Each ed448/EdwardsPoint has 4 different equivalent points. This can be
|
||||||
|
* a source of bugs for protocols like ring signatures. Decaf was created to solve this.
|
||||||
|
* Decaf point operates in X:Y:Z:T extended coordinates like EdwardsPoint,
|
||||||
|
* but it should work in its own namespace: do not combine those two.
|
||||||
|
* See [RFC9496](https://www.rfc-editor.org/rfc/rfc9496).
|
||||||
|
*/
|
||||||
|
class _DecafPoint extends edwards_ts_1.PrimeEdwardsPoint {
|
||||||
|
constructor(ep) {
|
||||||
|
super(ep);
|
||||||
|
}
|
||||||
|
static fromAffine(ap) {
|
||||||
|
return new _DecafPoint(exports.ed448.Point.fromAffine(ap));
|
||||||
|
}
|
||||||
|
assertSame(other) {
|
||||||
|
if (!(other instanceof _DecafPoint))
|
||||||
|
throw new Error('DecafPoint expected');
|
||||||
|
}
|
||||||
|
init(ep) {
|
||||||
|
return new _DecafPoint(ep);
|
||||||
|
}
|
||||||
|
/** @deprecated use `import { decaf448_hasher } from '@noble/curves/ed448.js';` */
|
||||||
|
static hashToCurve(hex) {
|
||||||
|
return decaf448_map((0, utils_ts_1.ensureBytes)('decafHash', hex, 112));
|
||||||
|
}
|
||||||
|
static fromBytes(bytes) {
|
||||||
|
(0, utils_js_1.abytes)(bytes, 56);
|
||||||
|
const { d } = ed448_CURVE;
|
||||||
|
const P = Fp.ORDER;
|
||||||
|
const mod = (n) => Fp448.create(n);
|
||||||
|
const s = Fp448.fromBytes(bytes);
|
||||||
|
// 1. Check that s_bytes is the canonical encoding of a field element, or else abort.
|
||||||
|
// 2. Check that s is non-negative, or else abort
|
||||||
|
if (!(0, utils_ts_1.equalBytes)(Fn448.toBytes(s), bytes) || (0, modular_ts_1.isNegativeLE)(s, P))
|
||||||
|
throw new Error('invalid decaf448 encoding 1');
|
||||||
|
const s2 = mod(s * s); // 1
|
||||||
|
const u1 = mod(_1n + s2); // 2
|
||||||
|
const u1sq = mod(u1 * u1);
|
||||||
|
const u2 = mod(u1sq - _4n * d * s2); // 3
|
||||||
|
const { isValid, value: invsqrt } = invertSqrt(mod(u2 * u1sq)); // 4
|
||||||
|
let u3 = mod((s + s) * invsqrt * u1 * SQRT_MINUS_D); // 5
|
||||||
|
if ((0, modular_ts_1.isNegativeLE)(u3, P))
|
||||||
|
u3 = mod(-u3);
|
||||||
|
const x = mod(u3 * invsqrt * u2 * INVSQRT_MINUS_D); // 6
|
||||||
|
const y = mod((_1n - s2) * invsqrt * u1); // 7
|
||||||
|
const t = mod(x * y); // 8
|
||||||
|
if (!isValid)
|
||||||
|
throw new Error('invalid decaf448 encoding 2');
|
||||||
|
return new _DecafPoint(new exports.ed448.Point(x, y, _1n, t));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Converts decaf-encoded string to decaf point.
|
||||||
|
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-decode-2).
|
||||||
|
* @param hex Decaf-encoded 56 bytes. Not every 56-byte string is valid decaf encoding
|
||||||
|
*/
|
||||||
|
static fromHex(hex) {
|
||||||
|
return _DecafPoint.fromBytes((0, utils_ts_1.ensureBytes)('decafHex', hex, 56));
|
||||||
|
}
|
||||||
|
/** @deprecated use `import { pippenger } from '@noble/curves/abstract/curve.js';` */
|
||||||
|
static msm(points, scalars) {
|
||||||
|
return (0, curve_ts_1.pippenger)(_DecafPoint, Fn, points, scalars);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Encodes decaf point to Uint8Array.
|
||||||
|
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-encode-2).
|
||||||
|
*/
|
||||||
|
toBytes() {
|
||||||
|
const { X, Z, T } = this.ep;
|
||||||
|
const P = Fp.ORDER;
|
||||||
|
const mod = (n) => Fp.create(n);
|
||||||
|
const u1 = mod(mod(X + T) * mod(X - T)); // 1
|
||||||
|
const x2 = mod(X * X);
|
||||||
|
const { value: invsqrt } = invertSqrt(mod(u1 * ONE_MINUS_D * x2)); // 2
|
||||||
|
let ratio = mod(invsqrt * u1 * SQRT_MINUS_D); // 3
|
||||||
|
if ((0, modular_ts_1.isNegativeLE)(ratio, P))
|
||||||
|
ratio = mod(-ratio);
|
||||||
|
const u2 = mod(INVSQRT_MINUS_D * ratio * Z - T); // 4
|
||||||
|
let s = mod(ONE_MINUS_D * invsqrt * X * u2); // 5
|
||||||
|
if ((0, modular_ts_1.isNegativeLE)(s, P))
|
||||||
|
s = mod(-s);
|
||||||
|
return Fn448.toBytes(s);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Compare one point to another.
|
||||||
|
* Described in [RFC9496](https://www.rfc-editor.org/rfc/rfc9496#name-equals-2).
|
||||||
|
*/
|
||||||
|
equals(other) {
|
||||||
|
this.assertSame(other);
|
||||||
|
const { X: X1, Y: Y1 } = this.ep;
|
||||||
|
const { X: X2, Y: Y2 } = other.ep;
|
||||||
|
// (x1 * y2 == y1 * x2)
|
||||||
|
return Fp.create(X1 * Y2) === Fp.create(Y1 * X2);
|
||||||
|
}
|
||||||
|
is0() {
|
||||||
|
return this.equals(_DecafPoint.ZERO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The following gymnastics is done because typescript strips comments otherwise
|
||||||
|
// prettier-ignore
|
||||||
|
_DecafPoint.BASE =
|
||||||
|
/* @__PURE__ */ (() => new _DecafPoint(exports.ed448.Point.BASE).multiplyUnsafe(_2n))();
|
||||||
|
// prettier-ignore
|
||||||
|
_DecafPoint.ZERO =
|
||||||
|
/* @__PURE__ */ (() => new _DecafPoint(exports.ed448.Point.ZERO))();
|
||||||
|
// prettier-ignore
|
||||||
|
_DecafPoint.Fp =
|
||||||
|
/* @__PURE__ */ (() => Fp448)();
|
||||||
|
// prettier-ignore
|
||||||
|
_DecafPoint.Fn =
|
||||||
|
/* @__PURE__ */ (() => Fn448)();
|
||||||
|
exports.decaf448 = { Point: _DecafPoint };
|
||||||
|
/** Hashing to decaf448 points / field. RFC 9380 methods. */
|
||||||
|
exports.decaf448_hasher = {
|
||||||
|
hashToCurve(msg, options) {
|
||||||
|
const DST = options?.DST || 'decaf448_XOF:SHAKE256_D448MAP_RO_';
|
||||||
|
return decaf448_map((0, hash_to_curve_ts_1.expand_message_xof)(msg, DST, 112, 224, sha3_js_1.shake256));
|
||||||
|
},
|
||||||
|
// Warning: has big modulo bias of 2^-64.
|
||||||
|
// RFC is invalid. RFC says "use 64-byte xof", while for 2^-112 bias
|
||||||
|
// it must use 84-byte xof (56+56/2), not 64.
|
||||||
|
hashToScalar(msg, options = { DST: hash_to_curve_ts_1._DST_scalar }) {
|
||||||
|
// Can't use `Fn448.fromBytes()`. 64-byte input => 56-byte field element
|
||||||
|
const xof = (0, hash_to_curve_ts_1.expand_message_xof)(msg, options.DST, 64, 256, sha3_js_1.shake256);
|
||||||
|
return Fn448.create((0, utils_ts_1.bytesToNumberLE)(xof));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// export const decaf448_oprf: OPRF = createORPF({
|
||||||
|
// name: 'decaf448-SHAKE256',
|
||||||
|
// Point: DecafPoint,
|
||||||
|
// hash: (msg: Uint8Array) => shake256(msg, { dkLen: 64 }),
|
||||||
|
// hashToGroup: decaf448_hasher.hashToCurve,
|
||||||
|
// hashToScalar: decaf448_hasher.hashToScalar,
|
||||||
|
// });
|
||||||
|
/**
|
||||||
|
* Weird / bogus points, useful for debugging.
|
||||||
|
* Unlike ed25519, there is no ed448 generator point which can produce full T subgroup.
|
||||||
|
* Instead, there is a Klein four-group, which spans over 2 independent 2-torsion points:
|
||||||
|
* (0, 1), (0, -1), (-1, 0), (1, 0).
|
||||||
|
*/
|
||||||
|
exports.ED448_TORSION_SUBGROUP = [
|
||||||
|
'010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||||
|
'fefffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff00',
|
||||||
|
'000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||||
|
'000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080',
|
||||||
|
];
|
||||||
|
/** @deprecated use `decaf448.Point` */
|
||||||
|
exports.DecafPoint = _DecafPoint;
|
||||||
|
/** @deprecated use `import { ed448_hasher } from '@noble/curves/ed448.js';` */
|
||||||
|
exports.hashToCurve = (() => exports.ed448_hasher.hashToCurve)();
|
||||||
|
/** @deprecated use `import { ed448_hasher } from '@noble/curves/ed448.js';` */
|
||||||
|
exports.encodeToCurve = (() => exports.ed448_hasher.encodeToCurve)();
|
||||||
|
/** @deprecated use `import { decaf448_hasher } from '@noble/curves/ed448.js';` */
|
||||||
|
exports.hashToDecaf448 = (() => exports.decaf448_hasher.hashToCurve)();
|
||||||
|
/** @deprecated use `import { decaf448_hasher } from '@noble/curves/ed448.js';` */
|
||||||
|
exports.hash_to_decaf448 = (() => exports.decaf448_hasher.hashToCurve)();
|
||||||
|
/** @deprecated use `ed448.utils.toMontgomery` */
|
||||||
|
function edwardsToMontgomeryPub(edwardsPub) {
|
||||||
|
return exports.ed448.utils.toMontgomery((0, utils_ts_1.ensureBytes)('pub', edwardsPub));
|
||||||
|
}
|
||||||
|
/** @deprecated use `ed448.utils.toMontgomery` */
|
||||||
|
exports.edwardsToMontgomery = edwardsToMontgomeryPub;
|
||||||
|
//# sourceMappingURL=ed448.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+19
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Utilities for short weierstrass curves, combined with noble-hashes.
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
import { type CurveFn, type CurveType } from './abstract/weierstrass.ts';
|
||||||
|
import type { CHash } from './utils.ts';
|
||||||
|
/** connects noble-curves to noble-hashes */
|
||||||
|
export declare function getHash(hash: CHash): {
|
||||||
|
hash: CHash;
|
||||||
|
};
|
||||||
|
/** Same API as @noble/hashes, with ability to create curve with custom hash */
|
||||||
|
export type CurveDef = Readonly<Omit<CurveType, 'hash'>>;
|
||||||
|
export type CurveFnWithCreate = CurveFn & {
|
||||||
|
create: (hash: CHash) => CurveFn;
|
||||||
|
};
|
||||||
|
/** @deprecated use new `weierstrass()` and `ecdsa()` methods */
|
||||||
|
export declare function createCurve(curveDef: CurveDef, defHash: CHash): CurveFnWithCreate;
|
||||||
|
//# sourceMappingURL=_shortw_utils.d.ts.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"_shortw_utils.d.ts","sourceRoot":"","sources":["../src/_shortw_utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,sEAAsE;AACtE,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,2BAA2B,CAAC;AACtF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,4CAA4C;AAC5C,wBAAgB,OAAO,CAAC,IAAI,EAAE,KAAK,GAAG;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,CAEpD;AACD,+EAA+E;AAC/E,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;AACzD,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG;IAAE,MAAM,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,OAAO,CAAA;CAAE,CAAC;AAE/E,gEAAgE;AAChE,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,GAAG,iBAAiB,CAGjF"}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Utilities for short weierstrass curves, combined with noble-hashes.
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
import { weierstrass } from "./abstract/weierstrass.js";
|
||||||
|
/** connects noble-curves to noble-hashes */
|
||||||
|
export function getHash(hash) {
|
||||||
|
return { hash };
|
||||||
|
}
|
||||||
|
/** @deprecated use new `weierstrass()` and `ecdsa()` methods */
|
||||||
|
export function createCurve(curveDef, defHash) {
|
||||||
|
const create = (hash) => weierstrass({ ...curveDef, hash: hash });
|
||||||
|
return { ...create(defHash), create };
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=_shortw_utils.js.map
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user