Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 1 addition & 11 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,6 @@ const SwitchDebtFiles = [
"packages/sdk-core/src/chain/PublicKey.ts",
"packages/sdk-core/src/chain/Signature.ts",
"packages/sdk-core/src/chain/Transaction.ts",
"packages/sdk-core/src/crypto/Curves.ts",
"packages/sdk-core/src/crypto/Generate.ts",
"packages/sdk-core/src/crypto/GetPublic.ts",
"packages/sdk-core/src/crypto/Recover.ts",
"packages/sdk-core/src/crypto/SharedSecret.ts",
"packages/sdk-core/src/crypto/Sign.ts",
"packages/sdk-core/src/crypto/Verify.ts",
"packages/sdk-core/src/serializer/Builtins.ts",
"packages/sdk-core/src/serializer/Decoder.ts",
"packages/sdk-core/src/serializer/Encoder.ts",
Expand Down Expand Up @@ -166,7 +159,6 @@ const InlineTypeLiteralDebtFiles = [
"packages/sdk-core/src/contracts/sysio/msig/Status.ts",
"packages/sdk-core/src/contracts/sysio/msig/Transaction.ts",
"packages/sdk-core/src/contracts/sysio/msig/Types.ts",
"packages/sdk-core/src/crypto/Curves.ts",
"packages/sdk-core/src/p2p/Client.ts",
"packages/sdk-core/src/p2p/Provider.ts",
"packages/sdk-core/src/serializer/Builtins.ts",
Expand Down Expand Up @@ -223,7 +215,6 @@ const RuleDebt = [
"examples/web-logging-example/src/web-logging-example.ts",
"packages/sdk-core/src/Utils.ts",
"packages/sdk-core/src/chain/PrivateKey.ts",
"packages/sdk-core/src/crypto/Sign.ts",
"packages/sdk-core/tests/chain/Abi.test.ts",
"packages/sdk-core/tests/chain/Asset.test.ts",
"packages/sdk-core/tests/crypto/crypto.test.ts",
Expand All @@ -246,8 +237,7 @@ const RuleDebt = [
"packages/sdk-core/src/api/Client.ts",
"packages/sdk-core/src/chain/Abi.ts",
"packages/sdk-core/src/chain/Bytes.ts",
"packages/sdk-core/src/chain/PublicKey.ts",
"packages/sdk-core/src/crypto/Sign.ts"
"packages/sdk-core/src/chain/PublicKey.ts"
]
},
{
Expand Down
47 changes: 23 additions & 24 deletions packages/sdk-core/src/crypto/Curves.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { p256 } from "@noble/curves/nist.js"
import { secp256k1 } from "@noble/curves/secp256k1.js"
import { ec } from "elliptic"
import { match } from "ts-pattern"
import { KeyType } from "../chain/KeyType.js"

const curves: { [type: string]: ec } = {}
const nobleCurves: { [type: string]: typeof secp256k1 } = {}
/** Cache of elliptic curve instances keyed by Wire key type. */
type EllipticCurveByKeyType = Partial<Record<KeyType, ec>>

/** Cache of Noble Weierstrass curve modules keyed by Wire key type. */
type NobleCurveByKeyType = Partial<Record<KeyType, typeof secp256k1>>

const curves: EllipticCurveByKeyType = {}
const nobleCurves: NobleCurveByKeyType = {}

/**
* Get curve for key type.
Expand All @@ -14,21 +21,17 @@ export function getCurve(type: KeyType): ec {
let rv = curves[type]

if (!rv) {
switch (type) {
case KeyType.K1:
case KeyType.EM:
rv = curves[type] = new ec("secp256k1")
break
case KeyType.R1:
rv = curves[type] = new ec("p256")
break
case KeyType.ED:
rv = curves[type] = match(type)
.with(KeyType.K1, KeyType.EM, () => new ec("secp256k1"))
.with(KeyType.R1, () => new ec("p256"))
.with(KeyType.ED, () => {
throw new Error(
"ED25519 keys are not supported via elliptic; use libsodium for ED-based operations"
)
default:
})
.otherwise(() => {
throw new Error(`Unknown curve type: ${type}`)
}
})
}

return rv
Expand All @@ -42,21 +45,17 @@ export function getNobleCurve(type: KeyType): typeof secp256k1 {
let curve = nobleCurves[type]

if (!curve) {
switch (type) {
case KeyType.K1:
case KeyType.EM:
curve = nobleCurves[type] = secp256k1
break
case KeyType.R1:
curve = nobleCurves[type] = p256
break
case KeyType.ED:
curve = nobleCurves[type] = match(type)
.with(KeyType.K1, KeyType.EM, () => secp256k1)
.with(KeyType.R1, () => p256)
.with(KeyType.ED, () => {
throw new Error(
"ED25519 keys are not supported via Noble Weierstrass curves"
)
default:
})
.otherwise(() => {
throw new Error(`Unknown curve type: ${type}`)
}
})
}

return curve
Expand Down
21 changes: 6 additions & 15 deletions packages/sdk-core/src/crypto/Generate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// src/crypto/generate.ts

import { match } from "ts-pattern"
import { KeyType } from "../chain/KeyType.js"
import { getNobleCurve } from "./Curves.js"
import nacl from "tweetnacl"
Expand All @@ -10,19 +11,9 @@ import { blsGenerate, skToLE } from "./BLS.js"
* @internal
*/
export function generate(type: KeyType): Uint8Array {
switch (type) {
case KeyType.ED: // ED25519 private key via tweetnacl
return nacl.sign.keyPair().secretKey // 64-byte secretKey = 32b seed + 32b pubkey

case KeyType.EM: {
return getNobleCurve(type).utils.randomSecretKey()
}

case KeyType.BLS:
return skToLE(blsGenerate())

default: {
return getNobleCurve(type).utils.randomSecretKey()
}
}
return match(type)
.with(KeyType.ED, () => nacl.sign.keyPair().secretKey)
.with(KeyType.EM, () => getNobleCurve(type).utils.randomSecretKey())
.with(KeyType.BLS, () => skToLE(blsGenerate()))
.otherwise(() => getNobleCurve(type).utils.randomSecretKey())
}
16 changes: 5 additions & 11 deletions packages/sdk-core/src/crypto/GetPublic.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { match } from "ts-pattern"
import { KeyType } from "../chain/KeyType.js"
import { getNobleCurve } from "./Curves.js"
import nacl from "tweetnacl"
Expand All @@ -8,15 +9,8 @@ import { blsGetPublicKey, skFromLE } from "./BLS.js"
* @internal
*/
export function getPublic(privkey: Uint8Array, type: KeyType) {
switch (type) {
case KeyType.ED: // Derive ED25519 public key via tweetnacl
return nacl.sign.keyPair.fromSecretKey(privkey).publicKey

case KeyType.BLS:
return blsGetPublicKey(skFromLE(privkey))

default: {
return getNobleCurve(type).getPublicKey(privkey, true)
}
}
return match(type)
.with(KeyType.ED, () => nacl.sign.keyPair.fromSecretKey(privkey).publicKey)
.with(KeyType.BLS, () => blsGetPublicKey(skFromLE(privkey)))
.otherwise(() => getNobleCurve(type).getPublicKey(privkey, true))
}
21 changes: 10 additions & 11 deletions packages/sdk-core/src/crypto/Recover.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { match } from "ts-pattern"
import { ethers } from "../EthersCompat.js"
import { Bytes } from "../chain/Bytes.js"
import { KeyType } from "../chain/KeyType.js"
Expand All @@ -19,14 +20,14 @@ export function recover(
message: Uint8Array,
type: KeyType
): PublicKey {
switch (type) {
case KeyType.ED:
return match(type)
.with(KeyType.ED, () => {
throw new Error("ED25519 does not support public key recovery")

case KeyType.BLS:
})
.with(KeyType.BLS, () => {
throw new Error("BLS does not support public key recovery")

case KeyType.EM: {
})
.with(KeyType.EM, () => {
// wire: [vWire(31/32)‖r(32)‖s(32)]
const vRaw = signature[0] - 4 // 27/28
const r = signature.subarray(1, 33)
Expand All @@ -40,9 +41,8 @@ export function recover(
KeyType.EM,
new Bytes(ethers.utils.arrayify(compressed))
)
}

default: {
})
.otherwise(() => {
// wire: [vWire(31/32)‖r‖s]
const recid = signature[0] - 31
const curve = getNobleCurve(type)
Expand All @@ -55,6 +55,5 @@ export function recover(
.recoverPublicKey(message)
.toBytes(true)
return new PublicKey(type, new Bytes(compressed))
}
}
})
}
16 changes: 8 additions & 8 deletions packages/sdk-core/src/crypto/SharedSecret.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// src/crypto/shared-secret.ts

import { match } from "ts-pattern"
import { KeyType } from "../chain/KeyType.js"
import { getNobleCurve } from "./Curves.js"

Expand All @@ -21,17 +22,17 @@ export function sharedSecret(
pubkey: Uint8Array,
type: KeyType
): Uint8Array {
switch (type) {
case KeyType.ED:
return match(type)
.with(KeyType.ED, () => {
// ED25519 does not support ECDH-derived shared secrets
throw new Error(
"Shared secret (ECDH) not supported for ED25519; convert to X25519 first"
)

case KeyType.BLS:
})
.with(KeyType.BLS, () => {
throw new Error("BLS does not support shared secret")

default: {
})
.otherwise(() => {
const sharedPoint = getNobleCurve(type).getSharedSecret(
privkey,
pubkey,
Expand All @@ -40,6 +41,5 @@ export function sharedSecret(
return trimLeadingZeroBytes(
sharedPoint.subarray(COMPRESSED_PUBLIC_KEY_PREFIX_BYTES)
)
}
}
})
}
31 changes: 13 additions & 18 deletions packages/sdk-core/src/crypto/Sign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import nacl from "tweetnacl"
import { isObject } from "@wireio/shared"
import { ethers } from "../EthersCompat.js"
import { blsSign, skFromLE } from "./BLS.js"
import { asOption, Option } from "@3fv/prelude-ts"
import { asOption } from "@3fv/prelude-ts"
import { match } from "ts-pattern"
import { defaults } from "lodash"
import { ChainKind } from "@wireio/opp-typescript-models"
Expand Down Expand Up @@ -34,16 +34,15 @@ export function sign<C extends ChainKind = ChainKind.UNKNOWN>(
type: KeyType,
options: sign.Options<C> | null = null
): SignatureParts {
switch (type) {
case KeyType.ED: {
return match(type)
.with(KeyType.ED, () => {
// ED25519 detached signature via tweetnacl
const sigBytes = nacl.sign.detached(message, secret)
const r = sigBytes.slice(0, 32)
const s = sigBytes.slice(32, 64)
return { type, r, s, recid: 0 }
}

case KeyType.EM: {
})
.with(KeyType.EM, () => {
// Ethereum signature using EIP-191 prefix by default.
const ethOptions = sign.getOptions(options, ChainKind.EVM)
const hash = ethOptions.personalMessage
Expand All @@ -57,15 +56,13 @@ export function sign<C extends ChainKind = ChainKind.UNKNOWN>(
// vWire = recoveryParam + 31 = ethV + 4 for EM signatures.
const recid = sig.recoveryParam!
return { type, r, s, recid }
}

case KeyType.BLS: {
})
.with(KeyType.BLS, () => {
const skBE = skFromLE(secret)
const sigLE = blsSign(skBE, message)
return { type, r: sigLE, s: new Uint8Array(0), recid: 0 }
}

case KeyType.K1: {
})
.with(KeyType.K1, () => {
const curve = getCurve(type)
const key = curve.keyFromPrivate(secret)
let sig: ReturnType<typeof key.sign>
Expand All @@ -88,9 +85,8 @@ export function sign<C extends ChainKind = ChainKind.UNKNOWN>(
} while (!isCanonical(r, s))

return { type, r, s, recid: sig.recoveryParam || 0 }
}

default: {
})
.otherwise(() => {
const signature = getNobleCurve(type).sign(message, secret, {
lowS: true
})
Expand All @@ -100,8 +96,7 @@ export function sign<C extends ChainKind = ChainKind.UNKNOWN>(
s: numberToBytesBE(signature.s, SIGNATURE_COMPONENT_BYTES),
recid: signature.recovery
}
}
}
})
}

export namespace sign {
Expand Down Expand Up @@ -134,7 +129,7 @@ export namespace sign {
.map(o => o as Options<C>)
.getOrElse({ chain } as Options<C>),
defaultOpts =
chain == ChainKind.EVM ? DefaultEthereumOptions : DefaultBaseOptions
chain === ChainKind.EVM ? DefaultEthereumOptions : DefaultBaseOptions

return defaults(opts, defaultOpts)
}
Expand Down
23 changes: 10 additions & 13 deletions packages/sdk-core/src/crypto/Verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { KeyType } from "../chain/KeyType.js"
import nacl from "tweetnacl"
import { ethers } from "../EthersCompat.js"
import { blsVerify } from "./BLS.js"
import { match } from "ts-pattern"

/**
* Verify signature using message and public key.
Expand All @@ -19,26 +20,22 @@ export function verify(
pubkey: Uint8Array,
type: KeyType
): boolean {
switch (type) {
case KeyType.ED: // ED25519 detached verification via tweetnacl
return nacl.sign.detached.verify(message, signature, pubkey)

case KeyType.EM: {
return match(type)
.with(KeyType.ED, () =>
nacl.sign.detached.verify(message, signature, pubkey)
)
.with(KeyType.EM, () => {
const sigBytes = ethers.utils.arrayify(signature)
const msgBytes = ethers.utils.arrayify(message)
const recovered = ethers.utils.verifyMessage(msgBytes, sigBytes)
const expected = ethers.utils.computeAddress(pubkey)
return recovered.toLowerCase() === expected.toLowerCase()
}

case KeyType.BLS:
return blsVerify(signature, message, pubkey)

default: {
})
.with(KeyType.BLS, () => blsVerify(signature, message, pubkey))
.otherwise(() => {
const compactSignature = signature.subarray(1)
return getNobleCurve(type).verify(compactSignature, message, pubkey, {
lowS: false
})
}
}
})
}
Loading