GithubHelp home page GithubHelp logo

google / webcrypto.dart Goto Github PK

View Code? Open in Web Editor NEW
70.0 4.0 38.0 30.97 MB

Cross-platform implementation of Web Cryptography APIs

Home Page: https://pub.dev/packages/webcrypto

License: Apache License 2.0

CMake 3.83% Kotlin 0.30% Dart 87.99% Swift 0.41% Objective-C 0.29% HTML 0.27% C 1.25% Shell 0.66% Python 1.11% Ruby 0.50% C++ 3.39%
dart flutter webcrypto cryptography crypto

webcrypto.dart's Introduction

Cross-Platform Web Cryptography Implemenation

This package provides a cross-platform implementation of the Web Cryptograph API.

Disclaimer: This is not an officially supported Google product.

This packages provides an implementation of the Web Cryptograph API across multiple platforms. Outside the browser, this package features a native implementation embedding BoringSSL using dart:ffi. When used inside a web browser this package wraps the window.crypto APIs and providing the same Dart API as the native implementation.

This way, package:webcrypto provides the same crypto API on Android, iOS, Web, Windows, Linux and Mac.

Example

import 'dart:convert' show base64, utf8;
import 'package:webcrypto/webcrypto.dart';

Future<void> main() async {
  final digest = await Hash.sha256.digestBytes(utf8.encode('Hello World'));
  print(base.encode(digest));
}

Features:

  • Get random bytes
  • Digest (sha-1/sha-256/sha-384/sha-512)
  • HMAC (sign/verify)
  • RSASSA-PKCS1-v1_5 (sign/verify)
  • RSA-PSS (sign/verify)
  • ECDSA (sign/verify)
  • RSA-OAEP (encrypt/decrypt)
  • AES-CTR, AES-CBC, AES-GCM (encrypt/decrypt)
  • ECDH (deriveBits)
  • HKDF (deriveBits)
  • PBKDF2 (deriveBits)
  • BoringSSL, Chrome and Firefox implementations pass the same test cases.

Missing:

  • Exceptions and errors thrown for invalid input is not tested yet.
  • The native implementation executes on the main-thread, however, all expensive APIs are asynchronous, so they can be offloaded in the future.

For a discussion of the API design of this package, see doc/design-rationale-md.

Use with flutter test

Unlike most plugins it is possible to run code that uses package:webcrypto with flutter test. For this to work the native library must be built in the application folder where flutter test is called. This can be done with:

# Only necessary when package:webcrypto is used from 'flutter test'
# This is not necessary for development with 'flutter run' and hot-reload
$ flutter pub run webcrypto:setup

# Now it's possible to run tests that uses package:webcrypto
$ flutter test test/my_test_file_using_webcrypto.dart

This requires:

  • cmake
  • a C compiler (like gcc or clang)
  • Linux or Mac.

The native library will be stored in .dart_tool/webcrypto/ which should not be under source control.

It is also possible to run tests with Flutter Web using flutter test -p chrome, this does not require any additional setup steps.

Limitations

This package has a few limitations compared to the Web Cryptograph API. For a discussion of parity with Web Cryptography APIs see doc/webcrypto-parity.md.

  • deriveKey is not supported, however, keys can always be created from derivedBits which is supported.
  • wrapKey is not supported, however, keys can be exported an encrypted.
  • unwrapKey is not supported, however, keys can be decrypted and imported.
  • AES-KW is not supported because it does not support encrypt/decrypt.

Compatibility notes

This package has many tests cases to asses compatibility across the native implementation using BoringSSL and various browser implementations of the Web Cryptography APIs.

At the moment compatibility testing is limited to native implementation, Chrome, Firefox and Safari.

Known Issues:

  • Chrome and BoringSSL does not support valid ECDH spki-formatted keys exported by Firefox prior to version 72.
  • Firefox does not support PKCS8 import/export for ECDSA and ECDH keys.
  • Firefox does not handle counter wrap around for AES-CTR.
  • Safari does not support P-521 for ECDSA and ECDH.
  • The browser implementation of streaming methods for encryption, decryption, signing and verification buffers the entire input, because window.crypto does not expose a streaming API. However, the native implementation using BoringSSL does support streaming.

References

webcrypto.dart's People

Contributors

corenion avatar dcharkes avatar dependabot[bot] avatar devoncarew avatar emersion avatar jonasfj avatar jsiedentop avatar koji-1009 avatar maltemedocs avatar mit-mit avatar sigurdm avatar szakarias avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

webcrypto.dart's Issues

0.5.4 Exception after upload to Google Play

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>

Ensure correct handling of errors from thread-local storage

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.

Migrate away from attaching finalizers to pointer objects

See: dart-lang/sdk#45072

We'll have to refactor code like:

final ret = dl.webcrypto_dart_dl_attach_finalizer(
key,
key.cast(),
ssl.EVP_PKEY_free_.cast(),
// We don't really have an estimate of how much space the EVP_PKEY structure
// takes up, but if we make it some non-trivial size then hopefully the GC
// will prioritize freeing them.
4096,
);

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.

EcdsaPrivateKey.importRawKey unavailable

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.

Unsupported operation: package:webcrypto does not work with this version of the Dart DL API.

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.

Can't use in a Dart web environment only.

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)

tool/update-boringssl.py requires `go` on `PATH`

$ 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.

Cleanup _withAllocation and _withAllocationAsync

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]);
  });

https://github.com/dart-lang/sdk/blob/master/samples/ffi/resource_management/pool_sample.dart#L48-L58

(Or even merge Pool to package:ffi and use it.)

Fix intermittents tests for macos desktop

.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.

Extend TestRunner with support for exception and error cases

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

boringssl lookup fails on iOS with static linkage

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! :)

Consider augmenting _Scope to ensure we clear errors after each operation

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.

update ffigen

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)

Ensure CBS is empty after importing keys

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

debug intermittent iOS integration tests

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.

Bug Report: Error while calling importJsonWebKey

Description

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

Steps to Reproduce

  1. Use the following input
String privateKeyHex: "03ecc060e7fc29863166f3fa6a490be9236304fca0b2d0bb5402cfd9cba1d100";
Map<String, String> privateKeyJWK = {
     "kty": "EC",
     "crv": "P-256",
     "x":
         "h0rbX_vxGVgx-u-ITxnydyy7W9VNGFITcqltL7hDa5A=",
     "y":
         "iQdcRQ8aghMIKnnE1RZEzrfag4c2PHBq8kVea6tK6w==",
     "d": "A-zAYOf8KYYxZvP6akkL6SNjBPygstC7VALP2cuh0QA="
   };

  1. Call the function EcdhPrivateKey.importJsonWebKey(privateKeyJWK, EllipticCurve.p256);

Expected Behavior

The function EcdhPrivateKey.importJsonWebKey should import the private key JWK object without errors.

Actual Behavior

The function EcdhPrivateKey.importJsonWebKey throws a FormatException with the message "JWK property "y" should hold 32 bytes".

Additional Context

  • y is only 31 bytes but paramSize is 32 bytes (line 169 in impl_ffi.ec_common.dart)
  • When i delete line 176 in impl_ffi.ec_common.dart it works normally
// Delete this line
 _checkData(
        bytes.length == paramSize,
        message: 'JWK property "$prop" should hold $paramSize bytes',
);

Environment

  • 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

Dart (server) apps support

Given that this package uses ffi and BoringSSL, what is blocking from decoupling this package from Flutter and provide it for pure dart applications?

AesGcmSecretKey.importRawKey on Chrome - Expected a value of type 'CryptoKey' (in null), but got one of type 'CryptoKey'

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?

CPU usage 100% during build for android gradle

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.

Run tests under valgrind

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.

ECDSA Key Generation Fails on Mobile Web Browser

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.

Steps

  1. Run a flutter app for web.
$ flutter run -d web-server --web-port 8080 --web-hostname 0.0.0.0
  1. Navigate to the app on a connected physical device.
http://<YOUR_IP_ADDRESS>:8080
  1. Generate key (throws error).
final keypair = await EcdsaPrivateKey.generateKey(EllipticCurve.p256);

Error

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

Possible to split into packages so that the non-flutter parts can be used in a non-flutter project?

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?

webcrypto:setup: CMake Error: The source directory does not exist

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

Support for Gradle 8

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

Move all computation off-the-mainthread

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.

Explore wrap/unwrap/derive-key and capabilities

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.

Support for capabilities (usages/extractable) and wrap/unwrap/derive-key operations are mutually dependent.

Summary:

  • A key exported in JWK format encodes capabilities (usages/extractable), thus, unwrapping in the browser enforces capabilities.
  • Supporting 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:

  • Using decrypt + import instead of the unwrap operation.
    • This would a bit of a hack in the browser, and reduce the security features expected by the user in the browser environment.
    • It would not work for AES-KW which supports wrap/unwrap operations, but not encrypt/decrypt. But JWK does not work will with AES-KW, see w3c/webcrypto#187
  • Not supporting wrap/unwrap for JWK formatted keys.

A 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> {}

Refactor test case generation

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.

package:webcrypto failed to attached finalizer

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

Error: Expected a value of type 'JSObject<CryptoKey>', but got one of type 'CryptoKey'

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.

Terminal Log

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)

Chrome Console Log

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 Info

[✓] 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!

Give access to the authorization tag in AES-GCM mode

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.

Use package:ffigen to generate bindings

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.

However, without addressing https://github.com/dart-lang/sdk/issues/36140 and https://github.com/dart-lang/native/issues/530, some of the integer sizes are not generated the right size for cross platform bindings.
// 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).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.