diff --git a/package.json b/package.json index ceb8adfa..d6dc5319 100644 --- a/package.json +++ b/package.json @@ -123,14 +123,11 @@ "webpack-cli": "^5.1.1" }, "dependencies": { + "@noble/curves": "^1.9.1", "@stellar/js-xdr": "^3.1.2", "base32.js": "^0.1.0", "bignumber.js": "^9.1.2", "buffer": "^6.0.3", - "sha.js": "^2.3.6", - "tweetnacl": "^1.0.3" - }, - "optionalDependencies": { - "sodium-native": "^4.3.3" + "sha.js": "^2.3.6" } } diff --git a/src/index.js b/src/index.js index fcbca04f..ded100fa 100644 --- a/src/index.js +++ b/src/index.js @@ -5,7 +5,7 @@ import cereal from './jsxdr'; export { xdr }; export { cereal }; export { hash } from './hashing'; -export { sign, verify, FastSigning } from './signing'; +export { sign, verify } from './signing'; export { getLiquidityPoolId, LiquidityPoolFeeV18 diff --git a/src/keypair.js b/src/keypair.js index c2cc6b8f..34ccc6a4 100644 --- a/src/keypair.js +++ b/src/keypair.js @@ -1,7 +1,6 @@ /* eslint no-bitwise: ["error", {"allow": ["^"]}] */ -import nacl from 'tweetnacl'; - +import { ed25519 } from '@noble/curves/ed25519'; import { sign, verify, generate } from './signing'; import { StrKey } from './strkey'; import { hash } from './hashing'; @@ -42,7 +41,7 @@ export class Keypair { this._secretSeed = keys.secretKey; this._publicKey = generate(keys.secretKey); - this._secretKey = Buffer.concat([keys.secretKey, this._publicKey]); + this._secretKey = keys.secretKey; if ( keys.publicKey && @@ -113,7 +112,7 @@ export class Keypair { * @returns {Keypair} */ static random() { - const secret = nacl.randomBytes(32); + const secret = ed25519.utils.randomPrivateKey(); return this.fromRawEd25519Seed(secret); } diff --git a/src/signing.js b/src/signing.js index c6030ce8..6bc47f0c 100644 --- a/src/signing.js +++ b/src/signing.js @@ -1,107 +1,20 @@ -// This module provides the signing functionality used by the stellar network -// The code below may look a little strange... this is because we try to provide -// the most efficient signing method possible. First, we try to load the -// native `sodium-native` package for node.js environments, and if that fails we -// fallback to `tweetnacl` - -const actualMethods = {}; - -/** - * Use this flag to check if fast signing (provided by `sodium-native` package) is available. - * If your app is signing a large number of transaction or verifying a large number - * of signatures make sure `sodium-native` package is installed. - */ -export const FastSigning = checkFastSigning(); - -export function sign(data, secretKey) { - return actualMethods.sign(data, secretKey); -} - -export function verify(data, signature, publicKey) { - return actualMethods.verify(data, signature, publicKey); -} +import { ed25519 } from '@noble/curves/ed25519'; export function generate(secretKey) { - return actualMethods.generate(secretKey); + return Buffer.from(ed25519.getPublicKey(secretKey)); } -function checkFastSigning() { - return typeof window === 'undefined' - ? checkFastSigningNode() - : checkFastSigningBrowser(); +export function sign(data, secretKey) { + return Buffer.from(ed25519.sign(Buffer.from(data), secretKey)); } -function checkFastSigningNode() { - // NOTE: we use commonjs style require here because es6 imports - // can only occur at the top level. thanks, obama. - let sodium; - try { - // eslint-disable-next-line - sodium = require('sodium-native'); - } catch (err) { - return checkFastSigningBrowser(); - } - - if (!Object.keys(sodium).length) { - return checkFastSigningBrowser(); - } - - actualMethods.generate = (secretKey) => { - const pk = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES); - const sk = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES); - sodium.crypto_sign_seed_keypair(pk, sk, secretKey); - return pk; - }; - - actualMethods.sign = (data, secretKey) => { - data = Buffer.from(data); - const signature = Buffer.alloc(sodium.crypto_sign_BYTES); - sodium.crypto_sign_detached(signature, data, secretKey); - return signature; - }; - - actualMethods.verify = (data, signature, publicKey) => { - data = Buffer.from(data); - try { - return sodium.crypto_sign_verify_detached(signature, data, publicKey); - } catch (e) { - return false; +export function verify(data, signature, publicKey) { + return ed25519.verify( + Buffer.from(signature), + Buffer.from(data), + Buffer.from(publicKey), + { + zip215: false } - }; - - return true; -} - -function checkFastSigningBrowser() { - // fallback to `tweetnacl` if we're in the browser or - // if there was a failure installing `sodium-native` - // eslint-disable-next-line - const nacl = require('tweetnacl'); - - actualMethods.generate = (secretKey) => { - const secretKeyUint8 = new Uint8Array(secretKey); - const naclKeys = nacl.sign.keyPair.fromSeed(secretKeyUint8); - return Buffer.from(naclKeys.publicKey); - }; - - actualMethods.sign = (data, secretKey) => { - data = Buffer.from(data); - data = new Uint8Array(data.toJSON().data); - secretKey = new Uint8Array(secretKey.toJSON().data); - - const signature = nacl.sign.detached(data, secretKey); - - return Buffer.from(signature); - }; - - actualMethods.verify = (data, signature, publicKey) => { - data = Buffer.from(data); - data = new Uint8Array(data.toJSON().data); - signature = new Uint8Array(signature.toJSON().data); - publicKey = new Uint8Array(publicKey.toJSON().data); - - return nacl.sign.detached.verify(data, signature, publicKey); - }; - - return false; + ); } diff --git a/test/unit/signing_test.js b/test/unit/signing_test.js index f7c2205f..177c1f00 100644 --- a/test/unit/signing_test.js +++ b/test/unit/signing_test.js @@ -7,10 +7,7 @@ let publicKey = Buffer.from( 'ffbdd7ef9933fe7249dc5ca1e7120b6d7b7b99a7a367e1a2fc6cb062fe420437', 'hex' ); -let secretKey = Buffer.from( - '1123740522f11bfef6b3671f51e159ccf589ccf8965262dd5f97d1721d383dd4ffbdd7ef9933fe7249dc5ca1e7120b6d7b7b99a7a367e1a2fc6cb062fe420437', - 'hex' -); +let secretKey = seed; describe('StellarBase#sign', function () { let expectedSig = diff --git a/yarn.lock b/yarn.lock index 541aee95..27455c7f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1062,6 +1062,18 @@ dependencies: eslint-scope "5.1.1" +"@noble/curves@^1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.1.tgz#9654a0bc6c13420ae252ddcf975eaf0f58f0a35c" + integrity sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA== + dependencies: + "@noble/hashes" "1.8.0" + +"@noble/hashes@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.8.0.tgz#cee43d801fcef9644b11b8194857695acd5f815a" + integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1954,45 +1966,6 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -bare-addon-resolve@^1.3.0: - version "1.9.4" - resolved "https://registry.yarnpkg.com/bare-addon-resolve/-/bare-addon-resolve-1.9.4.tgz#50ad7b26de493f978f09bbbbe987219c4ecbfd28" - integrity sha512-unn6Vy/Yke6F99vg/7tcrvM2KUvIhTNniaSqDbam4AWkd4NhvDVSrQiRYVlNzUV2P7SPobkCK7JFVxrJk9btCg== - dependencies: - bare-module-resolve "^1.10.0" - bare-semver "^1.0.0" - -bare-module-resolve@^1.10.0: - version "1.10.2" - resolved "https://registry.yarnpkg.com/bare-module-resolve/-/bare-module-resolve-1.10.2.tgz#c394a13ba1c9c06f7d59804fb62ab9550d3b0c0a" - integrity sha512-C9COe/GhWfVXKytW3DElTkiBU+Gb2OXeaVkdGdRB/lp26TVLESHkTGS876iceAGdvtPgohfp9nX8vXHGvN3++Q== - dependencies: - bare-semver "^1.0.0" - -bare-os@^3.0.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/bare-os/-/bare-os-3.6.1.tgz#9921f6f59edbe81afa9f56910658422c0f4858d4" - integrity sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g== - -bare-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bare-path/-/bare-path-3.0.0.tgz#b59d18130ba52a6af9276db3e96a2e3d3ea52178" - integrity sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw== - dependencies: - bare-os "^3.0.1" - -bare-semver@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/bare-semver/-/bare-semver-1.0.1.tgz#4737d660785f07cb723cefa6b022089e59120a61" - integrity sha512-UtggzHLiTrmFOC/ogQ+Hy7VfoKoIwrP1UFcYtTxoCUdLtsIErT8+SWtOC2DH/snT9h+xDrcBEPcwKei1mzemgg== - -bare-url@^2.1.0: - version "2.1.4" - resolved "https://registry.yarnpkg.com/bare-url/-/bare-url-2.1.4.tgz#a51c6dcf1c10e7ec9287efaf04cb679e00add898" - integrity sha512-eKwiYS8m5utvqWFFDDAwvG53KE2fFlxmit1O1I1GLmRtMZr01ZQOw+GZ+Q1QVd0vZ/tKBulVPlV/cDw1tpahRw== - dependencies: - bare-path "^3.0.0" - base32.js@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/base32.js/-/base32.js-0.1.0.tgz#b582dec693c2f11e893cf064ee6ac5b6131a2202" @@ -6054,14 +6027,6 @@ request@^2.88.2: tunnel-agent "^0.6.0" uuid "^3.3.2" -require-addon@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/require-addon/-/require-addon-1.1.0.tgz#0a1ef0ba98b186a3aa304a1abda5208902e63e17" - integrity sha512-KbXAD5q2+v1GJnkzd8zzbOxchTkStSyJZ9QwoCq3QwEXAaIlG3wDYRZGzVD357jmwaGY7hr5VaoEAL0BkF0Kvg== - dependencies: - bare-addon-resolve "^1.3.0" - bare-url "^2.1.0" - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -6442,13 +6407,6 @@ socket.io@^4.7.2: socket.io-adapter "~2.5.2" socket.io-parser "~4.2.4" -sodium-native@^4.3.3: - version "4.3.3" - resolved "https://registry.yarnpkg.com/sodium-native/-/sodium-native-4.3.3.tgz#fae4866b52366f5e6cc1b7ae8c8a71673d50c7df" - integrity sha512-OnxSlN3uyY8D0EsLHpmm2HOFmKddQVvEMmsakCrXUzSd8kjjbzL413t4ZNF3n0UxSwNgwTyUvkmZHTfuCeiYSw== - dependencies: - require-addon "^1.1.0" - source-map-support@^0.5.16, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -6931,11 +6889,6 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== -tweetnacl@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" - integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"