From 9c3f42f24132f8057805fe569bd280de3919e4b5 Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Wed, 7 Jan 2026 17:32:24 +0900 Subject: [PATCH 1/6] teddsa: add DTOs for key packages and signing commitments --- crypto/teddsa/teddsa_interface/package.json | 3 + .../teddsa/teddsa_interface/src/api/keygen.ts | 16 +- .../teddsa/teddsa_interface/src/api/sign.ts | 31 ++- crypto/teddsa/teddsa_interface/src/dtos.ts | 188 ++++++++++++++++++ crypto/teddsa/teddsa_interface/src/index.ts | 1 + crypto/teddsa/teddsa_interface/src/keygen.ts | 30 ++- crypto/teddsa/teddsa_interface/src/sign.ts | 44 +++- 7 files changed, 266 insertions(+), 47 deletions(-) create mode 100644 crypto/teddsa/teddsa_interface/src/dtos.ts diff --git a/crypto/teddsa/teddsa_interface/package.json b/crypto/teddsa/teddsa_interface/package.json index cc1fce944..800bd1d55 100644 --- a/crypto/teddsa/teddsa_interface/package.json +++ b/crypto/teddsa/teddsa_interface/package.json @@ -11,6 +11,9 @@ "clean": "del-cli dist", "build": "yarn clean && tsc && tsc-alias" }, + "dependencies": { + "@oko-wallet/bytes": "workspace:*" + }, "devDependencies": { "@types/node": "^24.10.1", "del-cli": "^6.0.0", diff --git a/crypto/teddsa/teddsa_interface/src/api/keygen.ts b/crypto/teddsa/teddsa_interface/src/api/keygen.ts index 101e554a9..c8b4c0378 100644 --- a/crypto/teddsa/teddsa_interface/src/api/keygen.ts +++ b/crypto/teddsa/teddsa_interface/src/api/keygen.ts @@ -1,21 +1,21 @@ -import type { TeddsaKeygenOutput } from "../keygen"; +import type { KeyPackageRaw, PublicKeyPackageRaw } from "../keygen"; -export interface TeddsaKeygenInitRequest { +export interface KeygenInitRequest { user_id: string; } -export interface TeddsaKeygenInitResponse { +export interface KeygenInitResponse { session_id: string; } -export interface TeddsaKeygenStoreRequest { +export interface KeygenStoreRequest { user_id: string; session_id: string; - keygen_output: TeddsaKeygenOutput; - public_key: number[]; + key_package: KeyPackageRaw; + public_key_package: PublicKeyPackageRaw; } -export interface TeddsaKeygenStoreResponse { +export interface KeygenStoreResponse { success: boolean; - public_key: number[]; + verifying_key: number[]; } diff --git a/crypto/teddsa/teddsa_interface/src/api/sign.ts b/crypto/teddsa/teddsa_interface/src/api/sign.ts index 9d18cffc1..5de60bf01 100644 --- a/crypto/teddsa/teddsa_interface/src/api/sign.ts +++ b/crypto/teddsa/teddsa_interface/src/api/sign.ts @@ -1,31 +1,28 @@ -import type { TeddsaCommitmentEntry, TeddsaSignatureShareEntry } from "../sign"; +import type { CommitmentEntry, SignatureShareEntry } from "../sign"; -export interface TeddsaSignRound1Request { +export interface SignInitRequest { session_id: string; message: number[]; - client_commitment: TeddsaCommitmentEntry; } -export interface TeddsaSignRound1Response { - server_commitment: TeddsaCommitmentEntry; +export interface SignInitResponse { + sign_session_id: string; } -export interface TeddsaSignRound2Request { - session_id: string; - client_signature_share: TeddsaSignatureShareEntry; +export interface SignRound1Request { + sign_session_id: string; + client_commitment: CommitmentEntry; } -export interface TeddsaSignRound2Response { - server_signature_share: TeddsaSignatureShareEntry; +export interface SignRound1Response { + server_commitment: CommitmentEntry; } -export interface TeddsaAggregateRequest { - session_id: string; - message: number[]; - all_commitments: TeddsaCommitmentEntry[]; - all_signature_shares: TeddsaSignatureShareEntry[]; +export interface SignRound2Request { + sign_session_id: string; + client_signature_share: SignatureShareEntry; } -export interface TeddsaAggregateResponse { - signature: number[]; +export interface SignRound2Response { + server_signature_share: SignatureShareEntry; } diff --git a/crypto/teddsa/teddsa_interface/src/dtos.ts b/crypto/teddsa/teddsa_interface/src/dtos.ts new file mode 100644 index 000000000..f4e25da44 --- /dev/null +++ b/crypto/teddsa/teddsa_interface/src/dtos.ts @@ -0,0 +1,188 @@ +// NOTE: Bytes types use @oko-wallet/bytes for type-safe fixed-length byte handling +// NOTE: Use *FromRaw() to convert WASM output to Bytes types, *ToRaw() for the reverse + +import { Bytes, type Bytes32, type Bytes64 } from "@oko-wallet/bytes"; +import type { + KeyPackageRaw, + PublicKeyPackageRaw, + CentralizedKeygenOutput, +} from "./keygen"; +import type { + SigningCommitmentOutput, + SignatureShareOutput, + CommitmentEntry, + SignatureShareEntry, + SignatureOutput, +} from "./sign"; + +export interface KeyPackage { + identifier: Bytes32; + signing_share: Bytes32; + verifying_share: Bytes32; + verifying_key: Bytes32; + min_signers: number; +} + +export interface PublicKeyPackage { + verifying_shares: Map; + verifying_key: Bytes32; +} + +export interface CentralizedKeygen { + keygen_outputs: KeyPackage[]; + public_key_package: PublicKeyPackage; +} + +export interface SigningCommitment { + nonces: Uint8Array; + commitments: Uint8Array; + identifier: Bytes32; +} + +export interface SignatureShare { + signature_share: Bytes32; + identifier: Bytes32; +} + +export interface Commitment { + identifier: Bytes32; + commitments: Uint8Array; +} + +export interface SignShare { + identifier: Bytes32; + signature_share: Bytes32; +} + +export interface Signature { + signature: Bytes64; +} + +export function keyPackageFromRaw(raw: KeyPackageRaw): KeyPackage { + return { + identifier: Bytes.fromUint8ArrayUnsafe(new Uint8Array(raw.identifier), 32), + signing_share: Bytes.fromUint8ArrayUnsafe( + new Uint8Array(raw.signing_share), + 32, + ), + verifying_share: Bytes.fromUint8ArrayUnsafe( + new Uint8Array(raw.verifying_share), + 32, + ), + verifying_key: Bytes.fromUint8ArrayUnsafe( + new Uint8Array(raw.verifying_key), + 32, + ), + min_signers: raw.min_signers, + }; +} + +export function publicKeyPackageFromRaw( + raw: PublicKeyPackageRaw, +): PublicKeyPackage { + const verifying_shares = new Map(); + for (const [idHex, share] of Object.entries(raw.verifying_shares)) { + verifying_shares.set( + idHex, + Bytes.fromUint8ArrayUnsafe(new Uint8Array(share), 32), + ); + } + return { + verifying_shares, + verifying_key: Bytes.fromUint8ArrayUnsafe( + new Uint8Array(raw.verifying_key), + 32, + ), + }; +} + +export function centralizedKeygenFromRaw( + raw: CentralizedKeygenOutput, +): CentralizedKeygen { + return { + keygen_outputs: raw.keygen_outputs.map(keyPackageFromRaw), + public_key_package: publicKeyPackageFromRaw(raw.public_key_package), + }; +} + +export function signingCommitmentFromRaw( + raw: SigningCommitmentOutput, +): SigningCommitment { + return { + nonces: new Uint8Array(raw.nonces), + commitments: new Uint8Array(raw.commitments), + identifier: Bytes.fromUint8ArrayUnsafe(new Uint8Array(raw.identifier), 32), + }; +} + +export function signatureShareFromRaw( + raw: SignatureShareOutput, +): SignatureShare { + return { + signature_share: Bytes.fromUint8ArrayUnsafe( + new Uint8Array(raw.signature_share), + 32, + ), + identifier: Bytes.fromUint8ArrayUnsafe(new Uint8Array(raw.identifier), 32), + }; +} + +export function signatureFromRaw(raw: SignatureOutput): Signature { + return { + signature: Bytes.fromUint8ArrayUnsafe(new Uint8Array(raw.signature), 64), + }; +} + +export function keyPackageToRaw(pkg: KeyPackage): KeyPackageRaw { + return { + identifier: Array.from(pkg.identifier.toUint8Array()), + signing_share: Array.from(pkg.signing_share.toUint8Array()), + verifying_share: Array.from(pkg.verifying_share.toUint8Array()), + verifying_key: Array.from(pkg.verifying_key.toUint8Array()), + min_signers: pkg.min_signers, + }; +} + +export function publicKeyPackageToRaw( + pkg: PublicKeyPackage, +): PublicKeyPackageRaw { + const verifying_shares: Record = {}; + for (const [idHex, share] of pkg.verifying_shares) { + verifying_shares[idHex] = Array.from(share.toUint8Array()); + } + return { + verifying_shares, + verifying_key: Array.from(pkg.verifying_key.toUint8Array()), + }; +} + +export function commitmentToEntry(c: Commitment): CommitmentEntry { + return { + identifier: Array.from(c.identifier.toUint8Array()), + commitments: Array.from(c.commitments), + }; +} + +export function signShareToEntry(s: SignShare): SignatureShareEntry { + return { + identifier: Array.from(s.identifier.toUint8Array()), + signature_share: Array.from(s.signature_share.toUint8Array()), + }; +} + +export function serializeKeyPackage(pkg: KeyPackage): number[] { + const result: number[] = []; + result.push(...pkg.identifier.toUint8Array()); + result.push(...pkg.signing_share.toUint8Array()); + result.push(...pkg.verifying_share.toUint8Array()); + result.push(...pkg.verifying_key.toUint8Array()); + result.push(pkg.min_signers & 0xff); + result.push((pkg.min_signers >> 8) & 0xff); + return result; +} + +export function serializePublicKeyPackage(pkg: PublicKeyPackage): number[] { + const raw = publicKeyPackageToRaw(pkg); + const json = JSON.stringify(raw); + return Array.from(new TextEncoder().encode(json)); +} diff --git a/crypto/teddsa/teddsa_interface/src/index.ts b/crypto/teddsa/teddsa_interface/src/index.ts index 30db38b20..ab473b102 100644 --- a/crypto/teddsa/teddsa_interface/src/index.ts +++ b/crypto/teddsa/teddsa_interface/src/index.ts @@ -2,4 +2,5 @@ export * from "./keygen"; export * from "./sign"; export * from "./participant"; export * from "./errors"; +export * from "./dtos"; export * from "./api"; diff --git a/crypto/teddsa/teddsa_interface/src/keygen.ts b/crypto/teddsa/teddsa_interface/src/keygen.ts index cb41542e0..714f63860 100644 --- a/crypto/teddsa/teddsa_interface/src/keygen.ts +++ b/crypto/teddsa/teddsa_interface/src/keygen.ts @@ -1,17 +1,25 @@ -export interface TeddsaKeygenOutput { - key_package: number[]; - public_key_package: number[]; +// NOTE: Raw types are direct WASM output format using number[] for byte arrays + +export interface KeyPackageRaw { identifier: number[]; + signing_share: number[]; + verifying_share: number[]; + verifying_key: number[]; + min_signers: number; +} + +export interface PublicKeyPackageRaw { + verifying_shares: Record; + verifying_key: number[]; } -export interface TeddsaCentralizedKeygenOutput { - private_key: number[]; - keygen_outputs: TeddsaKeygenOutput[]; - public_key: number[]; +export interface CentralizedKeygenOutput { + keygen_outputs: KeyPackageRaw[]; + public_key_package: PublicKeyPackageRaw; } -export interface TeddsaClientKeygenState { - keygen_1: TeddsaKeygenOutput | null; - keygen_2: TeddsaKeygenOutput | null; - public_key: number[] | null; +export interface ClientKeygenState { + client_key_package: KeyPackageRaw | null; + server_key_package: KeyPackageRaw | null; + public_key_package: PublicKeyPackageRaw | null; } diff --git a/crypto/teddsa/teddsa_interface/src/sign.ts b/crypto/teddsa/teddsa_interface/src/sign.ts index 19df1f240..e85b10321 100644 --- a/crypto/teddsa/teddsa_interface/src/sign.ts +++ b/crypto/teddsa/teddsa_interface/src/sign.ts @@ -1,36 +1,58 @@ -export interface TeddsaSignRound1Output { +// NOTE: Raw types are direct WASM I/O format using number[] for byte arrays + +import type { KeyPackageRaw, PublicKeyPackageRaw } from "./keygen"; + +export interface SigningCommitmentOutput { nonces: number[]; commitments: number[]; identifier: number[]; } -export interface TeddsaSignRound2Output { +export interface SignatureShareOutput { signature_share: number[]; identifier: number[]; } -export interface TeddsaCommitmentEntry { +export interface CommitmentEntry { identifier: number[]; commitments: number[]; } -export interface TeddsaSignatureShareEntry { +export interface SignatureShareEntry { identifier: number[]; signature_share: number[]; } -export interface TeddsaAggregateOutput { +export interface SignRound2Input { + message: number[]; + key_package: number[]; + nonces: number[]; + all_commitments: CommitmentEntry[]; +} + +export interface AggregateInput { + message: number[]; + all_commitments: CommitmentEntry[]; + all_signature_shares: SignatureShareEntry[]; + public_key_package: number[]; +} + +export interface SignatureOutput { signature: number[]; } -export interface TeddsaSignature { - signature: string; +export interface VerifyInput { + message: number[]; + signature: number[]; + public_key_package: number[]; } -export interface TeddsaClientSignState { - message: Uint8Array | null; +export interface ClientSignState { + message: number[] | null; + key_package: KeyPackageRaw | null; + public_key_package: PublicKeyPackageRaw | null; nonces: number[] | null; commitments: number[] | null; - all_commitments: TeddsaCommitmentEntry[] | null; - all_signature_shares: TeddsaSignatureShareEntry[] | null; + all_commitments: CommitmentEntry[] | null; + all_signature_shares: SignatureShareEntry[] | null; } From e49accca3c82d85beb741a0804a59ccc82d75df2 Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Wed, 7 Jan 2026 17:32:28 +0900 Subject: [PATCH 2/6] o --- .../src/app/users/forgot_password/page.tsx | 8 ++-- backend/openapi/src/tss/keygen_ed25519.ts | 16 +++---- .../tss_api/src/api/wallet_ed25519/index.ts | 6 ++- .../sol/tx_sig/msg/transfer/transfer.tsx | 7 +-- .../sol/use_sol_signature_base.tsx | 5 +- embed/oko_attached/src/crypto/sign_ed25519.ts | 48 ++++++++++++++----- .../src/tx-parsers/sol/instruction.ts | 10 ++-- .../oko_attached/src/tx-parsers/sol/parser.ts | 11 +++-- embed/oko_attached/src/wasm/index.ts | 10 +++- .../src/window_msgs/get_public_key_ed25519.ts | 4 +- .../src/components/siws_widget.tsx | 5 +- .../components/spl_token_transfer_widget.tsx | 4 +- .../components/test_transactions_widget.tsx | 7 +-- sandbox/sandbox_sol/tsconfig.json | 14 ++---- sdk/oko_sdk_sol/src/constructor.ts | 5 +- sdk/oko_sdk_sol/src/emitter.ts | 5 +- sdk/oko_sdk_sol/src/wallet-standard/wallet.ts | 3 +- 17 files changed, 95 insertions(+), 73 deletions(-) diff --git a/apps/customer_dashboard/src/app/users/forgot_password/page.tsx b/apps/customer_dashboard/src/app/users/forgot_password/page.tsx index def1c27ab..8444f6541 100644 --- a/apps/customer_dashboard/src/app/users/forgot_password/page.tsx +++ b/apps/customer_dashboard/src/app/users/forgot_password/page.tsx @@ -277,9 +277,7 @@ export default function ForgotPasswordPage() { tagType="span" size="sm" weight="semibold" - color={ - !isExpired || isResending ? "tertiary" : "primary" - } + color={!isExpired || isResending ? "tertiary" : "primary"} > Resend @@ -352,7 +350,9 @@ export default function ForgotPasswordPage() { className={styles.eyeButton} onClick={() => setShowConfirm((prev) => !prev)} aria-label={ - showConfirm ? "Hide confirm password" : "Show confirm password" + showConfirm + ? "Hide confirm password" + : "Show confirm password" } > {showConfirm ? : } diff --git a/backend/openapi/src/tss/keygen_ed25519.ts b/backend/openapi/src/tss/keygen_ed25519.ts index 20100989b..aeacb79d4 100644 --- a/backend/openapi/src/tss/keygen_ed25519.ts +++ b/backend/openapi/src/tss/keygen_ed25519.ts @@ -5,16 +5,12 @@ import { registry } from "../registry"; const TeddsaKeygenOutputSchema = registry.register( "TeddsaKeygenOutput", z.object({ - key_package: z - .array(z.number()) - .openapi({ - description: "FROST KeyPackage bytes (contains secret share)", - }), - public_key_package: z - .array(z.number()) - .openapi({ - description: "Public key package bytes (shared by all participants)", - }), + key_package: z.array(z.number()).openapi({ + description: "FROST KeyPackage bytes (contains secret share)", + }), + public_key_package: z.array(z.number()).openapi({ + description: "Public key package bytes (shared by all participants)", + }), identifier: z .array(z.number()) .openapi({ description: "Participant identifier bytes" }), diff --git a/backend/tss_api/src/api/wallet_ed25519/index.ts b/backend/tss_api/src/api/wallet_ed25519/index.ts index bdb47a935..107c2a360 100644 --- a/backend/tss_api/src/api/wallet_ed25519/index.ts +++ b/backend/tss_api/src/api/wallet_ed25519/index.ts @@ -34,7 +34,11 @@ export async function getWalletEd25519PublicInfo( const { user_identifier, auth_type } = request; // Get user - const getUserRes = await getUserByEmailAndAuthType(db, user_identifier, auth_type); + const getUserRes = await getUserByEmailAndAuthType( + db, + user_identifier, + auth_type, + ); if (getUserRes.success === false) { return { success: false, diff --git a/embed/oko_attached/src/components/modal_variants/sol/tx_sig/msg/transfer/transfer.tsx b/embed/oko_attached/src/components/modal_variants/sol/tx_sig/msg/transfer/transfer.tsx index 6e933914f..4caaf3333 100644 --- a/embed/oko_attached/src/components/modal_variants/sol/tx_sig/msg/transfer/transfer.tsx +++ b/embed/oko_attached/src/components/modal_variants/sol/tx_sig/msg/transfer/transfer.tsx @@ -36,12 +36,7 @@ export const SolTransferPretty: FC = ({
- + { +): Promise< + | { success: true; signature: string } + | { success: false; error: MakeSolSigError } +> { const signatureRes = await makeSignOutputEd25519( message, ctx.keyPackage, diff --git a/embed/oko_attached/src/crypto/sign_ed25519.ts b/embed/oko_attached/src/crypto/sign_ed25519.ts index f0bf9c358..0bcdc730e 100644 --- a/embed/oko_attached/src/crypto/sign_ed25519.ts +++ b/embed/oko_attached/src/crypto/sign_ed25519.ts @@ -9,10 +9,7 @@ import type { } from "@oko-wallet/teddsa-interface"; import type { Result } from "@oko-wallet/stdlib-js"; import type { MakeSignOutputError } from "@oko-wallet/oko-sdk-core"; -import { - reqPresignEd25519, - reqSignEd25519, -} from "@oko-wallet/teddsa-api-lib"; +import { reqPresignEd25519, reqSignEd25519 } from "@oko-wallet/teddsa-api-lib"; import { TSS_V1_ENDPOINT } from "@oko-wallet-attached/requests/oko_api"; @@ -45,16 +42,25 @@ export async function makeSignOutputEd25519( } // 1. Server presign: Get server commitments (requires apiKey for session creation) - const presignRes = await reqPresignEd25519(TSS_V1_ENDPOINT, {}, apiKey, authToken); + const presignRes = await reqPresignEd25519( + TSS_V1_ENDPOINT, + {}, + apiKey, + authToken, + ); if (!presignRes.success) { return { success: false, - err: { type: "sign_fail", error: { type: "error", msg: presignRes.msg } }, + err: { + type: "sign_fail", + error: { type: "error", msg: presignRes.msg }, + }, }; } - const { session_id: sessionId, commitments_0: serverCommitment } = presignRes.data; + const { session_id: sessionId, commitments_0: serverCommitment } = + presignRes.data; if (getIsAborted()) { return { success: false, err: { type: "aborted" } }; @@ -65,7 +71,10 @@ export async function makeSignOutputEd25519( if (!round1Result.success) { return { success: false, - err: { type: "sign_fail", error: { type: "error", msg: round1Result.err } }, + err: { + type: "sign_fail", + error: { type: "error", msg: round1Result.err }, + }, }; } @@ -97,11 +106,15 @@ export async function makeSignOutputEd25519( if (!serverSignRes.success) { return { success: false, - err: { type: "sign_fail", error: { type: "error", msg: serverSignRes.msg } }, + err: { + type: "sign_fail", + error: { type: "error", msg: serverSignRes.msg }, + }, }; } - const serverSignatureShare: TeddsaSignatureShareEntry = serverSignRes.data.signature_share_0; + const serverSignatureShare: TeddsaSignatureShareEntry = + serverSignRes.data.signature_share_0; if (getIsAborted()) { return { success: false, err: { type: "aborted" } }; @@ -118,7 +131,10 @@ export async function makeSignOutputEd25519( if (!round2Result.success) { return { success: false, - err: { type: "sign_fail", error: { type: "error", msg: round2Result.err } }, + err: { + type: "sign_fail", + error: { type: "error", msg: round2Result.err }, + }, }; } @@ -143,7 +159,10 @@ export async function makeSignOutputEd25519( if (!aggregateResult.success) { return { success: false, - err: { type: "sign_fail", error: { type: "error", msg: aggregateResult.err } }, + err: { + type: "sign_fail", + error: { type: "error", msg: aggregateResult.err }, + }, }; } @@ -153,7 +172,10 @@ export async function makeSignOutputEd25519( success: false, err: { type: "sign_fail", - error: { type: "error", msg: error instanceof Error ? error.message : String(error) }, + error: { + type: "error", + msg: error instanceof Error ? error.message : String(error), + }, }, }; } diff --git a/embed/oko_attached/src/tx-parsers/sol/instruction.ts b/embed/oko_attached/src/tx-parsers/sol/instruction.ts index 58be6d9a0..b7b18565e 100644 --- a/embed/oko_attached/src/tx-parsers/sol/instruction.ts +++ b/embed/oko_attached/src/tx-parsers/sol/instruction.ts @@ -35,11 +35,11 @@ async function getParserForProgram( const parser = new SolanaFMParser(idl, programId); const instructionParser = parser.createParser(ParserType.INSTRUCTION); - if ( - instructionParser && - "parseInstructions" in instructionParser - ) { - parserCache.set(programId, instructionParser as InstructionParserInterface); + if (instructionParser && "parseInstructions" in instructionParser) { + parserCache.set( + programId, + instructionParser as InstructionParserInterface, + ); return instructionParser as InstructionParserInterface; } } catch { diff --git a/embed/oko_attached/src/tx-parsers/sol/parser.ts b/embed/oko_attached/src/tx-parsers/sol/parser.ts index debfd67dd..b8bef1436 100644 --- a/embed/oko_attached/src/tx-parsers/sol/parser.ts +++ b/embed/oko_attached/src/tx-parsers/sol/parser.ts @@ -1,7 +1,4 @@ -import { - Transaction, - VersionedTransaction, -} from "@solana/web3.js"; +import { Transaction, VersionedTransaction } from "@solana/web3.js"; import { parseInstruction } from "./instruction"; import type { ParsedTransaction, @@ -76,7 +73,11 @@ async function parseVersionedTransaction( isWritable: message.isAccountWritable(idx), })); - const result = await parseInstruction(programId, bs58.encode(ix.data), accounts); + const result = await parseInstruction( + programId, + bs58.encode(ix.data), + accounts, + ); if (result.success) { instructions.push(result.data); diff --git a/embed/oko_attached/src/wasm/index.ts b/embed/oko_attached/src/wasm/index.ts index 17d3beed0..e9e5148cb 100644 --- a/embed/oko_attached/src/wasm/index.ts +++ b/embed/oko_attached/src/wasm/index.ts @@ -15,9 +15,15 @@ export async function initKeplrWasm() { } try { - await initFrostWasm(frostWasmModule, "/pkg/frost_ed25519_keplr_wasm_bg.wasm"); + await initFrostWasm( + frostWasmModule, + "/pkg/frost_ed25519_keplr_wasm_bg.wasm", + ); console.log("[attached] frost-ed25519 WASM initialized"); } catch (err) { - console.error("[attached] Error initializing frost-ed25519 WASM, err: %s", err); + console.error( + "[attached] Error initializing frost-ed25519 WASM, err: %s", + err, + ); } } diff --git a/embed/oko_attached/src/window_msgs/get_public_key_ed25519.ts b/embed/oko_attached/src/window_msgs/get_public_key_ed25519.ts index 7029d1a3c..3a3b55725 100644 --- a/embed/oko_attached/src/window_msgs/get_public_key_ed25519.ts +++ b/embed/oko_attached/src/window_msgs/get_public_key_ed25519.ts @@ -6,7 +6,9 @@ import type { MsgEventContext } from "./types"; export async function handleGetPublicKeyEd25519(ctx: MsgEventContext) { const { port, hostOrigin } = ctx; - const keyPackageEd25519 = useAppState.getState().getKeyPackageEd25519(hostOrigin); + const keyPackageEd25519 = useAppState + .getState() + .getKeyPackageEd25519(hostOrigin); let payload: OkoWalletMsgGetPublicKeyEd25519Ack["payload"]; if (keyPackageEd25519?.publicKey) { diff --git a/sandbox/sandbox_sol/src/components/siws_widget.tsx b/sandbox/sandbox_sol/src/components/siws_widget.tsx index f871e810c..0a4e799c2 100644 --- a/sandbox/sandbox_sol/src/components/siws_widget.tsx +++ b/sandbox/sandbox_sol/src/components/siws_widget.tsx @@ -2,10 +2,7 @@ import { useState } from "react"; import { useSdkStore } from "@/store/sdk"; -import { - OkoStandardWallet, - buildSignInMessage, -} from "@oko-wallet/oko-sdk-sol"; +import { OkoStandardWallet, buildSignInMessage } from "@oko-wallet/oko-sdk-sol"; import bs58 from "bs58"; import Button from "./Button"; diff --git a/sandbox/sandbox_sol/src/components/spl_token_transfer_widget.tsx b/sandbox/sandbox_sol/src/components/spl_token_transfer_widget.tsx index aefd69985..b4d368e40 100644 --- a/sandbox/sandbox_sol/src/components/spl_token_transfer_widget.tsx +++ b/sandbox/sandbox_sol/src/components/spl_token_transfer_widget.tsx @@ -227,7 +227,9 @@ export function SplTokenTransferWidget() { className="w-full bg-widget-field border border-widget-border rounded-2xl px-6 py-5 text-sm focus:outline-none focus:border-widget-border-hover focus:ring-2 focus:ring-widget-border-hover transition-all" value={selectedToken.mint} onChange={(e) => { - const token = DEVNET_TOKENS.find((t) => t.mint === e.target.value); + const token = DEVNET_TOKENS.find( + (t) => t.mint === e.target.value, + ); if (token) setSelectedToken(token); }} > diff --git a/sandbox/sandbox_sol/src/components/test_transactions_widget.tsx b/sandbox/sandbox_sol/src/components/test_transactions_widget.tsx index 13ed17077..71c8db4a2 100644 --- a/sandbox/sandbox_sol/src/components/test_transactions_widget.tsx +++ b/sandbox/sandbox_sol/src/components/test_transactions_widget.tsx @@ -63,12 +63,9 @@ export function TestTransactionsWidget() { if (!publicKey) throw new Error("No public key"); const fromPubkey = new PublicKey(publicKey); - const { blockhash } = - await DEVNET_CONNECTION.getLatestBlockhash(); + const { blockhash } = await DEVNET_CONNECTION.getLatestBlockhash(); - const testRecipient = new PublicKey( - "11111111111111111111111111111112", - ); + const testRecipient = new PublicKey("11111111111111111111111111111112"); switch (selectedTx) { case "sol_transfer": { diff --git a/sandbox/sandbox_sol/tsconfig.json b/sandbox/sandbox_sol/tsconfig.json index b575f7dac..19c51c836 100644 --- a/sandbox/sandbox_sol/tsconfig.json +++ b/sandbox/sandbox_sol/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "ES2017", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -23,9 +19,7 @@ } ], "paths": { - "@/*": [ - "./src/*" - ] + "@/*": ["./src/*"] } }, "include": [ @@ -35,7 +29,5 @@ ".next/types/**/*.ts", ".next/dev/types/**/*.ts" ], - "exclude": [ - "node_modules" - ] + "exclude": ["node_modules"] } diff --git a/sdk/oko_sdk_sol/src/constructor.ts b/sdk/oko_sdk_sol/src/constructor.ts index c6b8a0c3a..9fb3b295a 100644 --- a/sdk/oko_sdk_sol/src/constructor.ts +++ b/sdk/oko_sdk_sol/src/constructor.ts @@ -52,7 +52,10 @@ export const OkoSolWallet = function ( this._emitter.emit("accountChanged", newPublicKey); } } catch (e) { - console.warn("[Sol SDK] Failed to get Ed25519 key on account change:", e); + console.warn( + "[Sol SDK] Failed to get Ed25519 key on account change:", + e, + ); } } }; diff --git a/sdk/oko_sdk_sol/src/emitter.ts b/sdk/oko_sdk_sol/src/emitter.ts index 02d7391ff..635565148 100644 --- a/sdk/oko_sdk_sol/src/emitter.ts +++ b/sdk/oko_sdk_sol/src/emitter.ts @@ -7,7 +7,10 @@ import type { } from "./types"; export class SolWalletEventEmitter extends EventEmitter { - on(event: K, handler: SolWalletEventHandler): this { + on( + event: K, + handler: SolWalletEventHandler, + ): this { return super.on(event, handler as (...args: unknown[]) => void); } diff --git a/sdk/oko_sdk_sol/src/wallet-standard/wallet.ts b/sdk/oko_sdk_sol/src/wallet-standard/wallet.ts index de6c683cb..98ac11590 100644 --- a/sdk/oko_sdk_sol/src/wallet-standard/wallet.ts +++ b/sdk/oko_sdk_sol/src/wallet-standard/wallet.ts @@ -61,8 +61,7 @@ export class OkoStandardWallet implements Wallet { version: "1.0.0", connect: async () => { // Check if user is signed in - let existingKey = - await this.#wallet.okoWallet.getPublicKeyEd25519(); + let existingKey = await this.#wallet.okoWallet.getPublicKeyEd25519(); if (!existingKey) { // Trigger OAuth sign-in From de8c2b7b168a39fb262de450dd8ffb15acfa8827 Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Wed, 7 Jan 2026 17:40:44 +0900 Subject: [PATCH 3/6] o --- crypto/teddsa/teddsa_interface/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/teddsa/teddsa_interface/package.json b/crypto/teddsa/teddsa_interface/package.json index 800bd1d55..49d2754e8 100644 --- a/crypto/teddsa/teddsa_interface/package.json +++ b/crypto/teddsa/teddsa_interface/package.json @@ -12,7 +12,7 @@ "build": "yarn clean && tsc && tsc-alias" }, "dependencies": { - "@oko-wallet/bytes": "workspace:*" + "@oko-wallet/bytes": "0.0.3-alpha.62" }, "devDependencies": { "@types/node": "^24.10.1", From 29fd7cc6be133e6e53c80d50632e19f6adc82f30 Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Wed, 7 Jan 2026 17:55:28 +0900 Subject: [PATCH 4/6] o --- crypto/teddsa/teddsa_interface/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/teddsa/teddsa_interface/package.json b/crypto/teddsa/teddsa_interface/package.json index 49d2754e8..7dd86771c 100644 --- a/crypto/teddsa/teddsa_interface/package.json +++ b/crypto/teddsa/teddsa_interface/package.json @@ -12,7 +12,7 @@ "build": "yarn clean && tsc && tsc-alias" }, "dependencies": { - "@oko-wallet/bytes": "0.0.3-alpha.62" + "@oko-wallet/bytes": "^0.0.3-alpha.65" }, "devDependencies": { "@types/node": "^24.10.1", From 0926f3590d1e23c960b3dafdb863e8167d7d8f0e Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Wed, 7 Jan 2026 18:07:50 +0900 Subject: [PATCH 5/6] project: fix type errors --- .../tss_api/src/api/presign_ed25519/index.ts | 4 +- backend/tss_api/src/api/sign_ed25519/index.ts | 10 +-- common/oko_types/src/tss/keygen_ed25519.ts | 15 +++-- common/oko_types/src/tss/sign_ed25519.ts | 38 ++++------- .../teddsa/teddsa_addon/src/server/index.ts | 53 +++++++++++---- crypto/teddsa/teddsa_hooks/src/keygen.ts | 19 +++--- crypto/teddsa/teddsa_hooks/src/sign.ts | 67 ++++++++++++------- crypto/teddsa/teddsa_hooks/src/types.ts | 5 +- .../oko_attached/src/crypto/keygen_ed25519.ts | 40 +++++++++-- embed/oko_attached/src/crypto/sign_ed25519.ts | 35 +++------- .../src/window_msgs/oauth_info_pass/user.ts | 8 +-- 11 files changed, 176 insertions(+), 118 deletions(-) diff --git a/backend/tss_api/src/api/presign_ed25519/index.ts b/backend/tss_api/src/api/presign_ed25519/index.ts index 6e88c87e4..71c649019 100644 --- a/backend/tss_api/src/api/presign_ed25519/index.ts +++ b/backend/tss_api/src/api/presign_ed25519/index.ts @@ -11,7 +11,7 @@ import { TssStageType, PresignEd25519StageStatus, } from "@oko-wallet/oko-types/tss"; -import type { TeddsaKeygenOutput } from "@oko-wallet/teddsa-interface"; +import type { KeygenEd25519Output } from "@oko-wallet/oko-types/tss"; import type { OkoApiResponse } from "@oko-wallet/oko-types/api_response"; import { Pool } from "pg"; import { decryptDataAsync } from "@oko-wallet/crypto-js/node"; @@ -54,7 +54,7 @@ export async function runPresignEd25519( encryptedShare, encryptionSecret, ); - const keygenOutput: TeddsaKeygenOutput = JSON.parse(decryptedShare); + const keygenOutput: KeygenEd25519Output = JSON.parse(decryptedShare); // Generate nonces and commitments (Round 1 without message) const round1Result = runSignRound1Ed25519( diff --git a/backend/tss_api/src/api/sign_ed25519/index.ts b/backend/tss_api/src/api/sign_ed25519/index.ts index 9d5a4ac16..38c9eed68 100644 --- a/backend/tss_api/src/api/sign_ed25519/index.ts +++ b/backend/tss_api/src/api/sign_ed25519/index.ts @@ -21,7 +21,7 @@ import { PresignEd25519StageStatus, TssSessionState, } from "@oko-wallet/oko-types/tss"; -import type { TeddsaKeygenOutput } from "@oko-wallet/teddsa-interface"; +import type { KeygenEd25519Output } from "@oko-wallet/oko-types/tss"; import type { OkoApiResponse } from "@oko-wallet/oko-types/api_response"; import { Pool } from "pg"; import { decryptDataAsync } from "@oko-wallet/crypto-js/node"; @@ -73,7 +73,7 @@ export async function runSignEd25519Round1( encryptedShare, encryptionSecret, ); - const keygenOutput: TeddsaKeygenOutput = JSON.parse(decryptedShare); + const keygenOutput: KeygenEd25519Output = JSON.parse(decryptedShare); const round1Result = runSignRound1Ed25519( new Uint8Array(keygenOutput.key_package), @@ -219,7 +219,7 @@ export async function runSignEd25519Round2( encryptedShare, encryptionSecret, ); - const keygenOutput: TeddsaKeygenOutput = JSON.parse(decryptedShare); + const keygenOutput: KeygenEd25519Output = JSON.parse(decryptedShare); const serverCommitment = { identifier, @@ -360,7 +360,7 @@ export async function runSignEd25519( encryptedShare, encryptionSecret, ); - const keygenOutput: TeddsaKeygenOutput = JSON.parse(decryptedShare); + const keygenOutput: KeygenEd25519Output = JSON.parse(decryptedShare); const serverCommitment = { identifier, @@ -454,7 +454,7 @@ export async function runSignEd25519Aggregate( encryptedShare, encryptionSecret, ); - const keygenOutput: TeddsaKeygenOutput = JSON.parse(decryptedShare); + const keygenOutput: KeygenEd25519Output = JSON.parse(decryptedShare); const aggregateResult = runAggregateEd25519( new Uint8Array(msg), diff --git a/common/oko_types/src/tss/keygen_ed25519.ts b/common/oko_types/src/tss/keygen_ed25519.ts index cd885fb4a..160090098 100644 --- a/common/oko_types/src/tss/keygen_ed25519.ts +++ b/common/oko_types/src/tss/keygen_ed25519.ts @@ -1,21 +1,26 @@ -import type { TeddsaKeygenOutput } from "@oko-wallet/teddsa-interface"; - import type { AuthType, OAuthRequest } from "../auth"; -export interface TeddsaKeygenOutputWithPublicKey extends TeddsaKeygenOutput { +// NOTE: This matches NAPI addon's KeygenOutput structure (serialized bytes) +export interface KeygenEd25519Output { + key_package: number[]; + public_key_package: number[]; + identifier: number[]; +} + +export interface KeygenEd25519OutputWithPublicKey extends KeygenEd25519Output { public_key: number[]; } export interface KeygenEd25519Request { auth_type: AuthType; user_identifier: string; - keygen_2: TeddsaKeygenOutputWithPublicKey; + keygen_2: KeygenEd25519OutputWithPublicKey; email?: string; name?: string; } export type KeygenEd25519Body = { - keygen_2: TeddsaKeygenOutputWithPublicKey; + keygen_2: KeygenEd25519OutputWithPublicKey; }; export type KeygenEd25519RequestBody = OAuthRequest; diff --git a/common/oko_types/src/tss/sign_ed25519.ts b/common/oko_types/src/tss/sign_ed25519.ts index f1ab56146..4a28f1445 100644 --- a/common/oko_types/src/tss/sign_ed25519.ts +++ b/common/oko_types/src/tss/sign_ed25519.ts @@ -1,8 +1,6 @@ import type { - TeddsaSignRound1Output, - TeddsaSignRound2Output, - TeddsaCommitmentEntry, - TeddsaSignatureShareEntry, + CommitmentEntry, + SignatureShareEntry, } from "@oko-wallet/teddsa-interface"; export interface SignEd25519Round1Request { @@ -14,7 +12,7 @@ export interface SignEd25519Round1Request { export interface SignEd25519Round1Response { session_id: string; - commitments_0: TeddsaCommitmentEntry; + commitments_0: CommitmentEntry; } export type SignEd25519Round1Body = { @@ -25,24 +23,24 @@ export interface SignEd25519Round2Request { email: string; wallet_id: string; session_id: string; - commitments_1: TeddsaCommitmentEntry; + commitments_1: CommitmentEntry; } export interface SignEd25519Round2Response { - signature_share_0: TeddsaSignatureShareEntry; + signature_share_0: SignatureShareEntry; } export type SignEd25519Round2Body = { session_id: string; - commitments_1: TeddsaCommitmentEntry; + commitments_1: CommitmentEntry; }; export interface SignEd25519AggregateRequest { email: string; wallet_id: string; msg: number[]; - all_commitments: TeddsaCommitmentEntry[]; - all_signature_shares: TeddsaSignatureShareEntry[]; + all_commitments: CommitmentEntry[]; + all_signature_shares: SignatureShareEntry[]; } export interface SignEd25519AggregateResponse { @@ -51,8 +49,8 @@ export interface SignEd25519AggregateResponse { export type SignEd25519AggregateBody = { msg: number[]; - all_commitments: TeddsaCommitmentEntry[]; - all_signature_shares: TeddsaSignatureShareEntry[]; + all_commitments: CommitmentEntry[]; + all_signature_shares: SignatureShareEntry[]; }; export interface SignEd25519ServerState { @@ -60,10 +58,6 @@ export interface SignEd25519ServerState { identifier: number[]; } -// ============================================ -// Presign Ed25519 Types (message-independent) -// ============================================ - export interface PresignEd25519Request { email: string; wallet_id: string; @@ -72,29 +66,25 @@ export interface PresignEd25519Request { export interface PresignEd25519Response { session_id: string; - commitments_0: TeddsaCommitmentEntry; + commitments_0: CommitmentEntry; } export type PresignEd25519Body = Record; -// ============================================ -// Sign Ed25519 Types (using presign session) -// ============================================ - export interface SignEd25519Request { email: string; wallet_id: string; session_id: string; msg: number[]; - commitments_1: TeddsaCommitmentEntry; + commitments_1: CommitmentEntry; } export interface SignEd25519Response { - signature_share_0: TeddsaSignatureShareEntry; + signature_share_0: SignatureShareEntry; } export type SignEd25519Body = { session_id: string; msg: number[]; - commitments_1: TeddsaCommitmentEntry; + commitments_1: CommitmentEntry; }; diff --git a/crypto/teddsa/teddsa_addon/src/server/index.ts b/crypto/teddsa/teddsa_addon/src/server/index.ts index 668fe475e..9c7c73d1a 100644 --- a/crypto/teddsa/teddsa_addon/src/server/index.ts +++ b/crypto/teddsa/teddsa_addon/src/server/index.ts @@ -1,10 +1,7 @@ +// NOTE: NAPI addon returns serialized bytes, different from WASM Raw types import type { - TeddsaCentralizedKeygenOutput, - TeddsaSignRound1Output, - TeddsaSignRound2Output, - TeddsaAggregateOutput, - TeddsaCommitmentEntry, - TeddsaSignatureShareEntry, + CommitmentEntry, + SignatureShareEntry, } from "@oko-wallet/teddsa-interface"; import { @@ -16,19 +13,47 @@ import { napiVerifyEd25519, } from "../../addon/index.js"; -export function runKeygenCentralizedEd25519(): TeddsaCentralizedKeygenOutput { +// NOTE: NAPI-specific types (serialized bytes format) +export interface NapiKeygenOutput { + key_package: number[]; + public_key_package: number[]; + identifier: number[]; +} + +export interface NapiCentralizedKeygenOutput { + private_key: number[]; + keygen_outputs: NapiKeygenOutput[]; + public_key: number[]; +} + +export interface NapiSigningCommitmentOutput { + nonces: number[]; + commitments: number[]; + identifier: number[]; +} + +export interface NapiSignatureShareOutput { + signature_share: number[]; + identifier: number[]; +} + +export interface NapiSignatureOutput { + signature: number[]; +} + +export function runKeygenCentralizedEd25519(): NapiCentralizedKeygenOutput { return napiKeygenCentralizedEd25519(); } export function runKeygenImportEd25519( secretKey: Uint8Array, -): TeddsaCentralizedKeygenOutput { +): NapiCentralizedKeygenOutput { return napiKeygenImportEd25519(Array.from(secretKey)); } export function runSignRound1Ed25519( keyPackage: Uint8Array, -): TeddsaSignRound1Output { +): NapiSigningCommitmentOutput { return napiSignRound1Ed25519(Array.from(keyPackage)); } @@ -36,8 +61,8 @@ export function runSignRound2Ed25519( message: Uint8Array, keyPackage: Uint8Array, nonces: Uint8Array, - allCommitments: TeddsaCommitmentEntry[], -): TeddsaSignRound2Output { + allCommitments: CommitmentEntry[], +): NapiSignatureShareOutput { return napiSignRound2Ed25519( Array.from(message), Array.from(keyPackage), @@ -48,10 +73,10 @@ export function runSignRound2Ed25519( export function runAggregateEd25519( message: Uint8Array, - allCommitments: TeddsaCommitmentEntry[], - allSignatureShares: TeddsaSignatureShareEntry[], + allCommitments: CommitmentEntry[], + allSignatureShares: SignatureShareEntry[], publicKeyPackage: Uint8Array, -): TeddsaAggregateOutput { +): NapiSignatureOutput { return napiAggregateEd25519( Array.from(message), allCommitments, diff --git a/crypto/teddsa/teddsa_hooks/src/keygen.ts b/crypto/teddsa/teddsa_hooks/src/keygen.ts index 9dd76a808..f937f4ec7 100644 --- a/crypto/teddsa/teddsa_hooks/src/keygen.ts +++ b/crypto/teddsa/teddsa_hooks/src/keygen.ts @@ -2,7 +2,7 @@ import { wasmModule } from "@oko-wallet/frost-ed25519-keplr-wasm"; import { Bytes } from "@oko-wallet/bytes"; import type { Bytes32 } from "@oko-wallet/bytes"; import type { Result } from "@oko-wallet/stdlib-js"; -import type { TeddsaCentralizedKeygenOutput } from "@oko-wallet/teddsa-interface"; +import type { CentralizedKeygenOutput } from "@oko-wallet/teddsa-interface"; import type { TeddsaKeygenResult, TeddsaKeygenOutputBytes } from "./types"; @@ -10,7 +10,7 @@ export async function importExternalSecretKeyEd25519( secretKey: Bytes32, ): Promise> { try { - const keygenOutput: TeddsaCentralizedKeygenOutput = + const keygenOutput: CentralizedKeygenOutput = wasmModule.cli_keygen_import_ed25519([...secretKey.toUint8Array()]); return processKeygenOutput(keygenOutput); @@ -26,7 +26,7 @@ export async function runTeddsaKeygen(): Promise< Result > { try { - const keygenOutput: TeddsaCentralizedKeygenOutput = + const keygenOutput: CentralizedKeygenOutput = wasmModule.cli_keygen_centralized_ed25519(); return processKeygenOutput(keygenOutput); @@ -39,12 +39,13 @@ export async function runTeddsaKeygen(): Promise< } function processKeygenOutput( - keygenOutput: TeddsaCentralizedKeygenOutput, + keygenOutput: CentralizedKeygenOutput, ): Result { const [keygen_1_raw, keygen_2_raw] = keygenOutput.keygen_outputs; + const publicKeyPackage = keygenOutput.public_key_package; const publicKeyBytesRes = Bytes.fromUint8Array( - new Uint8Array(keygenOutput.public_key), + new Uint8Array(publicKeyPackage.verifying_key), 32, ); if (publicKeyBytesRes.success === false) { @@ -55,15 +56,15 @@ function processKeygenOutput( } const keygen_1: TeddsaKeygenOutputBytes = { - key_package: new Uint8Array(keygen_1_raw.key_package), - public_key_package: new Uint8Array(keygen_1_raw.public_key_package), + key_package: keygen_1_raw, + public_key_package: publicKeyPackage, identifier: new Uint8Array(keygen_1_raw.identifier), public_key: publicKeyBytesRes.data, }; const keygen_2: TeddsaKeygenOutputBytes = { - key_package: new Uint8Array(keygen_2_raw.key_package), - public_key_package: new Uint8Array(keygen_2_raw.public_key_package), + key_package: keygen_2_raw, + public_key_package: publicKeyPackage, identifier: new Uint8Array(keygen_2_raw.identifier), public_key: publicKeyBytesRes.data, }; diff --git a/crypto/teddsa/teddsa_hooks/src/sign.ts b/crypto/teddsa/teddsa_hooks/src/sign.ts index da69b159c..786a69106 100644 --- a/crypto/teddsa/teddsa_hooks/src/sign.ts +++ b/crypto/teddsa/teddsa_hooks/src/sign.ts @@ -1,11 +1,13 @@ import { wasmModule } from "@oko-wallet/frost-ed25519-keplr-wasm"; import type { Result } from "@oko-wallet/stdlib-js"; import type { - TeddsaSignRound1Output, - TeddsaSignRound2Output, - TeddsaAggregateOutput, - TeddsaCommitmentEntry, - TeddsaSignatureShareEntry, + SigningCommitmentOutput, + SignatureShareOutput, + SignatureOutput, + CommitmentEntry, + SignatureShareEntry, + KeyPackageRaw, + PublicKeyPackageRaw, } from "@oko-wallet/teddsa-interface"; import type { TeddsaKeygenOutputBytes } from "./types"; @@ -14,13 +16,29 @@ export type TeddsaSignError = | { type: "aborted" } | { type: "error"; msg: string }; +function serializeKeyPackage(pkg: KeyPackageRaw): number[] { + const result: number[] = []; + result.push(...pkg.identifier); + result.push(...pkg.signing_share); + result.push(...pkg.verifying_share); + result.push(...pkg.verifying_key); + result.push(pkg.min_signers & 0xff); + result.push((pkg.min_signers >> 8) & 0xff); + return result; +} + +function serializePublicKeyPackage(pkg: PublicKeyPackageRaw): number[] { + const json = JSON.stringify(pkg); + return Array.from(new TextEncoder().encode(json)); +} + export function teddsaSignRound1( - keyPackage: Uint8Array, -): Result { + keyPackage: KeyPackageRaw, +): Result { try { - const result: TeddsaSignRound1Output = wasmModule.cli_sign_round1_ed25519([ - ...keyPackage, - ]); + const serialized = serializeKeyPackage(keyPackage); + const result: SigningCommitmentOutput = + wasmModule.cli_sign_round1_ed25519(serialized); return { success: true, data: result }; } catch (error: any) { return { success: false, err: String(error) }; @@ -29,18 +47,18 @@ export function teddsaSignRound1( export function teddsaSignRound2( message: Uint8Array, - keyPackage: Uint8Array, + keyPackage: KeyPackageRaw, nonces: Uint8Array, - allCommitments: TeddsaCommitmentEntry[], -): Result { + allCommitments: CommitmentEntry[], +): Result { try { const input = { message: [...message], - key_package: [...keyPackage], + key_package: serializeKeyPackage(keyPackage), nonces: [...nonces], all_commitments: allCommitments, }; - const result: TeddsaSignRound2Output = + const result: SignatureShareOutput = wasmModule.cli_sign_round2_ed25519(input); return { success: true, data: result }; } catch (error: any) { @@ -50,19 +68,18 @@ export function teddsaSignRound2( export function teddsaAggregate( message: Uint8Array, - allCommitments: TeddsaCommitmentEntry[], - allSignatureShares: TeddsaSignatureShareEntry[], - publicKeyPackage: Uint8Array, + allCommitments: CommitmentEntry[], + allSignatureShares: SignatureShareEntry[], + publicKeyPackage: PublicKeyPackageRaw, ): Result { try { const input = { message: [...message], all_commitments: allCommitments, all_signature_shares: allSignatureShares, - public_key_package: [...publicKeyPackage], + public_key_package: serializePublicKeyPackage(publicKeyPackage), }; - const result: TeddsaAggregateOutput = - wasmModule.cli_aggregate_ed25519(input); + const result: SignatureOutput = wasmModule.cli_aggregate_ed25519(input); return { success: true, data: new Uint8Array(result.signature) }; } catch (error: any) { return { success: false, err: String(error) }; @@ -72,13 +89,13 @@ export function teddsaAggregate( export function teddsaVerify( message: Uint8Array, signature: Uint8Array, - publicKeyPackage: Uint8Array, + publicKeyPackage: PublicKeyPackageRaw, ): Result { try { const input = { message: [...message], signature: [...signature], - public_key_package: [...publicKeyPackage], + public_key_package: serializePublicKeyPackage(publicKeyPackage), }; const isValid: boolean = wasmModule.cli_verify_ed25519(input); return { success: true, data: isValid }; @@ -103,7 +120,7 @@ export async function runTeddsaSignLocal( return { success: false, err: { type: "error", msg: round1_2.err } }; } - const allCommitments: TeddsaCommitmentEntry[] = [ + const allCommitments: CommitmentEntry[] = [ { identifier: round1_1.data.identifier, commitments: round1_1.data.commitments, @@ -134,7 +151,7 @@ export async function runTeddsaSignLocal( return { success: false, err: { type: "error", msg: round2_2.err } }; } - const allSignatureShares: TeddsaSignatureShareEntry[] = [ + const allSignatureShares: SignatureShareEntry[] = [ { identifier: round2_1.data.identifier, signature_share: round2_1.data.signature_share, diff --git a/crypto/teddsa/teddsa_hooks/src/types.ts b/crypto/teddsa/teddsa_hooks/src/types.ts index 61c4ae498..573e666e6 100644 --- a/crypto/teddsa/teddsa_hooks/src/types.ts +++ b/crypto/teddsa/teddsa_hooks/src/types.ts @@ -1,8 +1,9 @@ import type { Bytes32 } from "@oko-wallet/bytes"; +import type { KeyPackageRaw, PublicKeyPackageRaw } from "@oko-wallet/teddsa-interface"; export interface TeddsaKeygenOutputBytes { - key_package: Uint8Array; - public_key_package: Uint8Array; + key_package: KeyPackageRaw; + public_key_package: PublicKeyPackageRaw; identifier: Uint8Array; public_key: Bytes32; } diff --git a/embed/oko_attached/src/crypto/keygen_ed25519.ts b/embed/oko_attached/src/crypto/keygen_ed25519.ts index cc09eadbd..3db05bf81 100644 --- a/embed/oko_attached/src/crypto/keygen_ed25519.ts +++ b/embed/oko_attached/src/crypto/keygen_ed25519.ts @@ -1,4 +1,5 @@ import type { TeddsaKeygenOutputBytes } from "@oko-wallet/teddsa-hooks"; +import type { KeyPackageRaw, PublicKeyPackageRaw } from "@oko-wallet/teddsa-interface"; import type { Result } from "@oko-wallet/stdlib-js"; import { Bytes, type Bytes32 } from "@oko-wallet/bytes"; @@ -24,12 +25,41 @@ export function uint8ArrayToHex(bytes: Uint8Array): string { .join(""); } +function keyPackageRawToJson(pkg: KeyPackageRaw): string { + return JSON.stringify(pkg); +} + +function publicKeyPackageRawToJson(pkg: PublicKeyPackageRaw): string { + return JSON.stringify(pkg); +} + +function jsonToKeyPackageRaw(json: string): KeyPackageRaw { + return JSON.parse(json) as KeyPackageRaw; +} + +function jsonToPublicKeyPackageRaw(json: string): PublicKeyPackageRaw { + return JSON.parse(json) as PublicKeyPackageRaw; +} + +function stringToHex(str: string): string { + return Array.from(new TextEncoder().encode(str)) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); +} + +function hexToString(hex: string): string { + const bytes = hexToUint8Array(hex); + return new TextDecoder().decode(bytes); +} + export function teddsaKeygenToHex( keygen: TeddsaKeygenOutputBytes, ): KeyPackageEd25519Hex { return { - keyPackage: uint8ArrayToHex(keygen.key_package), - publicKeyPackage: uint8ArrayToHex(keygen.public_key_package), + keyPackage: stringToHex(keyPackageRawToJson(keygen.key_package)), + publicKeyPackage: stringToHex( + publicKeyPackageRawToJson(keygen.public_key_package), + ), identifier: uint8ArrayToHex(keygen.identifier), publicKey: keygen.public_key.toHex(), }; @@ -47,8 +77,10 @@ export function teddsaKeygenFromHex( return { success: true, data: { - key_package: hexToUint8Array(data.keyPackage), - public_key_package: hexToUint8Array(data.publicKeyPackage), + key_package: jsonToKeyPackageRaw(hexToString(data.keyPackage)), + public_key_package: jsonToPublicKeyPackageRaw( + hexToString(data.publicKeyPackage), + ), identifier: hexToUint8Array(data.identifier), public_key: publicKeyRes.data, }, diff --git a/embed/oko_attached/src/crypto/sign_ed25519.ts b/embed/oko_attached/src/crypto/sign_ed25519.ts index 0bcdc730e..486991b65 100644 --- a/embed/oko_attached/src/crypto/sign_ed25519.ts +++ b/embed/oko_attached/src/crypto/sign_ed25519.ts @@ -4,8 +4,10 @@ import { teddsaAggregate, } from "@oko-wallet/teddsa-hooks"; import type { - TeddsaCommitmentEntry, - TeddsaSignatureShareEntry, + CommitmentEntry, + SignatureShareEntry, + KeyPackageRaw, + PublicKeyPackageRaw, } from "@oko-wallet/teddsa-interface"; import type { Result } from "@oko-wallet/stdlib-js"; import type { MakeSignOutputError } from "@oko-wallet/oko-sdk-core"; @@ -14,21 +16,11 @@ import { reqPresignEd25519, reqSignEd25519 } from "@oko-wallet/teddsa-api-lib"; import { TSS_V1_ENDPOINT } from "@oko-wallet-attached/requests/oko_api"; export interface KeyPackageEd25519 { - keyPackage: Uint8Array; - publicKeyPackage: Uint8Array; + keyPackage: KeyPackageRaw; + publicKeyPackage: PublicKeyPackageRaw; identifier: Uint8Array; } -/** - * Ed25519 signing using presign flow. - * - * Flow: - * 1. Server presign: Generate server nonces/commitments (requires apiKey) - * 2. Client round1: Generate client nonces/commitments - * 3. Server sign: Generate server signature share using presign (no apiKey needed) - * 4. Client round2: Generate client signature share - * 5. Aggregate: Combine signature shares into final signature - */ export async function makeSignOutputEd25519( message: Uint8Array, keyPackage: KeyPackageEd25519, @@ -41,7 +33,6 @@ export async function makeSignOutputEd25519( return { success: false, err: { type: "aborted" } }; } - // 1. Server presign: Get server commitments (requires apiKey for session creation) const presignRes = await reqPresignEd25519( TSS_V1_ENDPOINT, {}, @@ -66,7 +57,6 @@ export async function makeSignOutputEd25519( return { success: false, err: { type: "aborted" } }; } - // 2. Client round1: Generate client nonces and commitments const round1Result = teddsaSignRound1(keyPackage.keyPackage); if (!round1Result.success) { return { @@ -78,12 +68,12 @@ export async function makeSignOutputEd25519( }; } - const clientCommitment: TeddsaCommitmentEntry = { + const clientCommitment: CommitmentEntry = { identifier: round1Result.data.identifier, commitments: round1Result.data.commitments, }; - const allCommitments: TeddsaCommitmentEntry[] = [ + const allCommitments: CommitmentEntry[] = [ clientCommitment, serverCommitment, ].sort((a, b) => (a.identifier[0] ?? 0) - (b.identifier[0] ?? 0)); @@ -92,7 +82,6 @@ export async function makeSignOutputEd25519( return { success: false, err: { type: "aborted" } }; } - // 3. Server sign: Get server signature share using presign session (no apiKey needed) const serverSignRes = await reqSignEd25519( TSS_V1_ENDPOINT, { @@ -113,14 +102,13 @@ export async function makeSignOutputEd25519( }; } - const serverSignatureShare: TeddsaSignatureShareEntry = + const serverSignatureShare: SignatureShareEntry = serverSignRes.data.signature_share_0; if (getIsAborted()) { return { success: false, err: { type: "aborted" } }; } - // 4. Client round2: Generate client signature share const round2Result = teddsaSignRound2( message, keyPackage.keyPackage, @@ -138,17 +126,16 @@ export async function makeSignOutputEd25519( }; } - const clientSignatureShare: TeddsaSignatureShareEntry = { + const clientSignatureShare: SignatureShareEntry = { identifier: round2Result.data.identifier, signature_share: round2Result.data.signature_share, }; - const allSignatureShares: TeddsaSignatureShareEntry[] = [ + const allSignatureShares: SignatureShareEntry[] = [ clientSignatureShare, serverSignatureShare, ].sort((a, b) => (a.identifier[0] ?? 0) - (b.identifier[0] ?? 0)); - // 5. Aggregate: Combine signature shares into final signature const aggregateResult = teddsaAggregate( message, allCommitments, diff --git a/embed/oko_attached/src/window_msgs/oauth_info_pass/user.ts b/embed/oko_attached/src/window_msgs/oauth_info_pass/user.ts index 845e82072..92c01d8d0 100644 --- a/embed/oko_attached/src/window_msgs/oauth_info_pass/user.ts +++ b/embed/oko_attached/src/window_msgs/oauth_info_pass/user.ts @@ -282,8 +282,8 @@ user pk: ${signInResp.user.public_key}`, }, body: JSON.stringify({ keygen_2: { - key_package: [...ed25519Keygen2.key_package], - public_key_package: [...ed25519Keygen2.public_key_package], + key_package: ed25519Keygen2.key_package, + public_key_package: ed25519Keygen2.public_key_package, identifier: [...ed25519Keygen2.identifier], public_key: [...ed25519Keygen2.public_key.toUint8Array()], }, @@ -681,8 +681,8 @@ export async function handleNewUser( }, body: JSON.stringify({ keygen_2: { - key_package: [...ed25519Keygen2.key_package], - public_key_package: [...ed25519Keygen2.public_key_package], + key_package: ed25519Keygen2.key_package, + public_key_package: ed25519Keygen2.public_key_package, identifier: [...ed25519Keygen2.identifier], public_key: [...ed25519Keygen2.public_key.toUint8Array()], }, From 239adc63b1169df2c6d9c84bc5a4e6b395ae998a Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Wed, 7 Jan 2026 18:10:27 +0900 Subject: [PATCH 6/6] o --- yarn.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn.lock b/yarn.lock index 9260dcbab..d1437424a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11332,6 +11332,7 @@ __metadata: version: 0.0.0-use.local resolution: "@oko-wallet/teddsa-interface@workspace:crypto/teddsa/teddsa_interface" dependencies: + "@oko-wallet/bytes": "npm:^0.0.3-alpha.65" "@types/node": "npm:^24.10.1" del-cli: "npm:^6.0.0" tsc-alias: "npm:^1.8.16"