Skip to content

Conversation

@afeight
Copy link
Contributor

@afeight afeight commented Dec 4, 2025

Summary

Adds Swift implementations of Ed25519 and Secp256k1 cryptographic strategies, porting the functionality from the open-signer TypeScript implementation. This enables native iOS signing without relying on the WebView/iframe approach.

Ed25519Strategy (for Solana):

  • getPrivateKeyFromSeed(seed:) - Extract 32-byte private key from seed
  • getPublicKey(privateKey:) - Derive public key (handles 32 or 64-byte keys)
  • sign(privateKey:message:) - Sign arbitrary messages
  • formatPublicKey/formatSignature - Encode as base58 or hex

Secp256k1Strategy (for Ethereum):

  • getPrivateKeyFromSeed(seed:) - Derive key using SHA-256 with derivation path
  • getPublicKey(privateKey:) - Get uncompressed 65-byte public key
  • sign(privateKey:digest:) - Sign 32-byte digest with recovery byte (r+s+v format)
  • formatPublicKey/formatSignature - Encode as hex with 0x prefix

Review & Testing Checklist for Human

  • Verify cryptographic output matches TypeScript - Run both implementations with identical inputs and compare outputs. This is critical since I couldn't cross-validate during development.
  • Verify Secp256k1 recovery byte format - The implementation uses 0x1b/0x1c (27/28) matching Ethereum conventions. Confirm this matches what open-signer expects.
  • Verify Ed25519 compatibility - CryptoKit's Curve25519.Signing is used. Confirm signatures are valid Ed25519 signatures that Solana will accept.
  • Test Base58 encoding for 64-byte signatures - The existing Base58 utility was designed for 32-byte addresses. Verify it handles 64-byte signatures correctly.
  • Run CI and verify all tests pass - I couldn't run tests locally (no Swift installed).

Recommended test plan:

  1. Create a test that generates a signature using both TypeScript (open-signer) and Swift implementations with the same seed/message
  2. Verify the signatures are byte-for-byte identical
  3. Verify signatures can be validated by external tools (e.g., Solana CLI for Ed25519, ethers.js for Secp256k1)

Notes

  • The Secp256k1 derivation path bytes spell out secp256k1-derivation-path in ASCII, matching the TypeScript implementation
  • Uses existing swift-secp256k1 package dependency for low-level secp256k1 operations
  • Uses Apple's CryptoKit for Ed25519 (via Curve25519.Signing)

Link to Devin run: https://app.devin.ai/sessions/a80b8782ddae48d681d02ea7065d3b4a
Requested by: [email protected] (@afeight)

Add Swift implementations of Ed25519 and Secp256k1 cryptographic strategies
matching the TypeScript implementations in open-signer:

Ed25519Strategy:
- getPrivateKeyFromSeed(seed: Data) -> Data
- getPublicKey(privateKey: Data) -> Data
- sign(privateKey: Data, message: Data) -> Data
- formatPublicKey(publicKey: Data, encoding: KeyEncoding) -> Ed25519PublicKey
- formatSignature(signature: Data, encoding: KeyEncoding) -> Ed25519Signature

Secp256k1Strategy:
- getPrivateKeyFromSeed(seed: Data) async -> Data
- getPublicKey(privateKey: Data) -> Data
- sign(privateKey: Data, digest: Data) -> Data
- formatPublicKey(publicKey: Data) -> Secp256k1PublicKey
- formatSignature(signature: Data) -> Secp256k1Signature

Includes comprehensive unit tests for both implementations with real
cryptographic signature verification.

Co-Authored-By: [email protected] <[email protected]>
@devin-ai-integration
Copy link
Contributor

Original prompt from [email protected]
SYSTEM:
=== BEGIN THREAD HISTORY (in #proj-non-custodial-signers) ===
Austin Feight (U03BS1CLKAN): from discussion today

ATTACHMENT:"https://app.devin.ai/attachments/c5bccfe6-6aaf-40ec-888c-3a3b755da785/image.png"

Tomás Martins (U09TNM5QQ1F): thanks for putting this together <@U03BS1CLKAN> :pray:

on this whole process, which takes part inside the iframe and which happens in the native part of the SDK?

Having this in mind will really help me out lay down how we can log this whole thing on the sdks

Austin Feight (U03BS1CLKAN): Right now, everything after `[SDK] --> { request }` is handled within the WebView. Still important from mobile SDK perspective because:
1. SDK is the one doing the retries and defining the timeouts for them
2. Not pictured is “is the webview successfully loaded in the first place”
3. We should consider if the webview is helping or hurting; all of these steps we can probably do with native code and then we have a more native way to handle network latency/errors/etc. IndexedDB hasn’t proven to be super reliable

Austin Feight (U03BS1CLKAN): Re: #1
Timeouts don’t really make sense. We have an exception handler in JS to catch any errors that appear during the request handling process, so we _should_ reasonably be able to assume that we’ll always get a response back eventually, whether success or error.
However, there’s always the 1% chance that something completely unexpected happens and JS stops executing entirely. So I wanted to ask: would we know from iOS (and Android) that something critical went wrong within the webview like this?

Austin Feight (U03BS1CLKAN): Am I crazy for thinking that these steps would be relatively easy to replicate with native code?

Alberto Elias (U07DZFTNUQL): which steps specifically? the reason for loading the frame is making a TEE client-side

Austin Feight (U03BS1CLKAN): all of them
• get device ID
    ◦ check in keychain (user-scoped)
    ◦ else, generate keypair &amp; store in keychain
• get master secret
   ... (2550 chars truncated...)

@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants