Skip to content

Commit

Permalink
Merge pull request #7 from borislav-itskov/fix/remove-uint8array
Browse files Browse the repository at this point in the history
Fix/remove uint8array
  • Loading branch information
borislav-itskov authored Sep 11, 2023
2 parents 2c6119a + 50ad4dd commit 5e89d5b
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 72 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const {signature, finalPublicNonce} = Schnorrkel.sign(privateKey, msg)

Offchain verification:
```js
const publicKey: Uint8Array = ... (derived from the privateKey)
const publicKey: Buffer = ... (derived from the privateKey)
// signature and finalPublicNonce come from s
const result = Schnorrkel.verify(signature, msg, finalPublicNonce, publicKey)
```
Expand Down Expand Up @@ -95,7 +95,7 @@ const address = 'input schnorr generated address here'
const factory = new ethers.ContractFactory(SchnorrAccountAbstraction.abi, SchnorrAccountAbstraction.bytecode, wallet)
const contract: any = await factory.deploy([address])

const privateKey: Uint8Array = '...'
const privateKey: Buffer = '...'
const pkBuffer = new Key(Buffer.from(ethers.utils.arrayify(privateKey)))
const msg = 'just a test message';
const sig = schnorrkel.sign(msg, privateKey);
Expand Down Expand Up @@ -127,8 +127,8 @@ Below are all the steps needed to craft a successful multisig.
Public nonces need to be exchanged between signers before they sign. Normally, the Signer should implement this library as define a `getPublicNonces` method that will call the library and return the nonces. For our test example, we're going to call the schnorrkel library directly:

```js
const privateKey1: Uint8Array = '...'
const privateKey2: Uint8Array = '...'
const privateKey1: Buffer = '...'
const privateKey2: Buffer = '...'
const publicNonces1 = schnorrkel.generatePublicNonces(privateKey1);
const publicNonces2 = schnorrkel.generatePublicNonces(privateKey2);
```
Expand All @@ -140,8 +140,8 @@ Again, this isn't how the flow is supposed to work. A signer needs to implement
After we have them, here is how to sign:

```js
const publicKey1: Uint8Array = '...'
const publicKey2: Uint8Array = '...'
const publicKey1: Buffer = '...'
const publicKey2: Buffer = '...'
const publicKeys = [publicKey1, publicKey2];
const combinedPublicKey = schnorrkel.getCombinedPublicKey(publicKeys)
const {signature: sigOne, challenge: e, finalPublicNonce} = signerOne.multiSignMessage(msg, publicKeys, publicNonces)
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@borislav.itskov/schnorrkel.js",
"version": "2.0.83",
"version": "2.0.84",
"description": "",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
99 changes: 49 additions & 50 deletions src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { randomBytes } from 'crypto'
import { ethers } from 'ethers'
import secp256k1 from 'secp256k1'
import ecurve from 'ecurve'
Expand All @@ -16,10 +15,10 @@ const ec = new EC('secp256k1')
const generatorPoint = ec.g

const _generateNonce = (): InternalNoncePairs => {
const k = ethers.utils.randomBytes(32)
const kTwo = ethers.utils.randomBytes(32)
const kPublic = secp256k1.publicKeyCreate(k)
const kTwoPublic = secp256k1.publicKeyCreate(kTwo)
const k = Buffer.from(ethers.utils.randomBytes(32))
const kTwo = Buffer.from(ethers.utils.randomBytes(32))
const kPublic = Buffer.from(secp256k1.publicKeyCreate(k))
const kTwoPublic = Buffer.from(secp256k1.publicKeyCreate(kTwo))

return {
k,
Expand All @@ -29,67 +28,67 @@ const _generateNonce = (): InternalNoncePairs => {
}
}

const _bCoefficient = (combinedPublicKey: Uint8Array, msgHash: string, publicNonces: InternalPublicNonces[]): Uint8Array => {
const _bCoefficient = (combinedPublicKey: Buffer, msgHash: string, publicNonces: InternalPublicNonces[]): Buffer => {
type KeyOf = keyof InternalPublicNonces
const arrayColumn = (arr: Array<InternalPublicNonces>, n: KeyOf) => arr.map(x => x[n])
const kPublicNonces = secp256k1.publicKeyCombine(arrayColumn(publicNonces, 'kPublic'))
const kTwoPublicNonces = secp256k1.publicKeyCombine(arrayColumn(publicNonces, 'kTwoPublic'))

return ethers.utils.arrayify(ethers.utils.solidityKeccak256(
return Buffer.from(ethers.utils.arrayify(ethers.utils.solidityKeccak256(
['bytes', 'bytes32', 'bytes', 'bytes'],
[combinedPublicKey, msgHash, kPublicNonces, kTwoPublicNonces]
))
)))
}

const areBuffersSame = (buf1: Uint8Array, buf2: Uint8Array): boolean => {
const areBuffersSame = (buf1: Buffer, buf2: Buffer): boolean => {
if (buf1.byteLength != buf2.byteLength) return false

var dv1 = new Int8Array(buf1)
var dv2 = new Int8Array(buf2)
var dv1 = Buffer.from(buf1)
var dv2 = Buffer.from(buf2)
for (var i = 0; i != buf1.byteLength; i++) {
if (dv1[i] != dv2[i]) return false
}

return true
}

const challenge = (R: Uint8Array, msgHash: string, publicKey: Uint8Array): Uint8Array => {
const challenge = (R: Buffer, msgHash: string, publicKey: Buffer): Buffer => {
// convert R to address
var R_uncomp = secp256k1.publicKeyConvert(R, false)
var R_addr = ethers.utils.arrayify(ethers.utils.keccak256(R_uncomp.slice(1, 65))).slice(12, 32)

// e = keccak256(address(R) || compressed publicKey || msgHash)
return ethers.utils.arrayify(
return Buffer.from(ethers.utils.arrayify(
ethers.utils.solidityKeccak256(
['address', 'uint8', 'bytes32', 'bytes32'],
[R_addr, publicKey[0] + 27 - 2, publicKey.slice(1, 33), msgHash]
)
)
))
}

/**
* Sign the given hash by the private key
*
* @param Uint8Array privateKey
* @param Buffer privateKey
* @param string hash
* @returns InternalSignature
*/
const internalSign = (privateKey: Uint8Array, hash: string): InternalSignature => {
const localPk = new Uint8Array(privateKey)
const publicKey = secp256k1.publicKeyCreate((localPk as any))
const internalSign = (privateKey: Buffer, hash: string): InternalSignature => {
const localPk = Buffer.from(privateKey)
const publicKey = Buffer.from(secp256k1.publicKeyCreate(localPk))

// R = G * k
var k = ethers.utils.randomBytes(32)
var R = secp256k1.publicKeyCreate(k)
var R = Buffer.from(secp256k1.publicKeyCreate(k))

// e = h(address(R) || compressed pubkey || m)
var e = challenge(R, hash, publicKey)

// xe = x * e
var xe = secp256k1.privateKeyTweakMul((localPk as any), e)
var xe = secp256k1.privateKeyTweakMul(localPk, e)

// s = k + xe mod(n)
var s = secp256k1.privateKeyTweakAdd(k, xe)
var s = Buffer.from(secp256k1.privateKeyTweakAdd(k, xe))
s = bigi.fromBuffer(s).mod(n).toBuffer(32)

return {
Expand All @@ -99,35 +98,35 @@ const internalSign = (privateKey: Uint8Array, hash: string): InternalSignature =
}
}

const internalMultiSigSign = (nonces: InternalNonces, combinedPublicKey: Uint8Array, privateKey: Uint8Array, hash: string, publicKeys: Uint8Array[], publicNonces: InternalPublicNonces[]): InternalSignature => {
const internalMultiSigSign = (nonces: InternalNonces, combinedPublicKey: Buffer, privateKey: Buffer, hash: string, publicKeys: Buffer[], publicNonces: InternalPublicNonces[]): InternalSignature => {
if (publicKeys.length < 2) {
throw Error('At least 2 public keys should be provided')
}

const localPk = new Uint8Array(privateKey)
const localPk = Buffer.from(privateKey)
const xHashed = _hashPrivateKey(localPk)
if (!(xHashed in nonces) || Object.keys(nonces[xHashed]).length === 0) {
throw Error('Nonces should be exchanged before signing')
}

const publicKey = secp256k1.publicKeyCreate(localPk)
const publicKey = Buffer.from(secp256k1.publicKeyCreate(localPk))
const L = _generateL(publicKeys)
const a = _aCoefficient(publicKey, L)
const b = _bCoefficient(combinedPublicKey, hash, publicNonces)

const effectiveNonces = publicNonces.map((batch) => {
return secp256k1.publicKeyCombine([batch.kPublic, secp256k1.publicKeyTweakMul(batch.kTwoPublic, b)])
return Buffer.from(secp256k1.publicKeyCombine([batch.kPublic, secp256k1.publicKeyTweakMul(batch.kTwoPublic, b)]))
})
const signerEffectiveNonce = secp256k1.publicKeyCombine([
const signerEffectiveNonce = Buffer.from(secp256k1.publicKeyCombine([
nonces[xHashed].kPublic,
secp256k1.publicKeyTweakMul(nonces[xHashed].kTwoPublic, b)
])
]))
const inArray = effectiveNonces.filter(nonce => areBuffersSame(nonce, signerEffectiveNonce)).length != 0
if (!inArray) {
throw Error('Passed nonces are invalid')
}

const R = secp256k1.publicKeyCombine(effectiveNonces)
const R = Buffer.from(secp256k1.publicKeyCombine(effectiveNonces))
const e = challenge(R, hash, combinedPublicKey)

const { k, kTwo } = nonces[xHashed]
Expand Down Expand Up @@ -159,13 +158,13 @@ const internalMultiSigSign = (nonces: InternalNonces, combinedPublicKey: Uint8Ar
/**
* Verify a signature for the given hash, public nonce and public key
*
* @param Uint8Array s
* @param Buffer s
* @param string hash
* @param Uint8Array R
* @param Uint8Array publicKey
* @param Buffer R
* @param Buffer publicKey
* @returns boolean
*/
const internalVerify = (s: Uint8Array, hash: string, R: Uint8Array, publicKey: Uint8Array): boolean => {
const internalVerify = (s: Buffer, hash: string, R: Buffer, publicKey: Buffer): boolean => {
const eC = challenge(R, hash, publicKey)
const sG = generatorPoint.mul(ethers.utils.arrayify(s))
const P = ec.keyFromPublic(publicKey).getPublic()
Expand All @@ -176,28 +175,28 @@ const internalVerify = (s: Uint8Array, hash: string, R: Uint8Array, publicKey: U
return sG.eq(RplusPe)
}

export const _generateL = (publicKeys: Array<Uint8Array>) => {
export const _generateL = (publicKeys: Array<Buffer>) => {
return ethers.utils.keccak256(_concatTypedArrays(publicKeys.sort(Buffer.compare)))
}

export const _concatTypedArrays = (publicKeys: Uint8Array[]): Uint8Array => {
export const _concatTypedArrays = (publicKeys: Buffer[]): Buffer => {
const c: Buffer = Buffer.alloc(publicKeys.reduce((partialSum, publicKey) => partialSum + publicKey.length, 0))
publicKeys.map((publicKey, index) => c.set(publicKey, (index * publicKey.length)))
return new Uint8Array(c.buffer)
return Buffer.from(c.buffer)
}


export const _aCoefficient = (publicKey: Uint8Array, L: string): Uint8Array => {
return ethers.utils.arrayify(ethers.utils.solidityKeccak256(
export const _aCoefficient = (publicKey: Buffer, L: string): Buffer => {
return Buffer.from(ethers.utils.arrayify(ethers.utils.solidityKeccak256(
['bytes', 'bytes'],
[L, publicKey]
))
)))
}

export const generateRandomKeys = () => {
let privKeyBytes: Buffer | undefined
let privKeyBytes: Buffer
do {
privKeyBytes = randomBytes(32)
privKeyBytes = Buffer.from(ethers.utils.randomBytes(32))
} while (!secp256k1.privateKeyVerify(privKeyBytes))

const pubKey = Buffer.from(secp256k1.publicKeyCreate(privKeyBytes))
Expand All @@ -210,7 +209,7 @@ export const generateRandomKeys = () => {
return new KeyPair(data)
}

export const _hashPrivateKey = (privateKey: Uint8Array): string => {
export const _hashPrivateKey = (privateKey: Buffer): string => {
return ethers.utils.keccak256(privateKey)
}

Expand All @@ -235,17 +234,17 @@ export const _generatePublicNonces = (privateKey: Buffer): {
}
}

export const _multiSigSign = (nonces: InternalNonces, combinedPublicKey: Uint8Array, privateKey: Uint8Array, msg: string, publicKeys: Uint8Array[], publicNonces: InternalPublicNonces[], hashFn: Function|null = null): InternalSignature => {
export const _multiSigSign = (nonces: InternalNonces, combinedPublicKey: Buffer, privateKey: Buffer, msg: string, publicKeys: Buffer[], publicNonces: InternalPublicNonces[], hashFn: Function|null = null): InternalSignature => {
const hashMsg = hashFn ? hashFn : _hashMessage
const hash = hashMsg(msg)
return internalMultiSigSign(nonces, combinedPublicKey, privateKey, hash, publicKeys, publicNonces)
}

export const _multiSigSignHash = (nonces: InternalNonces, combinedPublicKey: Uint8Array, privateKey: Uint8Array, hash: string, publicKeys: Uint8Array[], publicNonces: InternalPublicNonces[]): InternalSignature => {
export const _multiSigSignHash = (nonces: InternalNonces, combinedPublicKey: Buffer, privateKey: Buffer, hash: string, publicKeys: Buffer[], publicNonces: InternalPublicNonces[]): InternalSignature => {
return internalMultiSigSign(nonces, combinedPublicKey, privateKey, hash, publicKeys, publicNonces)
}

export const _sumSigs = (signatures: Uint8Array[]): Buffer => {
export const _sumSigs = (signatures: Buffer[]): Buffer => {
let combined = bigi.fromBuffer(signatures[0])
signatures.shift()
signatures.forEach(sig => {
Expand All @@ -258,27 +257,27 @@ export const _hashMessage = (message: string): string => {
return ethers.utils.solidityKeccak256(['string'], [message])
}

export const _verify = (s: Uint8Array, msg: string, R: Uint8Array, publicKey: Uint8Array, hashFn: Function|null = null): boolean => {
export const _verify = (s: Buffer, msg: string, R: Buffer, publicKey: Buffer, hashFn: Function|null = null): boolean => {
const hashMsg = hashFn ? hashFn : _hashMessage
const hash = hashMsg(msg)
return internalVerify(s, hash, R, publicKey)
}

export const _verifyHash = (s: Uint8Array, hash: string, R: Uint8Array, publicKey: Uint8Array): boolean => {
export const _verifyHash = (s: Buffer, hash: string, R: Buffer, publicKey: Buffer): boolean => {
return internalVerify(s, hash, R, publicKey)
}

export const _generatePk = (combinedPublicKey: Uint8Array): string => {
const px = ethers.utils.hexlify(combinedPublicKey.slice(1,33))
export const _generatePk = (combinedPublicKey: Buffer): string => {
const px = ethers.utils.hexlify(combinedPublicKey.subarray(1,33))
return '0x' + px.slice(px.length - 40, px.length)
}

export const _sign = (privateKey: Uint8Array, msg: string, hashFn: Function|null = null): InternalSignature => {
export const _sign = (privateKey: Buffer, msg: string, hashFn: Function|null = null): InternalSignature => {
const hashMsg = hashFn ? hashFn : _hashMessage
const hash = hashMsg(msg)
return internalSign(privateKey, hash)
}

export const _signHash = (privateKey: Uint8Array, hash: string): InternalSignature => {
export const _signHash = (privateKey: Buffer, hash: string): InternalSignature => {
return internalSign(privateKey, hash)
}
26 changes: 13 additions & 13 deletions src/core/types.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
export interface InternalNoncePairs {
readonly k: Uint8Array,
readonly kTwo: Uint8Array,
readonly kPublic: Uint8Array,
readonly kTwoPublic: Uint8Array,
readonly k: Buffer,
readonly kTwo: Buffer,
readonly kPublic: Buffer,
readonly kTwoPublic: Buffer,
}

export interface InternalPublicNonces {
readonly kPublic: Uint8Array,
readonly kTwoPublic: Uint8Array,
readonly kPublic: Buffer,
readonly kTwoPublic: Buffer,
}

export interface InternalSignature {
finalPublicNonce: Uint8Array, // the final public nonce
challenge: Uint8Array, // the schnorr challenge
signature: Uint8Array, // the signature
finalPublicNonce: Buffer, // the final public nonce
challenge: Buffer, // the schnorr challenge
signature: Buffer, // the signature
}

export interface InternalNoncePairs {
readonly k: Uint8Array,
readonly kTwo: Uint8Array,
readonly kPublic: Uint8Array,
readonly kTwoPublic: Uint8Array,
readonly k: Buffer,
readonly kTwo: Buffer,
readonly kPublic: Buffer,
readonly kTwoPublic: Buffer,
}

export type InternalNonces = {
Expand Down

0 comments on commit 5e89d5b

Please sign in to comment.