From 9e2f4957c5205141d083b1d8d6abbffbcc32f957 Mon Sep 17 00:00:00 2001 From: CedarMist <134699267+CedarMist@users.noreply.github.com> Date: Tue, 24 Sep 2024 14:55:08 +0100 Subject: [PATCH] clients/js: more munacl tests --- clients/js/src/calldatapublickey.ts | 49 ++++++----- clients/js/src/munacl.ts | 122 ++++++++++++++++++++++++++-- clients/js/test/munacl.spec.ts | 120 ++++++++++++++++++--------- contracts/test/cdpk.ts | 53 ++++++++++-- runtime/Cargo.lock | 6 +- 5 files changed, 268 insertions(+), 82 deletions(-) diff --git a/clients/js/src/calldatapublickey.ts b/clients/js/src/calldatapublickey.ts index edf2a718..4526241d 100644 --- a/clients/js/src/calldatapublickey.ts +++ b/clients/js/src/calldatapublickey.ts @@ -5,7 +5,7 @@ import { fromQuantity, getBytes } from './ethersutils.js'; import type { EIP2696_EthereumProvider } from './provider.js'; import { SUBCALL_ADDR, CALLDATAPUBLICKEY_CALLDATA } from './constants.js'; import { Cipher, X25519DeoxysII } from './cipher.js'; -import { ed25519_verify_raw } from './munacl.js'; +import { ed25519_verify_raw_cofactorless } from './munacl.js'; import { sha512_256 } from '@noble/hashes/sha512'; /** @@ -15,6 +15,10 @@ import { sha512_256 } from '@noble/hashes/sha512'; */ const DEFAULT_PUBKEY_CACHE_EXPIRATION_MS = 60 * 5 * 1000; +const BIGINT_ZERO = BigInt(0); +const UINT64_MAX = BigInt(1) << BigInt(64); +const BIGINT_EIGHT = BigInt(8); + // ----------------------------------------------------------------------------- // Fetch calldata public key // Well use provider when possible, and fallback to HTTP(S)? requests @@ -57,8 +61,7 @@ export interface CachedCallDataPublicKey extends CallDataPublicKey { } function parseBigIntFromByteArray(bytes: Uint8Array): bigint { - const eight = BigInt(8); - return bytes.reduce((acc, byte) => (acc << eight) | BigInt(byte), BigInt(0)); + return bytes.reduce((acc, byte) => (acc << BIGINT_EIGHT) | BigInt(byte), BIGINT_ZERO); } class AbiDecodeError extends Error {} @@ -79,20 +82,16 @@ function parseAbiEncodedUintBytes(bytes: Uint8Array): [bigint, Uint8Array] { if (bytes.length < offset + 32 + data_length) { throw new AbiDecodeError('too short, data'); } - const data = bytes.slice(offset + 32, offset + 32 + data_length); - return [status, data]; + return [status, bytes.slice(offset + 32, offset + 32 + data_length)]; } -const UINT64_MIN = BigInt(0); -const UINT64_MAX = BigInt(1) << BigInt(64); - -function u64tobytes(x: bigint): Uint8Array { - if (x < UINT64_MIN || x > UINT64_MAX) { +function u64tobytes(x: bigint|number): Uint8Array { + const y = BigInt(x); + if (y < BIGINT_ZERO || y > UINT64_MAX) { throw new Error('Value out of range for uint64'); } const buffer = new ArrayBuffer(8); - const view = new DataView(buffer); - view.setBigUint64(0, x, false); // false for big-endian + new DataView(buffer).setBigUint64(0, y, false); // false for big-endian return new Uint8Array(buffer); } @@ -105,7 +104,7 @@ export function verifyRuntimePublicKey( cdpk: CallDataPublicKey, runtime_id: Uint8Array, key_pair_id: Uint8Array, -) { +): boolean { let body = new Uint8Array([ ...cdpk.public_key.key, ...cdpk.public_key.checksum, @@ -114,20 +113,20 @@ export function verifyRuntimePublicKey( ]); if (cdpk.epoch !== undefined) { - body = new Uint8Array([...body, ...u64tobytes(BigInt(cdpk.epoch))]); + body = new Uint8Array([...body, ...u64tobytes(cdpk.epoch)]); } const expiration = cdpk.public_key.expiration; if (expiration !== undefined) { - body = new Uint8Array([...body, ...u64tobytes(BigInt(expiration))]); + body = new Uint8Array([...body, ...u64tobytes(expiration)]); } - const ctx = sha512_256.create(); - ctx.update(PUBLIC_KEY_SIGNATURE_CONTEXT); - ctx.update(body); - const digest = ctx.digest(); + const digest = sha512_256.create() + .update(PUBLIC_KEY_SIGNATURE_CONTEXT) + .update(body) + .digest(); - return ed25519_verify_raw(cdpk.public_key.signature, signerPk, digest); + return ed25519_verify_raw_cofactorless(cdpk.public_key.signature, signerPk, digest); } /** @@ -165,17 +164,17 @@ export async function fetchRuntimePublicKey(args: { // NOTE: to avoid pulling-in a full ABI decoder dependency, slice it manually const [resp_status, resp_cbor] = parseAbiEncodedUintBytes(resp_bytes); - if (resp_status !== BigInt(0)) { + if (resp_status !== BIGINT_ZERO) { throw new Error(`fetchRuntimePublicKey - invalid status: ${resp_status}`); } - // TODO: validate response? + // TODO: validate resp_cbor? return { - ...cborDecode(resp_cbor), + ...cborDecode(resp_cbor) as CallDataPublicKey, chainId, fetched: new Date(), - } as CachedCallDataPublicKey; + }; } /** @@ -216,7 +215,7 @@ export class KeyFetcher { return X25519DeoxysII.ephemeral(public_key.key, epoch); } - public cipherSync() { + public cipherSync(): X25519DeoxysII { if (!this.pubkey) { throw new Error('No cached pubkey!'); } diff --git a/clients/js/src/munacl.ts b/clients/js/src/munacl.ts index 3da06cb7..762d423e 100644 --- a/clients/js/src/munacl.ts +++ b/clients/js/src/munacl.ts @@ -99,6 +99,9 @@ const I = gf([ 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83, ]); +const _8 = new Uint8Array(32); +_8[0] = 8; + const _9 = new Uint8Array(32); _9[0] = 9; const _121665 = gf([0xdb41, 1]); @@ -169,6 +172,7 @@ function A(o: Float64Array, a: Float64Array, b: Float64Array) { for (let i = 0; i < 16; i++) o[i] = a[i] + b[i]; } +/// Subtract field elements, o = a - b function Z(o: Float64Array, a: Float64Array, b: Float64Array) { for (let i = 0; i < 16; i++) o[i] = a[i] - b[i]; } @@ -807,6 +811,10 @@ function par25519(a: Float64Array) { return d[0] & 1; } +function unpack(r: Float64Array[], p: Uint8Array) { + return unpackneg(r, p, true); +} + /** * Unpacks a compressed Edwards point, then negates it * @@ -814,7 +822,7 @@ function par25519(a: Float64Array) { * @param p Input compressed point (32-byte Uint8Array) * @returns 0 on success, -1 if point is invalid */ -function unpackneg(r: Float64Array[], p: Uint8Array) { +function unpackneg(r: Float64Array[], p: Uint8Array, dontnegate?:boolean) { const t = gf(), chk = gf(), num = gf(), @@ -850,7 +858,9 @@ function unpackneg(r: Float64Array[], p: Uint8Array) { M(chk, chk, den); if (neq25519(chk, num)) return -1; - if (par25519(r[0]) === p[31] >> 7) Z(r[0], gf0, r[0]); + if( ! dontnegate ) { + if (par25519(r[0]) === p[31] >> 7) Z(r[0], gf0, r[0]); + } M(r[3], r[0], r[1]); return 0; @@ -1210,12 +1220,11 @@ function ed25519_is_small_order(p: Uint8Array): boolean { return false; } -/// Verify signature without applying domain separation. -export function ed25519_verify_raw( +function _ed25519_verify_raw_common( signature: Uint8Array, publicKey: Uint8Array, msg: Uint8Array, -): boolean { +) { if (ed25519_is_small_order(publicKey)) { return false; // Small order A } @@ -1224,11 +1233,12 @@ export function ed25519_verify_raw( const S_bits = signature.subarray(32, 64); if (ed25519_is_small_order(R_bits)) { + console.log('Small order R', hexlify(R_bits)); return false; // Small order R } if (!ed25519_is_valid_scalar(S_bits)) { - console.log('S is not minimal (reject malleability)'); + console.log('S is not minimal (reject malleability)', hexlify(S_bits)); return false; // S is not minimal (reject malleability) } @@ -1237,7 +1247,7 @@ export function ed25519_verify_raw( // Decompress A (PublicKey) const negA = [gf(), gf(), gf(), gf()]; if (unpackneg(negA, publicKey) !== 0) { - console.log('Point decompression failed, pubkey (A)', hexlify(publicKey)); + console.log('Decompress A (PublicKey) failed!', hexlify(publicKey)); return false; // Point decompression failed } @@ -1254,6 +1264,22 @@ export function ed25519_verify_raw( // kA = -A^k scalarmult(kA, negA, k); + return {kA, sB, k}; +} + +/// Verify signature without applying domain separation. +export function ed25519_verify_raw_cofactorless( + signature: Uint8Array, + publicKey: Uint8Array, + msg: Uint8Array, +): boolean { + const result = _ed25519_verify_raw_common(signature, publicKey, msg); + if( result === false ) { + return false; + } + + const {sB, kA, k} = result; + // sB = G^s - A^k add(sB, kA); @@ -1263,6 +1289,38 @@ export function ed25519_verify_raw( return crypto_verify_32(signature, 0, k, 0) === 0; } +/// Verify signature without applying domain separation. +export function ed25519_verify_raw_cofactored( + signature: Uint8Array, + publicKey: Uint8Array, + msg: Uint8Array, +): boolean { + const result = _ed25519_verify_raw_common(signature, publicKey, msg); + if( result === false ) { + return false; + } + + const {sB, kA, k} = result; + + scalarmult(sB, sB, _8); + + // kA = 8 * -A^k + scalarmult(kA, kA, _8); + + // R = 8*r + const R = [gf(), gf(), gf(), gf()]; + unpack(R, signature); + scalarmult(R, R, _8); + + // Check the cofactored group equation ([8][S]B = [8]R - [8][k]A) + add(R, kA); + pack(k, R); + const j = new Uint8Array(32); + pack(j, sB); + + return crypto_verify_32(j, 0, k, 0) === 0; +} + export class MuNaclError extends Error {} export interface BoxKeyPair { @@ -1296,3 +1354,53 @@ export function boxKeyPairFromSecretKey(secretKey: Uint8Array): BoxKeyPair { secretKey: new Uint8Array(secretKey), }; } + +/** + * https://eprint.iacr.org/2008/013.pdf + * + * The birational maps are: + * + * (u, v) = ((1+y)/(1-y), sqrt(-486664)*u/x) + * (x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1)) + * + * @param pk Curve25519 public key + * @returns Ed25519 public key + */ +export function curve25519_to_ed25519(pk: Uint8Array) { + const z = new Uint8Array(32), + u = gf(), + a = gf(), + b = gf(); + + unpack25519(u, pk); + + A(a, u, gf1); // a = u+1 + Z(b, u, gf1); // b = u-1 + inv25519(a, a); // a = 1/(u+1) + M(a, a, b); // a = a * b + + pack25519(z, a); + return z; +} + +export function ed25519_to_curve25519(pk: Uint8Array) { + const z = new Uint8Array(32) + const q = [gf(), gf(), gf(), gf()] + const a = gf() + const b = gf() + + if (unpackneg(q, pk)) { + return null + } + + const y = q[1] + + A(a, gf1, y) + Z(b, gf1, y) + inv25519(b, b) + M(a, a, b) + + pack25519(z, a) + + return z +} diff --git a/clients/js/test/munacl.spec.ts b/clients/js/test/munacl.spec.ts index 85565ffa..505a4596 100644 --- a/clients/js/test/munacl.spec.ts +++ b/clients/js/test/munacl.spec.ts @@ -7,10 +7,13 @@ import { naclScalarMultBase, boxKeyPairFromSecretKey, crypto_box_SECRETKEYBYTES, - ed25519_verify_raw, + ed25519_verify_raw_cofactorless, getBytes, ed25519_is_valid_scalar, crypto_sign_open, + ed25519_verify_raw_cofactored, + curve25519_to_ed25519, + ed25519_to_curve25519, } from '@oasisprotocol/sapphire-paratime'; import { readFile } from 'fs/promises'; @@ -165,38 +168,39 @@ describe('munacl', () => { }); describe('ed25519', () => { - it.skip('Test Cases', () => { + it('Test Cases', () => { const cases = [ { - message: '', + message: '0x', pub_key: - 'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a', + '0xd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a', signature: - 'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b', + '0xe5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b', }, { - message: '72', + message: '0x72', pub_key: - '3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c', + '0x3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c', signature: - '92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00', + '0x92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00', }, { - message: 'af82', + message: '0xaf82', pub_key: - 'fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025', + '0xfc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025', signature: - '6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a', + '0x6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a', }, ]; for (const c of cases) { - const sig = getBytes(`0x${c.signature}`); - const pbk = getBytes(`0x${c.pub_key}`); - const msg = getBytes(`0x${c.message}`); - const x = ed25519_verify_raw(sig, pbk, msg); + const sig = getBytes(c.signature); + const pbk = getBytes(c.pub_key); + const msg = getBytes(c.message); + const x = ed25519_verify_raw_cofactorless(sig, pbk, msg); + const z = ed25519_verify_raw_cofactored(sig, pbk, msg); const outMsg = new Uint8Array(msg.length + 64); const y = crypto_sign_open( outMsg, @@ -206,6 +210,12 @@ describe('munacl', () => { ); expect(x).toStrictEqual(true); expect(y).toStrictEqual(msg.length); + expect(z).toStrictEqual(true); + + // round-trip point conversion curve25519<->ed25519 + const pbk_curve25519 = curve25519_to_ed25519(pbk); + const pbk_ed25519 = ed25519_to_curve25519(pbk_curve25519); + expect(pbk_ed25519).toStrictEqual(pbk); } }); @@ -224,125 +234,154 @@ describe('munacl', () => { 64 + msg.length, pbk, ); - const z = ed25519_verify_raw(sig, pbk, msg); + const x = ed25519_verify_raw_cofactored(sig, pbk, msg); + const z = ed25519_verify_raw_cofactorless(sig, pbk, msg); + expect(x).toStrictEqual(z); + expect(x).toStrictEqual(true); expect(z).toStrictEqual(true); expect(y).toStrictEqual(msg.length); } }); - it.skip('speccheck', () => { + it('speccheck', () => { const cases = [ - { + { // #0: canonical S, small R, small A message: '8c93255d71dcab10e8f379c26200f3c7bd5f09d9bc3068d3ef4edeb4853022b6', pub_key: 'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa', signature: 'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000', + expected_with_cofactored: false, + expected_with_cofactorless: false, }, - { + { // #1: canonical S, mixed R, small A message: '9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79', pub_key: 'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa', signature: 'f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04', + expected_with_cofactored: false, + expected_with_cofactorless: false, }, - { + { // #2: canonical S, small R, mixed A message: 'aebf3f2601a0c8c5d39cc7d8911642f740b78168218da8471772b35f9d35b9ab', pub_key: 'f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43', signature: 'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8c4bd45aecaca5b24fb97bc10ac27ac8751a7dfe1baff8b953ec9f5833ca260e', + expected_with_cofactored: false, + expected_with_cofactorless: false, }, - { + { // #3-4: canonical S, mixed R, mixed A message: '9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79', pub_key: 'cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d', signature: '9046a64750444938de19f227bb80485e92b83fdb4b6506c160484c016cc1852f87909e14428a7a1d62e9f22f3d3ad7802db02eb2e688b6c52fcd6648a98bd009', + expected_with_cofactored: true, + expected_with_cofactorless: true, }, - { + { // 4 message: 'e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c', pub_key: 'cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d', signature: '160a1cb0dc9c0258cd0a7d23e94d8fa878bcb1925f2c64246b2dee1796bed5125ec6bc982a269b723e0668e540911a9a6a58921d6925e434ab10aa7940551a09', + expected_with_cofactored: true, + expected_with_cofactorless: false, }, - { + { // #5 Prereduce scalar which fails cofactorless message: 'e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c', pub_key: 'cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d', signature: '21122a84e0b5fca4052f5b1235c80a537878b38f3142356b2c2384ebad4668b7e40bc836dac0f71076f9abe3a53f9c03c1ceeeddb658d0030494ace586687405', + expected_with_cofactored: true, + expected_with_cofactorless: false, }, - { + { // #6 Large S message: '85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40', pub_key: '442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623', signature: 'e96f66be976d82e60150baecff9906684aebb1ef181f67a7189ac78ea23b6c0e547f7690a0e2ddcd04d87dbc3490dc19b3b3052f7ff0538cb68afb369ba3a514', + expected_with_cofactored: false, + expected_with_cofactorless: false, }, - { + { // #7 Large S beyond the high bit checks (i.e. non-canonical representation) message: '85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40', pub_key: '442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623', signature: '8ce5b96c8f26d0ab6c47958c9e68b937104cd36e13c33566acd2fe8d38aa19427e71f98a473474f2f13f06f97c20d58cc3f54b8bd0d272f42b695dd7e89a8c22', + expected_with_cofactored: false, + expected_with_cofactorless: false, }, - { + { // #8-9 Non canonical R message: '9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41', pub_key: 'f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43', signature: 'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f', + expected_with_cofactored: true, + expected_with_cofactorless: false, }, - { + { // 9 message: '9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41', pub_key: 'f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43', signature: 'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908', + expected_with_cofactored: true, + expected_with_cofactorless: false, }, - { + { // #10-11 Non canonical A message: 'e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b', pub_key: 'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', signature: 'a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04', + expected_with_cofactored: true, + expected_with_cofactorless: false, }, - { + + { // 11 message: '39a591f5321bbe07fd5a23dc2f39d025d74526615746727ceefd6e82ae65c06f', pub_key: 'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', signature: 'a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04', + expected_with_cofactored: true, + expected_with_cofactorless: true, }, - ]; + ] as const; let i = 0; for (const c of cases) { const sig = getBytes(`0x${c.signature}`); const pbk = getBytes(`0x${c.pub_key}`); const msg = getBytes(`0x${c.message}`); - const x = ed25519_verify_raw(sig, pbk, msg); - console.log(i, x); - expect(x).toStrictEqual(false); + const cofactorless = ed25519_verify_raw_cofactorless(sig, pbk, msg); + const cofactored = ed25519_verify_raw_cofactored(sig, pbk, msg); + expect(cofactorless).toStrictEqual(c.expected_with_cofactorless); + expect(cofactored).toStrictEqual(c.expected_with_cofactored); i += 1; } }); - it.skip('Case 1 - small order A', () => { + it('Case 1 - small order A', () => { const pbk = getBytes( '0xc7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa', ); @@ -352,10 +391,12 @@ describe('munacl', () => { const sig = getBytes( '0xf7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04', ); - expect(ed25519_verify_raw(sig, pbk, msg)).toStrictEqual(false); // small order A not rejected + // small order A not rejected + expect(ed25519_verify_raw_cofactored(sig, pbk, msg)).toStrictEqual(false); + expect(ed25519_verify_raw_cofactorless(sig, pbk, msg)).toStrictEqual(false); }); - it.skip('Case 2 - reject small order R', () => { + it('Case 2 - reject small order R', () => { const pbk = getBytes( '0xf7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43', ); @@ -365,7 +406,9 @@ describe('munacl', () => { const sig = getBytes( '0xc7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8c4bd45aecaca5b24fb97bc10ac27ac8751a7dfe1baff8b953ec9f5833ca260e', ); - expect(ed25519_verify_raw(sig, pbk, msg)).toStrictEqual(false); // small order R not rejected + // small order R not rejected + expect(ed25519_verify_raw_cofactored(sig, pbk, msg)).toStrictEqual(false); + expect(ed25519_verify_raw_cofactorless(sig, pbk, msg)).toStrictEqual(false); }); // Vector 4 is made to pass cofactored and fail in cofactorless verification, this vector @@ -383,7 +426,8 @@ describe('munacl', () => { const sig = getBytes( '0x160a1cb0dc9c0258cd0a7d23e94d8fa878bcb1925f2c64246b2dee1796bed5125ec6bc982a269b723e0668e540911a9a6a58921d6925e434ab10aa7940551a09', ); - expect(ed25519_verify_raw(sig, pbk, msg)).toStrictEqual(false); + expect(ed25519_verify_raw_cofactorless(sig, pbk, msg)).toStrictEqual(false); + expect(ed25519_verify_raw_cofactored(sig, pbk, msg)).toStrictEqual(true); }); }); }); diff --git a/contracts/test/cdpk.ts b/contracts/test/cdpk.ts index a1d17684..79cd2304 100644 --- a/contracts/test/cdpk.ts +++ b/contracts/test/cdpk.ts @@ -2,13 +2,13 @@ import { ethers } from 'hardhat'; import { expect } from 'chai'; import * as cborg from 'cborg'; import { AbiCoder, getBytes } from 'ethers'; -import { verifyRuntimePublicKey } from '@oasisprotocol/sapphire-paratime' +import { CallDataPublicKey, curve25519_to_ed25519, ed25519_to_curve25519, hexlify, verifyRuntimePublicKey } from '@oasisprotocol/sapphire-paratime' -async function doSubcall(name:string) { +async function doSubcall(name:string, args:any=null) { const coder = AbiCoder.defaultAbiCoder(); const data = coder.encode( ['string', 'bytes'], - [name, cborg.encode(null)], + [name, cborg.encode(args)], ); const subcallResult = await ethers.provider.call({ to: '0x0100000000000000000000000000000000000103', data @@ -21,16 +21,51 @@ async function doSubcall(name:string) { } describe('Call Data Public Key', () => { - it.skip('core.PublicKey', async () => { - console.log(await doSubcall('core.PublicKey')); + it.skip('core.KeyManagerPublicKey', async () => { + const blah = await doSubcall('core.KeyManagerPublicKey'); }); it('core.CallDataPublicKey', async () => { - const signerPk = await doSubcall('core.PublicKey'); - const callDataPublicKey = await doSubcall('core.CallDataPublicKey'); - console.log(callDataPublicKey); + const signerPk = await doSubcall('core.KeyManagerPublicKey'); + console.log('KeyManagerPublicKey', signerPk); - const vr = verifyRuntimePublicKey(signerPk.public_key, callDataPublicKey.public_key, signerPk.runtime_id, signerPk.key_pair_id); + const signerPkEd25519 = curve25519_to_ed25519(signerPk.public_key.key); + const signerPkX25519 = ed25519_to_curve25519(signerPkEd25519)!; + expect(hexlify(signerPkX25519)).eq(hexlify(signerPk.public_key.key)); + + const response = await doSubcall('core.CallDataPublicKey') as CallDataPublicKey; + console.log('CallDataPublicKey', response); + console.log('cdpk.public_key.key', hexlify(response.public_key.key)); + console.log('signerPK', hexlify(signerPk.public_key.key)); + console.log('signerPK (ed25519)', hexlify(signerPkEd25519)); + console.log('signerPK (x25519)', hexlify(signerPkX25519)); + console.log('cdpk sig', hexlify(response.public_key.signature)); + + const cdpk_sig_R = response.public_key.signature.slice(0, 32); + const cdpk_sig_R_ed25519 = curve25519_to_ed25519(cdpk_sig_R); + console.log('cdpk sig R', hexlify(response.public_key.signature)); + console.log('cdpk sig R (ed25519)', hexlify(cdpk_sig_R_ed25519)); + + const cdpk_sig_s = response.public_key.signature.slice(32); + console.log('cdpk sig s', hexlify(cdpk_sig_s)); + + const newsig = new Uint8Array([...cdpk_sig_R_ed25519, ...cdpk_sig_s]); + const newresponse = { + public_key: { + key: signerPkEd25519, + checksum: response.public_key.checksum, + signature: newsig + } + }; + + signerPkEd25519[31] |= newsig[63] & 128; + newsig[63] &= 127; + + const vr = verifyRuntimePublicKey( + signerPkEd25519, //signerPk.public_key.key, + newresponse, // response, + signerPk.runtime_id, + signerPk.key_pair_id); expect(vr).eq(true); }); }); diff --git a/runtime/Cargo.lock b/runtime/Cargo.lock index def9c6e0..87d73d3f 100644 --- a/runtime/Cargo.lock +++ b/runtime/Cargo.lock @@ -2107,7 +2107,7 @@ dependencies = [ [[package]] name = "oasis-runtime-sdk" version = "0.9.6" -source = "git+https://github.com/oasisprotocol/oasis-sdk?branch=CedarMist/GetPublicKey#64c77d4dd7c9aa7a9ba94794244aef3438664840" +source = "git+https://github.com/oasisprotocol/oasis-sdk?branch=CedarMist/GetPublicKey#2e959ec447f39963eeee55ff8143a2d7abc1147e" dependencies = [ "anyhow", "async-trait", @@ -2148,7 +2148,7 @@ dependencies = [ [[package]] name = "oasis-runtime-sdk-evm" version = "0.6.0" -source = "git+https://github.com/oasisprotocol/oasis-sdk?branch=CedarMist/GetPublicKey#64c77d4dd7c9aa7a9ba94794244aef3438664840" +source = "git+https://github.com/oasisprotocol/oasis-sdk?branch=CedarMist/GetPublicKey#2e959ec447f39963eeee55ff8143a2d7abc1147e" dependencies = [ "anyhow", "base64 0.22.1", @@ -2180,7 +2180,7 @@ dependencies = [ [[package]] name = "oasis-runtime-sdk-macros" version = "0.3.0" -source = "git+https://github.com/oasisprotocol/oasis-sdk?branch=CedarMist/GetPublicKey#64c77d4dd7c9aa7a9ba94794244aef3438664840" +source = "git+https://github.com/oasisprotocol/oasis-sdk?branch=CedarMist/GetPublicKey#2e959ec447f39963eeee55ff8143a2d7abc1147e" dependencies = [ "darling 0.20.10", "proc-macro2 1.0.86",