google / webcrypto.dart Goto Github PK
View Code? Open in Web Editor NEWCross-platform implementation of Web Cryptography APIs
Home Page: https://pub.dev/packages/webcrypto
License: Apache License 2.0
Cross-platform implementation of Web Cryptography APIs
Home Page: https://pub.dev/packages/webcrypto
License: Apache License 2.0
We should use final class
for everything in the public API.
Motiviation:
In practice, we might have to do something that we export from src/webcrypto/webcrypto.dart
and something we only export to other libraries inside this package.
// This is exported from lib/webcrypto.dart
// This means that in lib/webcrypto we have to use "show"
// to avoid exporting KeyPairBase.
// Example:
// export src/webcrypto.dart show KeyPair;
abstract final class KeyPair<S, T> {
/// Private key for [publicKey].
S get privateKey;
/// Public key matching [privateKey].
T get publicKey;
}
// This is not exported from lib/webcrypto.dart
// but implementations for different platforms can import this from
// src/webcrypto/webcrypto.dart
// and extend it inorder to implement KeyPair
abstract base class KeyPairBase<S, T> extends KeyPair<S, T> {}
The code in dartdoc examples mixes imports and statement-level code.
Ideally it would contain self-contained code that one could copy and run for one-self. And that we could extract programmatically and format, analyze etc.
To use another plugin (google MLKit), I had to set :linkage => :static
in the Podifle of my app. However, since I did this, I get the same error webcrypto throws if it's run in a script without having set it up for testing:
flutter: Unsupported operation: package:webcrypto cannot be used from scripts or `flutter test` unless `flutter pub run webcrypto:setup` has been run for the current root project.
flutter: #0 lookup.<anonymous closure> (package:webcrypto/src/boringssl/lookup/lookup_symbol_flutter.dart:47:5)
flutter: dart-lang/ffi#1 lookup (package:webcrypto/src/boringssl/lookup/lookup_symbol_flutter.dart:53:2)
flutter: dart-lang/ffi#2 lookup (package:webcrypto/src/boringssl/lookup/lookup_symbol_flutter.dart)
flutter: dart-lang/ffi#3 _cachedLookup (package:webcrypto/src/boringssl/lookup/lookup.dart:26:21)
flutter: dart-lang/ffi#4 _cachedLookup (package:webcrypto/src/boringssl/lookup/lookup.dart)
flutter: dart-lang/ffi#5 ssl (package:webcrypto/src/boringssl/lookup/lookup.dart:29:44)
flutter: dart-lang/ffi#6 ssl (package:webcrypto/src/boringssl/lookup/lookup.dart)
flutter: dart-lang/ffi#7 _aesCbcEncryptOrDecrypt (package:webcrypto/src/impl_ffi/impl_ffi.aescbc.dart:41:52)
I tried setting the Strip-Style to "Non-Global Symbols" in Xcode, as suggested in the docs, but it doesn't make a difference. Is there any chance to fix this?
Any help is appreciated! :)
We should cleanup _withAllocation
and _withAllocationAsync
to use _Scope
.
Possibly we can adopt the Pool
-pattern from the samples everywhere.
// In a pool multiple resources can be allocated, which will all be freed
// at the end of the scope.
using((Pool pool) {
final p = pool<Int64>(2);
final p2 = pool<Int64>(2);
p[0] = 1;
p[1] = 2;
MemMove(p2.cast<Void>(), p.cast<Void>(), 2 * sizeOf<Int64>());
Expect.equals(1, p2[0]);
Expect.equals(2, p2[1]);
});
(Or even merge Pool
to package:ffi
and use it.)
Using dart:js_interop
or package:web
instead of other libraries will make this wasm-ready.
Migrating https://github.com/google/webcrypto.dart/blob/master/lib/src/crypto_subtle.dart should be possible.
I am getting 100% CPU usage while building android gradle, when using this package, I have just added this package in pubspec.yaml and am getting 100% CPU usage while building android gradle.
And the surprising thing is that I am getting 100% usage from "Antimalware Service Executable" while building gradle.
Given that this package uses ffi and BoringSSL, what is blocking from decoupling this package from Flutter and provide it for pure dart applications?
We wrap all (or almost all) FFI code in _Scope
, thus, it might make sense to review all usage of _Scope
to see if we can augment _Scope
such that that clear errors after an operation is done.
We should be handling errors all the places where they can happen, but as added safety it might be wise to clear errors after each operation and throw an error, if BoringCrypto has an error on the stack that we didn't expect to find there.
Whenever we create a CBS object, it's usually because we're feeding some data into BoringCrypto. If all the data is not read, this usually indicates invalid data. If importing a key doesn't consume all the bytes supplied in the key, we should probably reject it.
This needs test cases to ensure compatibility with web implementations.
Look code referencingScope.createCBS
those places probably have to check that CBS is empty after it's been processed.
This requires testing of invalid key bytes, so probably blocked by #55
$ python2 tool/update-boringssl.py
Updating third_party/boringssl/
Cloning into '/var/folders/6l/j64crzh16j7djnq676q0994h00klzg/T/update-boringssl-Ckbn9Z/src'...
remote: Total 79591 (delta 48387), reused 79591 (delta 48387)
Receiving objects: 100% (79591/79591), 214.86 MiB | 19.26 MiB/s, done.
Resolving deltas: 100% (48387/48387), done.
Updating files: 100% (5498/5498), done.
HEAD is now at 33f8d33af Convert X.509 accessor macros to proper functions.
Traceback (most recent call last):
File "tool/update-boringssl.py", line 284, in <module>
sys.exit(main())
File "tool/update-boringssl.py", line 277, in main
generate(tmp)
File "tool/update-boringssl.py", line 241, in generate
generate_build_files.main([g])
File "/var/folders/6l/j64crzh16j7djnq676q0994h00klzg/T/update-boringssl-Ckbn9Z/src/util/generate_build_files.py", line 858, in main
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 185, in check_call
retcode = call(*popenargs, **kwargs)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 172, in call
return Popen(*popenargs, **kwargs).wait()
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 394, in __init__
errread, errwrite)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1047, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory
https://boringssl.googlesource.com/boringssl/+/HEAD/util/generate_build_files.py#876
subprocess.check_call(
['go', 'run', 'util/embed_test_data.go'] + cmake['CRYPTO_TEST_DATA'],
cwd='src',
stdout=out)
After installing go and cmake everything works.
We should probably add a check and sane error message.
And fix any lints that now apply.
This includes regenerating all the generated code.
Hello, I was wondering if with this package one could sign data with the SHA1withRSA algorithm.
hi, I am getting this error when I import the plugin and try to run Android in the emulator (the other targets work)
* What went wrong:
A problem occurred configuring project ':webcrypto'.
> Could not create an instance of type com.android.build.api.variant.impl.LibraryVariantBuilderImpl.
> Namespace not specified. Specify a namespace in the module's build file. See https://d.android.com/r/tools/upgrade-assistant/set-namespace for information about setting the namespace.
If you've specified the package attribute in the source AndroidManifest.xml, you can use the AGP Upgrade Assistant to migrate to the namespace value in the build file. Refer to https://d.android.com/r/tools/upgrade-assistant/agp-upgrade-assistant for general information about using the AGP Upgrade Assistant.
which from what I remember a couple of months back, indicates that the plugin does not have namespace specified in their build.gradle - it is a requirement for Gradle 8 for all subprojects.
Without this, I dont I have any workaround other than downgrading gradle, which is something I'd like to avoid.
My gradle dependency: com.android.tools.build:gradle:8.2.0
There are some JSON test vectors in chromium, it might be nice to embed these JSON files in third_party/chromium/...
and create a test that embeds the JSON test vectors, to see that we get the same results across platforms.
Hi,
I'm getting the error "Expected a value of type 'String', but got one of type 'Null'" when generating an ECDSA key on a Mobile web browser (Chrome - Android). The same code works fine when running on a Desktop browser.
$ flutter run -d web-server --web-port 8080 --web-hostname 0.0.0.0
http://<YOUR_IP_ADDRESS>:8080
final keypair = await EcdsaPrivateKey.generateKey(EllipticCurve.p256);
errors.dart:266 Uncaught (in promise) Error: Expected a value of type 'String', but got one of type 'Null'
at Object.throw_ [as throw] (errors.dart:266:49)
at Object.castError (errors.dart:99:3)
at Object.cast [as as] (operations.dart:485:10)
at String.as (core_patch.dart:719:17)
at Object.getProperty (js_util_patch.dart:60:5)
at get message [as message] (crypto_subtle.dart:125:25)
at Object._translateDomException (impl_js.utils.dart:58:18)
at _handleDomException (impl_js.utils.dart:113:11)
at _handleDomException.throw (<anonymous>)
at async_patch.dart:60:31
at _RootZone.runBinary (zone.dart:1665:54)
at _FutureListener.thenAwait.handleError (future_impl.dart:162:22)
at handleError (future_impl.dart:779:46)
at _Future._propagateToListeners (future_impl.dart:800:13)
at [_completeError] (future_impl.dart:575:5)
at async._AsyncCallbackEntry.new.callback (future_impl.dart:666:7)
at Object._microtaskLoop (schedule_microtask.dart:40:11)
at _startMicrotaskLoop (schedule_microtask.dart:49:5)
at async_patch.dart:166:15
Thank you
Flutter desktop is becoming more common.
We should add support for flutter desktop.
Documentation for AES-GCM should discourage AES-192 and encourage nonce size 96 (12 bytes).
This follows documentation from BoringSSL:
https://boringssl.googlesource.com/boringssl/+/HEAD/include/openssl/aead.h?pli=1
I don't think we should make these recommendations without appropriate sources.
Meaning we should like to somewhere reputable that backs up claims that aes-192 and aes-gcm nonce size other than 96 bits is bad and should be avoided for use-cases other than interoperability.
There is also some discussions in the following BoringSSL bug:
https://bugs.chromium.org/p/boringssl/issues/detail?id=34
Which in turn references:
https://eprint.iacr.org/2012/438.pdf
Which looks like it's written in LaTeX so it must be the truth 🤣
Okay, maybe we have to evaluate the credibility a bit, but it could be a credible source.
I don't think we should consider disabling AES-192 or AES-GCM with nonce size other than 96 bits.
If these are supported by BoringSSL and most browsers, then I think we should keep them.
They may be useful / necessary for interoperability with existing systems.
But it seems reasonable to update our documentation to clearly state:
While we're at it, it's very plausible that the AES-192 recommendation applies to AES in all other block modes too. Again, we need sources.
And it might be worth investigating if we can find similar recommendations (with sources) for nonces, counters, etc for other primitives or AES blockmodes.
When calling the function EcdhPrivateKey.importJsonWebKey with a specific private key JWK object, the following error occurs:
FormatException: JWK property "y" should hold 32 bytes
String privateKeyHex: "03ecc060e7fc29863166f3fa6a490be9236304fca0b2d0bb5402cfd9cba1d100";
Map<String, String> privateKeyJWK = {
"kty": "EC",
"crv": "P-256",
"x":
"h0rbX_vxGVgx-u-ITxnydyy7W9VNGFITcqltL7hDa5A=",
"y":
"iQdcRQ8aghMIKnnE1RZEzrfag4c2PHBq8kVea6tK6w==",
"d": "A-zAYOf8KYYxZvP6akkL6SNjBPygstC7VALP2cuh0QA="
};
EcdhPrivateKey.importJsonWebKey(privateKeyJWK, EllipticCurve.p256);
The function EcdhPrivateKey.importJsonWebKey should import the private key JWK object without errors.
The function EcdhPrivateKey.importJsonWebKey throws a FormatException with the message "JWK property "y" should hold 32 bytes".
y
is only 31 bytes but paramSize
is 32 bytes (line 169 in impl_ffi.ec_common.dart
)impl_ffi.ec_common.dart
it works normally// Delete this line
_checkData(
bytes.length == paramSize,
message: 'JWK property "$prop" should hold $paramSize bytes',
);
webcrypto: ^0.5.3
flutter doctor -v
[✓] Flutter (Channel stable, 3.7.6, on macOS 13.2.1 22D68 darwin-x64, locale en)
• Flutter version 3.7.6 on channel stable at /Users/user/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 12cb4eb7a0 (7 days ago), 2023-03-01 10:29:26 -0800
• Engine revision ada363ee93
• Dart version 2.19.3
• DevTools version 2.20.1
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
• Android SDK at /Users/user/Library/Android/sdk
• Platform android-33, build-tools 33.0.2
• ANDROID_HOME = /Users/user/Library/Android/sdk
• Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-8887301)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 14C18
• CocoaPods version 1.11.3
[✓] Android Studio (version 2022.1)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-8887301)
[✓] VS Code (version 1.76.0)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.60.0
[✓] Connected device (1 available)
• iPhone 14 Pro Max (mobile) • CB20F049-B979-48DD-8754-A9D4F2477ECA • ios • com.apple.CoreSimulator.SimRuntime.iOS-16-2 (simulator)
[✓] HTTP Host Availability
• All required HTTP hosts are available
Maybe we could somehow have support for test vectors where the importing the key throws an exception/error, and where we assert certain errors/exceptions.
This might be different from _TestCase
, but to make it fast maybe it can still be part of TestRunner
It would be nice to split this package into two parts, one for pure-Dart use, and the other for use in Flutter.
Reference:
With the latest flutter beta update my app started throwing the following exception:
I/flutter ( 6872): #1 lookupSymbol.<anonymous closure> (package:webcrypto/src/boringssl/lookup/lookup_symbol_flutter.dart:39:5)
I/flutter ( 6872): #2 lookupSymbol (package:webcrypto/src/boringssl/lookup/lookup_symbol_flutter.dart:54:2)
I/flutter ( 6872): #3 lookupSymbol (package:webcrypto/src/boringssl/lookup/lookup_symbol_flutter.dart)
I/flutter ( 6872): #4 SymbolResolver.lookupFunc (package:webcrypto/src/boringssl/lookup/lookup.dart:28:12)
I/flutter ( 6872): #5 RSA_new (package:webcrypto/src/boringssl/rsa.dart:31:6)
I/flutter ( 6872): #6 RSA_new (package:webcrypto/src/boringssl/rsa.dart)
I/flutter ( 6872): #7 _generateRsaKeyPair (package:webcrypto/src/impl_ffi/im
E/flutter ( 6872): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: Exception: Unsupported operation: package:webcrypto does not work with this version of the Dart DL API.Please update to a newer version of package:webcrypto. And ensure thatyou have rebuilt the current version with `flutter pub run webcrypt:setup` if running locally.
E/flutter ( 6872): #0 initialize_dart_dl (package:webcrypto/src/boringssl/lookup/utils.dart:82:5)
E/flutter ( 6872): #1 lookupSymbol.<anonymous closure> (package:webcrypto/src/boringssl/lookup/lookup_symbol_flutter.dart:39:5)
E/flutter ( 6872): #2 lookupSymbol (package:webcrypto/src/boringssl/lookup/lookup_symbol_flutter.dart:54:2)
E/flutter ( 6872): #3 lookupSymbol (package:webcrypto/src/boringssl/lookup/lookup_symbol_flutter.dart)
E/flutter ( 6872): #4 SymbolResolver.lookupFunc (package:webcrypto/src/boringssl/lookup/lookup.dart:28:12)
E/flutter ( 6872): #5 RSA_new (package:webcrypto/src/boringssl/rsa.dart:31:6)
E/flutter ( 6872): #6 RSA_new (package:webcrypto/src/boringssl/rsa.dart)
E/flutter ( 6872): #7 _generateRsaKeyPair (package:webcrypto/src/impl_ffi/impl_ffi.rsa_common.dart:261:38)
E/flutter ( 6872): #8 rsaOaepPrivateKey_generateKey (package:webcrypto/src/impl_ffi/impl_ffi.rsaoaep.dart:68:16)
E/flutter ( 6872): #9 RsaOaepPrivateKey.generateKey (package:webcrypto/src/webcrypto/webcrypto.rsaoaep.dart:50:12)
flutter doctor -v
[✓] Flutter (Channel beta, 1.24.0-10.2.pre, on Linux, locale en_US.UTF-8)
• Flutter version 1.24.0-10.2.pre at /ssd/home/csh/lib/flutter
• Framework revision 022b333a08 (13 days ago), 2020-11-18 11:35:09 -0800
• Engine revision 07c1eed46b
• Dart version 2.12.0 (build 2.12.0-29.10.beta)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
• Android SDK at /ssd/home/csh/Android/Sdk
• Platform android-30, build-tools 30.0.2
• Java binary at: /bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_272-b10)
• All Android licenses accepted.
[✓] Chrome - develop for the web
• Chrome at google-chrome
[!] Android Studio (not installed)
• Android Studio not found; download from https://developer.android.com/studio/index.html
(or visit https://flutter.dev/docs/get-started/install/linux#android-setup for detailed instructions).
[✓] VS Code (version 1.51.1)
• VS Code at /usr/share/code
• Flutter extension version 3.16.0
[✓] Connected device (4 available)
• moto x4 (mobile) • ZY224KQLWQ • android-arm64 • Android 9 (API 28)
• Android SDK built for x86 64 (mobile) • emulator-5554 • android-x64 • Android 9 (API 28) (emulator)
• Web Server (web) • web-server • web-javascript • Flutter Tools
• Chrome (web) • chrome • web-javascript • Google Chrome 87.0.4280.66
! Doctor found issues in 1 category.
I am not sure why it thinks AS is not installed, it is installed and is running.
I've encountered the following error when I was trying to derive a key using the PBKDF2 algorithm for AES Encryption/Decryption on Flutter Web (Beta Channel).
static Future<String> generateKey(
String password,
String salt,
) async {
final algo = await Pbkdf2SecretKey.importRawKey(
utf8.encode(password),
);
final key = await algo.deriveBits(
256,
Hash.sha256,
base64.decode(salt),
65537,
);
return base64.encode(key);
}
More specifically at await Pbkdf2SecretKey.importRawKey(...)
.
EDIT: This seems to be the case wherever CryptoKey
is being used.
Here is the link to a sample GitHub project to reproduce the issue: Repo
Here are the logs from both the Terminal and the Chrome console.
Error: Expected a value of type 'JSObject<CryptoKey>', but got one of type 'CryptoKey'
at Object.throw_ [as throw] (http://localhost:64164/dart_sdk.js:5331:11)
at Object.castError (http://localhost:64164/dart_sdk.js:5302:15)
at dart.LazyJSType.new.as (http://localhost:64164/dart_sdk.js:7109:40)
at Object._argumentErrors (http://localhost:64164/dart_sdk.js:5454:19)
at Object._checkAndCall (http://localhost:64164/dart_sdk.js:5537:29)
at Object.dcall (http://localhost:64164/dart_sdk.js:5546:17)
at ret (http://localhost:64164/dart_sdk.js:60990:21)
Uncaught (in promise) Error: Expected a value of type 'JSObject<CryptoKey>', but got one of type 'CryptoKey'
at Object.throw_ [as throw] (:64807/dart_sdk.js:5331)
at Object.castError (:64807/dart_sdk.js:5302)
at dart.LazyJSType.new.as (:64807/dart_sdk.js:7109)
at Object._argumentErrors (:64807/dart_sdk.js:5454)
at Object._checkAndCall (:64807/dart_sdk.js:5537)
at Object.dcall (:64807/dart_sdk.js:5546)
at ret (:64807/dart_sdk.js:60990)
[✓] Flutter (Channel beta, 1.26.0-17.3.pre, on macOS 11.1 20C69 darwin-x64, locale en-US)
• Flutter version 1.26.0-17.3.pre at /Users/rahulmuthyam/.flutter
• Framework revision 4b50ca7f7f (7 days ago), 2021-02-04 19:44:27 -0800
• Engine revision 2c527d6c7e
• Dart version 2.12.0 (build 2.12.0-259.8.beta)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
• Android SDK at /Users/rahulmuthyam/Library/Android/sdk
• Platform android-30, build-tools 30.0.3
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 12.4, Build version 12D4e
• CocoaPods version 1.10.0
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 4.1)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
[✓] IntelliJ IDEA Community Edition (version 2020.3.1)
• IntelliJ at /Applications/IntelliJ IDEA CE.app
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
[✓] VS Code (version 1.53.2)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.19.0
[✓] Connected device (1 available)
• Chrome (web) • chrome • web-javascript • Google Chrome 88.0.4324.150
• No issues found!
I try to use this library in a small dart script (inside a flutter project), and I got:
Unsupported operation: package:webcrypto cannot be used from Dart or `pub run test` unless `flutter pub run webcrypto:setup` has been run for the current root project.
So I try:
> flutter pub run webcrypto:setup
Building with assumed project root in:
/Users/xavier/projects/app/
Using package:webcrypto from /Users/xavier/.pub-cache/hosted/pub.dartlang.org/webcrypto-0.5.2
Generating build system with cmake
CMake Error: The source directory "/Users/xavier/.pub-cache/hosted/pub.dartlang.org/src" does not exist.
Specify --help for usage, or press the help button on the CMake GUI.
Generating with cmake failed, ensure you have dependencies!
Webcrypto version: 0.5.2
> flutter --version
Flutter 2.0.3 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 4d7946a68d (6 days ago) • 2021-03-18 17:24:33 -0700
Engine • revision 3459eb2436
Tools • Dart 2.12.2
> cmake --version
cmake version 3.16.1
BoringCrypto exposes ERR_clear_error()
which clears the thread-local error stack.
For any function in BoringCrypto which may set error state, we must read the error stack, before we do any async/await, as this may trigger the code to continue running on a different thread.
Perhaps, we have to use _Scope.sync
for everything, and ensure that we check/clear the error stack at the end of every _Scope.sync
block.
Or maybe we have to invent a new block form.
Our goal is to make the API hard to use incorrectly. For the HmacSecretKey
class we would want to validate the properties on importJsonWebKey
against imported JWK to reduce the risk of accidentally using incorrect JWK. Below are a few options suggested by @jonasfj:
importJsonWebKey
optional, and validate them when present.required
and validate with jwk
.We can try a mix for different parameters and we have a consensus of requiring the alg
property to make it consistent with other JWK imports.
However, note that the RFC 7517 states that the alg
property is indeed optional and this should be kept into consideration while drafting a permanent solution for webcrypto.dart
.
I will post my discovery here and would love to hear opinions on the same.
I was looking through your package and didn't find any similar documentation or something on generating public/private RSA-OAEP key.
I did find this but I am not sure if that's what I am looking for??
I'm not sure exactly how we want test case generation to work, but probably it should be a thing that TestRunner.runTest
does automatically, and just prints the generated test case.
Flutter version: (Channel beta, 2.3.0-24.1.pre, on Manjaro Linux 5.10.42-1-MANJARO)
Assertion failed: "package:webcrypto failed to attached finalizer"
package:webcrypto/src/impl_ffi/impl_ffi.utils.dart 39:5 _attachFinalizerEVP_PKEY
package:webcrypto/src/impl_ffi/impl_ffi.utils.dart 49:3 _createEVP_PKEYwithFinalizer
package:webcrypto/src/impl_ffi/impl_ffi.rsa_common.dart 287:21 _generateRsaKeyPair
package:webcrypto/src/impl_ffi/impl_ffi.rsaoaep.dart 68:16 rsaOaepPrivateKey_generateKey
package:webcrypto/src/webcrypto/webcrypto.rsaoaep.dart 50:12 RsaOaepPrivateKey.generateKey
package:betro_dart_lib/rsa.dart 12:31 generateRsaPair
Note: This is running fine on flutter stable. With beta, there seems to be some change in ffi API which is causing this issue
It's sad when print(HmacSecretKey.generateKey(Hash.sha256))
shows Instance of '_HmacSecretKey'
.
Perhaps we should override the toString()
method on all the private classes.
The user doesn't need to know about _HmacSecretKey
, since it's a private class.
And we could probably display something more useful for debugging purposes.
I haven't figured this out yet, but we could do something like:
HmacSecretKey(hash: Hash.sha256, key: ***)
HmacSecretKey(Hash.sha256)
Instance of 'HmacSecretKey with Hash.sha256'
Instance of 'HmacSecretKey'
HmacSecretKey with sha256
It could be useful if print was helpful when debugging stuff :D
On the flip side, the HmacSecretKey
doesn't have a hash
property, so if you have an instance of HmacSecretKey
and you want to know what hash it uses, the only thing you can do is:
HmacSecretKey.toString().contains('sha256')
, if we added a fancy variant of toString()
.I imagine that we'd prefer to avoid using doing (b). Not necessarily be adding a hash
property they don't need, but by forcing them to do (a) 🤣
I think the point I'm trying to make is that, if we add to much information in toString()
, then maybe people will use to inspect an object at runtime and rely on the behavior of toString()
.
And I don't think we want people to rely on the behavior of toString()
.
So maybe it's safest to just do:
macSecretKey.generateKey(Hash.sha256).toString() == "Instance of 'HmacSecretKey'"
It's boring, it's simple, but it doesn't leak any information that could lure a user into misusing toString()
.
I could ofcourse be convinced otherwise.
EcdsaPublicKey.importRawKey is available, and we can import the raw public key (65 bytes) and verify a signature. However, I am securely storing a private key as 32 bytes, and I would like to be able to create an EcdsaPrivateKey which I can then use for signing. Is this at all possible, or is this a feature request? It can be done on other libraries, e.g. CryptoKit, PointyCastle, but I so far prefer using the web crypto package, and I am having other issues with pointycastle and cryptography packages.
Running:
flutter pub run ffigen --config=lib/src/boringssl/bindings/ffigen.yaml
flutter pub run ffigen --config=lib/src/third_party/boringssl/ffigen.yaml
causes things like:
[WARNING]: Resolved name conflict: Declaration 'RSA_set0_key' and has been renamed to 'RSA_set0_key1'.
[WARNING]: Resolved name conflict: Declaration 'RSA_set0_key' and has been renamed to 'RSA_set0_key2'.
[WARNING]: Resolved name conflict: Declaration 'RSA_set0_key' and has been renamed to 'RSA_set0_key3'.
[WARNING]: Resolved name conflict: Declaration 'RSA_set0_key' and has been renamed to 'RSA_set0_key4'.
[WARNING]: Resolved name conflict: Declaration 'RSA_set0_key' and has been renamed to 'RSA_set0_key5'.
[WARNING]: Resolved name conflict: Declaration 'RSA_set0_key' and has been renamed to 'RSA_set0_key6'.
where multiple methods for the same thing is generated.
@dcharkes, any ideas? (this is upgrading from ffigen
version 4 to 6)
Try creating a trivial PR changing nothing and see that iOS integration tests fails half the time.
We could probably try to debug by using a Mac with iOS, or adding log lines to the integration test script and try to guess what line it is getting stuck at.
I've taken a stable at generating the bindings with package:ffigen
.
With dart-lang/native#394 addressed, I can make it work on the host machine.
// EVP_MD_size returns the digest size of |md|, in bytes.
OPENSSL_EXPORT size_t EVP_MD_size(const EVP_MD *md);
Handwritten bindings:
/// EVP_MD_size returns the digest size of md, in bytes.
///
/// ```c
/// size_t EVP_MD_size(const EVP_MD *md);
/// ```
final EVP_MD_size = resolve(Sym.EVP_MD_size)
.lookupFunc<IntPtr Function(Pointer<EVP_MD>)>()
.asFunction<int Function(Pointer<EVP_MD>)>();
Generated bindings (notice Uint64
instead of IntPtr
):
/// // EVP_MD_size returns the digest size of |md|, in bytes.
int EVP_MD_size(
ffi.Pointer<env_md_st> md,
) {
return (_EVP_MD_size ??=
_lookup<ffi.NativeFunction<_c_EVP_MD_size>>('EVP_MD_size')
.asFunction<_dart_EVP_MD_size>())(
md,
);
}
_dart_EVP_MD_size? _EVP_MD_size;
typedef _c_EVP_MD_size = ffi.Uint64 Function(
ffi.Pointer<env_md_st> md,
);
typedef _dart_EVP_MD_size = int Function(
ffi.Pointer<env_md_st> md,
);
Edit: We have a workaround
typedef-map:
'size_t': 'IntPtr`
Experiment: https://github.com/google/webcrypto.dart/tree/use-ffigen (ignore all the code duplication and pointer casts, I just wanted to migrate a single file).
It would be awesome if we could figure out how to run tests under valgrind, even if we can only do it in one configuration.
This already the case on the web, or at-least it's moved the browser which is most likely moving it off-the-mainthread.
We should do the same on Android and iOS.
A workaround is to wrap functions using crypto with compute.
NOTE, the API is already fully async, so we should be able to move computation off the main thread, without breaking existing users of this package.
See: dart-lang/sdk#45072
We'll have to refactor code like:
webcrypto.dart/lib/src/impl_ffi/impl_ffi.utils.dart
Lines 29 to 37 in 6dd3460
As the finalizer will need to be attached to an object that isn't Pointer
, hence, either we'll probably need to wrap with a EVP_PKEYResource
or something like that.
I am having problem with importing raw key on a web project.
This is the code:
final key = base64Decode("iP4P+MTq9GjUzZ2amDFmJIj+D/jE6vRo1M2dmpgxZiQ=");
final webkey = await AesGcmSecretKey.importRawKey(key);
and fails with:
Error: Expected a value of type 'CryptoKey' (in null), but got one of type 'CryptoKey' (in dart:html)
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 266:49 throw_
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 99:3 castError
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart 226:34 as
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 201:40 _argumentErrors
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 339:39 dcall
Tried to run this on a different project and works just fine.
I guess there must be something specific in my project which conflicts with AesGcmSecretKey
, but not really sure how to find it out.
Do you have any ideas where to start?
In one of our non-flutter project, we need to do encryption/decryption on the Web platform. I see that most of our needs are fulfilled by this library, but the library requires the Flutter SDK.
Would it be thinkable that this library be split in multiple packages (like this one), where specific implementations (in our case, the web one) can be depended on without dragging with it a dependency on Flutter?
In chromium this is implemented in VerifyUsages
:
https://source.chromium.org/chromium/chromium/src/+/main:components/webcrypto/jwk.cc;l=144
Something similar should be done for all the places we import JWK keys.
Then we should still strip use
and key_ops
before importing keys on the web, unless we decide to properly implement capabilities, see #53
The Android version of my application is throwing the following exception after updating to 0.5.4
(ios seems to be unaffected)
Unsupported operation: package:webcrypto cannot be used from scripts or `flutter test` unless `flutter pub run webcrypto:setup` has been run for the current root project.
The error does not happen in debug mode on the simulator nor when running in release mode.
My guess is the upload process is stripping some symbols that is causing the failure?
Reverting to 0.5.3 resolved the issue for now.
Using:
Flutter (Channel stable, 3.16.7, on Fedora Linux 39 (Thirty Nine) 6.6.8-200.fc39.x86_64, locale en_US.UTF-8)
Android toolchain - develop for Android devices (Android SDK version 33.0.0)
#0 lookup.<anonymous closure> (package:webcrypto/src/boringssl/lookup/lookup.dart:69)
#1 lookup (package:webcrypto/src/boringssl/lookup/lookup.dart:75)
#2 _cachedLookup (package:webcrypto/src/boringssl/lookup/lookup.dart)
#3 ssl (package:webcrypto/src/boringssl/lookup/lookup.dart)
#4 _extension#4.createRSA (package:webcrypto/src/impl_ffi/impl_ffi.utils.dart)
#5 _generateRsaKeyPair.<anonymous closure> (package:webcrypto/src/impl_ffi/impl_ffi.rsa_common.dart:256)
#6 _Scope.sync (package:webcrypto/src/impl_ffi/impl_ffi.utils.dart:305)
#7 _generateRsaKeyPair (package:webcrypto/src/impl_ffi/impl_ffi.rsa_common.dart:254)
#8 rsaOaepPrivateKey_generateKey (package:webcrypto/src/impl_ffi/impl_ffi.rsaoaep.dart:70)
#9 RsaOaepPrivateKey.generateKey (package:webcrypto/src/webcrypto/webcrypto.rsaoaep.dart:219)
#10 WebcryptoRsa._generateRSAKeyRaw (package:common/rsa/webcrypto.dart:142)
#11 WebcryptoRsa.generateKeyPair (package:common/rsa/webcrypto.dart:82)
#12 CryptoBloc._compute (package:common/bloc/crypto/crypto_bloc.dart:249)
#13 CryptoBloc.init (package:common/bloc/crypto/crypto_bloc.dart:216)
<asynchronous suspension>
It would be great to have this feature because it would allow us to check for data integrity before actually having to decrypt the whole data.
Right now the only way to use this built-in check is to do something like that :
Future<bool> isValid(Uint8List encryptedData, AesGcmSecretKey key) async => await decrypt(encryptedData, key) != null;
This is not optimal if the key
is valid because the whole data is then proceeded.
Specs summarized here : https://crypto.stackexchange.com/a/25256.
.github/workflows/test.yml
contains:
# TODO: Enable macos desktop when supported
#- run: flutter test integration_test/webcrypto_test.dart -d macos
# working-directory: ./example
We really should enable integration tests on macos desktop. If anyone is interested in figuring how to get this working on Github Actions that would be great.
Contributions are highly appreciated, I tried in #74, but there is probably a tiny thing not working.
The web cryptography specification the CryptoKey
interface have the following attributes not currently supported in this package:
usages: 'encrypt' | 'decrypt' | 'sign' | 'verify' | 'deriveKey' | 'deriveBits' | 'wrapKey' | 'unwrapKey'
extractable: true | false
These capabilities limits what a key object can be used for. In a Dart API trying to export a key with extractable: false
would probably throw a StateError
.
The web crypto spec also have the following operations not currently supported in this package:
deriveKey
-- equivalent to derive bits and import key.wrapKey
-- equivalent to exporting the key and encrypting it.unwrapKey
-- equivalent to decrypting and importing key.If we wish to add support for capabilities, then we need to also support wrap/unwrap/derive-key and vice-versus.
usages
/extractable
) and wrap/unwrap/derive-key operations are mutually dependent.Summary:
usages
/extractable
), thus, unwrapping in the browser enforces capabilities.usages
/extractable
) without supporting all operations that can be encoded is weird.When exporting a key in JWK format the extractable
bit and the usages
are encoded in the exported JSON Web Key.
If we unwrap an encrypted JWK key using window.crypto
in the browser, then whatever capabilities was encoded in the encrypted JWK is what the decrypted+imported CryptoKey
will have. And the browser won't give us an opportunity to increase the capabilities of the unwrapped key. So we abstractions that wrap CryptoKey
in Dart must support capabilities, if we wish to support the unwrapKey
operation.
This could be worked around by:
decrypt
+ import
instead of the unwrap
operation.
AES-KW
which supports wrap
/unwrap
operations, but not encrypt
/decrypt
. But JWK does not work will with AES-KW
, see w3c/webcrypto#187A possible API design for this could be something along the lines of what is illustrated here:
final hmacKey = await HmacSecretKey.generate(Hash.sha256);
final wrappedKeyAsBytes = await secretKey.wrapKey(
// this is a WrapKeyOptions -- just a way type key + format, ensuring you
// can't use a format not supported for a given key.
// Also weird to have a format enum, not used in import/export methods!
hmacKey.wrapRawKeyOptions(),
// options for encryptBytes, differs depending on secretKey type!
iv,
);
final wrappedKeyAsBytes = await secretKey.wrapKey(
hmacKey.wrapJsonWebKeyOptions(),
iv,
);
final hmacKey = await secretKey.unwrapKey(
// This is a UnwrapKeyOptions -- which is format + import options
HmacSecretKey.unwrapJsonWebKeyOptions(Hash.sha256),
wrappedKeyAsBytes,
// options for decryptBytes, differs depending on secretKey type
iv,
);
final hmacKey = await secretKey.unwrapKey(
HmacSecretKey.unwrapRawKeyOptions(Hash.sha256),
wrappedKeyAsBytes,
iv,
);
final hmacKey = await hkdfKey.deriveKey(
// This must be a DeriveKeyOptions<T>, and then deriveKey returns Future<T>
// This allows us to limit what keys can be derived, webcrypto can only derive
// HMAC, AES-CBC, AES-CTR, AES-GCM and AES-KW.
HmacSecretKey.deriveKeyOptions(Hash.sha256),
salt,
info,
);
@sealed
abstract class WrapKeyOptions {}
@sealed
abstract class UnwrapKeyOptions<T> {}
@sealed
abstract class DeriveKeyOptions<T> {}
Hi, I'm currently trying to use this package in a webdev environment.
I'm facing the issue where I can't do dart pub get
or any webdev command because I don't have a flutter dependency.
Would there be a way for me to use this in a web only environment without having to depend on flutter specifically?
I'm thinking the web part could be separated in its own package and this package (webcrypto.dart) could depend on the web only package to conditionally export web stuff when needed?
Just to add more details, here's the kind of error I get when interacting with dart pub
in a non-flutter context, while depending this package
λ ~/dart/web_crypto_poc/ webdev serve
Unhandled exception:
ProcessException: ***OUT***
***ERR***
The Flutter SDK is not available.
***
Command: /Users/joel/.asdf/installs/dart/2.17.6/dart-sdk/bin/dart pub deps
#0 _runPubDeps (package:webdev/src/pubspec.dart:66:5)
#1 PubspecLock.read (package:webdev/src/pubspec.dart:80:11)
#2 readPubspecLock (package:webdev/src/command/shared.dart:100:39)
#3 ServeCommand.run (package:webdev/src/command/serve_command.dart:98:29)
#4 CommandRunner.runCommand (package:args/command_runner.dart:209:27)
#5 _CommandRunner.runCommand (package:webdev/src/webdev_command_runner.dart:40:24)
#6 CommandRunner.run.<anonymous closure> (package:args/command_runner.dart:119:25)
#7 new Future.sync (dart:async/future.dart:301:31)
#8 CommandRunner.run (package:args/command_runner.dart:119:14)
#9 run (package:webdev/src/webdev_command_runner.dart:21:56)
#10 main (file:///Users/joel/.pub-cache/hosted/pub.dartlang.org/webdev-2.7.10/bin/webdev.dart:19:22)
#11 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:295:32)
#12 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.