Skip to content

Commit

Permalink
✂️ Split out signers to separate module
Browse files Browse the repository at this point in the history
  • Loading branch information
KimlikDAO-bot committed Aug 25, 2024
1 parent 53e17b6 commit 81acfcc
Show file tree
Hide file tree
Showing 13 changed files with 219 additions and 198 deletions.
2 changes: 1 addition & 1 deletion crosschain/mock/signer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import evm from "../../ethereum/evm";
import { Signer as EvmSigner } from "../../ethereum/mock/signer";
import { signMessage } from "../../mina/mina";
import { addr as minaAddr } from "../../mina/mock/signer";
import { signMessage } from "../../mina/signer";
import { assertEq } from "../../testing/assert";
import base58 from "../../util/base58";
import hex from "../../util/hex";
Expand Down
5 changes: 3 additions & 2 deletions crypto/test/integration.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expect, it, test } from "bun:test";
import evm from "../../ethereum/evm";
import { addr, signCompact, signWide } from "../../ethereum/mock/signer";
import { signerAddress } from "../../ethereum/signer";

test("address derivation from private key", () => {
expect(addr(1n)).toBe("0x7e5f4552091a69125d5dfcb7b8c2659029395bdf");
Expand All @@ -24,8 +25,8 @@ test("compact signature conversion methods", () => {
it("should recover signer address correctly", () => {
/** @const {bigint} */
const digest = 103n;
expect(evm.signerAddress(evm.uint256(digest), signCompact(digest, 101n)))
expect(signerAddress(evm.uint256(digest), signCompact(digest, 101n)))
.toBe(addr(101n));
expect(evm.signerAddress(digest.toString(16), signCompact(digest, 1337n)))
expect(signerAddress(digest.toString(16), signCompact(digest, 1337n)))
.toBe(addr(1337n));
});
11 changes: 5 additions & 6 deletions did/sectionSigner.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChainGroup } from "../crosschain/chains";
import evm from "../ethereum/evm";
import { signFields, verifyFields } from "../mina/mina";
import { signCompact, signerAddress } from "../ethereum/signer";
import { signFields, verifyFields } from "../mina/signer";
import { base64tenSayıya } from "../util/çevir";
import { commit } from "./commitment";
import { hash } from "./section";
Expand Down Expand Up @@ -59,7 +59,7 @@ const signSection = (sectionName, section, signParams) => {
: signParams.commitment;
section.signatureTs = signParams.signatureTs;
section.secp256k1 = [
evm.signCompact(hash(sectionName, section), signParams.privateKey)
signCompact(hash(sectionName, section), signParams.privateKey)
];
if (sectionName == "humanID")
signHumanID(/** @type {!did.HumanID} */(section), signParams.privateKeyPallas);
Expand All @@ -84,8 +84,7 @@ const recoverSectionSigners = (sectionName, section, chainGroup, ownerAddress) =
/** @const {string} */
const h = hash(sectionName, section);
/** @const {!Array<string>} */
const signers = section.secp256k1.map((signature) =>
evm.signerAddress(h, signature));
const signers = section.secp256k1.map((signature) => signerAddress(h, signature));
return [...new Set(signers)];
}

Expand All @@ -103,9 +102,9 @@ const signDecryptedSections = (decryptedSections, signParams) => {
}

export {
SignParams,
recoverHumanIDSigners,
recoverSectionSigners,
signDecryptedSections,
SignParams,
signSection
};
7 changes: 7 additions & 0 deletions ethereum/eth.d.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
/** @const */
const eth = {};

/**
* A length 80 hex string denoting and address without the leading 0x.
*
* @typedef {string}
*/
eth.PackedAddress;

/**
* A string of length 132, starting with 0x.
*
Expand Down
59 changes: 3 additions & 56 deletions ethereum/evm.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
*
* @author KimlikDAO
*/
import { Point, recoverSigner, sign } from "../crypto/secp256k1";
import { keccak256, keccak256Uint32, keccak256Uint8 } from '../crypto/sha3';
import { hex, uint8ArrayBEyeSayıdan } from "../util/çevir";
import { keccak256, keccak256Uint8 } from '../crypto/sha3';
import { hex } from "../util/çevir";
import eth from "./eth.d";

/**
Expand Down Expand Up @@ -115,58 +114,9 @@ const personalDigest = (msg) => {
return hex(keccak256Uint8(encoded));
}

/**
* @param {!Point} Q
* @return {string} address
*/
const pointToAddress = (Q) => {
/** @const {!Uint8Array} */
const buff = new Uint8Array(64);
uint8ArrayBEyeSayıdan(buff, 32, Q.x);
uint8ArrayBEyeSayıdan(buff, 64, Q.y);
/** @const {!Uint8Array} */
const hash = new Uint8Array(
keccak256Uint32(new Uint32Array(buff.buffer)).buffer, 12, 20);
return "0x" + hex(hash);
}

/**
* Given a digest and a signature, recovers the signer address if the signature
* is valid; outputs an arbitrary value otherwise.
*
* @param {string} digest as a length 64 hex string
* @param {eth.CompactSignature} signature as a length 128 compact signature
* @return {string} 42 characters long EVM address
*/
const signerAddress = (digest, signature) => {
/** @const {number} */
const highNibble = parseInt(signature[64], 16);
/** @const {boolean} */
const yParity = highNibble >= 8;
/** @const {bigint} */
const r = BigInt("0x" + signature.slice(0, 64));
/** @const {bigint} */
const s = BigInt("0x" + (yParity
? (highNibble - 8).toString(16) + signature.slice(65)
: signature.slice(64))
);
return pointToAddress(
recoverSigner(BigInt("0x" + digest), r, s, yParity));
}

/**
* @param {string} digest
* @param {bigint} privateKey
* @return {eth.CompactSignature}
*/
const signCompact = (digest, privateKey) => {
const { r, s, yParity } = sign(BigInt("0x" + digest), privateKey);
return uint256(r) + uint256(yParity ? s + (1n << 255n) : s);
}

/**
* @param {string} addr EVM adresi; 0x ile başlamalı.
* @return {string} 80 uzunluğunde hex kodlanmış adres
* @return {eth.PackedAddress} 80 uzunluğunde hex kodlanmış adres
*/
const packedAddress = (addr) => addr.slice(2).toLowerCase();

Expand Down Expand Up @@ -212,9 +162,6 @@ export default {
isZero,
packedAddress,
personalDigest,
pointToAddress,
signerAddress,
signCompact,
uint160,
uint256,
Uint256Max,
Expand Down
60 changes: 60 additions & 0 deletions ethereum/signer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Point, recoverSigner, sign } from "../crypto/secp256k1";
import { keccak256Uint32 } from "../crypto/sha3";
import { hex, uint8ArrayBEyeSayıdan } from "../util/çevir";
import eth from "./eth.d";
import evm from "./evm";

/**
* @param {!Point} Q
* @return {string} address
*/
const pointToAddress = (Q) => {
/** @const {!Uint8Array} */
const buff = new Uint8Array(64);
uint8ArrayBEyeSayıdan(buff, 32, Q.x);
uint8ArrayBEyeSayıdan(buff, 64, Q.y);
/** @const {!Uint8Array} */
const hash = new Uint8Array(
keccak256Uint32(new Uint32Array(buff.buffer)).buffer, 12, 20);
return "0x" + hex(hash);
}

/**
* Given a digest and a signature, recovers the signer address if the signature
* is valid; outputs an arbitrary value otherwise.
*
* @param {string} digest as a length 64 hex string
* @param {eth.CompactSignature} signature as a length 128 compact signature
* @return {string} 42 characters long EVM address
*/
const signerAddress = (digest, signature) => {
/** @const {number} */
const highNibble = parseInt(signature[64], 16);
/** @const {boolean} */
const yParity = highNibble >= 8;
/** @const {bigint} */
const r = BigInt("0x" + signature.slice(0, 64));
/** @const {bigint} */
const s = BigInt("0x" + (yParity
? (highNibble - 8).toString(16) + signature.slice(65)
: signature.slice(64))
);
return pointToAddress(
recoverSigner(BigInt("0x" + digest), r, s, yParity));
}

/**
* @param {string} digest
* @param {bigint} privateKey
* @return {eth.CompactSignature}
*/
const signCompact = (digest, privateKey) => {
const { r, s, yParity } = sign(BigInt("0x" + digest), privateKey);
return evm.uint256(r) + evm.uint256(yParity ? s + (1n << 255n) : s);
}

export {
pointToAddress,
signCompact,
signerAddress
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from "bun:test";
import evm from "../../evm";
import evm from "../evm";

describe("adresDüzelt testleri", () => {
it("should add checksum to lower case address", () => {
Expand Down
29 changes: 15 additions & 14 deletions ethereum/test/evm/sign.test.js → ethereum/test/signer.test.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,48 @@
import { describe, expect, it, test } from "bun:test";
import { G } from "../../../crypto/secp256k1";
import { keccak256 } from "../../../crypto/sha3";
import evm from "../../evm";
import { G } from "../../crypto/secp256k1";
import { keccak256 } from "../../crypto/sha3";
import evm from "../evm";
import { pointToAddress, signCompact, signerAddress } from "../signer";

describe("Signature tests", () => {
it("should recover signer address", () => {
expect(evm.signerAddress(
expect(signerAddress(
evm.personalDigest("140e575468d2a8dcbcc437e0f12e37606491f1621fe71239b99b793cd590b7f4"),
evm.compactSignature(
"0x278f49cb66db8b104751fe3413dbf50e58288e66b625fa704b33255d06012295" +
"6dc164431e9b78805da4bb590d2a8f7a340c89ed74ec04d054a3b49977ec6b4a1c")))
.toBe("0x79883d9acbc4abac6d2d216693f66fcc5a0bcbc1");
expect(evm.signerAddress(
expect(signerAddress(
evm.personalDigest("92c0aea56bd108db8f41000707872c9e5f72bd79c10d9285ddf0e32c51791948"),
evm.compactSignature(
"0x9400092866ff50e1d88014ca3cc3d878f6cecacc5ae8f752f3874c0b0584a90f" +
"462f2947f7fbc119843e3c4319dc2da3cbb482745d42dd0e5a1cd27033270cbd1b")))
.toBe("0x79883d9acbc4abac6d2d216693f66fcc5a0bcbc1");
expect(evm.signerAddress(
expect(signerAddress(
evm.personalDigest("3e38bda94a98f1b3019dd7b48e050cbb18986724d1cfd29ceb9c8e57c7351866"),
evm.compactSignature(
"0x09af513d1be6760e9a2e6bfa9b2166b337c2e2f830a3b75b01f41b25804c3710" +
"44065ee77b34b36fd8a8ea9bda7acf4fd5ab7bf56879a33df8293d744784cb1e1b")))
.toBe("0x79883d9acbc4abac6d2d216693f66fcc5a0bcbc1");
expect(evm.signerAddress(
expect(signerAddress(
evm.personalDigest("İmza, KimlikDAO"),
evm.compactSignature(
"0x6c8b90ce7867d3bab682a8d8ac47293e3b8e733b5c21843d9425da4de1fa4b4d" +
"1495991af65b57164b042186c02bac62a292b3247e322aa4d1f9ab7519e87f4d1b")))
.toBe("0x79883d9acbc4abac6d2d216693f66fcc5a0bcbc1");
expect(evm.signerAddress(
expect(signerAddress(
evm.personalDigest("Signed, KimlikDAO"),
evm.compactSignature(
"0xf0912fda0d8d456eaa711fa58c3788813fc7a0ebe3906f38a5b57f18dd379b69" +
"3a7afde0f05e858af8be9d8c62ca03dac9d52dc9c8cc2c00a4d74cb875988dae1c")))
.toBe("0x79883d9acbc4abac6d2d216693f66fcc5a0bcbc1")
expect(evm.signerAddress(
expect(signerAddress(
evm.personalDigest("Çekoslovakyalılaştıramadıklarımızdanmışçasına"),
evm.compactSignature(
"0x8b00f90e6920617f49daad913d8492534d85a7dfc2a2ec0f8de149870fda62a5" +
"0f7abf9b7488d8304deb635248e45ba65099d3bfc86ff43711311d4b4b18e1e41b")))
.toBe("0x79883d9acbc4abac6d2d216693f66fcc5a0bcbc1")
expect(evm.signerAddress(
expect(signerAddress(
evm.personalDigest("Öşür yoğuşturup, aşı kovuşturmak. Iğdır’ın ilk harfi ı’dır."),
evm.compactSignature(
"0x2a64a7063ca328bee1d612a327757389b73970598f0e964b1ef075de3937c198" +
Expand All @@ -53,11 +54,11 @@ describe("Signature tests", () => {
test("signerAddress(d, signCompact(d, s)) == pointToAddress(s.G)", () => {
/** @const {string} */
const digest = keccak256("sign me!");
for (let /** bigint */ i = 1n; i < 1000n; ++i)
expect(evm.signerAddress(digest, evm.signCompact(digest, i)))
.toBe(evm.pointToAddress(G.copy().multiply(i).project()));
for (let /** bigint */ i = 1n; i < 100n; ++i)
expect(signerAddress(digest, signCompact(digest, i)))
.toBe(pointToAddress(G.copy().multiply(i).project()));
});

test("pointToAddress()", () => {
expect(evm.pointToAddress(G)).toBe("0x7e5f4552091a69125d5dfcb7b8c2659029395bdf");
expect(pointToAddress(G)).toBe("0x7e5f4552091a69125d5dfcb7b8c2659029395bdf");
});
Loading

0 comments on commit 81acfcc

Please sign in to comment.