diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a6a88353..cbac6a02 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,14 +56,15 @@ jobs: - run: flutter test --platform chrome - run: flutter test integration_test/webcrypto_test.dart -d macos working-directory: ./example - - uses: nanasess/setup-chromedriver@v2 - - name: Run integration_test with chromedriver - working-directory: ./example - run: | - ../tool/with-chromedriver.sh flutter drive \ - --driver=test_driver/integration_test.dart \ - --target=integration_test/webcrypto_test.dart \ - -d chrome + # TODO: Enable chromdriver testing on MacOS when it works reliably + #- uses: nanasess/setup-chromedriver@v2 + #- name: Run integration_test with chromedriver + # working-directory: ./example + # run: | + # ../tool/with-chromedriver.sh flutter drive \ + # --driver=test_driver/integration_test.dart \ + # --target=integration_test/webcrypto_test.dart \ + # -d chrome - run: flutter pub run test -p vm,chrome # TODO: Enable firefox if it works windows: name: webcrypto on Windows desktop / Chrome / Firefox diff --git a/darwin/webcrypto.podspec b/darwin/webcrypto.podspec index 13c9d766..14a1d006 100644 --- a/darwin/webcrypto.podspec +++ b/darwin/webcrypto.podspec @@ -28,7 +28,6 @@ Pod::Spec.new do |s| s.compiler_flags = [ '-DOPENSSL_NO_ASM', '-DDOPENSSL_SMALL', - '-GCC_WARN_INHIBIT_ALL_WARNINGS', '-w', ] diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 0a9de12b..818d3854 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,10 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { compileSdkVersion flutter.compileSdkVersion ndkVersion flutter.ndkVersion @@ -66,5 +63,4 @@ flutter { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } diff --git a/example/android/build.gradle b/example/android/build.gradle index 882fae21..bc157bd1 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,16 +1,3 @@ -buildscript { - ext.kotlin_version = '1.6.21' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.4.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 44e62bcf..82990c39 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,11 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.4.1" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" diff --git a/lib/src/impl_ffi/impl_ffi.aescbc.dart b/lib/src/impl_ffi/impl_ffi.aescbc.dart index 34d9ae48..55a5e5a1 100644 --- a/lib/src/impl_ffi/impl_ffi.aescbc.dart +++ b/lib/src/impl_ffi/impl_ffi.aescbc.dart @@ -114,7 +114,7 @@ final class _StaticAesCbcSecretKeyImpl implements StaticAesCbcSecretKeyImpl { } } -final class _AesCbcSecretKeyImpl extends AesCbcSecretKeyImpl { +final class _AesCbcSecretKeyImpl implements AesCbcSecretKeyImpl { final Uint8List _key; _AesCbcSecretKeyImpl(this._key); diff --git a/lib/src/impl_ffi/impl_ffi.aesctr.dart b/lib/src/impl_ffi/impl_ffi.aesctr.dart index cfd8599f..709b5fff 100644 --- a/lib/src/impl_ffi/impl_ffi.aesctr.dart +++ b/lib/src/impl_ffi/impl_ffi.aesctr.dart @@ -221,7 +221,7 @@ final class _StaticAesCtrSecretKeyImpl implements StaticAesCtrSecretKeyImpl { } } -final class _AesCtrSecretKeyImpl extends AesCtrSecretKeyImpl { +final class _AesCtrSecretKeyImpl implements AesCtrSecretKeyImpl { final Uint8List _key; _AesCtrSecretKeyImpl(this._key); diff --git a/lib/src/impl_ffi/impl_ffi.aesgcm.dart b/lib/src/impl_ffi/impl_ffi.aesgcm.dart index 5adc463d..7922dd1a 100644 --- a/lib/src/impl_ffi/impl_ffi.aesgcm.dart +++ b/lib/src/impl_ffi/impl_ffi.aesgcm.dart @@ -16,19 +16,19 @@ part of 'impl_ffi.dart'; -Future aesGcm_importRawKey(List keyData) async => - _AesGcmSecretKey(_aesImportRawKey(keyData)); +Future aesGcm_importRawKey(List keyData) async => + _AesGcmSecretKeyImpl(_aesImportRawKey(keyData)); -Future aesGcm_importJsonWebKey( +Future aesGcm_importJsonWebKey( Map jwk, ) async => - _AesGcmSecretKey(_aesImportJwkKey( + _AesGcmSecretKeyImpl(_aesImportJwkKey( jwk, expectedJwkAlgSuffix: 'GCM', )); -Future aesGcm_generateKey(int length) async => - _AesGcmSecretKey(_aesGenerateKey(length)); +Future aesGcm_generateKey(int length) async => + _AesGcmSecretKeyImpl(_aesGenerateKey(length)); Future _aesGcmEncryptDecrypt( List key, @@ -111,9 +111,28 @@ Future _aesGcmEncryptDecrypt( }); } -class _AesGcmSecretKey implements AesGcmSecretKey { +final class _StaticAesGcmSecretKeyImpl implements StaticAesGcmSecretKeyImpl { + const _StaticAesGcmSecretKeyImpl(); + + @override + Future importRawKey(List keyData) async { + return await aesGcm_importRawKey(keyData); + } + + @override + Future importJsonWebKey(Map jwk) async { + return await aesGcm_importJsonWebKey(jwk); + } + + @override + Future generateKey(int length) async { + return await aesGcm_generateKey(length); + } +} + +final class _AesGcmSecretKeyImpl implements AesGcmSecretKeyImpl { final Uint8List _key; - _AesGcmSecretKey(this._key); + _AesGcmSecretKeyImpl(this._key); @override String toString() { diff --git a/lib/src/impl_ffi/impl_ffi.dart b/lib/src/impl_ffi/impl_ffi.dart index 85fd6ccf..591412a8 100644 --- a/lib/src/impl_ffi/impl_ffi.dart +++ b/lib/src/impl_ffi/impl_ffi.dart @@ -86,4 +86,13 @@ final class _WebCryptoImpl implements WebCryptoImpl { @override final ecdhPublicKey = const _StaticEcdhPublicKeyImpl(); + + @override + final aesGcmSecretKey = const _StaticAesGcmSecretKeyImpl(); + + @override + final hmacSecretKey = const _StaticHmacSecretKeyImpl(); + + @override + final pbkdf2SecretKey = const _StaticPbkdf2SecretKeyImpl(); } diff --git a/lib/src/impl_ffi/impl_ffi.hmac.dart b/lib/src/impl_ffi/impl_ffi.hmac.dart index 72dfdbed..cb2d22f1 100644 --- a/lib/src/impl_ffi/impl_ffi.hmac.dart +++ b/lib/src/impl_ffi/impl_ffi.hmac.dart @@ -49,18 +49,18 @@ String _hmacJwkAlgFromHash(_Hash hash) { throw UnsupportedError('hash is not supported'); } -Future hmacSecretKey_importRawKey( +Future hmacSecretKey_importRawKey( List keyData, Hash hash, { int? length, }) async { - return _HmacSecretKey( + return _HmacSecretKeyImpl( _asUint8ListZeroedToBitLength(keyData, length), _Hash.fromHash(hash), ); } -Future hmacSecretKey_importJsonWebKey( +Future hmacSecretKey_importJsonWebKey( Map jwk, Hash hash, { int? length, @@ -86,7 +86,7 @@ Future hmacSecretKey_importJsonWebKey( return hmacSecretKey_importRawKey(keyData, hash, length: length); } -Future hmacSecretKey_generateKey( +Future hmacSecretKey_generateKey( Hash hash, { int? length, }) async { @@ -95,25 +95,44 @@ Future hmacSecretKey_generateKey( final keyData = Uint8List((length / 8).ceil()); fillRandomBytes(keyData); - return _HmacSecretKey( + return _HmacSecretKeyImpl( _asUint8ListZeroedToBitLength(keyData, length), h, ); } -class _HmacSecretKey implements HmacSecretKey { +final class _StaticHmacSecretKeyImpl implements StaticHmacSecretKeyImpl { + const _StaticHmacSecretKeyImpl(); + + @override + Future importRawKey(List keyData, Hash hash, {int? length}) { + return hmacSecretKey_importRawKey(keyData, hash, length: length); + } + + @override + Future importJsonWebKey(Map jwk, Hash hash, {int? length}) { + return hmacSecretKey_importJsonWebKey(jwk, hash, length: length); + } + + @override + Future generateKey(Hash hash, {int? length = 32}) { + return hmacSecretKey_generateKey(hash, length: length); + } +} + +final class _HmacSecretKeyImpl implements HmacSecretKeyImpl { final _Hash _hash; final Uint8List _keyData; - _HmacSecretKey(this._keyData, this._hash); + _HmacSecretKeyImpl(this._keyData, this._hash); @override String toString() { - return 'Instance of \'HmacSecretKey\''; + return 'Instance of \'HmacSecretKeyImpl\''; } @override - Future signBytes(List data) => signStream(Stream.value(data)); + Future signBytes(List data) => signStream(Stream.value(data)); @override Future signStream(Stream> data) { diff --git a/lib/src/impl_ffi/impl_ffi.pbkdf2.dart b/lib/src/impl_ffi/impl_ffi.pbkdf2.dart index 153eaa26..c9d4b16f 100644 --- a/lib/src/impl_ffi/impl_ffi.pbkdf2.dart +++ b/lib/src/impl_ffi/impl_ffi.pbkdf2.dart @@ -16,14 +16,23 @@ part of 'impl_ffi.dart'; -Future pbkdf2SecretKey_importRawKey(List keyData) async { - return _Pbkdf2SecretKey(Uint8List.fromList(keyData)); +Future pbkdf2SecretKey_importRawKey(List keyData) async { + return _Pbkdf2SecretKeyImpl(Uint8List.fromList(keyData)); } -class _Pbkdf2SecretKey implements Pbkdf2SecretKey { +final class _StaticPbkdf2SecretKeyImpl implements StaticPbkdf2SecretKeyImpl { + const _StaticPbkdf2SecretKeyImpl(); + + @override + Future importRawKey(List keyData) { + return pbkdf2SecretKey_importRawKey(keyData); + } +} + +final class _Pbkdf2SecretKeyImpl implements Pbkdf2SecretKeyImpl { final Uint8List _key; - _Pbkdf2SecretKey(this._key); + _Pbkdf2SecretKeyImpl(this._key); @override String toString() { diff --git a/lib/src/impl_interface/impl_interface.aescbc.dart b/lib/src/impl_interface/impl_interface.aescbc.dart index 47e5a382..543dc87d 100644 --- a/lib/src/impl_interface/impl_interface.aescbc.dart +++ b/lib/src/impl_interface/impl_interface.aescbc.dart @@ -20,7 +20,7 @@ abstract interface class StaticAesCbcSecretKeyImpl { Future generateKey(int length); } -abstract class AesCbcSecretKeyImpl { +abstract interface class AesCbcSecretKeyImpl { Future encryptBytes(List data, List iv); Future decryptBytes(List data, List iv); Stream encryptStream(Stream> data, List iv); diff --git a/lib/src/impl_interface/impl_interface.aesctr.dart b/lib/src/impl_interface/impl_interface.aesctr.dart index 9714eecc..65db9dce 100644 --- a/lib/src/impl_interface/impl_interface.aesctr.dart +++ b/lib/src/impl_interface/impl_interface.aesctr.dart @@ -20,7 +20,7 @@ abstract interface class StaticAesCtrSecretKeyImpl { Future generateKey(int length); } -abstract class AesCtrSecretKeyImpl { +abstract interface class AesCtrSecretKeyImpl { Future encryptBytes(List data, List counter, int length); Future decryptBytes(List data, List counter, int length); Stream encryptStream(Stream> data, List counter, int length); diff --git a/lib/src/impl_interface/impl_interface.aesgcm.dart b/lib/src/impl_interface/impl_interface.aesgcm.dart new file mode 100644 index 00000000..fa0afb88 --- /dev/null +++ b/lib/src/impl_interface/impl_interface.aesgcm.dart @@ -0,0 +1,28 @@ +// 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 StaticAesGcmSecretKeyImpl { + Future importRawKey(List keyData); + Future importJsonWebKey(Map jwk); + Future generateKey(int length); +} + +abstract interface class AesGcmSecretKeyImpl { + Future encryptBytes(List data, List iv, {List? additionalData, int? tagLength}); + Future decryptBytes(List data, List iv, {List? additionalData, int? tagLength}); + Future exportRawKey(); + Future> exportJsonWebKey(); +} diff --git a/lib/src/impl_interface/impl_interface.dart b/lib/src/impl_interface/impl_interface.dart index 51eeac51..d9e9f129 100644 --- a/lib/src/impl_interface/impl_interface.dart +++ b/lib/src/impl_interface/impl_interface.dart @@ -23,6 +23,14 @@ part 'impl_interface.aescbc.dart'; part 'impl_interface.aesctr.dart'; part 'impl_interface.ecdh.dart'; +import 'package:webcrypto/webcrypto.dart'; + + +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'; /// Interface to be provided by platform implementations. /// @@ -45,4 +53,7 @@ abstract interface class WebCryptoImpl { StaticAesCtrSecretKeyImpl get aesCtrSecretKey; StaticEcdhPrivateKeyImpl get ecdhPrivateKey; StaticEcdhPublicKeyImpl get ecdhPublicKey; + StaticAesGcmSecretKeyImpl get aesGcmSecretKey; + StaticHmacSecretKeyImpl get hmacSecretKey; + StaticPbkdf2SecretKeyImpl get pbkdf2SecretKey; } diff --git a/lib/src/impl_interface/impl_interface.hmac.dart b/lib/src/impl_interface/impl_interface.hmac.dart new file mode 100644 index 00000000..ca12715c --- /dev/null +++ b/lib/src/impl_interface/impl_interface.hmac.dart @@ -0,0 +1,30 @@ +// 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 StaticHmacSecretKeyImpl { + Future importRawKey(List keyData, Hash hash, {int? length}); + Future importJsonWebKey(Map jwk, Hash hash, {int? length}); + Future generateKey(Hash hash, {int? length}); +} + +abstract interface class HmacSecretKeyImpl { + Future signBytes(List data); + Future verifyBytes(List signature, List data); + Future signStream(Stream> data); + Future verifyStream(List signature, Stream> data); + Future exportRawKey(); + Future> exportJsonWebKey(); +} diff --git a/lib/src/impl_interface/impl_interface.pbkdf2.dart b/lib/src/impl_interface/impl_interface.pbkdf2.dart new file mode 100644 index 00000000..26f1d1ea --- /dev/null +++ b/lib/src/impl_interface/impl_interface.pbkdf2.dart @@ -0,0 +1,23 @@ +// 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 StaticPbkdf2SecretKeyImpl { + Future importRawKey(List keyData); +} + +abstract interface class Pbkdf2SecretKeyImpl { + Future deriveBits(int length, Hash hash, List salt, int iterations); +} diff --git a/lib/src/impl_js/impl_js.aesctr.dart b/lib/src/impl_js/impl_js.aesctr.dart index 895701f5..3a97ff80 100644 --- a/lib/src/impl_js/impl_js.aesctr.dart +++ b/lib/src/impl_js/impl_js.aesctr.dart @@ -66,7 +66,7 @@ final class _StaticAesCtrSecretKeyImpl implements StaticAesCtrSecretKeyImpl { } } -final class _AesCtrSecretKeyImpl extends AesCtrSecretKeyImpl { +final class _AesCtrSecretKeyImpl implements AesCtrSecretKeyImpl { final subtle.JSCryptoKey _key; _AesCtrSecretKeyImpl(this._key); diff --git a/lib/src/impl_js/impl_js.aesgcm.dart b/lib/src/impl_js/impl_js.aesgcm.dart index d1dae77b..d630e15a 100644 --- a/lib/src/impl_js/impl_js.aesgcm.dart +++ b/lib/src/impl_js/impl_js.aesgcm.dart @@ -18,8 +18,8 @@ part of 'impl_js.dart'; const _aesGcmAlgorithm = subtle.Algorithm(name: 'AES-GCM'); -Future aesGcm_importRawKey(List keyData) async { - return _AesGcmSecretKey(await _importKey( +Future aesGcm_importRawKey(List keyData) async { + return _AesGcmSecretKeyImpl(await _importKey( 'raw', keyData, _aesGcmAlgorithm, @@ -28,10 +28,10 @@ Future aesGcm_importRawKey(List keyData) async { )); } -Future aesGcm_importJsonWebKey( +Future aesGcm_importJsonWebKey( Map jwk, ) async { - return _AesGcmSecretKey(await _importJsonWebKey( + return _AesGcmSecretKeyImpl(await _importJsonWebKey( jwk, _aesGcmAlgorithm, _usagesEncryptDecrypt, @@ -39,17 +39,36 @@ Future aesGcm_importJsonWebKey( )); } -Future aesGcm_generateKey(int length) async { - return _AesGcmSecretKey(await _generateKey( +Future aesGcm_generateKey(int length) async { + return _AesGcmSecretKeyImpl(await _generateKey( _aesGcmAlgorithm.update(length: length), _usagesEncryptDecrypt, 'secret', )); } -class _AesGcmSecretKey implements AesGcmSecretKey { +final class _StaticAesGcmSecretKeyImpl implements StaticAesGcmSecretKeyImpl { + const _StaticAesGcmSecretKeyImpl(); + + @override + Future importRawKey(List keyData) async { + return await aesGcm_importRawKey(keyData); + } + + @override + Future importJsonWebKey(Map jwk) async { + return await aesGcm_importJsonWebKey(jwk); + } + + @override + Future generateKey(int length) async { + return await aesGcm_generateKey(length); + } +} + +final class _AesGcmSecretKeyImpl implements AesGcmSecretKeyImpl { final subtle.JSCryptoKey _key; - _AesGcmSecretKey(this._key); + _AesGcmSecretKeyImpl(this._key); @override String toString() { diff --git a/lib/src/impl_js/impl_js.dart b/lib/src/impl_js/impl_js.dart index 042b4921..97a955d6 100644 --- a/lib/src/impl_js/impl_js.dart +++ b/lib/src/impl_js/impl_js.dart @@ -72,4 +72,13 @@ final class _WebCryptoImpl implements WebCryptoImpl { @override final ecdhPublicKey = const _StaticEcdhPublicKeyImpl(); + + @override + final aesGcmSecretKey = const _StaticAesGcmSecretKeyImpl(); + + @override + final hmacSecretKey = const _StaticHmacSecretKeyImpl(); + + @override + final pbkdf2SecretKey = const _StaticPbkdf2SecretKeyImpl(); } diff --git a/lib/src/impl_js/impl_js.hmac.dart b/lib/src/impl_js/impl_js.hmac.dart index 7857a71c..d3403728 100644 --- a/lib/src/impl_js/impl_js.hmac.dart +++ b/lib/src/impl_js/impl_js.hmac.dart @@ -18,12 +18,12 @@ part of 'impl_js.dart'; const _hmacAlgorithm = subtle.Algorithm(name: 'HMAC'); -Future hmacSecretKey_importRawKey( +Future hmacSecretKey_importRawKey( List keyData, Hash hash, { int? length, }) async { - return _HmacSecretKey(await _importKey( + return _HmacSecretKeyImpl(await _importKey( 'raw', keyData, length == null @@ -41,12 +41,12 @@ Future hmacSecretKey_importRawKey( )); } -Future hmacSecretKey_importJsonWebKey( +Future hmacSecretKey_importJsonWebKey( Map jwk, Hash hash, { int? length, }) async { - return _HmacSecretKey(await _importJsonWebKey( + return _HmacSecretKeyImpl(await _importJsonWebKey( jwk, length == null ? subtle.Algorithm( @@ -63,9 +63,9 @@ Future hmacSecretKey_importJsonWebKey( )); } -Future hmacSecretKey_generateKey(Hash hash, +Future hmacSecretKey_generateKey(Hash hash, {int? length}) async { - return _HmacSecretKey(await _generateKey( + return _HmacSecretKeyImpl(await _generateKey( length == null ? subtle.Algorithm( name: 'HMAC', @@ -81,9 +81,30 @@ Future hmacSecretKey_generateKey(Hash hash, )); } -class _HmacSecretKey implements HmacSecretKey { +final class _StaticHmacSecretKeyImpl implements StaticHmacSecretKeyImpl { + const _StaticHmacSecretKeyImpl(); + + @override + Future importRawKey(List keyData, Hash hash, + {int? length}) { + return hmacSecretKey_importRawKey(keyData, hash, length: length); + } + + @override + Future importJsonWebKey(Map jwk, Hash hash, + {int? length}) { + return hmacSecretKey_importJsonWebKey(jwk, hash, length: length); + } + + @override + Future generateKey(Hash hash, {int? length = 32}) { + return hmacSecretKey_generateKey(hash, length: length); + } +} + +final class _HmacSecretKeyImpl implements HmacSecretKeyImpl { final subtle.JSCryptoKey _key; - _HmacSecretKey(this._key); + _HmacSecretKeyImpl(this._key); @override String toString() { diff --git a/lib/src/impl_js/impl_js.pbkdf2.dart b/lib/src/impl_js/impl_js.pbkdf2.dart index b8479971..8f72f857 100644 --- a/lib/src/impl_js/impl_js.pbkdf2.dart +++ b/lib/src/impl_js/impl_js.pbkdf2.dart @@ -18,8 +18,8 @@ part of 'impl_js.dart'; const _pbkdf2AlgorithmName = 'PBKDF2'; -Future pbkdf2SecretKey_importRawKey(List keyData) async { - return _Pbkdf2SecretKey(await _importKey( +Future pbkdf2SecretKey_importRawKey(List keyData) async { + return _Pbkdf2SecretKeyImpl(await _importKey( 'raw', keyData, const subtle.Algorithm(name: _pbkdf2AlgorithmName), @@ -31,9 +31,18 @@ Future pbkdf2SecretKey_importRawKey(List keyData) async { )); } -class _Pbkdf2SecretKey implements Pbkdf2SecretKey { +final class _StaticPbkdf2SecretKeyImpl implements StaticPbkdf2SecretKeyImpl { + const _StaticPbkdf2SecretKeyImpl(); + + @override + Future importRawKey(List keyData) { + return pbkdf2SecretKey_importRawKey(keyData); + } +} + +final class _Pbkdf2SecretKeyImpl implements Pbkdf2SecretKeyImpl { final subtle.JSCryptoKey _key; - _Pbkdf2SecretKey(this._key); + _Pbkdf2SecretKeyImpl(this._key); @override String toString() { diff --git a/lib/src/impl_stub.dart b/lib/src/impl_stub.dart index 475e8210..b12c0882 100644 --- a/lib/src/impl_stub.dart +++ b/lib/src/impl_stub.dart @@ -45,23 +45,6 @@ const Hash sha256 = _UnimplementedHash(); const Hash sha384 = _UnimplementedHash(); const Hash sha512 = _UnimplementedHash(); -//---------------------- HMAC -Future hmacSecretKey_importRawKey( - List keyData, - Hash hash, { - int? length, -}) => - throw _notImplemented; - -Future hmacSecretKey_importJsonWebKey( - Map jwk, - Hash hash, { - int? length, -}) => - throw _notImplemented; - -Future hmacSecretKey_generateKey(Hash hash, {int? length}) => - throw _notImplemented; //---------------------- RSASSA_PKCS1_v1_5 @@ -207,14 +190,6 @@ Future rsaOaepPublicKey_importJsonWebKey( //---------------------- AES-GCM -Future aesGcm_importRawKey(List keyData) => - throw _notImplemented; - -Future aesGcm_importJsonWebKey(Map jwk) => - throw _notImplemented; - -Future aesGcm_generateKey(int length) => throw _notImplemented; - //---------------------- ECDH //---------------------- HKDF @@ -224,5 +199,3 @@ Future hkdfSecretKey_importRawKey(List keyData) => //---------------------- PBKDF2 -Future pbkdf2SecretKey_importRawKey(List keyData) => - throw _notImplemented; diff --git a/lib/src/impl_stub/impl_stub.aesgcm.dart b/lib/src/impl_stub/impl_stub.aesgcm.dart new file mode 100644 index 00000000..7f98490e --- /dev/null +++ b/lib/src/impl_stub/impl_stub.aesgcm.dart @@ -0,0 +1,31 @@ +// 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 _StaticAesGcmSecretKeyImpl implements StaticAesGcmSecretKeyImpl { + const _StaticAesGcmSecretKeyImpl(); + + @override + Future importRawKey(List keyData) => + throw UnimplementedError('Not implemented'); + + @override + Future importJsonWebKey(Map jwk) => + throw UnimplementedError('Not implemented'); + + @override + Future generateKey(int length) => + throw UnimplementedError('Not implemented'); +} diff --git a/lib/src/impl_stub/impl_stub.dart b/lib/src/impl_stub/impl_stub.dart index 7b043ecb..515f57b0 100644 --- a/lib/src/impl_stub/impl_stub.dart +++ b/lib/src/impl_stub/impl_stub.dart @@ -15,11 +15,14 @@ library webcrypto.impl_stub; import 'package:webcrypto/src/impl_interface/impl_interface.dart'; -import '../webcrypto/webcrypto.dart'; +import 'package:webcrypto/webcrypto.dart'; part 'impl_stub.aescbc.dart'; part 'impl_stub.aesctr.dart'; +part 'impl_stub.aesgcm.dart'; part 'impl_stub.ecdh.dart'; +part 'impl_stub.hmac.dart'; +part 'impl_stub.pbkdf2.dart'; const WebCryptoImpl webCryptImpl = _WebCryptoImpl(); @@ -37,4 +40,13 @@ final class _WebCryptoImpl implements WebCryptoImpl { @override final ecdhPublicKey = const _StaticEcdhPublicKeyImpl(); + + @override + final aesGcmSecretKey = const _StaticAesGcmSecretKeyImpl(); + + @override + final hmacSecretKey = const _StaticHmacSecretKeyImpl(); + + @override + final pbkdf2SecretKey = const _StaticPbkdf2SecretKeyImpl(); } diff --git a/lib/src/impl_stub/impl_stub.hmac.dart b/lib/src/impl_stub/impl_stub.hmac.dart new file mode 100644 index 00000000..c5deb07d --- /dev/null +++ b/lib/src/impl_stub/impl_stub.hmac.dart @@ -0,0 +1,34 @@ +// 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 _StaticHmacSecretKeyImpl implements StaticHmacSecretKeyImpl { + const _StaticHmacSecretKeyImpl(); + + @override + Future importRawKey(List keyData, Hash hash, {int? length}) { + throw UnimplementedError('Not implemented'); + } + + @override + Future importJsonWebKey(Map jwk, Hash hash, {int? length}) { + throw UnimplementedError('Not implemented'); + } + + @override + Future generateKey(Hash hash, {int? length = 32}) { + throw UnimplementedError('Not implemented'); + } +} diff --git a/lib/src/impl_stub/impl_stub.pbkdf2.dart b/lib/src/impl_stub/impl_stub.pbkdf2.dart new file mode 100644 index 00000000..e1fc586f --- /dev/null +++ b/lib/src/impl_stub/impl_stub.pbkdf2.dart @@ -0,0 +1,24 @@ +// 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 _StaticPbkdf2SecretKeyImpl implements StaticPbkdf2SecretKeyImpl { + const _StaticPbkdf2SecretKeyImpl(); + + @override + Future importRawKey(List keyData) { + throw UnimplementedError('Not implemented'); + } +} diff --git a/lib/src/webcrypto/webcrypto.aesgcm.dart b/lib/src/webcrypto/webcrypto.aesgcm.dart index c27f09b1..f5acbf53 100644 --- a/lib/src/webcrypto/webcrypto.aesgcm.dart +++ b/lib/src/webcrypto/webcrypto.aesgcm.dart @@ -32,9 +32,10 @@ part of 'webcrypto.dart'; /// [1]: https://csrc.nist.gov/pubs/sp/800/38/d/final /// [2]: https://tools.ietf.org/html/rfc7517 /// [3]: https://en.wikipedia.org/wiki/Authenticated_encryption -@sealed -abstract class AesGcmSecretKey { - AesGcmSecretKey._(); // keep the constructor private. +final class AesGcmSecretKey { + final AesGcmSecretKeyImpl _impl; + + AesGcmSecretKey._(this._impl); // keep the constructor private. /// Import an [AesGcmSecretKey] from raw [keyData]. /// @@ -66,8 +67,9 @@ abstract class AesGcmSecretKey { /// // Decrypt message (requires the same iv) /// print(utf8.decode(await k.decryptBytes(c, iv))); // hello world /// ``` - static Future importRawKey(List keyData) { - return impl.aesGcm_importRawKey(keyData); + static Future importRawKey(List keyData) async { + final impl = await webCryptImpl.aesGcmSecretKey.importRawKey(keyData); + return AesGcmSecretKey._(impl); } /// Import an [AesGcmSecretKey] from [JSON Web Key][1]. @@ -102,8 +104,9 @@ abstract class AesGcmSecretKey { /// ``` /// /// [1]: https://tools.ietf.org/html/rfc7517 - static Future importJsonWebKey(Map jwk) { - return impl.aesGcm_importJsonWebKey(jwk); + static Future importJsonWebKey(Map jwk) async { + final impl = await webCryptImpl.aesGcmSecretKey.importJsonWebKey(jwk); + return AesGcmSecretKey._(impl); } /// Generate a random [AesGcmSecretKey]. @@ -122,8 +125,9 @@ abstract class AesGcmSecretKey { /// // Generate a new random AES-GCM secret key for AES-256. /// final key = await AesGcmSecretKey.generate(256); /// ``` - static Future generateKey(int length) { - return impl.aesGcm_generateKey(length); + static Future generateKey(int length) async { + final impl = await webCryptImpl.aesGcmSecretKey.generateKey(length); + return AesGcmSecretKey._(impl); } /// Encrypt [data] with this [AesCbcSecretKey] using AES in @@ -218,7 +222,8 @@ abstract class AesGcmSecretKey { List iv, { List? additionalData, int? tagLength = 128, - }); + }) => + _impl.encryptBytes(data, iv, additionalData: additionalData, tagLength: tagLength); // TODO: Document this method, notice that [data] must be concatenation of // ciphertext and authentication tag. @@ -228,7 +233,8 @@ abstract class AesGcmSecretKey { List iv, { List? additionalData, int? tagLength = 128, - }); + }) => + _impl.decryptBytes(data, iv, additionalData: additionalData, tagLength: tagLength); /// Export [AesGcmSecretKey] as raw bytes. /// @@ -250,7 +256,7 @@ abstract class AesGcmSecretKey { /// // If we wanted to we could import the key as follows: /// // key = await AesGcmSecretKey.importRawKey(secretBytes); /// ``` - Future exportRawKey(); + Future exportRawKey() => _impl.exportRawKey(); /// Export [AesGcmSecretKey] as [JSON Web Key][1]. /// @@ -274,5 +280,5 @@ abstract class AesGcmSecretKey { /// ``` /// /// [1]: https://tools.ietf.org/html/rfc7517 - Future> exportJsonWebKey(); + Future> exportJsonWebKey() => _impl.exportJsonWebKey(); } diff --git a/lib/src/webcrypto/webcrypto.hmac.dart b/lib/src/webcrypto/webcrypto.hmac.dart index 04b046bd..e5256754 100644 --- a/lib/src/webcrypto/webcrypto.hmac.dart +++ b/lib/src/webcrypto/webcrypto.hmac.dart @@ -48,9 +48,10 @@ part of 'webcrypto.dart'; /// ``` /// /// [1]: https://doi.org/10.6028/NIST.FIPS.180-4 -@sealed -abstract class HmacSecretKey { - HmacSecretKey._(); // keep the constructor private. +final class HmacSecretKey { + final HmacSecretKeyImpl _impl; + + HmacSecretKey._(this._impl); // keep the constructor private. /// Import [HmacSecretKey] from raw [keyData]. /// @@ -76,7 +77,7 @@ abstract class HmacSecretKey { List keyData, Hash hash, { int? length, - }) { + }) async { // These limitations are given in Web Cryptography Spec: // https://www.w3.org/TR/WebCryptoAPI/#hmac-operations if (length != null && length > keyData.length * 8) { @@ -92,7 +93,13 @@ abstract class HmacSecretKey { ); } - return impl.hmacSecretKey_importRawKey(keyData, hash, length: length); + final impl = await webCryptImpl.hmacSecretKey.importRawKey( + keyData, + hash, + length: length, + ); + + return HmacSecretKey._(impl); } /// Import [HmacSecretKey] from [JSON Web Key][1]. @@ -147,7 +154,7 @@ abstract class HmacSecretKey { // Note. it's not yet clear if JWK always contains key parameters. Hash hash, { int? length, - }) { + }) async { /* TODO: Validate these in the native implememtation // These limitations are given in Web Cryptography Spec: @@ -165,7 +172,9 @@ abstract class HmacSecretKey { ); }*/ - return impl.hmacSecretKey_importJsonWebKey(jwk, hash, length: length); + final impl = await webCryptImpl.hmacSecretKey.importJsonWebKey(jwk, hash); + + return HmacSecretKey._(impl); } /// Generate random [HmacSecretKey]. @@ -181,12 +190,14 @@ abstract class HmacSecretKey { /// // Generate a new random HMAC secret key. /// final key = await HmacSecretKey.generate(Hash.sha256); /// ``` - static Future generateKey(Hash hash, {int? length}) { + static Future generateKey(Hash hash, {int? length}) async { if (length != null && length <= 0) { throw ArgumentError.value(length, 'length', 'must be positive'); } - return impl.hmacSecretKey_generateKey(hash, length: length); + final impl = await webCryptImpl.hmacSecretKey.generateKey(hash, length: length); + + return HmacSecretKey._(impl); } /// Compute an HMAC signature of given [data]. @@ -219,7 +230,7 @@ abstract class HmacSecretKey { /// instead, these methods computes a signature and does a /// fixed-time comparison. /// {@endtemplate} - Future signBytes(List data); + Future signBytes(List data) async => await _impl.signBytes(data); /// Compute an HMAC signature of given [data] stream. /// @@ -246,7 +257,8 @@ abstract class HmacSecretKey { /// ``` /// /// {@macro HMAC-sign:do-not-validate-using-sign} - Future signStream(Stream> data); + Future signStream(Stream> data) => + _impl.signStream(data); /// Verify the HMAC [signature] of given [data]. /// @@ -282,7 +294,8 @@ abstract class HmacSecretKey { /// ); /// assert(result == true, 'this signature should be valid'); /// ``` - Future verifyBytes(List signature, List data); + Future verifyBytes(List signature, List data) => + _impl.verifyBytes(signature, data); /// Verify the HMAC [signature] of given [data] stream. /// @@ -313,7 +326,8 @@ abstract class HmacSecretKey { /// ])); /// assert(result == true, 'this signature should be valid'); /// ``` - Future verifyStream(List signature, Stream> data); + Future verifyStream(List signature, Stream> data) => + _impl.verifyStream(signature, data); /// Export [HmacSecretKey] as raw bytes. /// @@ -336,7 +350,7 @@ abstract class HmacSecretKey { /// // If we wanted to we could import the key as follows: /// // key = await HmacSecretKey.importRawKey(secretBytes, Hash.sha256); /// ``` - Future exportRawKey(); + Future exportRawKey() => _impl.exportRawKey(); /// Export [HmacSecretKey] as [JSON Web Key][1]. /// @@ -360,5 +374,5 @@ abstract class HmacSecretKey { /// ``` /// /// [1]: https://tools.ietf.org/html/rfc7517 - Future> exportJsonWebKey(); + Future> exportJsonWebKey() => _impl.exportJsonWebKey(); } diff --git a/lib/src/webcrypto/webcrypto.pbkdf2.dart b/lib/src/webcrypto/webcrypto.pbkdf2.dart index 4c6892bf..f35fc5ee 100644 --- a/lib/src/webcrypto/webcrypto.pbkdf2.dart +++ b/lib/src/webcrypto/webcrypto.pbkdf2.dart @@ -50,17 +50,20 @@ part of 'webcrypto.dart'; /// /// [1]: https://tools.ietf.org/html/rfc8018 // TODO: Rewrite all RFC links to use https://www.rfc-editor.org/rfc/rfcXXXX -@sealed -abstract class Pbkdf2SecretKey { - Pbkdf2SecretKey._(); // keep the constructor private. + +final class Pbkdf2SecretKey { + final Pbkdf2SecretKeyImpl _impl; + + Pbkdf2SecretKey._(this._impl); // keep the constructor private. /// Import [Pbkdf2SecretKey] from raw [keyData]. /// /// Creates a [Pbkdf2SecretKey] for key derivation using [keyData]. /// /// {@macro Pbkdf2SecretKey:example} - static Future importRawKey(List keyData) { - return impl.pbkdf2SecretKey_importRawKey(keyData); + static Future importRawKey(List keyData) async { + final impl = await webCryptImpl.pbkdf2SecretKey.importRawKey(keyData); + return Pbkdf2SecretKey._(impl); } /// Derive key from [salt] and password specified as `keyData` in @@ -90,5 +93,6 @@ abstract class Pbkdf2SecretKey { Hash hash, List salt, int iterations, - ); + ) => + _impl.deriveBits(length, hash, salt, iterations); }