diff --git a/lib/src/impl_ffi/impl_ffi.dart b/lib/src/impl_ffi/impl_ffi.dart index 01aa633a..091adb06 100644 --- a/lib/src/impl_ffi/impl_ffi.dart +++ b/lib/src/impl_ffi/impl_ffi.dart @@ -58,18 +58,6 @@ class _OperationError extends Error implements OperationError { String toString() => _message; } -/// Implementation of [KeyPair]. -class _KeyPair implements KeyPair { - @override - final S privateKey; - - @override - final T publicKey; - - _KeyPair({required this.privateKey, required this.publicKey}); -} - - const WebCryptoImpl webCryptImpl = _WebCryptoImpl(); final class _WebCryptoImpl implements WebCryptoImpl { @@ -89,4 +77,10 @@ final class _WebCryptoImpl implements WebCryptoImpl { @override final pbkdf2SecretKey = const _StaticPbkdf2SecretKeyImpl(); + + @override + final ecdhPrivateKey = const _StaticEcdhPrivateKeyImpl(); + + @override + final ecdhPublicKey = const _StaticEcdhPublicKeyImpl(); } diff --git a/lib/src/impl_ffi/impl_ffi.ec_common.dart b/lib/src/impl_ffi/impl_ffi.ec_common.dart index 3d4de119..bc7b37f1 100644 --- a/lib/src/impl_ffi/impl_ffi.ec_common.dart +++ b/lib/src/impl_ffi/impl_ffi.ec_common.dart @@ -351,9 +351,9 @@ KeyPair<_EvpPKey, _EvpPKey> _generateEcKeyPair( final pubKey = _EvpPKey(); _checkOpIsOne(ssl.EVP_PKEY_set1_EC_KEY.invoke(pubKey, ecPub)); - return _KeyPair( - privateKey: privKey, - publicKey: pubKey, + return createKeyPair( + privKey, + pubKey, ); }); } diff --git a/lib/src/impl_ffi/impl_ffi.ecdh.dart b/lib/src/impl_ffi/impl_ffi.ecdh.dart index 530a7604..0a7800b0 100644 --- a/lib/src/impl_ffi/impl_ffi.ecdh.dart +++ b/lib/src/impl_ffi/impl_ffi.ecdh.dart @@ -16,17 +16,17 @@ part of 'impl_ffi.dart'; -Future ecdhPrivateKey_importPkcs8Key( +Future ecdhPrivateKey_importPkcs8Key( List keyData, EllipticCurve curve, ) async => - _EcdhPrivateKey(_importPkcs8EcPrivateKey(keyData, curve)); + _EcdhPrivateKeyImpl(_importPkcs8EcPrivateKey(keyData, curve)); -Future ecdhPrivateKey_importJsonWebKey( +Future ecdhPrivateKey_importJsonWebKey( Map jwk, EllipticCurve curve, ) async => - _EcdhPrivateKey(_importJwkEcPrivateOrPublicKey( + _EcdhPrivateKeyImpl(_importJwkEcPrivateOrPublicKey( JsonWebKey.fromJson(jwk), curve, isPrivateKey: true, @@ -34,33 +34,33 @@ Future ecdhPrivateKey_importJsonWebKey( expectedAlg: null, // ECDH has no validation of 'jwk.alg' )); -Future> ecdhPrivateKey_generateKey( +Future> ecdhPrivateKey_generateKey( EllipticCurve curve, ) async { final p = _generateEcKeyPair(curve); - return _KeyPair( - privateKey: _EcdhPrivateKey(p.privateKey), - publicKey: _EcdhPublicKey(p.publicKey), + return createKeyPair( + _EcdhPrivateKeyImpl(p.privateKey), + _EcdhPublicKeyImpl(p.publicKey), ); } -Future ecdhPublicKey_importRawKey( +Future ecdhPublicKey_importRawKey( List keyData, EllipticCurve curve, ) async => - _EcdhPublicKey(_importRawEcPublicKey(keyData, curve)); + _EcdhPublicKeyImpl(_importRawEcPublicKey(keyData, curve)); -Future ecdhPublicKey_importSpkiKey( +Future ecdhPublicKey_importSpkiKey( List keyData, EllipticCurve curve, ) async => - _EcdhPublicKey(_importSpkiEcPublicKey(keyData, curve)); + _EcdhPublicKeyImpl(_importSpkiEcPublicKey(keyData, curve)); -Future ecdhPublicKey_importJsonWebKey( +Future ecdhPublicKey_importJsonWebKey( Map jwk, EllipticCurve curve, ) async => - _EcdhPublicKey(_importJwkEcPrivateOrPublicKey( + _EcdhPublicKeyImpl(_importJwkEcPrivateOrPublicKey( JsonWebKey.fromJson(jwk), curve, isPrivateKey: false, @@ -68,10 +68,29 @@ Future ecdhPublicKey_importJsonWebKey( expectedAlg: null, // ECDH has no validation of 'jwk.alg' )); -class _EcdhPrivateKey implements EcdhPrivateKey { +final class _StaticEcdhPrivateKeyImpl implements StaticEcdhPrivateKeyImpl { + const _StaticEcdhPrivateKeyImpl(); + + @override + Future importPkcs8Key(List keyData, EllipticCurve curve) => + ecdhPrivateKey_importPkcs8Key(keyData, curve); + + @override + Future importJsonWebKey(Map jwk, EllipticCurve curve) => + ecdhPrivateKey_importJsonWebKey(jwk, curve); + + @override + Future<(EcdhPrivateKeyImpl, EcdhPublicKeyImpl)> generateKey(EllipticCurve curve) async { + final KeyPair keyPair = await ecdhPrivateKey_generateKey(curve); + + return (keyPair.privateKey, keyPair.publicKey); + } +} + +final class _EcdhPrivateKeyImpl implements EcdhPrivateKeyImpl { final _EvpPKey _key; - _EcdhPrivateKey(this._key); + _EcdhPrivateKeyImpl(this._key); @override String toString() { @@ -79,8 +98,8 @@ class _EcdhPrivateKey implements EcdhPrivateKey { } @override - Future deriveBits(int length, EcdhPublicKey publicKey) async { - if (publicKey is! _EcdhPublicKey) { + Future deriveBits(int length, EcdhPublicKeyImpl publicKey) async { + if (publicKey is! _EcdhPublicKeyImpl) { throw ArgumentError.value( publicKey, 'publicKey', @@ -167,10 +186,26 @@ class _EcdhPrivateKey implements EcdhPrivateKey { Future exportPkcs8Key() async => _exportPkcs8Key(_key); } -class _EcdhPublicKey implements EcdhPublicKey { +final class _StaticEcdhPublicKeyImpl implements StaticEcdhPublicKeyImpl { + const _StaticEcdhPublicKeyImpl(); + + @override + Future importRawKey(List keyData, EllipticCurve curve) => + ecdhPublicKey_importRawKey(keyData, curve); + + @override + Future importSpkiKey(List keyData, EllipticCurve curve) => + ecdhPublicKey_importSpkiKey(keyData, curve); + + @override + Future importJsonWebKey(Map jwk, EllipticCurve curve) => + ecdhPublicKey_importJsonWebKey(jwk, curve); +} + +final class _EcdhPublicKeyImpl implements EcdhPublicKeyImpl { final _EvpPKey _key; - _EcdhPublicKey(this._key); + _EcdhPublicKeyImpl(this._key); @override String toString() { diff --git a/lib/src/impl_ffi/impl_ffi.ecdsa.dart b/lib/src/impl_ffi/impl_ffi.ecdsa.dart index 58072d85..d94d7f60 100644 --- a/lib/src/impl_ffi/impl_ffi.ecdsa.dart +++ b/lib/src/impl_ffi/impl_ffi.ecdsa.dart @@ -54,9 +54,9 @@ Future> ecdsaPrivateKey_generateKey( EllipticCurve curve, ) async { final p = _generateEcKeyPair(curve); - return _KeyPair( - privateKey: _EcdsaPrivateKey(p.privateKey), - publicKey: _EcdsaPublicKey(p.publicKey), + return createKeyPair( + _EcdsaPrivateKey(p.privateKey), + _EcdsaPublicKey(p.publicKey), ); } diff --git a/lib/src/impl_ffi/impl_ffi.rsa_common.dart b/lib/src/impl_ffi/impl_ffi.rsa_common.dart index 499a318e..5d8f1603 100644 --- a/lib/src/impl_ffi/impl_ffi.rsa_common.dart +++ b/lib/src/impl_ffi/impl_ffi.rsa_common.dart @@ -229,7 +229,7 @@ Map _exportJwkRsaPrivateOrPublicKey( }); } -_KeyPair<_EvpPKey, _EvpPKey> _generateRsaKeyPair( +KeyPair<_EvpPKey, _EvpPKey> _generateRsaKeyPair( int modulusLength, BigInt publicExponent, ) { @@ -278,9 +278,9 @@ _KeyPair<_EvpPKey, _EvpPKey> _generateRsaKeyPair( final pubKey = _EvpPKey(); _checkOp(ssl.EVP_PKEY_set1_RSA.invoke(pubKey, pubRSA) == 1); - return _KeyPair( - privateKey: privKey, - publicKey: pubKey, + return createKeyPair( + privKey, + pubKey, ); }); } diff --git a/lib/src/impl_ffi/impl_ffi.rsaoaep.dart b/lib/src/impl_ffi/impl_ffi.rsaoaep.dart index bdd9c028..c24c1845 100644 --- a/lib/src/impl_ffi/impl_ffi.rsaoaep.dart +++ b/lib/src/impl_ffi/impl_ffi.rsaoaep.dart @@ -68,9 +68,9 @@ Future> // Get hash first, to avoid a leak of EVP_PKEY if _Hash.fromHash throws final h = _Hash.fromHash(hash); final keys = _generateRsaKeyPair(modulusLength, publicExponent); - return _KeyPair( - privateKey: _RsaOaepPrivateKey(keys.privateKey, h), - publicKey: _RsaOaepPublicKey(keys.publicKey, h), + return createKeyPair( + _RsaOaepPrivateKey(keys.privateKey, h), + _RsaOaepPublicKey(keys.publicKey, h), ); } diff --git a/lib/src/impl_ffi/impl_ffi.rsapss.dart b/lib/src/impl_ffi/impl_ffi.rsapss.dart index 55ec6063..0ec309ea 100644 --- a/lib/src/impl_ffi/impl_ffi.rsapss.dart +++ b/lib/src/impl_ffi/impl_ffi.rsapss.dart @@ -67,9 +67,9 @@ Future> rsaPssPrivateKey_generateKey( // Validate and get hash function final h = _Hash.fromHash(hash); final keys = _generateRsaKeyPair(modulusLength, publicExponent); - return _KeyPair( - privateKey: _RsaPssPrivateKey(keys.privateKey, h), - publicKey: _RsaPssPublicKey(keys.publicKey, h), + return createKeyPair( + _RsaPssPrivateKey(keys.privateKey, h), + _RsaPssPublicKey(keys.publicKey, h), ); } diff --git a/lib/src/impl_ffi/impl_ffi.rsassapkcs1v15.dart b/lib/src/impl_ffi/impl_ffi.rsassapkcs1v15.dart index 7e64fb43..f59a6871 100644 --- a/lib/src/impl_ffi/impl_ffi.rsassapkcs1v15.dart +++ b/lib/src/impl_ffi/impl_ffi.rsassapkcs1v15.dart @@ -68,9 +68,9 @@ Future> // Get hash first, to avoid a leak of EVP_PKEY if _Hash.fromHash throws final h = _Hash.fromHash(hash); final keys = _generateRsaKeyPair(modulusLength, publicExponent); - return _KeyPair( - privateKey: _RsassaPkcs1V15PrivateKey(keys.privateKey, h), - publicKey: _RsassaPkcs1V15PublicKey(keys.publicKey, h), + return createKeyPair( + _RsassaPkcs1V15PrivateKey(keys.privateKey, h), + _RsassaPkcs1V15PublicKey(keys.publicKey, h), ); } diff --git a/lib/src/impl_interface/impl_interface.dart b/lib/src/impl_interface/impl_interface.dart index 75ccd1f9..7fe4cdd0 100644 --- a/lib/src/impl_interface/impl_interface.dart +++ b/lib/src/impl_interface/impl_interface.dart @@ -17,14 +17,46 @@ library impl_stub; import 'dart:typed_data'; import 'dart:async'; -import 'package:webcrypto/webcrypto.dart'; - +import 'package:webcrypto/webcrypto.dart' show Hash; part 'impl_interface.aescbc.dart'; part 'impl_interface.aesctr.dart'; part 'impl_interface.hmac.dart'; part 'impl_interface.pbkdf2.dart'; part 'impl_interface.aesgcm.dart'; +part 'impl_interface.ecdh.dart'; + +/// A key-pair as returned from key generation. +class KeyPair { + KeyPair._(this.privateKey, this.publicKey); // keep the constructor private. + + /// Private key for [publicKey]. + final S privateKey; + + /// Public key matching [privateKey]. + final T publicKey; +} + +/// Factory method to create KeyPair instance +KeyPair createKeyPair(S privateKey, T publicKey) { + return KeyPair._(privateKey, publicKey); +} + +/// Elliptic curves supported by ECDSA and ECDH. +/// +/// > [!NOTE] +/// > Additional values may be added to this enum in the future. +enum EllipticCurve { + p256, + p384, + + /// + /// + /// P-521 is **not supported on Safari**, see [bug 216755 (bugs.webkit.org)][1]. + /// + /// [1]: https://bugs.webkit.org/show_bug.cgi?id=216755 + p521, +} /// Interface to be provided by platform implementations. /// @@ -48,4 +80,6 @@ abstract interface class WebCryptoImpl { StaticAesGcmSecretKeyImpl get aesGcmSecretKey; StaticHmacSecretKeyImpl get hmacSecretKey; StaticPbkdf2SecretKeyImpl get pbkdf2SecretKey; + StaticEcdhPrivateKeyImpl get ecdhPrivateKey; + StaticEcdhPublicKeyImpl get ecdhPublicKey; } diff --git a/lib/src/impl_interface/impl_interface.ecdh.dart b/lib/src/impl_interface/impl_interface.ecdh.dart new file mode 100644 index 00000000..6229c9c9 --- /dev/null +++ b/lib/src/impl_interface/impl_interface.ecdh.dart @@ -0,0 +1,39 @@ +// Copyright 2020 Google LLC +// +// 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. + +part of 'impl_interface.dart'; + +abstract interface class StaticEcdhPrivateKeyImpl { + Future importPkcs8Key(List keyData, EllipticCurve curve); + Future importJsonWebKey(Map jwk, EllipticCurve curve); + Future<(EcdhPrivateKeyImpl, EcdhPublicKeyImpl)> generateKey(EllipticCurve curve); +} + +abstract interface class EcdhPrivateKeyImpl { + Future deriveBits(int length, EcdhPublicKeyImpl publicKey); + Future exportPkcs8Key(); + Future> exportJsonWebKey(); +} + +abstract interface class StaticEcdhPublicKeyImpl { + Future importRawKey(List keyData, EllipticCurve curve); + Future importSpkiKey(List keyData, EllipticCurve curve); + Future importJsonWebKey(Map jwk, EllipticCurve curve); +} + +abstract interface class EcdhPublicKeyImpl { + Future exportRawKey(); + Future exportSpkiKey(); + Future> exportJsonWebKey(); +} diff --git a/lib/src/impl_js/impl_js.dart b/lib/src/impl_js/impl_js.dart index 5acd0e37..b03a5255 100644 --- a/lib/src/impl_js/impl_js.dart +++ b/lib/src/impl_js/impl_js.dart @@ -45,17 +45,6 @@ class _OperationError extends Error implements OperationError { String toString() => _message; } -/// Implementation of [KeyPair]. -class _KeyPair implements KeyPair { - @override - final S privateKey; - - @override - final T publicKey; - - _KeyPair({required this.privateKey, required this.publicKey}); -} - const WebCryptoImpl webCryptImpl = _WebCryptoImpl(); final class _WebCryptoImpl implements WebCryptoImpl { @@ -75,4 +64,10 @@ final class _WebCryptoImpl implements WebCryptoImpl { @override final pbkdf2SecretKey = const _StaticPbkdf2SecretKeyImpl(); + + @override + final ecdhPrivateKey = const _StaticEcdhPrivateKeyImpl(); + + @override + final ecdhPublicKey = const _StaticEcdhPublicKeyImpl(); } diff --git a/lib/src/impl_js/impl_js.ecdh.dart b/lib/src/impl_js/impl_js.ecdh.dart index c9d732a6..0a54950e 100644 --- a/lib/src/impl_js/impl_js.ecdh.dart +++ b/lib/src/impl_js/impl_js.ecdh.dart @@ -18,11 +18,11 @@ part of 'impl_js.dart'; const _ecdhAlgorithmName = 'ECDH'; -Future ecdhPrivateKey_importPkcs8Key( +Future ecdhPrivateKey_importPkcs8Key( List keyData, EllipticCurve curve, ) async { - return _EcdhPrivateKey(await _importKey( + return _EcdhPrivateKeyImpl(await _importKey( 'pkcs8', keyData, subtle.Algorithm( @@ -34,7 +34,7 @@ Future ecdhPrivateKey_importPkcs8Key( )); } -Future ecdhPrivateKey_importJsonWebKey( +Future ecdhPrivateKey_importJsonWebKey( Map jwk, EllipticCurve curve, ) async { @@ -44,7 +44,7 @@ Future ecdhPrivateKey_importJsonWebKey( // See: https://crbug.com/641499 jwk = Map.fromEntries(jwk.entries.where((e) => e.key != 'use')); } - return _EcdhPrivateKey(await _importJsonWebKey( + return _EcdhPrivateKeyImpl(await _importJsonWebKey( jwk, subtle.Algorithm( name: _ecdhAlgorithmName, @@ -55,7 +55,7 @@ Future ecdhPrivateKey_importJsonWebKey( )); } -Future> ecdhPrivateKey_generateKey( +Future> ecdhPrivateKey_generateKey( EllipticCurve curve, ) async { final pair = await _generateKeyPair( @@ -65,17 +65,17 @@ Future> ecdhPrivateKey_generateKey( ), _usagesDeriveBits, ); - return _KeyPair( - privateKey: _EcdhPrivateKey(pair.privateKey), - publicKey: _EcdhPublicKey(pair.publicKey), + return createKeyPair( + _EcdhPrivateKeyImpl(pair.privateKey), + _EcdhPublicKeyImpl(pair.publicKey), ); } -Future ecdhPublicKey_importRawKey( +Future ecdhPublicKey_importRawKey( List keyData, EllipticCurve curve, ) async { - return _EcdhPublicKey(await _importKey( + return _EcdhPublicKeyImpl(await _importKey( 'raw', keyData, subtle.Algorithm( @@ -87,11 +87,11 @@ Future ecdhPublicKey_importRawKey( )); } -Future ecdhPublicKey_importSpkiKey( +Future ecdhPublicKey_importSpkiKey( List keyData, EllipticCurve curve, ) async { - return _EcdhPublicKey(await _importKey( + return _EcdhPublicKeyImpl(await _importKey( 'spki', keyData, subtle.Algorithm( @@ -103,7 +103,7 @@ Future ecdhPublicKey_importSpkiKey( )); } -Future ecdhPublicKey_importJsonWebKey( +Future ecdhPublicKey_importJsonWebKey( Map jwk, EllipticCurve curve, ) async { @@ -113,7 +113,7 @@ Future ecdhPublicKey_importJsonWebKey( // See: https://crbug.com/641499 jwk = Map.fromEntries(jwk.entries.where((e) => e.key != 'use')); } - return _EcdhPublicKey(await _importJsonWebKey( + return _EcdhPublicKeyImpl(await _importJsonWebKey( jwk, subtle.Algorithm( name: _ecdhAlgorithmName, @@ -124,22 +124,41 @@ Future ecdhPublicKey_importJsonWebKey( )); } -class _EcdhPrivateKey implements EcdhPrivateKey { +final class _StaticEcdhPrivateKeyImpl implements StaticEcdhPrivateKeyImpl { + const _StaticEcdhPrivateKeyImpl(); + + @override + Future importPkcs8Key(List keyData, EllipticCurve curve) => + ecdhPrivateKey_importPkcs8Key(keyData, curve); + + @override + Future importJsonWebKey(Map jwk, EllipticCurve curve) => + ecdhPrivateKey_importJsonWebKey(jwk, curve); + + @override + Future<(EcdhPrivateKeyImpl, EcdhPublicKeyImpl)> generateKey(EllipticCurve curve) async { + final KeyPair keyPair = await ecdhPrivateKey_generateKey(curve); + + return (keyPair.privateKey, keyPair.publicKey); + } +} + +final class _EcdhPrivateKeyImpl implements EcdhPrivateKeyImpl { final subtle.JSCryptoKey _key; - _EcdhPrivateKey(this._key); + _EcdhPrivateKeyImpl(this._key); @override String toString() { - return 'Instance of \'EcdhPrivateKey\''; + return 'Instance of \'EcdhPrivateKeyImpl\''; } @override - Future deriveBits(int length, EcdhPublicKey publicKey) async { - if (publicKey is! _EcdhPublicKey) { + Future deriveBits(int length, EcdhPublicKeyImpl publicKey) async { + if (publicKey is! _EcdhPublicKeyImpl) { throw ArgumentError.value( publicKey, 'publicKey', - 'custom implementations of EcdhPublicKey is not supported', + 'custom implementations of EcdhPublicKeyImpl is not supported', ); } final lengthInBytes = (length / 8).ceil(); @@ -176,13 +195,29 @@ class _EcdhPrivateKey implements EcdhPrivateKey { } } -class _EcdhPublicKey implements EcdhPublicKey { +final class _StaticEcdhPublicKeyImpl implements StaticEcdhPublicKeyImpl { + const _StaticEcdhPublicKeyImpl(); + + @override + Future importRawKey(List keyData, EllipticCurve curve) => + ecdhPublicKey_importRawKey(keyData, curve); + + @override + Future importSpkiKey(List keyData, EllipticCurve curve) => + ecdhPublicKey_importSpkiKey(keyData, curve); + + @override + Future importJsonWebKey(Map jwk, EllipticCurve curve) => + ecdhPublicKey_importJsonWebKey(jwk, curve); +} + +final class _EcdhPublicKeyImpl implements EcdhPublicKeyImpl { final subtle.JSCryptoKey _key; - _EcdhPublicKey(this._key); + _EcdhPublicKeyImpl(this._key); @override String toString() { - return 'Instance of \'EcdhPublicKey\''; + return 'Instance of \'EcdhPublicKeyImpl\''; } @override diff --git a/lib/src/impl_js/impl_js.ecdsa.dart b/lib/src/impl_js/impl_js.ecdsa.dart index 2d0326b0..3bedf93c 100644 --- a/lib/src/impl_js/impl_js.ecdsa.dart +++ b/lib/src/impl_js/impl_js.ecdsa.dart @@ -59,9 +59,9 @@ Future> ecdsaPrivateKey_generateKey( ), _usagesSignVerify, ); - return _KeyPair( - privateKey: _EcdsaPrivateKey(pair.privateKey), - publicKey: _EcdsaPublicKey(pair.publicKey), + return createKeyPair( + _EcdsaPrivateKey(pair.privateKey), + _EcdsaPublicKey(pair.publicKey), ); } diff --git a/lib/src/impl_js/impl_js.rsaoaep.dart b/lib/src/impl_js/impl_js.rsaoaep.dart index 0cc42210..0f10c383 100644 --- a/lib/src/impl_js/impl_js.rsaoaep.dart +++ b/lib/src/impl_js/impl_js.rsaoaep.dart @@ -64,9 +64,9 @@ Future> ), _usagesEncryptDecrypt, ); - return _KeyPair( - privateKey: _RsaOaepPrivateKey(pair.privateKey), - publicKey: _RsaOaepPublicKey(pair.publicKey), + return createKeyPair( + _RsaOaepPrivateKey(pair.privateKey), + _RsaOaepPublicKey(pair.publicKey), ); } diff --git a/lib/src/impl_js/impl_js.rsapss.dart b/lib/src/impl_js/impl_js.rsapss.dart index daff864c..741059b8 100644 --- a/lib/src/impl_js/impl_js.rsapss.dart +++ b/lib/src/impl_js/impl_js.rsapss.dart @@ -57,9 +57,9 @@ Future> rsaPssPrivateKey_generateKey( ), _usagesSignVerify, ); - return _KeyPair( - privateKey: _RsaPssPrivateKey(pair.privateKey), - publicKey: _RsaPssPublicKey(pair.publicKey), + return createKeyPair( + _RsaPssPrivateKey(pair.privateKey), + _RsaPssPublicKey(pair.publicKey), ); } diff --git a/lib/src/impl_js/impl_js.rsassapkcs1v15.dart b/lib/src/impl_js/impl_js.rsassapkcs1v15.dart index 1c7b1b6a..5b56b451 100644 --- a/lib/src/impl_js/impl_js.rsassapkcs1v15.dart +++ b/lib/src/impl_js/impl_js.rsassapkcs1v15.dart @@ -57,9 +57,9 @@ Future> ), _usagesSignVerify, ); - return _KeyPair( - privateKey: _RsassaPkcs1V15PrivateKey(pair.privateKey), - publicKey: _RsassaPkcs1V15PublicKey(pair.publicKey), + return createKeyPair( + _RsassaPkcs1V15PrivateKey(pair.privateKey), + _RsassaPkcs1V15PublicKey(pair.publicKey), ); } diff --git a/lib/src/impl_stub.dart b/lib/src/impl_stub.dart index f1e3b991..b12c0882 100644 --- a/lib/src/impl_stub.dart +++ b/lib/src/impl_stub.dart @@ -192,41 +192,6 @@ Future rsaOaepPublicKey_importJsonWebKey( //---------------------- ECDH -Future ecdhPrivateKey_importPkcs8Key( - List keyData, - EllipticCurve curve, -) => - throw _notImplemented; - -Future ecdhPrivateKey_importJsonWebKey( - Map jwk, - EllipticCurve curve, -) => - throw _notImplemented; - -Future> ecdhPrivateKey_generateKey( - EllipticCurve curve, -) => - throw _notImplemented; - -Future ecdhPublicKey_importRawKey( - List keyData, - EllipticCurve curve, -) => - throw _notImplemented; - -Future ecdhPublicKey_importSpkiKey( - List keyData, - EllipticCurve curve, -) => - throw _notImplemented; - -Future ecdhPublicKey_importJsonWebKey( - Map jwk, - EllipticCurve curve, -) => - throw _notImplemented; - //---------------------- HKDF Future hkdfSecretKey_importRawKey(List keyData) => diff --git a/lib/src/impl_stub/impl_stub.dart b/lib/src/impl_stub/impl_stub.dart index beeb582b..e6de51d1 100644 --- a/lib/src/impl_stub/impl_stub.dart +++ b/lib/src/impl_stub/impl_stub.dart @@ -16,12 +16,14 @@ library webcrypto.impl_stub; import 'package:webcrypto/src/impl_interface/impl_interface.dart'; import 'package:webcrypto/webcrypto.dart'; +import '../webcrypto/webcrypto.dart'; part 'impl_stub.aescbc.dart'; part 'impl_stub.aesctr.dart'; part 'impl_stub.aesgcm.dart'; part 'impl_stub.hmac.dart'; part 'impl_stub.pbkdf2.dart'; +part 'impl_stub.ecdh.dart'; const WebCryptoImpl webCryptImpl = _WebCryptoImpl(); @@ -42,4 +44,10 @@ final class _WebCryptoImpl implements WebCryptoImpl { @override final pbkdf2SecretKey = const _StaticPbkdf2SecretKeyImpl(); + + @override + final ecdhPrivateKey = const _StaticEcdhPrivateKeyImpl(); + + @override + final ecdhPublicKey = const _StaticEcdhPublicKeyImpl(); } diff --git a/lib/src/impl_stub/impl_stub.ecdh.dart b/lib/src/impl_stub/impl_stub.ecdh.dart new file mode 100644 index 00000000..59e115ef --- /dev/null +++ b/lib/src/impl_stub/impl_stub.ecdh.dart @@ -0,0 +1,47 @@ +// Copyright 2020 Google LLC +// +// 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. + +part of 'impl_stub.dart'; + +final class _StaticEcdhPrivateKeyImpl implements StaticEcdhPrivateKeyImpl { + const _StaticEcdhPrivateKeyImpl(); + + @override + Future importPkcs8Key(List keyData, EllipticCurve curve) => + throw UnimplementedError('Not implemented'); + + @override + Future importJsonWebKey(Map jwk, EllipticCurve curve) => + throw UnimplementedError('Not implemented'); + + @override + Future<(EcdhPrivateKeyImpl, EcdhPublicKeyImpl)> generateKey(EllipticCurve curve) => + throw UnimplementedError('Not implemented'); +} + +final class _StaticEcdhPublicKeyImpl implements StaticEcdhPublicKeyImpl { + const _StaticEcdhPublicKeyImpl(); + + @override + Future importRawKey(List keyData, EllipticCurve curve) => + throw UnimplementedError('Not implemented'); + + @override + Future importSpkiKey(List keyData, EllipticCurve curve) => + throw UnimplementedError('Not implemented'); + + @override + Future importJsonWebKey(Map jwk, EllipticCurve curve) => + throw UnimplementedError('Not implemented'); +} diff --git a/lib/src/testing/webcrypto/ecdh.dart b/lib/src/testing/webcrypto/ecdh.dart index 2d3c18ac..9322f88b 100644 --- a/lib/src/testing/webcrypto/ecdh.dart +++ b/lib/src/testing/webcrypto/ecdh.dart @@ -12,12 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'package:webcrypto/src/impl_interface/impl_interface.dart'; import 'package:webcrypto/webcrypto.dart'; import '../utils/utils.dart'; import '../utils/testrunner.dart'; import '../utils/detected_runtime.dart'; -class _KeyPair implements KeyPair { +class _KeyPair + implements KeyPair { @override final S privateKey; diff --git a/lib/src/webcrypto/webcrypto.dart b/lib/src/webcrypto/webcrypto.dart index c95064bd..d6bf9dc0 100644 --- a/lib/src/webcrypto/webcrypto.dart +++ b/lib/src/webcrypto/webcrypto.dart @@ -32,6 +32,8 @@ import '../impl_stub/impl_stub.dart' if (dart.library.ffi) '../impl_ffi/impl_ffi.dart' if (dart.library.js) '../impl_js/impl_js.dart' show webCryptImpl; +export '../impl_interface/impl_interface.dart' show KeyPair, EllipticCurve; + part 'webcrypto.aescbc.dart'; part 'webcrypto.aesctr.dart'; part 'webcrypto.aesgcm.dart'; @@ -51,31 +53,3 @@ part 'webcrypto.rsassapkcs1v15.dart'; abstract class OperationError extends Error { OperationError._(); // keep the constructor private. } - -/// A key-pair as returned from key generation. -@sealed -abstract class KeyPair { - KeyPair._(); // keep the constructor private. - - /// Private key for [publicKey]. - S get privateKey; - - /// Public key matching [privateKey]. - T get publicKey; -} - -/// Elliptic curves supported by ECDSA and ECDH. -/// -/// > [!NOTE] -/// > Additional values may be added to this enum in the future. -enum EllipticCurve { - p256, - p384, - - /// - /// - /// P-521 is **not supported on Safari**, see [bug 216755 (bugs.webkit.org)][1]. - /// - /// [1]: https://bugs.webkit.org/show_bug.cgi?id=216755 - p521, -} diff --git a/lib/src/webcrypto/webcrypto.ecdh.dart b/lib/src/webcrypto/webcrypto.ecdh.dart index 221c60ac..68a5c1a1 100644 --- a/lib/src/webcrypto/webcrypto.ecdh.dart +++ b/lib/src/webcrypto/webcrypto.ecdh.dart @@ -51,9 +51,14 @@ part of 'webcrypto.dart'; /// } /// ``` /// {@endtemplate} -@sealed -abstract class EcdhPrivateKey { - EcdhPrivateKey._(); // keep the constructor private. +final class EcdhPrivateKey { + final EcdhPrivateKeyImpl _impl; + + EcdhPrivateKey._(this._impl); // keep the constructor private. + + factory EcdhPrivateKey(EcdhPrivateKeyImpl impl) { + return EcdhPrivateKey._(impl); + } /// Import [EcdhPrivateKey] in the [PKCS #8][1] format. /// @@ -91,8 +96,9 @@ abstract class EcdhPrivateKey { static Future importPkcs8Key( List keyData, EllipticCurve curve, - ) { - return impl.ecdhPrivateKey_importPkcs8Key(keyData, curve); + ) async { + final impl = await webCryptImpl.ecdhPrivateKey.importPkcs8Key(keyData, curve); + return EcdhPrivateKey._(impl); } /// Import ECDH private key in [JSON Web Key][1] format. @@ -150,8 +156,9 @@ abstract class EcdhPrivateKey { static Future importJsonWebKey( Map jwk, EllipticCurve curve, - ) { - return impl.ecdhPrivateKey_importJsonWebKey(jwk, curve); + ) async { + final impl = await webCryptImpl.ecdhPrivateKey.importJsonWebKey(jwk, curve); + return EcdhPrivateKey._(impl); } /// Generate a new [EcdhPrivateKey] and [EcdhPublicKey] pair. @@ -178,8 +185,13 @@ abstract class EcdhPrivateKey { /// static Future> generateKey( EllipticCurve curve, - ) { - return impl.ecdhPrivateKey_generateKey(curve); + ) async { + final (privateKeyImpl, publicKeyImpl) = await webCryptImpl.ecdhPrivateKey.generateKey(curve); + + final privateKey = EcdhPrivateKey(privateKeyImpl); + final publicKey = EcdhPublicKey(publicKeyImpl); + + return createKeyPair(privateKey, publicKey); } /// Derive a shared secret from two ECDH key pairs using the private key from one pair @@ -202,7 +214,11 @@ abstract class EcdhPrivateKey { // See: https://tools.ietf.org/html/rfc6090#section-4 // Notice that this is not uniformly distributed, see also: // https://tools.ietf.org/html/rfc6090#appendix-B - Future deriveBits(int length, EcdhPublicKey publicKey); + Future deriveBits(int length, EcdhPublicKey publicKey) async { + final publicKeyImpl = publicKey._impl; + + return _impl.deriveBits(length, publicKeyImpl); + } /// Export the [EcdhPrivateKey] as a [PKCS #8][1] key. /// @@ -227,7 +243,7 @@ abstract class EcdhPrivateKey { /// } /// ``` /// [1]: https://datatracker.ietf.org/doc/html/rfc5208 - Future exportPkcs8Key(); + Future exportPkcs8Key() => _impl.exportPkcs8Key(); /// Export the [EcdhPrivateKey] as a [JSON Web Key][1]. /// @@ -251,19 +267,25 @@ abstract class EcdhPrivateKey { /// } /// ``` /// [1]: https://www.rfc-editor.org/rfc/rfc7518.html#section-6.2 - Future> exportJsonWebKey(); + Future> exportJsonWebKey() => _impl.exportJsonWebKey(); } -@sealed -abstract class EcdhPublicKey { - EcdhPublicKey._(); // keep the constructor private. +final class EcdhPublicKey { + final EcdhPublicKeyImpl _impl; + + EcdhPublicKey._(this._impl); // keep the constructor private. + + factory EcdhPublicKey(EcdhPublicKeyImpl impl) { + return EcdhPublicKey._(impl); + } /// TODO: find out of this works on Firefox static Future importRawKey( List keyData, EllipticCurve curve, - ) { - return impl.ecdhPublicKey_importRawKey(keyData, curve); + ) async { + final impl = await webCryptImpl.ecdhPublicKey.importRawKey(keyData, curve); + return EcdhPublicKey._(impl); } /// ## Compatibility @@ -283,8 +305,9 @@ abstract class EcdhPublicKey { static Future importSpkiKey( List keyData, EllipticCurve curve, - ) { - return impl.ecdhPublicKey_importSpkiKey(keyData, curve); + ) async { + final impl = await webCryptImpl.ecdhPublicKey.importSpkiKey(keyData, curve); + return EcdhPublicKey._(impl); } /// Import ECDH public key in [JSON Web Key][1] format. @@ -341,16 +364,17 @@ abstract class EcdhPublicKey { static Future importJsonWebKey( Map jwk, EllipticCurve curve, - ) { - return impl.ecdhPublicKey_importJsonWebKey(jwk, curve); + ) async { + final impl = await webCryptImpl.ecdhPublicKey.importJsonWebKey(jwk, curve); + return EcdhPublicKey._(impl); } - Future exportRawKey(); + Future exportRawKey() => _impl.exportRawKey(); /// Note: Due to bug in Chrome/BoringSSL, SPKI keys exported from Firefox < 72 /// cannot be imported in Chrome/BoringSSL. /// See compatibility section in [EcdhPublicKey.importSpkiKey]. - Future exportSpkiKey(); + Future exportSpkiKey() => _impl.exportSpkiKey(); /// Export the [EcdhPublicKey] as a [JSON Web Key][1]. /// @@ -382,5 +406,5 @@ abstract class EcdhPublicKey { /// } /// ``` /// [1]: https://www.rfc-editor.org/rfc/rfc7518.html#section-6.2 - Future> exportJsonWebKey(); + Future> exportJsonWebKey() => _impl.exportJsonWebKey(); }