Skip to content

Commit 23538be

Browse files
committed
Add workarounds for WebCrypto X25519 bugs on WebKit Linux (openpgpjs#1870)
1 parent 0c9f6ea commit 23538be

4 files changed

Lines changed: 45 additions & 12 deletions

File tree

.github/workflows/tests.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ jobs:
106106
npx playwright install --with-deps firefox
107107
108108
- name: Install WebKit # caching not possible, external shared libraries required
109-
if: ${{ matrix.runner == 'macos-latest' }} # do not install on ubuntu, since the X25519 WebCrypto implementation has issues
110109
run: npx playwright install --with-deps webkit
111110

112111
- name: Run browser tests

src/crypto/public_key/elliptic/ecdh_x.js

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,25 @@ export async function generate(algo) {
2828
case enums.publicKey.x25519:
2929
try {
3030
const webCrypto = util.getWebCrypto();
31-
const webCryptoKey = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits']);
31+
const webCryptoKey = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits'])
32+
.catch(err => {
33+
if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
34+
const newErr = new Error('Unexpected key generation issue');
35+
newErr.name = 'NotSupportedError';
36+
throw newErr;
37+
}
38+
throw err;
39+
});
3240

3341
const privateKey = await webCrypto.exportKey('jwk', webCryptoKey.privateKey);
3442
const publicKey = await webCrypto.exportKey('jwk', webCryptoKey.publicKey);
3543

44+
if (privateKey.x !== publicKey.x) { // Weird issue with Webkit on Linux: https://bugs.webkit.org/show_bug.cgi?id=289693
45+
const err = new Error('Unexpected mismatching public point');
46+
err.name = 'NotSupportedError';
47+
throw err;
48+
}
49+
3650
return {
3751
A: new Uint8Array(b64ToUint8Array(publicKey.x)),
3852
k: b64ToUint8Array(privateKey.d)
@@ -190,15 +204,29 @@ export async function generateEphemeralEncryptionMaterial(algo, recipientA) {
190204
case enums.publicKey.x25519:
191205
try {
192206
const webCrypto = util.getWebCrypto();
207+
const ephemeralKeyPair = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits'])
208+
.catch(err => {
209+
if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
210+
const newErr = new Error('Unexpected key generation issue');
211+
newErr.name = 'NotSupportedError';
212+
throw newErr;
213+
}
214+
throw err;
215+
});
216+
const ephemeralPublicKeyJwt = await webCrypto.exportKey('jwk', ephemeralKeyPair.publicKey);
217+
const ephemeralPrivateKeyJwt = await webCrypto.exportKey('jwk', ephemeralKeyPair.privateKey);
218+
if (ephemeralPrivateKeyJwt.x !== ephemeralPublicKeyJwt.x) { // Weird issue with Webkit on Linux: https://bugs.webkit.org/show_bug.cgi?id=289693
219+
const err = new Error('Unexpected mismatching public point');
220+
err.name = 'NotSupportedError';
221+
throw err;
222+
}
193223
const jwk = publicKeyToJWK(algo, recipientA);
194-
const ephemeralKeyPair = await webCrypto.generateKey('X25519', true, ['deriveKey', 'deriveBits']);
195224
const recipientPublicKey = await webCrypto.importKey('jwk', jwk, 'X25519', false, []);
196225
const sharedSecretBuffer = await webCrypto.deriveBits(
197226
{ name: 'X25519', public: recipientPublicKey },
198227
ephemeralKeyPair.privateKey,
199228
getPayloadSize(algo) * 8 // in bits
200229
);
201-
const ephemeralPublicKeyJwt = await webCrypto.exportKey('jwk', ephemeralKeyPair.publicKey);
202230
return {
203231
sharedSecret: new Uint8Array(sharedSecretBuffer),
204232
ephemeralPublicKey: new Uint8Array(b64ToUint8Array(ephemeralPublicKeyJwt.x))

src/crypto/public_key/elliptic/eddsa.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,15 @@ export async function generate(algo) {
3939
case enums.publicKey.ed25519:
4040
try {
4141
const webCrypto = util.getWebCrypto();
42-
const webCryptoKey = await webCrypto.generateKey('Ed25519', true, ['sign', 'verify']);
42+
const webCryptoKey = await webCrypto.generateKey('Ed25519', true, ['sign', 'verify'])
43+
.catch(err => {
44+
if (err.name === 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
45+
const newErr = new Error('Unexpected key generation issue');
46+
newErr.name = 'NotSupportedError';
47+
throw newErr;
48+
}
49+
throw err;
50+
});
4351

4452
const privateKey = await webCrypto.exportKey('jwk', webCryptoKey.privateKey);
4553
const publicKey = await webCrypto.exportKey('jwk', webCryptoKey.publicKey);
@@ -49,7 +57,7 @@ export async function generate(algo) {
4957
seed: b64ToUint8Array(privateKey.d, true)
5058
};
5159
} catch (err) {
52-
if (err.name !== 'NotSupportedError' && err.name !== 'OperationError') { // Temporary (hopefully) fix for WebKit on Linux
60+
if (err.name !== 'NotSupportedError') {
5361
throw err;
5462
}
5563
const seed = getRandomBytes(getPayloadSize(algo));

test/web-test-runner.config.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { existsSync } from 'fs';
2-
import { playwrightLauncher, playwright } from '@web/test-runner-playwright';
1+
import { playwrightLauncher } from '@web/test-runner-playwright';
32

43
const sharedPlaywrightCIOptions = {
54
// createBrowserContext: ({ browser }) => browser.newContext({ ignoreHTTPSErrors: true }),
65
headless: true
76
};
7+
88
export default {
99
nodeResolve: true, // to resolve npm module imports in `unittests.html`
1010
files: './test/unittests.html',
@@ -29,13 +29,11 @@ export default {
2929
...sharedPlaywrightCIOptions,
3030
product: 'firefox'
3131
}),
32-
// try setting up webkit, but ignore if not available
33-
// (e.g. on ubuntu, where we don't want to test webkit as the WebCrypto X25519 implementation has issues)
34-
existsSync(playwright.webkit.executablePath()) && playwrightLauncher({
32+
playwrightLauncher({
3533
...sharedPlaywrightCIOptions,
3634
product: 'webkit'
3735
})
38-
].filter(Boolean)
36+
]
3937
}
4038
]
4139
};

0 commit comments

Comments
 (0)