-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: restore
crypto-signature-ecdsa
package (#720)
* add back crypto-signature-ecdsa * signature recovery test * fix
- Loading branch information
Showing
8 changed files
with
231 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Mainsail - Crypto signature ECDSA | ||
|
||
![banner](https://raw.githubusercontent.com/ArkEcosystem/mainsail/main/banner.jpeg) | ||
|
||
## Documentation | ||
|
||
You can find installation instructions and detailed instructions on how to use this package at the [dedicated documentation site](https://ark.dev/docs/mainsail). | ||
|
||
## Security | ||
|
||
If you discover a security vulnerability within this package, please send an e-mail to [[email protected]](mailto:[email protected]). All security vulnerabilities will be promptly addressed. | ||
|
||
## Credits | ||
|
||
This project exists thanks to all the people who [contribute](https://github.com/ArkEcosystem/mainsail/graphs/contributors). | ||
|
||
## License | ||
|
||
[GPL-3.0-only](https://github.com/ArkEcosystem/mainsail/blob/main/LICENSE) © [ARK Ecosystem](https://ark.io) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{ | ||
"name": "@mainsail/crypto-signature-ecdsa", | ||
"version": "0.0.1-alpha.20", | ||
"description": "Elliptic Curve Cryptography (ECDSA) signatures for the Mainsail blockchain", | ||
"license": "GPL-3.0-only", | ||
"contributors": [], | ||
"type": "module", | ||
"main": "distribution/index.js", | ||
"types": "distribution/index.d.ts", | ||
"files": [ | ||
"distribution" | ||
], | ||
"scripts": { | ||
"build": "pnpm run clean && tsc", | ||
"build:watch": "pnpm run clean && tsc -w", | ||
"clean": "del distribution", | ||
"release": "pnpm publish --access public", | ||
"test": "pnpm run uvu source .test.ts", | ||
"test:coverage": "c8 pnpm run test", | ||
"test:coverage:html": "c8 -r html --all pnpm run test", | ||
"test:file": "pnpm run uvu source", | ||
"uvu": "tsx --tsconfig ../../tsconfig.test.json ./node_modules/uvu/bin.js" | ||
}, | ||
"dependencies": { | ||
"@mainsail/container": "workspace:*", | ||
"@mainsail/contracts": "workspace:*", | ||
"@mainsail/kernel": "workspace:*", | ||
"@mainsail/utils": "workspace:*", | ||
"bcrypto": "5.5.2" | ||
}, | ||
"devDependencies": { | ||
"uvu": "^0.5.6" | ||
}, | ||
"engines": { | ||
"node": ">=20.x" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { Selectors } from "@mainsail/container"; | ||
import { Identifiers } from "@mainsail/contracts"; | ||
import { Providers } from "@mainsail/kernel"; | ||
import { ByteBuffer } from "@mainsail/utils"; | ||
|
||
import { Signature } from "./signature.js"; | ||
|
||
export class ServiceProvider extends Providers.ServiceProvider { | ||
public async register(): Promise<void> { | ||
this.app | ||
.bind(Identifiers.Cryptography.Signature.Size) | ||
.toFunction((buffer: ByteBuffer) => { | ||
buffer.mark(); | ||
buffer.skip(1); | ||
|
||
const lengthHex: string = buffer.readBytes(1).toString("hex"); | ||
|
||
buffer.reset(); | ||
|
||
return Number.parseInt(lengthHex, 16) + 2; | ||
}) | ||
.when(Selectors.anyAncestorOrTargetTaggedFirst("type", "wallet")); | ||
|
||
this.app | ||
.bind(Identifiers.Cryptography.Signature.Instance) | ||
.to(Signature) | ||
.inSingletonScope() | ||
.when(Selectors.anyAncestorOrTargetTaggedFirst("type", "wallet")); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { describe } from "../../test-framework/source"; | ||
import { Signature } from "./signature"; | ||
import { secp256k1 } from "bcrypto"; | ||
|
||
describe("Signature", ({ assert, it }) => { | ||
it("should sign and verify", async () => { | ||
assert.true( | ||
await new Signature().verify( | ||
Buffer.from( | ||
await new Signature().sign( | ||
Buffer.from("64726e3da8", "hex"), | ||
Buffer.from("814857ce48e291893feab95df02e1dbf7ad3994ba46f247f77e4eefd5d8734a2", "hex"), | ||
), | ||
"hex", | ||
), | ||
Buffer.from("64726e3da8", "hex"), | ||
Buffer.from("03e84093c072af70004a38dd95e34def119d2348d5261228175d032e5f2070e19f", "hex"), | ||
), | ||
); | ||
}); | ||
|
||
it("should sign recoverable and return r,s,v", async () => { | ||
const privateKey = Buffer.from("814857ce48e291893feab95df02e1dbf7ad3994ba46f247f77e4eefd5d8734a2", "hex"); | ||
const message = Buffer.from("64726e3da8", "hex"); | ||
|
||
const [signature, recoverId] = secp256k1.signRecoverable(message, privateKey); | ||
|
||
const r = signature.subarray(0, 32); | ||
const s = signature.subarray(32, 64); | ||
|
||
const sig = { | ||
r: r.toString("hex"), | ||
s: s.toString("hex"), | ||
v: recoverId + 27, | ||
}; | ||
|
||
assert.equal(sig, { | ||
r: "66f1c6d9fe13834f6e348aae40426060339ed8cba7d9b2f105c8220be095877c", | ||
s: "1368fffd8294f1e22086703d33511fc8bb25231d6e9dc64d6449035003184bdd", | ||
v: 28, | ||
}); | ||
|
||
const signatureBuffer = Buffer.concat([Buffer.from(sig.r, "hex"), Buffer.from(sig.s, "hex")]); | ||
const publicKey = secp256k1.recover(message, signatureBuffer, sig.v - 27, true); | ||
assert.equal( | ||
publicKey, | ||
Buffer.from("03e84093c072af70004a38dd95e34def119d2348d5261228175d032e5f2070e19f", "hex"), | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { inject, injectable } from "@mainsail/container"; | ||
import { Contracts, Exceptions, Identifiers } from "@mainsail/contracts"; | ||
import { ByteBuffer } from "@mainsail/utils"; | ||
import { secp256k1 } from "bcrypto"; | ||
|
||
@injectable() | ||
export class Signature implements Contracts.Crypto.Signature { | ||
@inject(Identifiers.Cryptography.Signature.Size) | ||
private readonly signatureSize!: Function; | ||
|
||
public async sign(message: Buffer, privateKey: Buffer): Promise<string> { | ||
return secp256k1.signatureExport(secp256k1.sign(message, privateKey)).toString("hex"); | ||
} | ||
|
||
public async verify(signature: Buffer, message: Buffer, publicKey: Buffer): Promise<boolean> { | ||
const signatureRS = secp256k1.signatureImport(signature); | ||
|
||
if (!secp256k1.isLowS(signatureRS)) { | ||
return false; | ||
} | ||
|
||
// check that global signature length matches R and S length, see DER format : | ||
// <header byte><signature length><integer marker><R length><R><integer marker><S length><S> | ||
const signatureLength = signature.readUInt8(1); | ||
const rLength = signature.readUInt8(3); | ||
const sLength = signature.readUInt8(4 + rLength + 1); | ||
if ( | ||
signature.length !== 4 + rLength + 2 + sLength || | ||
signatureLength !== 2 + rLength + 2 + sLength || | ||
signatureLength > 127 | ||
) { | ||
return false; | ||
} | ||
|
||
// check that first byte is positive, if it is then the whole R / S will be positive as required | ||
const rFirstByte = signature.readInt8(4); | ||
const sFirstByte = signature.readInt8(4 + rLength + 2); | ||
if (rFirstByte < 0 || sFirstByte < 0 || rFirstByte > 127 || sFirstByte > 127) { | ||
return false; | ||
} | ||
|
||
// if first byte is zero it is to make R/S positive, so second byte should be negative | ||
if ( | ||
(rFirstByte === 0 && signature.readInt8(4 + 1) >= 0) || | ||
(sFirstByte === 0 && signature.readInt8(4 + rLength + 2 + 1) >= 0) | ||
) { | ||
return false; | ||
} | ||
|
||
return secp256k1.verify(message, signatureRS, publicKey); | ||
} | ||
|
||
public serialize(buffer: ByteBuffer, signature: string): void { | ||
buffer.writeBytes(Buffer.from(signature, "hex")); | ||
} | ||
|
||
public deserialize(buffer: ByteBuffer): Buffer { | ||
return buffer.readBytes(this.signatureSize(buffer)); | ||
} | ||
|
||
public async aggregate(signatures: Buffer[]): Promise<string> { | ||
throw new Exceptions.NotImplemented(this.constructor.name, "aggregate"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"extends": "../../tsconfig.json", | ||
"compilerOptions": { | ||
"outDir": "distribution" | ||
}, | ||
"include": ["source/**/**.ts"] | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.