Skip to content

Commit

Permalink
Fix for key operations in Firefox
Browse files Browse the repository at this point in the history
  • Loading branch information
steveluscher committed Jan 13, 2025
1 parent 5405f81 commit 18813e3
Show file tree
Hide file tree
Showing 7 changed files with 31 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/four-parents-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@solana/keys': patch
---

Key operations now work in versions of Firefox that support `Ed25519` natively
4 changes: 3 additions & 1 deletion examples/deserialize-transaction/src/example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@ const signedBySignature = decodedTransaction.signatures[signedByAddress]!;
const sourceAddressBytes = getAddressEncoder().encode(signedByAddress);
// Then we create a public Ed25519 key with those bytes
// This is a SubtleCrypto CryptoKey object that we create with role `verify`
const signedByPublicKey = await crypto.subtle.importKey('raw', sourceAddressBytes, 'Ed25519', true, ['verify']);
const signedByPublicKey = await crypto.subtle.importKey('raw', sourceAddressBytes, { name: 'Ed25519' }, true, [
'verify',
]);
// Now we can verify the signature using that key
const verifiedSignature = await verifySignature(signedByPublicKey, signedBySignature, decodedTransaction.messageBytes);
log.info(
Expand Down
4 changes: 4 additions & 0 deletions packages/keys/src/algorithm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const ED25519_ALGORITHM_IDENTIFIER =
// Resist the temptation to convert this to a simple string; As of version 133.0.3, Firefox
// requires the object form of `AlgorithmIdentifier` and will throw a `DOMException` otherwise.
Object.freeze({ name: 'Ed25519' });
7 changes: 5 additions & 2 deletions packages/keys/src/key-pair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import {
SolanaError,
} from '@solana/errors';

import { ED25519_ALGORITHM_IDENTIFIER } from './algorithm';
import { createPrivateKeyFromBytes } from './private-key';
import { getPublicKeyFromPrivateKey } from './public-key';
import { signBytes, verifySignature } from './signatures';

export async function generateKeyPair(): Promise<CryptoKeyPair> {
await assertKeyGenerationIsAvailable();
const keyPair = await crypto.subtle.generateKey(
/* algorithm */ 'Ed25519', // Native implementation status: https://github.com/WICG/webcrypto-secure-curves/issues/20
/* algorithm */ ED25519_ALGORITHM_IDENTIFIER, // Native implementation status: https://github.com/WICG/webcrypto-secure-curves/issues/20
/* extractable */ false, // Prevents the bytes of the private key from being visible to JS.
/* allowed uses */ ['sign', 'verify'],
);
Expand All @@ -27,7 +28,9 @@ export async function createKeyPairFromBytes(bytes: ReadonlyUint8Array, extracta
throw new SolanaError(SOLANA_ERROR__KEYS__INVALID_KEY_PAIR_BYTE_LENGTH, { byteLength: bytes.byteLength });
}
const [publicKey, privateKey] = await Promise.all([
crypto.subtle.importKey('raw', bytes.slice(32), 'Ed25519', /* extractable */ true, ['verify']),
crypto.subtle.importKey('raw', bytes.slice(32), ED25519_ALGORITHM_IDENTIFIER, /* extractable */ true, [
'verify',
]),
createPrivateKeyFromBytes(bytes.slice(0, 32), extractable),
]);

Expand Down
10 changes: 9 additions & 1 deletion packages/keys/src/private-key.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ReadonlyUint8Array } from '@solana/codecs-core';
import { SOLANA_ERROR__KEYS__INVALID_PRIVATE_KEY_BYTE_LENGTH, SolanaError } from '@solana/errors';

import { ED25519_ALGORITHM_IDENTIFIER } from './algorithm';

function addPkcs8Header(bytes: ReadonlyUint8Array): ReadonlyUint8Array {
// prettier-ignore
return new Uint8Array([
Expand Down Expand Up @@ -46,5 +48,11 @@ export async function createPrivateKeyFromBytes(bytes: ReadonlyUint8Array, extra
});
}
const privateKeyBytesPkcs8 = addPkcs8Header(bytes);
return await crypto.subtle.importKey('pkcs8', privateKeyBytesPkcs8, 'Ed25519', extractable ?? false, ['sign']);
return await crypto.subtle.importKey(
'pkcs8',
privateKeyBytesPkcs8,
ED25519_ALGORITHM_IDENTIFIER,
extractable ?? false,
['sign'],
);
}
6 changes: 4 additions & 2 deletions packages/keys/src/signatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
SolanaError,
} from '@solana/errors';

import { ED25519_ALGORITHM_IDENTIFIER } from './algorithm';

export type Signature = string & { readonly __brand: unique symbol };
export type SignatureBytes = Uint8Array & { readonly __brand: unique symbol };

Expand Down Expand Up @@ -58,7 +60,7 @@ export function isSignature(putativeSignature: string): putativeSignature is Sig

export async function signBytes(key: CryptoKey, data: ReadonlyUint8Array): Promise<SignatureBytes> {
assertSigningCapabilityIsAvailable();
const signedData = await crypto.subtle.sign('Ed25519', key, data);
const signedData = await crypto.subtle.sign(ED25519_ALGORITHM_IDENTIFIER, key, data);
return new Uint8Array(signedData) as SignatureBytes;
}

Expand All @@ -73,5 +75,5 @@ export async function verifySignature(
data: ReadonlyUint8Array,
): Promise<boolean> {
assertVerificationCapabilityIsAvailable();
return await crypto.subtle.verify('Ed25519', key, signature, data);
return await crypto.subtle.verify(ED25519_ALGORITHM_IDENTIFIER, key, signature, data);
}
2 changes: 1 addition & 1 deletion packages/webcrypto-ed25519-polyfill/src/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
function isAlgorithmEd25519(putativeEd25519Algorithm: AlgorithmIdentifier): boolean {
const name =
typeof putativeEd25519Algorithm === 'string' ? putativeEd25519Algorithm : putativeEd25519Algorithm.name;
return name.localeCompare('ed25519', 'en-US', { sensitivity: 'base' }) === 0;
return name.localeCompare('Ed25519', 'en-US', { sensitivity: 'base' }) === 0;
}

export function install() {
Expand Down

0 comments on commit 18813e3

Please sign in to comment.