mirror of
https://github.com/xtr-dev/rondevu-client.git
synced 2025-12-13 20:33:25 +00:00
Implement crypto adapter pattern for platform independence
Adds CryptoAdapter interface with WebCryptoAdapter (browser) and
NodeCryptoAdapter (Node.js 19+) implementations.
Changes:
- Created crypto-adapter.ts interface
- Created web-crypto-adapter.ts for browser environments
- Created node-crypto-adapter.ts for Node.js environments
- Updated RondevuAPI to accept optional CryptoAdapter
- Updated Rondevu class to pass crypto adapter through
- Exported adapters and types in index.ts
- Updated README with platform support documentation
- Bumped version to 0.15.0
This allows the client library to work in both browser and Node.js
environments by providing platform-specific crypto implementations.
Example usage in Node.js:
import { Rondevu, NodeCryptoAdapter } from '@xtr-dev/rondevu-client'
const rondevu = new Rondevu({
apiUrl: 'https://api.ronde.vu',
cryptoAdapter: new NodeCryptoAdapter()
})
🤖 Generated with Claude Code
https://claude.com/claude-code
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
67
src/web-crypto-adapter.ts
Normal file
67
src/web-crypto-adapter.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Web Crypto adapter for browser environments
|
||||
*/
|
||||
|
||||
import * as ed25519 from '@noble/ed25519'
|
||||
import { CryptoAdapter, Keypair } from './crypto-adapter.js'
|
||||
|
||||
// Set SHA-512 hash function for ed25519 (required in @noble/ed25519 v3+)
|
||||
ed25519.hashes.sha512Async = async (message: Uint8Array) => {
|
||||
return new Uint8Array(await crypto.subtle.digest('SHA-512', message as BufferSource))
|
||||
}
|
||||
|
||||
/**
|
||||
* Web Crypto implementation using browser APIs
|
||||
* Uses btoa/atob for base64 encoding and crypto.getRandomValues for random bytes
|
||||
*/
|
||||
export class WebCryptoAdapter implements CryptoAdapter {
|
||||
async generateKeypair(): Promise<Keypair> {
|
||||
const privateKey = ed25519.utils.randomSecretKey()
|
||||
const publicKey = await ed25519.getPublicKeyAsync(privateKey)
|
||||
|
||||
return {
|
||||
publicKey: this.bytesToBase64(publicKey),
|
||||
privateKey: this.bytesToBase64(privateKey),
|
||||
}
|
||||
}
|
||||
|
||||
async signMessage(message: string, privateKeyBase64: string): Promise<string> {
|
||||
const privateKey = this.base64ToBytes(privateKeyBase64)
|
||||
const encoder = new TextEncoder()
|
||||
const messageBytes = encoder.encode(message)
|
||||
const signature = await ed25519.signAsync(messageBytes, privateKey)
|
||||
|
||||
return this.bytesToBase64(signature)
|
||||
}
|
||||
|
||||
async verifySignature(
|
||||
message: string,
|
||||
signatureBase64: string,
|
||||
publicKeyBase64: string
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const signature = this.base64ToBytes(signatureBase64)
|
||||
const publicKey = this.base64ToBytes(publicKeyBase64)
|
||||
const encoder = new TextEncoder()
|
||||
const messageBytes = encoder.encode(message)
|
||||
|
||||
return await ed25519.verifyAsync(signature, messageBytes, publicKey)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
bytesToBase64(bytes: Uint8Array): string {
|
||||
const binString = Array.from(bytes, byte => String.fromCodePoint(byte)).join('')
|
||||
return btoa(binString)
|
||||
}
|
||||
|
||||
base64ToBytes(base64: string): Uint8Array {
|
||||
const binString = atob(base64)
|
||||
return Uint8Array.from(binString, char => char.codePointAt(0)!)
|
||||
}
|
||||
|
||||
randomBytes(length: number): Uint8Array {
|
||||
return crypto.getRandomValues(new Uint8Array(length))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user