tiny-secp256k1
Advanced tools
Comparing version 2.0.1 to 2.1.0
115
lib/index.js
@@ -0,1 +1,2 @@ | ||
import { compare } from "uint8array-tools"; | ||
import * as validate from "./validate.js"; | ||
@@ -7,2 +8,4 @@ import wasm from "./wasm_loader.js"; | ||
const WASM_PUBLIC_KEY_INPUT_PTR2 = wasm.PUBLIC_KEY_INPUT2.value; | ||
const WASM_X_ONLY_PUBLIC_KEY_INPUT_PTR = wasm.X_ONLY_PUBLIC_KEY_INPUT.value; | ||
const WASM_X_ONLY_PUBLIC_KEY_INPUT2_PTR = wasm.X_ONLY_PUBLIC_KEY_INPUT2.value; | ||
const WASM_TWEAK_INPUT_PTR = wasm.TWEAK_INPUT.value; | ||
@@ -15,2 +18,4 @@ const WASM_HASH_INPUT_PTR = wasm.HASH_INPUT.value; | ||
const PUBLIC_KEY_INPUT2 = WASM_BUFFER.subarray(WASM_PUBLIC_KEY_INPUT_PTR2, WASM_PUBLIC_KEY_INPUT_PTR2 + validate.PUBLIC_KEY_UNCOMPRESSED_SIZE); | ||
const X_ONLY_PUBLIC_KEY_INPUT = WASM_BUFFER.subarray(WASM_X_ONLY_PUBLIC_KEY_INPUT_PTR, WASM_X_ONLY_PUBLIC_KEY_INPUT_PTR + validate.X_ONLY_PUBLIC_KEY_SIZE); | ||
const X_ONLY_PUBLIC_KEY_INPUT2 = WASM_BUFFER.subarray(WASM_X_ONLY_PUBLIC_KEY_INPUT2_PTR, WASM_X_ONLY_PUBLIC_KEY_INPUT2_PTR + validate.X_ONLY_PUBLIC_KEY_SIZE); | ||
const TWEAK_INPUT = WASM_BUFFER.subarray(WASM_TWEAK_INPUT_PTR, WASM_TWEAK_INPUT_PTR + validate.TWEAK_SIZE); | ||
@@ -41,3 +46,3 @@ const HASH_INPUT = WASM_BUFFER.subarray(WASM_HASH_INPUT_PTR, WASM_HASH_INPUT_PTR + validate.HASH_SIZE); | ||
export function isPoint(p) { | ||
return validate.isPoint(p) && _isPoint(p); | ||
return validate.isDERPoint(p) && _isPoint(p); | ||
} | ||
@@ -47,2 +52,5 @@ export function isPointCompressed(p) { | ||
} | ||
export function isXOnlyPoint(p) { | ||
return validate.isXOnlyPoint(p) && _isPoint(p); | ||
} | ||
export function isPrivate(d) { | ||
@@ -109,2 +117,26 @@ return validate.isPrivate(d); | ||
} | ||
export function xOnlyPointFromScalar(d) { | ||
validate.validatePrivate(d); | ||
try { | ||
PRIVATE_KEY_INPUT.set(d); | ||
wasm.xOnlyPointFromScalar(); | ||
return X_ONLY_PUBLIC_KEY_INPUT.slice(0, validate.X_ONLY_PUBLIC_KEY_SIZE); | ||
} | ||
finally { | ||
PRIVATE_KEY_INPUT.fill(0); | ||
X_ONLY_PUBLIC_KEY_INPUT.fill(0); | ||
} | ||
} | ||
export function xOnlyPointFromPoint(p) { | ||
validate.validatePoint(p); | ||
try { | ||
PUBLIC_KEY_INPUT.set(p); | ||
wasm.xOnlyPointFromPoint(p.length); | ||
return X_ONLY_PUBLIC_KEY_INPUT.slice(0, validate.X_ONLY_PUBLIC_KEY_SIZE); | ||
} | ||
finally { | ||
PUBLIC_KEY_INPUT.fill(0); | ||
X_ONLY_PUBLIC_KEY_INPUT.fill(0); | ||
} | ||
} | ||
export function pointMultiply(p, tweak, compressed) { | ||
@@ -161,2 +193,47 @@ validate.validatePoint(p); | ||
} | ||
export function xOnlyPointAddTweak(p, tweak) { | ||
validate.validateXOnlyPoint(p); | ||
validate.validateTweak(tweak); | ||
try { | ||
X_ONLY_PUBLIC_KEY_INPUT.set(p); | ||
TWEAK_INPUT.set(tweak); | ||
const parity = wasm.xOnlyPointAddTweak(); | ||
return parity !== -1 | ||
? { | ||
parity, | ||
xOnlyPubkey: X_ONLY_PUBLIC_KEY_INPUT.slice(0, validate.X_ONLY_PUBLIC_KEY_SIZE), | ||
} | ||
: null; | ||
} | ||
finally { | ||
X_ONLY_PUBLIC_KEY_INPUT.fill(0); | ||
TWEAK_INPUT.fill(0); | ||
} | ||
} | ||
export function xOnlyPointAddTweakCheck(point, tweak, resultToCheck, tweakParity) { | ||
validate.validateXOnlyPoint(point); | ||
validate.validateXOnlyPoint(resultToCheck); | ||
validate.validateTweak(tweak); | ||
const hasParity = tweakParity !== undefined; | ||
if (hasParity) | ||
validate.validateParity(tweakParity); | ||
try { | ||
X_ONLY_PUBLIC_KEY_INPUT.set(point); | ||
X_ONLY_PUBLIC_KEY_INPUT2.set(resultToCheck); | ||
TWEAK_INPUT.set(tweak); | ||
if (hasParity) { | ||
return wasm.xOnlyPointAddTweakCheck(tweakParity) === 1; | ||
} | ||
else { | ||
wasm.xOnlyPointAddTweak(); | ||
const newKey = X_ONLY_PUBLIC_KEY_INPUT.slice(0, validate.X_ONLY_PUBLIC_KEY_SIZE); | ||
return compare(newKey, resultToCheck) === 0; | ||
} | ||
} | ||
finally { | ||
X_ONLY_PUBLIC_KEY_INPUT.fill(0); | ||
X_ONLY_PUBLIC_KEY_INPUT2.fill(0); | ||
TWEAK_INPUT.fill(0); | ||
} | ||
} | ||
export function sign(h, d, e) { | ||
@@ -182,2 +259,22 @@ validate.validateHash(h); | ||
} | ||
export function signSchnorr(h, d, e) { | ||
validate.validateHash(h); | ||
validate.validatePrivate(d); | ||
validate.validateExtraData(e); | ||
try { | ||
HASH_INPUT.set(h); | ||
PRIVATE_KEY_INPUT.set(d); | ||
if (e !== undefined) | ||
EXTRA_DATA_INPUT.set(e); | ||
wasm.signSchnorr(e === undefined ? 0 : 1); | ||
return SIGNATURE_INPUT.slice(0, validate.SIGNATURE_SIZE); | ||
} | ||
finally { | ||
HASH_INPUT.fill(0); | ||
PRIVATE_KEY_INPUT.fill(0); | ||
if (e !== undefined) | ||
EXTRA_DATA_INPUT.fill(0); | ||
SIGNATURE_INPUT.fill(0); | ||
} | ||
} | ||
export function verify(h, Q, signature, strict = false) { | ||
@@ -199,1 +296,17 @@ validate.validateHash(h); | ||
} | ||
export function verifySchnorr(h, Q, signature) { | ||
validate.validateHash(h); | ||
validate.validateXOnlyPoint(Q); | ||
validate.validateSignature(signature); | ||
try { | ||
HASH_INPUT.set(h); | ||
X_ONLY_PUBLIC_KEY_INPUT.set(Q); | ||
SIGNATURE_INPUT.set(signature); | ||
return wasm.verifySchnorr() === 1 ? true : false; | ||
} | ||
finally { | ||
HASH_INPUT.fill(0); | ||
X_ONLY_PUBLIC_KEY_INPUT.fill(0); | ||
SIGNATURE_INPUT.fill(0); | ||
} | ||
} |
@@ -0,4 +1,9 @@ | ||
function get4RandomBytes() { | ||
const bytes = new Uint8Array(4); | ||
window.crypto.getRandomValues(bytes); | ||
return bytes; | ||
} | ||
// Only to be used to initialize the context for rust-secp256k1 | ||
export function generateInt32() { | ||
const array = new Uint8Array(4); | ||
window.crypto.getRandomValues(array); | ||
const array = get4RandomBytes(); | ||
return ((array[0] << (3 * 8)) + | ||
@@ -5,0 +10,0 @@ (array[1] << (2 * 8)) + |
@@ -5,4 +5,1 @@ import { randomBytes } from "crypto"; | ||
} | ||
export function generateSeed() { | ||
return randomBytes(32); | ||
} |
@@ -7,2 +7,3 @@ export const ERROR_BAD_PRIVATE = 0; | ||
export const ERROR_BAD_EXTRA_DATA = 5; | ||
export const ERROR_BAD_PARITY = 6; | ||
const ERRORS_MESSAGES = { | ||
@@ -15,2 +16,3 @@ [ERROR_BAD_PRIVATE.toString()]: "Expected Private", | ||
[ERROR_BAD_EXTRA_DATA.toString()]: "Expected Extra Data (32 bytes)", | ||
[ERROR_BAD_PARITY.toString()]: "Expected Parity (1 | 0)", | ||
}; | ||
@@ -17,0 +19,0 @@ export function throwError(errcode) { |
@@ -1,5 +0,6 @@ | ||
import { ERROR_BAD_PRIVATE, ERROR_BAD_POINT, ERROR_BAD_TWEAK, throwError, ERROR_BAD_HASH, ERROR_BAD_EXTRA_DATA, ERROR_BAD_SIGNATURE, } from "./validate_error.js"; | ||
import { ERROR_BAD_PRIVATE, ERROR_BAD_POINT, ERROR_BAD_TWEAK, throwError, ERROR_BAD_HASH, ERROR_BAD_EXTRA_DATA, ERROR_BAD_SIGNATURE, ERROR_BAD_PARITY, } from "./validate_error.js"; | ||
export const PRIVATE_KEY_SIZE = 32; | ||
export const PUBLIC_KEY_COMPRESSED_SIZE = 33; | ||
export const PUBLIC_KEY_UNCOMPRESSED_SIZE = 65; | ||
export const X_ONLY_PUBLIC_KEY_SIZE = 32; | ||
export const TWEAK_SIZE = 32; | ||
@@ -11,34 +12,4 @@ export const HASH_SIZE = 32; | ||
const BN32_N = new Uint8Array([ | ||
255, | ||
255, | ||
255, | ||
255, | ||
255, | ||
255, | ||
255, | ||
255, | ||
255, | ||
255, | ||
255, | ||
255, | ||
255, | ||
255, | ||
255, | ||
254, | ||
186, | ||
174, | ||
220, | ||
230, | ||
175, | ||
72, | ||
160, | ||
59, | ||
191, | ||
210, | ||
94, | ||
140, | ||
208, | ||
54, | ||
65, | ||
65, | ||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, | ||
254, 186, 174, 220, 230, 175, 72, 160, 59, 191, 210, 94, 140, 208, 54, 65, 65, | ||
]); | ||
@@ -68,2 +39,11 @@ function isUint8Array(value) { | ||
(p.length === PUBLIC_KEY_COMPRESSED_SIZE || | ||
p.length === PUBLIC_KEY_UNCOMPRESSED_SIZE || | ||
p.length === X_ONLY_PUBLIC_KEY_SIZE)); | ||
} | ||
export function isXOnlyPoint(p) { | ||
return isUint8Array(p) && p.length === X_ONLY_PUBLIC_KEY_SIZE; | ||
} | ||
export function isDERPoint(p) { | ||
return (isUint8Array(p) && | ||
(p.length === PUBLIC_KEY_COMPRESSED_SIZE || | ||
p.length === PUBLIC_KEY_UNCOMPRESSED_SIZE)); | ||
@@ -91,2 +71,6 @@ } | ||
} | ||
export function validateParity(p) { | ||
if (p !== 0 && p !== 1) | ||
throwError(ERROR_BAD_PARITY); | ||
} | ||
export function validatePrivate(d) { | ||
@@ -100,2 +84,6 @@ if (!isPrivate(d)) | ||
} | ||
export function validateXOnlyPoint(p) { | ||
if (!isXOnlyPoint(p)) | ||
throwError(ERROR_BAD_POINT); | ||
} | ||
export function validateTweak(tweak) { | ||
@@ -102,0 +90,0 @@ if (!isTweak(tweak)) |
{ | ||
"name": "tiny-secp256k1", | ||
"version": "2.0.1", | ||
"version": "2.1.0", | ||
"description": "A tiny secp256k1 JS", | ||
@@ -20,7 +20,9 @@ "homepage": "https://github.com/bitcoinjs/tiny-secp256k1#readme", | ||
}, | ||
"types": "./types/index.d.ts", | ||
"types": "./lib/index.d.ts", | ||
"files": [ | ||
"lib", | ||
"types" | ||
"lib" | ||
], | ||
"dependencies": { | ||
"uint8array-tools": "0.0.6" | ||
}, | ||
"devDependencies": { | ||
@@ -30,3 +32,3 @@ "@types/node": "^14.14.35", | ||
"@typescript-eslint/parser": "^4.18.0", | ||
"browser-run": "^10.0.0", | ||
"browser-run": "^10.1.0", | ||
"buffer": "^6.0.3", | ||
@@ -33,0 +35,0 @@ "eslint": "^7.22.0", |
104
README.md
@@ -29,3 +29,3 @@ # tiny-secp256k1 | ||
For building locally you need C/C++ toolchain, Rust nightly version and `wasm-opt` from [binaryen](https://github.com/WebAssembly/binaryen). | ||
For building locally you need C/C++ toolchain, Rust version >=1.50.0 and `wasm-opt` from [binaryen](https://github.com/WebAssembly/binaryen). | ||
@@ -35,3 +35,3 @@ [rustup](https://rustup.rs/) is a recommended way to install `Rust`. You also will need `wasm32-unknown-unknown` target. | ||
``` | ||
rustup toolchain install nightly --target wasm32-unknown-unknown --component clippy --component rustfmt | ||
rustup toolchain install stable --target wasm32-unknown-unknown --component clippy --component rustfmt | ||
``` | ||
@@ -56,5 +56,3 @@ | ||
% docker run -it --rm -v `pwd`:/tiny-secp256k1 -w /tiny-secp256k1 tiny-secp256k1 | ||
# npm install --unsafe-perm | ||
# make test | ||
# make clean | ||
# make build | ||
``` | ||
@@ -88,4 +86,12 @@ | ||
Returns `false` if the signature is **not** compressed. | ||
Returns `false` if the pubkey is **not** compressed. | ||
### isXOnlyPoint (A) | ||
```haskell | ||
isXOnlyPoint :: Buffer -> Bool | ||
``` | ||
Returns `false` if the pubkey is **not** an xOnlyPubkey. | ||
### isPrivate (d) | ||
@@ -150,2 +156,26 @@ | ||
### xOnlyPointFromScalar (d) | ||
```haskell | ||
xOnlyPointFromScalar :: Buffer -> Buffer | ||
``` | ||
Returns the xOnlyPubkey for a given private key | ||
##### Throws: | ||
- `Expected Private` if `!isPrivate(d)` | ||
### xOnlyPointFromPoint (p) | ||
```haskell | ||
xOnlyPointFromPoint :: Buffer -> Buffer | ||
``` | ||
Returns the xOnlyPubkey for a given DER public key | ||
##### Throws: | ||
- `Expected Point` if `!isPoint(p)` | ||
### pointMultiply (A, tweak[, compressed]) | ||
@@ -190,2 +220,32 @@ | ||
### xOnlyPointAddTweak (p, tweak) | ||
```haskell | ||
xOnlyPointAddTweak :: Buffer -> Buffer -> { parity: 1 | 0; xOnlyPubkey: Buffer; } | ||
``` | ||
Returns the tweaked xOnlyPubkey along with the parity bit (number type of 1|0) | ||
##### Throws: | ||
- `Expected Point` if `!isXOnlyPoint(p)` | ||
- `Expected Tweak` if `!isXOnlyPoint(tweak)` | ||
### xOnlyPointAddTweakCheck (p1, p2, tweak[, tweakParity]) | ||
```haskell | ||
xOnlyPointAddTweakCheck :: Buffer -> Buffer -> Buffer [-> 1 | 0] -> Bool | ||
``` | ||
Checks the tweaked pubkey (p2) against the original pubkey (p1) and tweak. | ||
This is slightly slower if you include tweakParity, tweakParity will make it | ||
faster for aggregation later on. | ||
##### Throws: | ||
- `Expected Point` if `!isXOnlyPoint(p1)` | ||
- `Expected Point` if `!isXOnlyPoint(p2)` | ||
- `Expected Tweak` if `!isXOnlyPoint(tweak)` | ||
- `Expected Parity` if `tweakParity is not 1 or 0` | ||
### sign (h, d[, e]) | ||
@@ -207,6 +267,22 @@ | ||
### signSchnorr (h, d[, e]) | ||
```haskell | ||
signSchnorr :: Buffer -> Buffer [-> Buffer] -> Buffer | ||
``` | ||
Returns normalized schnorr signature. | ||
Uses BIP340 nonce generation. | ||
Adds `e` as Added Entropy. | ||
##### Throws: | ||
- `Expected Private` if `!isPrivate(d)` | ||
- `Expected Scalar` if `h` is not 256-bit | ||
- `Expected Extra Data (32 bytes)` if `e` is not 256-bit | ||
### verify (h, Q, signature[, strict = false]) | ||
```haskell | ||
verify :: Buffer -> Buffer -> Buffer -> Bool | ||
verify :: Buffer -> Buffer -> Buffer [-> Bool] -> Bool | ||
``` | ||
@@ -224,2 +300,16 @@ | ||
### verifySchnorr (h, Q, signature) | ||
```haskell | ||
verifySchnorr :: Buffer -> Buffer -> Buffer -> Bool | ||
``` | ||
Returns `false` if any of (r, s) values are equal to `0`, or if the signature is rejected. | ||
##### Throws: | ||
- `Expected Point` if `!isPoint(Q)` | ||
- `Expected Signature` if `signature` has any (r, s) values not in range `[0...order - 1]` | ||
- `Expected Scalar` if `h` is not 256-bit | ||
## Credit | ||
@@ -226,0 +316,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
555
312
194914
1
+ Addeduint8array-tools@0.0.6
+ Addeduint8array-tools@0.0.6(transitive)