Skip to content

Commit 777e6d2

Browse files
authored
Merge pull request #211 from dint-dev/feature/browser_curve25519
Add BrowserEd25519 and BrowserX25519
2 parents 936f1a9 + 1ff9595 commit 777e6d2

18 files changed

+349
-53
lines changed

cryptography/lib/src/browser/_javascript_bindings.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,11 @@ extension type AlgorithmNameParams._(JSObject jsObject) {
340340
@internal
341341
extension type CryptoKey._(JSObject _) implements JSObject {
342342
external JSObject get algorithm;
343+
343344
external bool get extractable;
345+
344346
external JSAny get type;
347+
345348
external JSObject get usages;
346349
}
347350

cryptography/lib/src/browser/aes_cbc.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import 'dart:typed_data';
1818

1919
import 'package:cryptography/cryptography.dart';
2020

21-
import '_javascript_bindings.dart' show jsUint8ListFrom;
2221
import '_javascript_bindings.dart' as web_crypto;
22+
import '_javascript_bindings.dart' show jsUint8ListFrom;
2323
import 'browser_secret_key.dart';
2424

2525
/// AES-CBC implementation that uses _Web Cryptography API_ in browsers.

cryptography/lib/src/browser/aes_ctr.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import 'dart:typed_data';
1818

1919
import 'package:cryptography/cryptography.dart';
2020

21-
import '_javascript_bindings.dart' show jsUint8ListFrom;
2221
import '_javascript_bindings.dart' as web_crypto;
22+
import '_javascript_bindings.dart' show jsUint8ListFrom;
2323
import 'browser_secret_key.dart';
2424

2525
/// AES-CTR implementation that uses _Web Cryptography API_ in browsers.

cryptography/lib/src/browser/aes_gcm.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,14 @@
1414

1515
import 'dart:js_interop';
1616
import 'dart:js_interop_unsafe';
17-
1817
import 'dart:math';
1918
import 'dart:typed_data';
2019

2120
import 'package:cryptography/cryptography.dart';
2221
import 'package:cryptography/src/browser/browser_secret_key.dart';
2322

24-
import '_javascript_bindings.dart' show jsUint8ListFrom;
2523
import '_javascript_bindings.dart' as web_crypto;
24+
import '_javascript_bindings.dart' show jsUint8ListFrom;
2625

2726
/// AES-GCM implementation that uses _Web Cryptography API_ in browsers.
2827
class BrowserAesGcm extends AesGcm implements StreamingCipher {

cryptography/lib/src/browser/browser_cryptography.dart

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ import 'dart:math';
1616

1717
import 'package:cryptography/cryptography.dart';
1818
import 'package:cryptography/dart.dart';
19-
import 'package:cryptography/src/browser/rsa_pss.dart';
20-
import 'package:cryptography/src/browser/rsa_ssa_pkcs1v15.dart';
2119
import 'package:meta/meta.dart';
2220

2321
import '_javascript_bindings.dart' show isWebCryptoAvailable;
@@ -26,10 +24,14 @@ import 'aes_ctr.dart';
2624
import 'aes_gcm.dart';
2725
import 'ecdh.dart';
2826
import 'ecdsa.dart';
27+
import 'ed25519.dart';
2928
import 'hash.dart';
3029
import 'hkdf.dart';
3130
import 'hmac.dart';
3231
import 'pbkdf2.dart';
32+
import 'rsa_pss.dart';
33+
import 'rsa_ssa_pkcs1v15.dart';
34+
import 'x25519.dart';
3335

3436
class BrowserCryptography extends DartCryptography {
3537
// Documented in browser_cryptography_when_not_browser.dart
@@ -42,10 +44,10 @@ class BrowserCryptography extends DartCryptography {
4244
static bool isDisabledForTesting = false;
4345

4446
// Documented in browser_cryptography_when_not_browser.dart
45-
static bool get isSupported => isWebCryptoAvailable && !isDisabledForTesting;
47+
static bool get isRunningInWasm => (0 as num) is! double;
4648

4749
// Documented in browser_cryptography_when_not_browser.dart
48-
static bool get isRunningInWasm => (0 as num) is! double;
50+
static bool get isSupported => isWebCryptoAvailable && !isDisabledForTesting;
4951

5052
final Random? _random;
5153

@@ -54,6 +56,11 @@ class BrowserCryptography extends DartCryptography {
5456
super.random,
5557
}) : _random = random;
5658

59+
bool get _isDeterministicTest {
60+
final random = _random;
61+
return random is SecureRandom && !random.isSecure;
62+
}
63+
5764
@override
5865
AesCbc aesCbc({
5966
required MacAlgorithm macAlgorithm,
@@ -183,6 +190,15 @@ class BrowserCryptography extends DartCryptography {
183190
return super.ecdsaP521(hashAlgorithm);
184191
}
185192

193+
@override
194+
Ed25519 ed25519() {
195+
final fallback = super.ed25519();
196+
if (isSupported && !_isDeterministicTest) {
197+
return BrowserEd25519(fallback: fallback);
198+
}
199+
return fallback;
200+
}
201+
186202
@override
187203
Hkdf hkdf({required Hmac hmac, required int outputLength}) {
188204
if (isSupported) {
@@ -324,4 +340,12 @@ class BrowserCryptography extends DartCryptography {
324340
BrowserCryptography withRandom(Random? random) {
325341
return BrowserCryptography(random: random);
326342
}
343+
344+
@override
345+
X25519 x25519() {
346+
if (isSupported && !_isDeterministicTest) {
347+
return const BrowserX25519(fallback: DartX25519());
348+
}
349+
return super.x25519();
350+
}
327351
}

cryptography/lib/src/browser/browser_cryptography_when_not_browser.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ class BrowserCryptography extends DartCryptography {
6666
@visibleForTesting
6767
static bool isDisabledForTesting = false;
6868

69+
/// Whether WASM is used.
70+
static bool get isRunningInWasm => false;
71+
6972
/// Whether Web Cryptography is supported in this platform.
7073
///
7174
/// Browsers support Web Cryptography only in
@@ -77,9 +80,6 @@ class BrowserCryptography extends DartCryptography {
7780
/// always available.
7881
static bool get isSupported => false;
7982

80-
/// Whether WASM is used.
81-
static bool get isRunningInWasm => false;
82-
8383
/// Constructs an instance of [BrowserCryptography].
8484
///
8585
/// If [random] is not given, algorithms will use some cryptographically
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright 2019-2020 Gohilla.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import 'dart:js_interop';
16+
17+
import 'package:cryptography/cryptography.dart';
18+
import 'package:cryptography/dart.dart';
19+
import 'package:meta/meta.dart';
20+
21+
import '_javascript_bindings.dart' as web_crypto;
22+
23+
class BrowserEd25519 extends Ed25519 {
24+
static final _jsAlgorithm = web_crypto.AlgorithmNameParams(
25+
name: 'Ed25519'.toJS,
26+
).jsObject;
27+
28+
final Ed25519? _fallback;
29+
30+
@literal
31+
const BrowserEd25519({required Ed25519? fallback})
32+
: _fallback = fallback,
33+
super.constructor();
34+
35+
@override
36+
Future<SimpleKeyPair> newKeyPair() async {
37+
late web_crypto.Jwk jwk;
38+
try {
39+
final jsCryptoKey = await web_crypto.generateKeyWhenKeyPair(
40+
_jsAlgorithm, true.toJS, ['sign'.toJS, 'verify'.toJS].toJS);
41+
jwk = await web_crypto.exportKeyWhenJwk(jsCryptoKey.privateKey);
42+
} catch (e) {
43+
final fallback = _fallback;
44+
if (fallback != null) {
45+
return fallback.newKeyPair();
46+
}
47+
throw StateError('$runtimeType.newKeyPair(...) failed: $e');
48+
}
49+
return SimpleKeyPairData(
50+
web_crypto.base64UrlDecode(jwk.d!.toDart),
51+
publicKey: SimplePublicKey(
52+
web_crypto.base64UrlDecode(jwk.x!.toDart),
53+
type: KeyPairType.ed25519,
54+
),
55+
type: KeyPairType.ed25519,
56+
);
57+
}
58+
59+
@override
60+
Future<SimpleKeyPair> newKeyPairFromSeed(List<int> seed) {
61+
KeyPairType.ed25519.checkPrivateKeyBytesFormat(seed);
62+
return DartEd25519().newKeyPairFromSeed(seed);
63+
}
64+
65+
@override
66+
Future<Signature> sign(List<int> message, {required KeyPair keyPair}) async {
67+
try {
68+
final publicKeyFuture = keyPair.extractPublicKey();
69+
final keyPairData = await (keyPair as SimpleKeyPair).extract();
70+
final jsCryptoKey = await web_crypto.importKeyWhenJwk(
71+
web_crypto.Jwk(
72+
kty: 'OKP'.toJS,
73+
crv: 'Ed25519'.toJS,
74+
d: web_crypto.base64UrlEncode(keyPairData.bytes).toJS,
75+
x: web_crypto.base64UrlEncode(keyPairData.publicKey.bytes).toJS,
76+
),
77+
_jsAlgorithm,
78+
false.toJS,
79+
['sign'.toJS].toJS,
80+
);
81+
final signature = await web_crypto.sign(
82+
_jsAlgorithm,
83+
jsCryptoKey,
84+
web_crypto.jsUint8ListFrom(message),
85+
);
86+
return Signature(
87+
signature,
88+
publicKey: await publicKeyFuture,
89+
);
90+
} catch (e) {
91+
final fallback = _fallback;
92+
if (fallback != null) {
93+
return fallback.sign(message, keyPair: keyPair);
94+
}
95+
throw StateError('$runtimeType.sign(...) failed: $e');
96+
}
97+
}
98+
99+
@override
100+
Future<bool> verify(List<int> message, {required Signature signature}) async {
101+
Ed25519.checkSignatureLength(signature.bytes.length);
102+
final simplePublicKey = signature.publicKey as SimplePublicKey;
103+
KeyPairType.ed25519.checkPublicKeyBytesFormat(simplePublicKey.bytes);
104+
try {
105+
final jsPublicKey = await web_crypto.importKeyWhenRaw(
106+
web_crypto.jsUint8ListFrom(simplePublicKey.bytes),
107+
_jsAlgorithm,
108+
true.toJS,
109+
['verify'.toJS].toJS,
110+
);
111+
return web_crypto.verify(
112+
_jsAlgorithm,
113+
jsPublicKey,
114+
web_crypto.jsUint8ListFrom(signature.bytes),
115+
web_crypto.jsUint8ListFrom(message),
116+
);
117+
} catch (e) {
118+
final fallback = _fallback;
119+
if (fallback != null) {
120+
return fallback.verify(message, signature: signature);
121+
}
122+
throw StateError('$runtimeType.verify(...) failed: $e');
123+
}
124+
}
125+
}

cryptography/lib/src/browser/pbkdf2.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ import 'dart:js_interop';
1616

1717
import 'package:cryptography/cryptography.dart';
1818

19-
import '_javascript_bindings.dart' show jsUint8ListFrom;
2019
import '_javascript_bindings.dart' as web_crypto;
20+
import '_javascript_bindings.dart' show jsUint8ListFrom;
2121
import 'hmac.dart';
2222

2323
/// PBKDF2 implementation that uses _Web Cryptography API_ in browsers.

cryptography/lib/src/browser/rsa_pss.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ import 'dart:typed_data';
1818

1919
import 'package:cryptography/cryptography.dart';
2020

21-
import '_javascript_bindings.dart' as web_crypto;
2221
import '_javascript_bindings.dart'
2322
show
2423
base64UrlEncode,
2524
base64UrlEncodeMaybe,
2625
jsUint8ListFrom,
2726
base64UrlDecode,
2827
base64UrlDecodeMaybe;
28+
import '_javascript_bindings.dart' as web_crypto;
2929
import 'hash.dart';
3030

3131
/// RSA-PSS implementation that uses _Web Cryptography API_ in browsers.

cryptography/lib/src/browser/rsa_ssa_pkcs1v15.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ import 'dart:math';
1717

1818
import 'package:cryptography/cryptography.dart';
1919

20-
import '_javascript_bindings.dart' as web_crypto;
2120
import '_javascript_bindings.dart'
2221
show base64UrlEncode, base64UrlEncodeMaybe, base64UrlDecode;
22+
import '_javascript_bindings.dart' as web_crypto;
2323
import 'hash.dart';
2424

2525
/// RSA-SSA-PKCS1v15 implementation that uses _Web Cryptography API_ in browsers.

0 commit comments

Comments
 (0)