Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cryptography/lib/src/browser/_javascript_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,11 @@ extension type AlgorithmNameParams._(JSObject jsObject) {
@internal
extension type CryptoKey._(JSObject _) implements JSObject {
external JSObject get algorithm;

external bool get extractable;

external JSAny get type;

external JSObject get usages;
}

Expand Down
2 changes: 1 addition & 1 deletion cryptography/lib/src/browser/aes_cbc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import 'dart:typed_data';

import 'package:cryptography/cryptography.dart';

import '_javascript_bindings.dart' show jsUint8ListFrom;
import '_javascript_bindings.dart' as web_crypto;
import '_javascript_bindings.dart' show jsUint8ListFrom;
import 'browser_secret_key.dart';

/// AES-CBC implementation that uses _Web Cryptography API_ in browsers.
Expand Down
2 changes: 1 addition & 1 deletion cryptography/lib/src/browser/aes_ctr.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import 'dart:typed_data';

import 'package:cryptography/cryptography.dart';

import '_javascript_bindings.dart' show jsUint8ListFrom;
import '_javascript_bindings.dart' as web_crypto;
import '_javascript_bindings.dart' show jsUint8ListFrom;
import 'browser_secret_key.dart';

/// AES-CTR implementation that uses _Web Cryptography API_ in browsers.
Expand Down
3 changes: 1 addition & 2 deletions cryptography/lib/src/browser/aes_gcm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@

import 'dart:js_interop';
import 'dart:js_interop_unsafe';

import 'dart:math';
import 'dart:typed_data';

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

import '_javascript_bindings.dart' show jsUint8ListFrom;
import '_javascript_bindings.dart' as web_crypto;
import '_javascript_bindings.dart' show jsUint8ListFrom;

/// AES-GCM implementation that uses _Web Cryptography API_ in browsers.
class BrowserAesGcm extends AesGcm implements StreamingCipher {
Expand Down
32 changes: 28 additions & 4 deletions cryptography/lib/src/browser/browser_cryptography.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ import 'dart:math';

import 'package:cryptography/cryptography.dart';
import 'package:cryptography/dart.dart';
import 'package:cryptography/src/browser/rsa_pss.dart';
import 'package:cryptography/src/browser/rsa_ssa_pkcs1v15.dart';
import 'package:meta/meta.dart';

import '_javascript_bindings.dart' show isWebCryptoAvailable;
Expand All @@ -26,10 +24,14 @@ import 'aes_ctr.dart';
import 'aes_gcm.dart';
import 'ecdh.dart';
import 'ecdsa.dart';
import 'ed25519.dart';
import 'hash.dart';
import 'hkdf.dart';
import 'hmac.dart';
import 'pbkdf2.dart';
import 'rsa_pss.dart';
import 'rsa_ssa_pkcs1v15.dart';
import 'x25519.dart';

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

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

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

final Random? _random;

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

bool get _isDeterministicTest {
final random = _random;
return random is SecureRandom && !random.isSecure;
}

@override
AesCbc aesCbc({
required MacAlgorithm macAlgorithm,
Expand Down Expand Up @@ -183,6 +190,15 @@ class BrowserCryptography extends DartCryptography {
return super.ecdsaP521(hashAlgorithm);
}

@override
Ed25519 ed25519() {
final fallback = super.ed25519();
if (isSupported && !_isDeterministicTest) {
return BrowserEd25519(fallback: fallback);
}
return fallback;
}

@override
Hkdf hkdf({required Hmac hmac, required int outputLength}) {
if (isSupported) {
Expand Down Expand Up @@ -324,4 +340,12 @@ class BrowserCryptography extends DartCryptography {
BrowserCryptography withRandom(Random? random) {
return BrowserCryptography(random: random);
}

@override
X25519 x25519() {
if (isSupported && !_isDeterministicTest) {
return const BrowserX25519(fallback: DartX25519());
}
return super.x25519();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ class BrowserCryptography extends DartCryptography {
@visibleForTesting
static bool isDisabledForTesting = false;

/// Whether WASM is used.
static bool get isRunningInWasm => false;

/// Whether Web Cryptography is supported in this platform.
///
/// Browsers support Web Cryptography only in
Expand All @@ -77,9 +80,6 @@ class BrowserCryptography extends DartCryptography {
/// always available.
static bool get isSupported => false;

/// Whether WASM is used.
static bool get isRunningInWasm => false;

/// Constructs an instance of [BrowserCryptography].
///
/// If [random] is not given, algorithms will use some cryptographically
Expand Down
125 changes: 125 additions & 0 deletions cryptography/lib/src/browser/ed25519.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright 2019-2020 Gohilla.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import 'dart:js_interop';

import 'package:cryptography/cryptography.dart';
import 'package:cryptography/dart.dart';
import 'package:meta/meta.dart';

import '_javascript_bindings.dart' as web_crypto;

class BrowserEd25519 extends Ed25519 {
static final _jsAlgorithm = web_crypto.AlgorithmNameParams(
name: 'Ed25519'.toJS,
).jsObject;

final Ed25519? _fallback;

@literal
const BrowserEd25519({required Ed25519? fallback})
: _fallback = fallback,
super.constructor();

@override
Future<SimpleKeyPair> newKeyPair() async {
late web_crypto.Jwk jwk;
try {
final jsCryptoKey = await web_crypto.generateKeyWhenKeyPair(
_jsAlgorithm, true.toJS, ['sign'.toJS, 'verify'.toJS].toJS);
jwk = await web_crypto.exportKeyWhenJwk(jsCryptoKey.privateKey);
} catch (e) {
final fallback = _fallback;
if (fallback != null) {
return fallback.newKeyPair();
}
throw StateError('$runtimeType.newKeyPair(...) failed: $e');
}
return SimpleKeyPairData(
web_crypto.base64UrlDecode(jwk.d!.toDart),
publicKey: SimplePublicKey(
web_crypto.base64UrlDecode(jwk.x!.toDart),
type: KeyPairType.ed25519,
),
type: KeyPairType.ed25519,
);
}

@override
Future<SimpleKeyPair> newKeyPairFromSeed(List<int> seed) {
KeyPairType.ed25519.checkPrivateKeyBytesFormat(seed);
return DartEd25519().newKeyPairFromSeed(seed);
}

@override
Future<Signature> sign(List<int> message, {required KeyPair keyPair}) async {
try {
final publicKeyFuture = keyPair.extractPublicKey();
final keyPairData = await (keyPair as SimpleKeyPair).extract();
final jsCryptoKey = await web_crypto.importKeyWhenJwk(
web_crypto.Jwk(
kty: 'OKP'.toJS,
crv: 'Ed25519'.toJS,
d: web_crypto.base64UrlEncode(keyPairData.bytes).toJS,
x: web_crypto.base64UrlEncode(keyPairData.publicKey.bytes).toJS,
),
_jsAlgorithm,
false.toJS,
['sign'.toJS].toJS,
);
final signature = await web_crypto.sign(
_jsAlgorithm,
jsCryptoKey,
web_crypto.jsUint8ListFrom(message),
);
return Signature(
signature,
publicKey: await publicKeyFuture,
);
} catch (e) {
final fallback = _fallback;
if (fallback != null) {
return fallback.sign(message, keyPair: keyPair);
}
throw StateError('$runtimeType.sign(...) failed: $e');
}
}

@override
Future<bool> verify(List<int> message, {required Signature signature}) async {
Ed25519.checkSignatureLength(signature.bytes.length);
final simplePublicKey = signature.publicKey as SimplePublicKey;
KeyPairType.ed25519.checkPublicKeyBytesFormat(simplePublicKey.bytes);
try {
final jsPublicKey = await web_crypto.importKeyWhenRaw(
web_crypto.jsUint8ListFrom(simplePublicKey.bytes),
_jsAlgorithm,
true.toJS,
['verify'.toJS].toJS,
);
return web_crypto.verify(
_jsAlgorithm,
jsPublicKey,
web_crypto.jsUint8ListFrom(signature.bytes),
web_crypto.jsUint8ListFrom(message),
);
} catch (e) {
final fallback = _fallback;
if (fallback != null) {
return fallback.verify(message, signature: signature);
}
throw StateError('$runtimeType.verify(...) failed: $e');
}
}
}
2 changes: 1 addition & 1 deletion cryptography/lib/src/browser/pbkdf2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import 'dart:js_interop';

import 'package:cryptography/cryptography.dart';

import '_javascript_bindings.dart' show jsUint8ListFrom;
import '_javascript_bindings.dart' as web_crypto;
import '_javascript_bindings.dart' show jsUint8ListFrom;
import 'hmac.dart';

/// PBKDF2 implementation that uses _Web Cryptography API_ in browsers.
Expand Down
2 changes: 1 addition & 1 deletion cryptography/lib/src/browser/rsa_pss.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ import 'dart:typed_data';

import 'package:cryptography/cryptography.dart';

import '_javascript_bindings.dart' as web_crypto;
import '_javascript_bindings.dart'
show
base64UrlEncode,
base64UrlEncodeMaybe,
jsUint8ListFrom,
base64UrlDecode,
base64UrlDecodeMaybe;
import '_javascript_bindings.dart' as web_crypto;
import 'hash.dart';

/// RSA-PSS implementation that uses _Web Cryptography API_ in browsers.
Expand Down
2 changes: 1 addition & 1 deletion cryptography/lib/src/browser/rsa_ssa_pkcs1v15.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import 'dart:math';

import 'package:cryptography/cryptography.dart';

import '_javascript_bindings.dart' as web_crypto;
import '_javascript_bindings.dart'
show base64UrlEncode, base64UrlEncodeMaybe, base64UrlDecode;
import '_javascript_bindings.dart' as web_crypto;
import 'hash.dart';

/// RSA-SSA-PKCS1v15 implementation that uses _Web Cryptography API_ in browsers.
Expand Down
Loading