Merge branch 'codex/windows-parity'
This commit is contained in:
commit
c44a0dec47
@ -9,6 +9,12 @@
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
analyzer:
|
||||
exclude:
|
||||
- third_party/**/example/**
|
||||
- third_party/**/test/**
|
||||
- third_party/**/analysis_options.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
|
||||
@ -265,12 +265,11 @@ packages:
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
flutter_secure_storage_windows:
|
||||
dependency: transitive
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: flutter_secure_storage_windows
|
||||
sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
path: "third_party/flutter_secure_storage_windows"
|
||||
relative: true
|
||||
source: path
|
||||
version: "3.1.2"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
|
||||
@ -32,6 +32,10 @@ dependencies:
|
||||
web_socket_channel: ^3.0.3
|
||||
yaml: ^3.1.3
|
||||
|
||||
dependency_overrides:
|
||||
flutter_secure_storage_windows:
|
||||
path: third_party/flutter_secure_storage_windows
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
43
third_party/flutter_secure_storage_windows/CHANGELOG.md
vendored
Normal file
43
third_party/flutter_secure_storage_windows/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
## 3.1.2
|
||||
Reverts onCupertinoProtectedDataAvailabilityChanged and isCupertinoProtectedDataAvailable.
|
||||
|
||||
## 3.1.1
|
||||
Updated flutter_secure_storage_platform_interface to latest version.
|
||||
|
||||
## 3.1.0
|
||||
Fixed CompanyName and CompanyProduct on Windows are ignored when the lang-charset in the Runner.rc file is not 040904e4
|
||||
|
||||
## 3.0.0
|
||||
- Migrated to win32 package replacing C.
|
||||
- Changed PathNotFoundException to FileSystemException to be backwards compatible with Flutter SDK 2.12.0
|
||||
- Applied lint suggestions
|
||||
|
||||
## 2.1.1
|
||||
Revert changes made in version 2.1.0 due to breaking changes.
|
||||
These changes will be republished under a new major version number 3.0.0.
|
||||
|
||||
## 2.1.0
|
||||
- Changed PathNotFoundException to FileSystemException to be backwards compatible with Flutter SDK 2.12.0
|
||||
- Applied lint suggestions
|
||||
|
||||
## 2.0.0
|
||||
Write encrypted data to files instead of the windows credential system.
|
||||
|
||||
## 1.1.3
|
||||
Updated flutter_secure_storage_platform_interface to latest version.
|
||||
|
||||
## 1.1.2
|
||||
- Silently ignore errors when deleting keys that don't exist
|
||||
|
||||
## 1.1.1
|
||||
- Fix application crash when key doesn't exists.
|
||||
|
||||
## 1.1.0
|
||||
Features
|
||||
- Add readAll, deleteAll and containsKey functions.
|
||||
|
||||
Bugfixes
|
||||
- Fix implementation of delete operation to allow null value.
|
||||
|
||||
## 1.0.0
|
||||
- Initial Windows implementation
|
||||
29
third_party/flutter_secure_storage_windows/LICENSE
vendored
Normal file
29
third_party/flutter_secure_storage_windows/LICENSE
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright 2017 German Saprykin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
12
third_party/flutter_secure_storage_windows/README.md
vendored
Normal file
12
third_party/flutter_secure_storage_windows/README.md
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# flutter_secure_storage_windows
|
||||
|
||||
The windows implementation of [`flutter_secure_storage`][1].
|
||||
|
||||
## Usage
|
||||
|
||||
This package is [endorsed][2], which means you can simply use `flutter_secure_storage`
|
||||
normally. This package will be automatically included in your app when you do.
|
||||
|
||||
[1]: https://pub.dev/packages/flutter_secure_storage
|
||||
[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin
|
||||
|
||||
1
third_party/flutter_secure_storage_windows/analysis_options.yaml
vendored
Normal file
1
third_party/flutter_secure_storage_windows/analysis_options.yaml
vendored
Normal file
@ -0,0 +1 @@
|
||||
include: package:lint/package.yaml
|
||||
16
third_party/flutter_secure_storage_windows/example/README.md
vendored
Normal file
16
third_party/flutter_secure_storage_windows/example/README.md
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
# flutter_secure_storage_windows_example
|
||||
|
||||
Demonstrates how to use the flutter_secure_storage_windows plugin.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||
|
||||
For help getting started with Flutter development, view the
|
||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
29
third_party/flutter_secure_storage_windows/example/analysis_options.yaml
vendored
Normal file
29
third_party/flutter_secure_storage_windows/example/analysis_options.yaml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at
|
||||
# https://dart-lang.github.io/linter/lints/index.html.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
1117
third_party/flutter_secure_storage_windows/example/integration_test/app_test.dart
vendored
Normal file
1117
third_party/flutter_secure_storage_windows/example/integration_test/app_test.dart
vendored
Normal file
File diff suppressed because it is too large
Load Diff
383
third_party/flutter_secure_storage_windows/example/lib/main.dart
vendored
Normal file
383
third_party/flutter_secure_storage_windows/example/lib/main.dart
vendored
Normal file
@ -0,0 +1,383 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_secure_storage_platform_interface/flutter_secure_storage_platform_interface.dart';
|
||||
import 'package:flutter_secure_storage_windows/flutter_secure_storage_windows.dart';
|
||||
|
||||
// testing application.
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatefulWidget {
|
||||
const MyApp({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<MyApp> createState() => MyAppState();
|
||||
}
|
||||
|
||||
class MyAppState extends State<MyApp> {
|
||||
final TextEditingController keyFieldController = TextEditingController();
|
||||
final TextEditingController valueFieldController = TextEditingController();
|
||||
final TextEditingController resultSummaryFieldController =
|
||||
TextEditingController();
|
||||
final TextEditingController resultDetailFieldController =
|
||||
TextEditingController();
|
||||
final GlobalKey<LabeledCheckboxState> useMethodChannelOnlyKey = GlobalKey();
|
||||
final GlobalKey<LabeledCheckboxState> useBackwardCompatibilityKey =
|
||||
GlobalKey();
|
||||
|
||||
Future<TestResult>? _future;
|
||||
|
||||
FlutterSecureStoragePlatform _flutterSecureStorageWindowsPlugin =
|
||||
FlutterSecureStorageWindows();
|
||||
final Map<String, String> _options = {'useBackwardCompatibility': 'false'};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Plugin example app'),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(children: [
|
||||
TextField(
|
||||
controller: keyFieldController,
|
||||
decoration: const InputDecoration(label: Text('Key')),
|
||||
),
|
||||
TextField(
|
||||
controller: valueFieldController,
|
||||
decoration: const InputDecoration(label: Text('Value')),
|
||||
),
|
||||
LabeledCheckbox(
|
||||
key: useMethodChannelOnlyKey,
|
||||
initialValue: false,
|
||||
label: 'UseMethodChannelOnly',
|
||||
onChanged: (useMethodChannelOnly) {
|
||||
setState(() {
|
||||
_flutterSecureStorageWindowsPlugin = useMethodChannelOnly
|
||||
? MethodChannelFlutterSecureStorage()
|
||||
: FlutterSecureStorageWindows();
|
||||
});
|
||||
},
|
||||
),
|
||||
LabeledCheckbox(
|
||||
key: useBackwardCompatibilityKey,
|
||||
initialValue: false,
|
||||
label: 'UseBackwardCompatibility',
|
||||
onChanged: (useBackwardCompatibility) {
|
||||
setState(() {
|
||||
_options['useBackwardCompatibility'] =
|
||||
useBackwardCompatibility.toString();
|
||||
});
|
||||
},
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: ElevatedButton(
|
||||
onPressed: doRead,
|
||||
child: const Text('Read'),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: ElevatedButton(
|
||||
onPressed: doReadAll,
|
||||
child: const Text('ReadAll'),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: ElevatedButton(
|
||||
onPressed: doContainsKey,
|
||||
child: const Text('ContainsKey'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: ElevatedButton(
|
||||
onPressed: doWrite,
|
||||
child: const Text('Write'),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: ElevatedButton(
|
||||
onPressed: doDelete,
|
||||
child: const Text('Delete'),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: ElevatedButton(
|
||||
onPressed: doDeleteAll,
|
||||
child: const Text('DeleteAll'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: ElevatedButton(
|
||||
onPressed: doLegacyWrite,
|
||||
child: const Text('LegacyWrite'),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: ElevatedButton(
|
||||
onPressed: doLegacyReadAll,
|
||||
child: const Text('LegacyReadAll'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_future != null)
|
||||
FutureBuilder<TestResult>(
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData && !snapshot.hasError) {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
|
||||
resultSummaryFieldController.text =
|
||||
(snapshot.data?.success ?? false) ? 'SUCCESS' : 'FAIL';
|
||||
|
||||
return TextField(
|
||||
controller: resultSummaryFieldController,
|
||||
decoration: const InputDecoration(label: Text('Result')),
|
||||
);
|
||||
},
|
||||
future: _future,
|
||||
),
|
||||
|
||||
if (_future != null)
|
||||
FutureBuilder<TestResult>(
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData && !snapshot.hasError) {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
|
||||
resultDetailFieldController.text =
|
||||
snapshot.error?.toString() ??
|
||||
snapshot.data!.detail ??
|
||||
'<null>';
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
TextField(
|
||||
controller: resultSummaryFieldController,
|
||||
decoration:
|
||||
const InputDecoration(label: Text('Result')),
|
||||
),
|
||||
TextField(
|
||||
controller: resultDetailFieldController,
|
||||
decoration:
|
||||
const InputDecoration(label: Text('Detail')),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
future: _future,
|
||||
),
|
||||
// const Expanded(child: SizedBox()),
|
||||
]),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<TestResult> doTestCore(FutureOr<TestResult> Function() test) async {
|
||||
late final TestResult result;
|
||||
try {
|
||||
result = await test();
|
||||
} catch (e, s) {
|
||||
debugPrint(e.toString());
|
||||
debugPrintStack(stackTrace: s);
|
||||
result = TestResult(success: false, detail: e.toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void doTest(FutureOr<TestResult> Function() test) {
|
||||
setState(() {
|
||||
_future = doTestCore(test);
|
||||
});
|
||||
}
|
||||
|
||||
void doRead() => doTest(() async {
|
||||
final key = keyFieldController.text;
|
||||
return TestResult(
|
||||
success: true,
|
||||
detail: await _flutterSecureStorageWindowsPlugin.read(
|
||||
key: key,
|
||||
options: _options,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
void doReadAll() => doTest(() async {
|
||||
return TestResult(
|
||||
success: true,
|
||||
detail: (await _flutterSecureStorageWindowsPlugin.readAll(
|
||||
options: _options,
|
||||
))
|
||||
.toString(),
|
||||
);
|
||||
});
|
||||
|
||||
void doContainsKey() => doTest(() async {
|
||||
final key = keyFieldController.text;
|
||||
return TestResult(
|
||||
success: true,
|
||||
detail: (await _flutterSecureStorageWindowsPlugin.containsKey(
|
||||
key: key,
|
||||
options: _options,
|
||||
))
|
||||
.toString(),
|
||||
);
|
||||
});
|
||||
|
||||
void doWrite() => doTest(() async {
|
||||
final key = keyFieldController.text;
|
||||
final value = valueFieldController.text.isNotEmpty
|
||||
? valueFieldController.text
|
||||
: DateTime.now().toIso8601String();
|
||||
await _flutterSecureStorageWindowsPlugin.write(
|
||||
key: key,
|
||||
value: value,
|
||||
options: _options,
|
||||
);
|
||||
return TestResult(success: true, detail: value);
|
||||
});
|
||||
|
||||
void doDelete() => doTest(() async {
|
||||
final key = keyFieldController.text;
|
||||
await _flutterSecureStorageWindowsPlugin.delete(
|
||||
key: key,
|
||||
options: _options,
|
||||
);
|
||||
return TestResult(
|
||||
success: true,
|
||||
detail: null,
|
||||
);
|
||||
});
|
||||
|
||||
void doDeleteAll() => doTest(() async {
|
||||
await _flutterSecureStorageWindowsPlugin.deleteAll(
|
||||
options: _options,
|
||||
);
|
||||
return TestResult(
|
||||
success: true,
|
||||
detail: null,
|
||||
);
|
||||
});
|
||||
|
||||
void doLegacyWrite() => doTest(() async {
|
||||
final key = keyFieldController.text;
|
||||
final value = valueFieldController.text.isNotEmpty
|
||||
? valueFieldController.text
|
||||
: DateTime.now().toIso8601String();
|
||||
// call MethodChannelFlutterSecureStorage directly
|
||||
final legacyStorage = MethodChannelFlutterSecureStorage();
|
||||
await legacyStorage.write(
|
||||
key: key,
|
||||
value: value,
|
||||
options: _options,
|
||||
);
|
||||
return TestResult(success: true, detail: value);
|
||||
});
|
||||
|
||||
void doLegacyReadAll() => doTest(() async {
|
||||
// call MethodChannelFlutterSecureStorage directly
|
||||
final legacyStorage = MethodChannelFlutterSecureStorage();
|
||||
return TestResult(
|
||||
success: true,
|
||||
detail: (await legacyStorage.readAll(
|
||||
options: _options,
|
||||
))
|
||||
.toString());
|
||||
});
|
||||
}
|
||||
|
||||
class TestResult {
|
||||
final bool success;
|
||||
final String? detail;
|
||||
TestResult({
|
||||
required this.success,
|
||||
required this.detail,
|
||||
});
|
||||
}
|
||||
|
||||
class LabeledCheckbox extends StatefulWidget {
|
||||
final String label;
|
||||
final EdgeInsetsGeometry padding;
|
||||
final bool initialValue;
|
||||
final ValueChanged<bool>? onChanged;
|
||||
const LabeledCheckbox({
|
||||
Key? key,
|
||||
required this.label,
|
||||
this.padding = const EdgeInsets.all(4),
|
||||
this.initialValue = false,
|
||||
this.onChanged,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => LabeledCheckboxState._();
|
||||
}
|
||||
|
||||
class LabeledCheckboxState extends State<LabeledCheckbox> {
|
||||
late bool _value;
|
||||
|
||||
bool get value => _value;
|
||||
set value(bool v) {
|
||||
setState(() {
|
||||
_value = v;
|
||||
});
|
||||
|
||||
widget.onChanged?.call(v);
|
||||
}
|
||||
|
||||
LabeledCheckboxState._();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_value = widget.initialValue;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => InkWell(
|
||||
onTap: () {
|
||||
value = !value;
|
||||
},
|
||||
child: Padding(
|
||||
padding: widget.padding,
|
||||
child: Row(children: [
|
||||
Expanded(child: Text(widget.label)),
|
||||
Checkbox(
|
||||
value: value,
|
||||
onChanged: (newValue) {
|
||||
if (newValue != null) {
|
||||
value = newValue;
|
||||
}
|
||||
})
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
30
third_party/flutter_secure_storage_windows/example/pubspec.yaml
vendored
Normal file
30
third_party/flutter_secure_storage_windows/example/pubspec.yaml
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
name: flutter_secure_storage_windows_example
|
||||
description: Demonstrates how to use the flutter_secure_storage_windows plugin.
|
||||
publish_to: 'none'
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
flutter: ">=2.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_secure_storage_platform_interface:
|
||||
flutter_secure_storage_windows:
|
||||
path: ../
|
||||
path: ^1.8.0
|
||||
path_provider: ^2.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^2.0.0
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
integration_test:
|
||||
sdk: flutter
|
||||
|
||||
dependency_overrides:
|
||||
flutter_secure_storage_platform_interface:
|
||||
path: ../../flutter_secure_storage_platform_interface
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
101
third_party/flutter_secure_storage_windows/example/windows/CMakeLists.txt
vendored
Normal file
101
third_party/flutter_secure_storage_windows/example/windows/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
# Project-level configuration.
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(flutter_secure_storage_windows_example LANGUAGES CXX)
|
||||
|
||||
# The name of the executable created for the application. Change this to change
|
||||
# the on-disk name of your application.
|
||||
set(BINARY_NAME "flutter_secure_storage_windows_example")
|
||||
|
||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||
# versions of CMake.
|
||||
cmake_policy(SET CMP0063 NEW)
|
||||
|
||||
# Define build configuration option.
|
||||
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(IS_MULTICONFIG)
|
||||
set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
|
||||
CACHE STRING "" FORCE)
|
||||
else()
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE "Debug" CACHE
|
||||
STRING "Flutter build mode" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
|
||||
"Debug" "Profile" "Release")
|
||||
endif()
|
||||
endif()
|
||||
# Define settings for the Profile build mode.
|
||||
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
|
||||
# Use Unicode for all projects.
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
|
||||
# Compilation settings that should be applied to most targets.
|
||||
#
|
||||
# Be cautious about adding new options here, as plugins use this function by
|
||||
# default. In most cases, you should add new options to specific targets instead
|
||||
# of modifying this function.
|
||||
function(APPLY_STANDARD_SETTINGS TARGET)
|
||||
target_compile_features(${TARGET} PUBLIC cxx_std_17)
|
||||
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
|
||||
target_compile_options(${TARGET} PRIVATE /EHsc)
|
||||
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
|
||||
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
|
||||
endfunction()
|
||||
|
||||
# Flutter library and tool build rules.
|
||||
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
|
||||
add_subdirectory(${FLUTTER_MANAGED_DIR})
|
||||
|
||||
# Application build; see runner/CMakeLists.txt.
|
||||
add_subdirectory("runner")
|
||||
|
||||
# Generated plugin build rules, which manage building the plugins and adding
|
||||
# them to the application.
|
||||
include(flutter/generated_plugins.cmake)
|
||||
|
||||
|
||||
# === Installation ===
|
||||
# Support files are copied into place next to the executable, so that it can
|
||||
# run in place. This is done instead of making a separate bundle (as on Linux)
|
||||
# so that building and running from within Visual Studio will work.
|
||||
set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
|
||||
# Make the "install" step default, as it's required to run.
|
||||
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
|
||||
endif()
|
||||
|
||||
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
|
||||
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
|
||||
|
||||
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
if(PLUGIN_BUNDLED_LIBRARIES)
|
||||
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
|
||||
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
endif()
|
||||
|
||||
# Fully re-copy the assets directory on each build to avoid having stale files
|
||||
# from a previous install.
|
||||
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
|
||||
install(CODE "
|
||||
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
|
||||
" COMPONENT Runtime)
|
||||
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
|
||||
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
|
||||
|
||||
# Install the AOT library on non-Debug builds only.
|
||||
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
||||
CONFIGURATIONS Profile;Release
|
||||
COMPONENT Runtime)
|
||||
104
third_party/flutter_secure_storage_windows/example/windows/flutter/CMakeLists.txt
vendored
Normal file
104
third_party/flutter_secure_storage_windows/example/windows/flutter/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
# This file controls Flutter-level build steps. It should not be edited.
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
|
||||
|
||||
# Configuration provided via flutter tool.
|
||||
include(${EPHEMERAL_DIR}/generated_config.cmake)
|
||||
|
||||
# TODO: Move the rest of this into files in ephemeral. See
|
||||
# https://github.com/flutter/flutter/issues/57146.
|
||||
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
|
||||
|
||||
# === Flutter Library ===
|
||||
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
|
||||
|
||||
# Published to parent scope for install step.
|
||||
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
|
||||
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
|
||||
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
|
||||
set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
|
||||
|
||||
list(APPEND FLUTTER_LIBRARY_HEADERS
|
||||
"flutter_export.h"
|
||||
"flutter_windows.h"
|
||||
"flutter_messenger.h"
|
||||
"flutter_plugin_registrar.h"
|
||||
"flutter_texture_registrar.h"
|
||||
)
|
||||
list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
|
||||
add_library(flutter INTERFACE)
|
||||
target_include_directories(flutter INTERFACE
|
||||
"${EPHEMERAL_DIR}"
|
||||
)
|
||||
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
|
||||
add_dependencies(flutter flutter_assemble)
|
||||
|
||||
# === Wrapper ===
|
||||
list(APPEND CPP_WRAPPER_SOURCES_CORE
|
||||
"core_implementations.cc"
|
||||
"standard_codec.cc"
|
||||
)
|
||||
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
|
||||
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
|
||||
"plugin_registrar.cc"
|
||||
)
|
||||
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
|
||||
list(APPEND CPP_WRAPPER_SOURCES_APP
|
||||
"flutter_engine.cc"
|
||||
"flutter_view_controller.cc"
|
||||
)
|
||||
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
|
||||
|
||||
# Wrapper sources needed for a plugin.
|
||||
add_library(flutter_wrapper_plugin STATIC
|
||||
${CPP_WRAPPER_SOURCES_CORE}
|
||||
${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||
)
|
||||
apply_standard_settings(flutter_wrapper_plugin)
|
||||
set_target_properties(flutter_wrapper_plugin PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON)
|
||||
set_target_properties(flutter_wrapper_plugin PROPERTIES
|
||||
CXX_VISIBILITY_PRESET hidden)
|
||||
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
|
||||
target_include_directories(flutter_wrapper_plugin PUBLIC
|
||||
"${WRAPPER_ROOT}/include"
|
||||
)
|
||||
add_dependencies(flutter_wrapper_plugin flutter_assemble)
|
||||
|
||||
# Wrapper sources needed for the runner.
|
||||
add_library(flutter_wrapper_app STATIC
|
||||
${CPP_WRAPPER_SOURCES_CORE}
|
||||
${CPP_WRAPPER_SOURCES_APP}
|
||||
)
|
||||
apply_standard_settings(flutter_wrapper_app)
|
||||
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
|
||||
target_include_directories(flutter_wrapper_app PUBLIC
|
||||
"${WRAPPER_ROOT}/include"
|
||||
)
|
||||
add_dependencies(flutter_wrapper_app flutter_assemble)
|
||||
|
||||
# === Flutter tool backend ===
|
||||
# _phony_ is a non-existent file to force this command to run every time,
|
||||
# since currently there's no way to get a full input/output list from the
|
||||
# flutter tool.
|
||||
set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
|
||||
set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
|
||||
add_custom_command(
|
||||
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
|
||||
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||
${CPP_WRAPPER_SOURCES_APP}
|
||||
${PHONY_OUTPUT}
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
${FLUTTER_TOOL_ENVIRONMENT}
|
||||
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
|
||||
windows-x64 $<CONFIG>
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(flutter_assemble DEPENDS
|
||||
"${FLUTTER_LIBRARY}"
|
||||
${FLUTTER_LIBRARY_HEADERS}
|
||||
${CPP_WRAPPER_SOURCES_CORE}
|
||||
${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||
${CPP_WRAPPER_SOURCES_APP}
|
||||
)
|
||||
@ -0,0 +1,14 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||
}
|
||||
15
third_party/flutter_secure_storage_windows/example/windows/flutter/generated_plugin_registrant.h
vendored
Normal file
15
third_party/flutter_secure_storage_windows/example/windows/flutter/generated_plugin_registrant.h
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef GENERATED_PLUGIN_REGISTRANT_
|
||||
#define GENERATED_PLUGIN_REGISTRANT_
|
||||
|
||||
#include <flutter/plugin_registry.h>
|
||||
|
||||
// Registers Flutter plugins.
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry);
|
||||
|
||||
#endif // GENERATED_PLUGIN_REGISTRANT_
|
||||
24
third_party/flutter_secure_storage_windows/example/windows/flutter/generated_plugins.cmake
vendored
Normal file
24
third_party/flutter_secure_storage_windows/example/windows/flutter/generated_plugins.cmake
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
flutter_secure_storage_windows
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||
endforeach(plugin)
|
||||
|
||||
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
|
||||
endforeach(ffi_plugin)
|
||||
40
third_party/flutter_secure_storage_windows/example/windows/runner/CMakeLists.txt
vendored
Normal file
40
third_party/flutter_secure_storage_windows/example/windows/runner/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(runner LANGUAGES CXX)
|
||||
|
||||
# Define the application target. To change its name, change BINARY_NAME in the
|
||||
# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
|
||||
# work.
|
||||
#
|
||||
# Any new source files that you add to the application should be added here.
|
||||
add_executable(${BINARY_NAME} WIN32
|
||||
"flutter_window.cpp"
|
||||
"main.cpp"
|
||||
"utils.cpp"
|
||||
"win32_window.cpp"
|
||||
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
|
||||
"Runner.rc"
|
||||
"runner.exe.manifest"
|
||||
)
|
||||
|
||||
# Apply the standard set of build settings. This can be removed for applications
|
||||
# that need different build settings.
|
||||
apply_standard_settings(${BINARY_NAME})
|
||||
|
||||
# Add preprocessor definitions for the build version.
|
||||
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
|
||||
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
|
||||
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
|
||||
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
|
||||
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
|
||||
|
||||
# Disable Windows macros that collide with C++ standard library functions.
|
||||
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
|
||||
|
||||
# Add dependency libraries and include directories. Add any application-specific
|
||||
# dependencies here.
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
|
||||
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
|
||||
|
||||
# Run the Flutter tool portions of the build. This must not be removed.
|
||||
add_dependencies(${BINARY_NAME} flutter_assemble)
|
||||
121
third_party/flutter_secure_storage_windows/example/windows/runner/Runner.rc
vendored
Normal file
121
third_party/flutter_secure_storage_windows/example/windows/runner/Runner.rc
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#pragma code_page(65001)
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (United States) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
IDI_APP_ICON ICON "resources\\app_icon.ico"
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
|
||||
#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
|
||||
#else
|
||||
#define VERSION_AS_NUMBER 1,0,0,0
|
||||
#endif
|
||||
|
||||
#if defined(FLUTTER_VERSION)
|
||||
#define VERSION_AS_STRING FLUTTER_VERSION
|
||||
#else
|
||||
#define VERSION_AS_STRING "1.0.0"
|
||||
#endif
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION VERSION_AS_NUMBER
|
||||
PRODUCTVERSION VERSION_AS_NUMBER
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS__WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904e4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "com.example" "\0"
|
||||
VALUE "FileDescription", "flutter_secure_storage_windows_example" "\0"
|
||||
VALUE "FileVersion", VERSION_AS_STRING "\0"
|
||||
VALUE "InternalName", "flutter_secure_storage_windows_example" "\0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0"
|
||||
VALUE "OriginalFilename", "flutter_secure_storage_windows_example.exe" "\0"
|
||||
VALUE "ProductName", "flutter_secure_storage_windows_example" "\0"
|
||||
VALUE "ProductVersion", VERSION_AS_STRING "\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1252
|
||||
END
|
||||
END
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
66
third_party/flutter_secure_storage_windows/example/windows/runner/flutter_window.cpp
vendored
Normal file
66
third_party/flutter_secure_storage_windows/example/windows/runner/flutter_window.cpp
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
#include "flutter_window.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "flutter/generated_plugin_registrant.h"
|
||||
|
||||
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
|
||||
: project_(project) {}
|
||||
|
||||
FlutterWindow::~FlutterWindow() {}
|
||||
|
||||
bool FlutterWindow::OnCreate() {
|
||||
if (!Win32Window::OnCreate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RECT frame = GetClientArea();
|
||||
|
||||
// The size here must match the window dimensions to avoid unnecessary surface
|
||||
// creation / destruction in the startup path.
|
||||
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
|
||||
frame.right - frame.left, frame.bottom - frame.top, project_);
|
||||
// Ensure that basic setup of the controller was successful.
|
||||
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
|
||||
return false;
|
||||
}
|
||||
RegisterPlugins(flutter_controller_->engine());
|
||||
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
||||
|
||||
flutter_controller_->engine()->SetNextFrameCallback([&]() {
|
||||
this->Show();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FlutterWindow::OnDestroy() {
|
||||
if (flutter_controller_) {
|
||||
flutter_controller_ = nullptr;
|
||||
}
|
||||
|
||||
Win32Window::OnDestroy();
|
||||
}
|
||||
|
||||
LRESULT
|
||||
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept {
|
||||
// Give Flutter, including plugins, an opportunity to handle window messages.
|
||||
if (flutter_controller_) {
|
||||
std::optional<LRESULT> result =
|
||||
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
|
||||
lparam);
|
||||
if (result) {
|
||||
return *result;
|
||||
}
|
||||
}
|
||||
|
||||
switch (message) {
|
||||
case WM_FONTCHANGE:
|
||||
flutter_controller_->engine()->ReloadSystemFonts();
|
||||
break;
|
||||
}
|
||||
|
||||
return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
|
||||
}
|
||||
33
third_party/flutter_secure_storage_windows/example/windows/runner/flutter_window.h
vendored
Normal file
33
third_party/flutter_secure_storage_windows/example/windows/runner/flutter_window.h
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef RUNNER_FLUTTER_WINDOW_H_
|
||||
#define RUNNER_FLUTTER_WINDOW_H_
|
||||
|
||||
#include <flutter/dart_project.h>
|
||||
#include <flutter/flutter_view_controller.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "win32_window.h"
|
||||
|
||||
// A window that does nothing but host a Flutter view.
|
||||
class FlutterWindow : public Win32Window {
|
||||
public:
|
||||
// Creates a new FlutterWindow hosting a Flutter view running |project|.
|
||||
explicit FlutterWindow(const flutter::DartProject& project);
|
||||
virtual ~FlutterWindow();
|
||||
|
||||
protected:
|
||||
// Win32Window:
|
||||
bool OnCreate() override;
|
||||
void OnDestroy() override;
|
||||
LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept override;
|
||||
|
||||
private:
|
||||
// The project to run.
|
||||
flutter::DartProject project_;
|
||||
|
||||
// The Flutter instance hosted by this window.
|
||||
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
|
||||
};
|
||||
|
||||
#endif // RUNNER_FLUTTER_WINDOW_H_
|
||||
43
third_party/flutter_secure_storage_windows/example/windows/runner/main.cpp
vendored
Normal file
43
third_party/flutter_secure_storage_windows/example/windows/runner/main.cpp
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
#include <flutter/dart_project.h>
|
||||
#include <flutter/flutter_view_controller.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "flutter_window.h"
|
||||
#include "utils.h"
|
||||
|
||||
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
||||
_In_ wchar_t *command_line, _In_ int show_command) {
|
||||
// Attach to console when present (e.g., 'flutter run') or create a
|
||||
// new console when running with a debugger.
|
||||
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
|
||||
CreateAndAttachConsole();
|
||||
}
|
||||
|
||||
// Initialize COM, so that it is available for use in the library and/or
|
||||
// plugins.
|
||||
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||
|
||||
flutter::DartProject project(L"data");
|
||||
|
||||
std::vector<std::string> command_line_arguments =
|
||||
GetCommandLineArguments();
|
||||
|
||||
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
|
||||
|
||||
FlutterWindow window(project);
|
||||
Win32Window::Point origin(10, 10);
|
||||
Win32Window::Size size(1280, 720);
|
||||
if (!window.Create(L"flutter_secure_storage_windows_example", origin, size)) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
window.SetQuitOnClose(true);
|
||||
|
||||
::MSG msg;
|
||||
while (::GetMessage(&msg, nullptr, 0, 0)) {
|
||||
::TranslateMessage(&msg);
|
||||
::DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
::CoUninitialize();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
16
third_party/flutter_secure_storage_windows/example/windows/runner/resource.h
vendored
Normal file
16
third_party/flutter_secure_storage_windows/example/windows/runner/resource.h
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by Runner.rc
|
||||
//
|
||||
#define IDI_APP_ICON 101
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
BIN
third_party/flutter_secure_storage_windows/example/windows/runner/resources/app_icon.ico
vendored
Normal file
BIN
third_party/flutter_secure_storage_windows/example/windows/runner/resources/app_icon.ico
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
20
third_party/flutter_secure_storage_windows/example/windows/runner/runner.exe.manifest
vendored
Normal file
20
third_party/flutter_secure_storage_windows/example/windows/runner/runner.exe.manifest
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10 and Windows 11 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
64
third_party/flutter_secure_storage_windows/example/windows/runner/utils.cpp
vendored
Normal file
64
third_party/flutter_secure_storage_windows/example/windows/runner/utils.cpp
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
#include "utils.h"
|
||||
|
||||
#include <flutter_windows.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void CreateAndAttachConsole() {
|
||||
if (::AllocConsole()) {
|
||||
FILE *unused;
|
||||
if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
|
||||
_dup2(_fileno(stdout), 1);
|
||||
}
|
||||
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
|
||||
_dup2(_fileno(stdout), 2);
|
||||
}
|
||||
std::ios::sync_with_stdio();
|
||||
FlutterDesktopResyncOutputStreams();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> GetCommandLineArguments() {
|
||||
// Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
|
||||
int argc;
|
||||
wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
|
||||
if (argv == nullptr) {
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
std::vector<std::string> command_line_arguments;
|
||||
|
||||
// Skip the first argument as it's the binary name.
|
||||
for (int i = 1; i < argc; i++) {
|
||||
command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
|
||||
}
|
||||
|
||||
::LocalFree(argv);
|
||||
|
||||
return command_line_arguments;
|
||||
}
|
||||
|
||||
std::string Utf8FromUtf16(const wchar_t* utf16_string) {
|
||||
if (utf16_string == nullptr) {
|
||||
return std::string();
|
||||
}
|
||||
int target_length = ::WideCharToMultiByte(
|
||||
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
|
||||
-1, nullptr, 0, nullptr, nullptr);
|
||||
std::string utf8_string;
|
||||
if (target_length == 0 || target_length > utf8_string.max_size()) {
|
||||
return utf8_string;
|
||||
}
|
||||
utf8_string.resize(target_length);
|
||||
int converted_length = ::WideCharToMultiByte(
|
||||
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
|
||||
-1, utf8_string.data(),
|
||||
target_length, nullptr, nullptr);
|
||||
if (converted_length == 0) {
|
||||
return std::string();
|
||||
}
|
||||
return utf8_string;
|
||||
}
|
||||
19
third_party/flutter_secure_storage_windows/example/windows/runner/utils.h
vendored
Normal file
19
third_party/flutter_secure_storage_windows/example/windows/runner/utils.h
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef RUNNER_UTILS_H_
|
||||
#define RUNNER_UTILS_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Creates a console for the process, and redirects stdout and stderr to
|
||||
// it for both the runner and the Flutter library.
|
||||
void CreateAndAttachConsole();
|
||||
|
||||
// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
|
||||
// encoded in UTF-8. Returns an empty std::string on failure.
|
||||
std::string Utf8FromUtf16(const wchar_t* utf16_string);
|
||||
|
||||
// Gets the command line arguments passed in as a std::vector<std::string>,
|
||||
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
|
||||
std::vector<std::string> GetCommandLineArguments();
|
||||
|
||||
#endif // RUNNER_UTILS_H_
|
||||
288
third_party/flutter_secure_storage_windows/example/windows/runner/win32_window.cpp
vendored
Normal file
288
third_party/flutter_secure_storage_windows/example/windows/runner/win32_window.cpp
vendored
Normal file
@ -0,0 +1,288 @@
|
||||
#include "win32_window.h"
|
||||
|
||||
#include <dwmapi.h>
|
||||
#include <flutter_windows.h>
|
||||
|
||||
#include "resource.h"
|
||||
|
||||
namespace {
|
||||
|
||||
/// Window attribute that enables dark mode window decorations.
|
||||
///
|
||||
/// Redefined in case the developer's machine has a Windows SDK older than
|
||||
/// version 10.0.22000.0.
|
||||
/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
|
||||
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
|
||||
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
|
||||
#endif
|
||||
|
||||
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
|
||||
|
||||
/// Registry key for app theme preference.
|
||||
///
|
||||
/// A value of 0 indicates apps should use dark mode. A non-zero or missing
|
||||
/// value indicates apps should use light mode.
|
||||
constexpr const wchar_t kGetPreferredBrightnessRegKey[] =
|
||||
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
|
||||
constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";
|
||||
|
||||
// The number of Win32Window objects that currently exist.
|
||||
static int g_active_window_count = 0;
|
||||
|
||||
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
|
||||
|
||||
// Scale helper to convert logical scaler values to physical using passed in
|
||||
// scale factor
|
||||
int Scale(int source, double scale_factor) {
|
||||
return static_cast<int>(source * scale_factor);
|
||||
}
|
||||
|
||||
// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
|
||||
// This API is only needed for PerMonitor V1 awareness mode.
|
||||
void EnableFullDpiSupportIfAvailable(HWND hwnd) {
|
||||
HMODULE user32_module = LoadLibraryA("User32.dll");
|
||||
if (!user32_module) {
|
||||
return;
|
||||
}
|
||||
auto enable_non_client_dpi_scaling =
|
||||
reinterpret_cast<EnableNonClientDpiScaling*>(
|
||||
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
|
||||
if (enable_non_client_dpi_scaling != nullptr) {
|
||||
enable_non_client_dpi_scaling(hwnd);
|
||||
}
|
||||
FreeLibrary(user32_module);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Manages the Win32Window's window class registration.
|
||||
class WindowClassRegistrar {
|
||||
public:
|
||||
~WindowClassRegistrar() = default;
|
||||
|
||||
// Returns the singleton registar instance.
|
||||
static WindowClassRegistrar* GetInstance() {
|
||||
if (!instance_) {
|
||||
instance_ = new WindowClassRegistrar();
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
// Returns the name of the window class, registering the class if it hasn't
|
||||
// previously been registered.
|
||||
const wchar_t* GetWindowClass();
|
||||
|
||||
// Unregisters the window class. Should only be called if there are no
|
||||
// instances of the window.
|
||||
void UnregisterWindowClass();
|
||||
|
||||
private:
|
||||
WindowClassRegistrar() = default;
|
||||
|
||||
static WindowClassRegistrar* instance_;
|
||||
|
||||
bool class_registered_ = false;
|
||||
};
|
||||
|
||||
WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
|
||||
|
||||
const wchar_t* WindowClassRegistrar::GetWindowClass() {
|
||||
if (!class_registered_) {
|
||||
WNDCLASS window_class{};
|
||||
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
window_class.lpszClassName = kWindowClassName;
|
||||
window_class.style = CS_HREDRAW | CS_VREDRAW;
|
||||
window_class.cbClsExtra = 0;
|
||||
window_class.cbWndExtra = 0;
|
||||
window_class.hInstance = GetModuleHandle(nullptr);
|
||||
window_class.hIcon =
|
||||
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
|
||||
window_class.hbrBackground = 0;
|
||||
window_class.lpszMenuName = nullptr;
|
||||
window_class.lpfnWndProc = Win32Window::WndProc;
|
||||
RegisterClass(&window_class);
|
||||
class_registered_ = true;
|
||||
}
|
||||
return kWindowClassName;
|
||||
}
|
||||
|
||||
void WindowClassRegistrar::UnregisterWindowClass() {
|
||||
UnregisterClass(kWindowClassName, nullptr);
|
||||
class_registered_ = false;
|
||||
}
|
||||
|
||||
Win32Window::Win32Window() {
|
||||
++g_active_window_count;
|
||||
}
|
||||
|
||||
Win32Window::~Win32Window() {
|
||||
--g_active_window_count;
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool Win32Window::Create(const std::wstring& title,
|
||||
const Point& origin,
|
||||
const Size& size) {
|
||||
Destroy();
|
||||
|
||||
const wchar_t* window_class =
|
||||
WindowClassRegistrar::GetInstance()->GetWindowClass();
|
||||
|
||||
const POINT target_point = {static_cast<LONG>(origin.x),
|
||||
static_cast<LONG>(origin.y)};
|
||||
HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
|
||||
UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
|
||||
double scale_factor = dpi / 96.0;
|
||||
|
||||
HWND window = CreateWindow(
|
||||
window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
|
||||
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
|
||||
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
|
||||
nullptr, nullptr, GetModuleHandle(nullptr), this);
|
||||
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateTheme(window);
|
||||
|
||||
return OnCreate();
|
||||
}
|
||||
|
||||
bool Win32Window::Show() {
|
||||
return ShowWindow(window_handle_, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
// static
|
||||
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
|
||||
UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept {
|
||||
if (message == WM_NCCREATE) {
|
||||
auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
|
||||
SetWindowLongPtr(window, GWLP_USERDATA,
|
||||
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
|
||||
|
||||
auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
|
||||
EnableFullDpiSupportIfAvailable(window);
|
||||
that->window_handle_ = window;
|
||||
} else if (Win32Window* that = GetThisFromHandle(window)) {
|
||||
return that->MessageHandler(window, message, wparam, lparam);
|
||||
}
|
||||
|
||||
return DefWindowProc(window, message, wparam, lparam);
|
||||
}
|
||||
|
||||
LRESULT
|
||||
Win32Window::MessageHandler(HWND hwnd,
|
||||
UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept {
|
||||
switch (message) {
|
||||
case WM_DESTROY:
|
||||
window_handle_ = nullptr;
|
||||
Destroy();
|
||||
if (quit_on_close_) {
|
||||
PostQuitMessage(0);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_DPICHANGED: {
|
||||
auto newRectSize = reinterpret_cast<RECT*>(lparam);
|
||||
LONG newWidth = newRectSize->right - newRectSize->left;
|
||||
LONG newHeight = newRectSize->bottom - newRectSize->top;
|
||||
|
||||
SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
|
||||
newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
case WM_SIZE: {
|
||||
RECT rect = GetClientArea();
|
||||
if (child_content_ != nullptr) {
|
||||
// Size and position the child window.
|
||||
MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
|
||||
rect.bottom - rect.top, TRUE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_ACTIVATE:
|
||||
if (child_content_ != nullptr) {
|
||||
SetFocus(child_content_);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_DWMCOLORIZATIONCOLORCHANGED:
|
||||
UpdateTheme(hwnd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DefWindowProc(window_handle_, message, wparam, lparam);
|
||||
}
|
||||
|
||||
void Win32Window::Destroy() {
|
||||
OnDestroy();
|
||||
|
||||
if (window_handle_) {
|
||||
DestroyWindow(window_handle_);
|
||||
window_handle_ = nullptr;
|
||||
}
|
||||
if (g_active_window_count == 0) {
|
||||
WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
|
||||
}
|
||||
}
|
||||
|
||||
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
|
||||
return reinterpret_cast<Win32Window*>(
|
||||
GetWindowLongPtr(window, GWLP_USERDATA));
|
||||
}
|
||||
|
||||
void Win32Window::SetChildContent(HWND content) {
|
||||
child_content_ = content;
|
||||
SetParent(content, window_handle_);
|
||||
RECT frame = GetClientArea();
|
||||
|
||||
MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
|
||||
frame.bottom - frame.top, true);
|
||||
|
||||
SetFocus(child_content_);
|
||||
}
|
||||
|
||||
RECT Win32Window::GetClientArea() {
|
||||
RECT frame;
|
||||
GetClientRect(window_handle_, &frame);
|
||||
return frame;
|
||||
}
|
||||
|
||||
HWND Win32Window::GetHandle() {
|
||||
return window_handle_;
|
||||
}
|
||||
|
||||
void Win32Window::SetQuitOnClose(bool quit_on_close) {
|
||||
quit_on_close_ = quit_on_close;
|
||||
}
|
||||
|
||||
bool Win32Window::OnCreate() {
|
||||
// No-op; provided for subclasses.
|
||||
return true;
|
||||
}
|
||||
|
||||
void Win32Window::OnDestroy() {
|
||||
// No-op; provided for subclasses.
|
||||
}
|
||||
|
||||
void Win32Window::UpdateTheme(HWND const window) {
|
||||
DWORD light_mode;
|
||||
DWORD light_mode_size = sizeof(light_mode);
|
||||
LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
|
||||
kGetPreferredBrightnessRegValue,
|
||||
RRF_RT_REG_DWORD, nullptr, &light_mode,
|
||||
&light_mode_size);
|
||||
|
||||
if (result == ERROR_SUCCESS) {
|
||||
BOOL enable_dark_mode = light_mode == 0;
|
||||
DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,
|
||||
&enable_dark_mode, sizeof(enable_dark_mode));
|
||||
}
|
||||
}
|
||||
102
third_party/flutter_secure_storage_windows/example/windows/runner/win32_window.h
vendored
Normal file
102
third_party/flutter_secure_storage_windows/example/windows/runner/win32_window.h
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
#ifndef RUNNER_WIN32_WINDOW_H_
|
||||
#define RUNNER_WIN32_WINDOW_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
// A class abstraction for a high DPI-aware Win32 Window. Intended to be
|
||||
// inherited from by classes that wish to specialize with custom
|
||||
// rendering and input handling
|
||||
class Win32Window {
|
||||
public:
|
||||
struct Point {
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
Point(unsigned int x, unsigned int y) : x(x), y(y) {}
|
||||
};
|
||||
|
||||
struct Size {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
Size(unsigned int width, unsigned int height)
|
||||
: width(width), height(height) {}
|
||||
};
|
||||
|
||||
Win32Window();
|
||||
virtual ~Win32Window();
|
||||
|
||||
// Creates a win32 window with |title| that is positioned and sized using
|
||||
// |origin| and |size|. New windows are created on the default monitor. Window
|
||||
// sizes are specified to the OS in physical pixels, hence to ensure a
|
||||
// consistent size this function will scale the inputted width and height as
|
||||
// as appropriate for the default monitor. The window is invisible until
|
||||
// |Show| is called. Returns true if the window was created successfully.
|
||||
bool Create(const std::wstring& title, const Point& origin, const Size& size);
|
||||
|
||||
// Show the current window. Returns true if the window was successfully shown.
|
||||
bool Show();
|
||||
|
||||
// Release OS resources associated with window.
|
||||
void Destroy();
|
||||
|
||||
// Inserts |content| into the window tree.
|
||||
void SetChildContent(HWND content);
|
||||
|
||||
// Returns the backing Window handle to enable clients to set icon and other
|
||||
// window properties. Returns nullptr if the window has been destroyed.
|
||||
HWND GetHandle();
|
||||
|
||||
// If true, closing this window will quit the application.
|
||||
void SetQuitOnClose(bool quit_on_close);
|
||||
|
||||
// Return a RECT representing the bounds of the current client area.
|
||||
RECT GetClientArea();
|
||||
|
||||
protected:
|
||||
// Processes and route salient window messages for mouse handling,
|
||||
// size change and DPI. Delegates handling of these to member overloads that
|
||||
// inheriting classes can handle.
|
||||
virtual LRESULT MessageHandler(HWND window,
|
||||
UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept;
|
||||
|
||||
// Called when CreateAndShow is called, allowing subclass window-related
|
||||
// setup. Subclasses should return false if setup fails.
|
||||
virtual bool OnCreate();
|
||||
|
||||
// Called when Destroy is called.
|
||||
virtual void OnDestroy();
|
||||
|
||||
private:
|
||||
friend class WindowClassRegistrar;
|
||||
|
||||
// OS callback called by message pump. Handles the WM_NCCREATE message which
|
||||
// is passed when the non-client area is being created and enables automatic
|
||||
// non-client DPI scaling so that the non-client area automatically
|
||||
// responsponds to changes in DPI. All other messages are handled by
|
||||
// MessageHandler.
|
||||
static LRESULT CALLBACK WndProc(HWND const window,
|
||||
UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept;
|
||||
|
||||
// Retrieves a class instance pointer for |window|
|
||||
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
|
||||
|
||||
// Update the window frame's theme to match the system theme.
|
||||
static void UpdateTheme(HWND const window);
|
||||
|
||||
bool quit_on_close_ = false;
|
||||
|
||||
// window handle for top level window.
|
||||
HWND window_handle_ = nullptr;
|
||||
|
||||
// window handle for hosted content.
|
||||
HWND child_content_ = nullptr;
|
||||
};
|
||||
|
||||
#endif // RUNNER_WIN32_WINDOW_H_
|
||||
3
third_party/flutter_secure_storage_windows/lib/flutter_secure_storage_windows.dart
vendored
Normal file
3
third_party/flutter_secure_storage_windows/lib/flutter_secure_storage_windows.dart
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
export 'src/flutter_secure_storage_windows_stub.dart'
|
||||
if (dart.library.ffi) 'src/flutter_secure_storage_windows_ffi.dart'
|
||||
show FlutterSecureStorageWindows;
|
||||
404
third_party/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart
vendored
Normal file
404
third_party/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart
vendored
Normal file
@ -0,0 +1,404 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter/foundation.dart' show debugPrint, visibleForTesting;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_secure_storage_platform_interface/flutter_secure_storage_platform_interface.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:win32/win32.dart';
|
||||
|
||||
@visibleForTesting
|
||||
extension OptionsExtension on Map<String, String> {
|
||||
bool get useBackwardCompatibility =>
|
||||
this['useBackwardCompatibility'] != 'false';
|
||||
}
|
||||
|
||||
class FlutterSecureStorageWindows extends FlutterSecureStoragePlatform {
|
||||
final FlutterSecureStoragePlatform _backwardCompatible;
|
||||
final MapStorage _storage;
|
||||
|
||||
FlutterSecureStorageWindows()
|
||||
: this._(
|
||||
MethodChannelFlutterSecureStorage(),
|
||||
DpapiJsonFileMapStorage(),
|
||||
);
|
||||
|
||||
FlutterSecureStorageWindows._(
|
||||
this._backwardCompatible,
|
||||
this._storage,
|
||||
);
|
||||
|
||||
/// Registers this plugin.
|
||||
static void registerWith() {
|
||||
FlutterSecureStoragePlatform.instance = FlutterSecureStorageWindows();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> containsKey({
|
||||
required String key,
|
||||
required Map<String, String> options,
|
||||
}) async {
|
||||
final map = await _storage.load(options);
|
||||
if (map.containsKey(key)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (options.useBackwardCompatibility) {
|
||||
return _backwardCompatible.containsKey(key: key, options: options);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete({
|
||||
required String key,
|
||||
required Map<String, String> options,
|
||||
}) async {
|
||||
final map = await _storage.load(options);
|
||||
final initialSize = map.length;
|
||||
map.remove(key);
|
||||
if (map.length != initialSize) {
|
||||
await _storage.save(map, options);
|
||||
}
|
||||
|
||||
if (options.useBackwardCompatibility) {
|
||||
await _backwardCompatible.delete(key: key, options: options);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAll({required Map<String, String> options}) async {
|
||||
await _storage.clear(options);
|
||||
|
||||
if (options.useBackwardCompatibility) {
|
||||
await _backwardCompatible.deleteAll(options: options);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> read({
|
||||
required String key,
|
||||
required Map<String, String> options,
|
||||
}) async {
|
||||
final map = await _storage.load(options);
|
||||
|
||||
var result = map[key];
|
||||
if (options.useBackwardCompatibility) {
|
||||
if (result == null) {
|
||||
final compatible =
|
||||
await _backwardCompatible.read(key: key, options: options);
|
||||
if (compatible != null) {
|
||||
// Write back now, so the value should be retrieved from JSON file next.
|
||||
result = map[key] = compatible;
|
||||
await _storage.save(map, options);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear old entry.
|
||||
await _backwardCompatible.delete(key: key, options: options);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> readAll({
|
||||
required Map<String, String> options,
|
||||
}) async {
|
||||
final map = await _storage.load(options);
|
||||
if (!options.useBackwardCompatibility) {
|
||||
// Just return a map.
|
||||
return map;
|
||||
}
|
||||
|
||||
final compatible = await _backwardCompatible.readAll(options: options);
|
||||
|
||||
if (compatible.isEmpty) {
|
||||
return map;
|
||||
}
|
||||
|
||||
for (final entry in compatible.entries) {
|
||||
map.putIfAbsent(entry.key, () => entry.value);
|
||||
}
|
||||
|
||||
// Write back now, so the value should be retrieved from JSON file next.
|
||||
await _storage.save(map, options);
|
||||
|
||||
// Clear old entries.
|
||||
await _backwardCompatible.deleteAll(options: options);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> write({
|
||||
required String key,
|
||||
required String value,
|
||||
required Map<String, String> options,
|
||||
}) async {
|
||||
final map = await _storage.load(options);
|
||||
map[key] = value;
|
||||
await _storage.save(map, options);
|
||||
|
||||
if (options.useBackwardCompatibility) {
|
||||
// Clear old entry.
|
||||
_backwardCompatible.delete(key: key, options: options);
|
||||
}
|
||||
}
|
||||
|
||||
// @override
|
||||
// Future<bool> isCupertinoProtectedDataAvailable() => Future.value(true);
|
||||
//
|
||||
// @override
|
||||
// Stream<bool> get onCupertinoProtectedDataAvailabilityChanged =>
|
||||
// Stream.value(true);
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
FlutterSecureStorageWindows createFlutterSecureStorageWindows(
|
||||
FlutterSecureStoragePlatform backwardCompatible,
|
||||
MapStorage mapStorage,
|
||||
) =>
|
||||
FlutterSecureStorageWindows._(backwardCompatible, mapStorage);
|
||||
|
||||
@visibleForTesting
|
||||
abstract class MapStorage {
|
||||
FutureOr<Map<String, String>> load(Map<String, String> options);
|
||||
FutureOr<void> save(Map<String, String> data, Map<String, String> options);
|
||||
FutureOr<void> clear(Map<String, String> options);
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
const String encryptedJsonFileName = 'flutter_secure_storage.dat';
|
||||
|
||||
@visibleForTesting
|
||||
class DpapiJsonFileMapStorage extends MapStorage {
|
||||
DpapiJsonFileMapStorage();
|
||||
|
||||
FutureOr<String> _getJsonFilePath() async {
|
||||
final appDataDirectory = await getApplicationSupportDirectory();
|
||||
|
||||
return path.canonicalize(
|
||||
path.join(
|
||||
appDataDirectory.path,
|
||||
encryptedJsonFileName,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<Map<String, String>> load(Map<String, String> options) async {
|
||||
final file = File(await _getJsonFilePath());
|
||||
if (!(await file.exists())) {
|
||||
return {};
|
||||
}
|
||||
|
||||
late final Uint8List encryptedText;
|
||||
try {
|
||||
encryptedText = await file.readAsBytes();
|
||||
} on FileSystemException catch (e) {
|
||||
// Another process has been deleted a file or parent directory
|
||||
// since previous File.exists() call.
|
||||
// We can ignore it.
|
||||
debugPrint(
|
||||
'Reading file has been deleted by another process. $e',
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
late final String plainText;
|
||||
try {
|
||||
plainText = using((alloc) {
|
||||
final Pointer<Uint8> pEncryptedText = alloc(encryptedText.length);
|
||||
pEncryptedText
|
||||
.asTypedList(encryptedText.length)
|
||||
.setAll(0, encryptedText);
|
||||
|
||||
// Specify size of the struct explicitly.
|
||||
final Pointer<CRYPT_INTEGER_BLOB> encryptedTextBlob =
|
||||
alloc.allocate(sizeOf<CRYPT_INTEGER_BLOB>());
|
||||
encryptedTextBlob.ref.cbData = encryptedText.length;
|
||||
encryptedTextBlob.ref.pbData = pEncryptedText;
|
||||
|
||||
// Specify size of the struct explicitly.
|
||||
final Pointer<CRYPT_INTEGER_BLOB> plainTextBlob =
|
||||
alloc.allocate(sizeOf<CRYPT_INTEGER_BLOB>());
|
||||
if (CryptUnprotectData(
|
||||
encryptedTextBlob,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0,
|
||||
plainTextBlob,
|
||||
) ==
|
||||
0) {
|
||||
throw WindowsException(
|
||||
GetLastError(),
|
||||
message: 'Failure on CryptUnprotectData()',
|
||||
);
|
||||
}
|
||||
|
||||
if (plainTextBlob.ref.pbData.address == NULL) {
|
||||
throw WindowsException(
|
||||
ERROR_OUTOFMEMORY,
|
||||
message: 'Failure on CryptUnprotectData()',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
return utf8.decoder.convert(
|
||||
plainTextBlob.ref.pbData.asTypedList(plainTextBlob.ref.cbData),
|
||||
);
|
||||
} finally {
|
||||
if (plainTextBlob.ref.pbData.address != NULL) {
|
||||
if (LocalFree(plainTextBlob.ref.pbData).address != NULL) {
|
||||
debugPrint(
|
||||
'load: Failed to LocalFree with: 0x${GetLastError().toHexString(32)}',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} on FormatException catch (e) {
|
||||
// A file content should be malformed.
|
||||
debugPrint(
|
||||
'Failed to decrypt data: $e Delete corrupt file: ${file.path}',
|
||||
);
|
||||
await file.delete();
|
||||
rethrow;
|
||||
} on WindowsException catch (e) {
|
||||
// A file content should be malformed.
|
||||
debugPrint(
|
||||
'Failed to decrypt data: $e Delete corrupt file: ${file.path}',
|
||||
);
|
||||
await file.delete();
|
||||
rethrow;
|
||||
}
|
||||
|
||||
final dynamic decoded;
|
||||
try {
|
||||
decoded = jsonDecode(plainText);
|
||||
} on FormatException catch (e) {
|
||||
// A file content should be malformed.
|
||||
debugPrint(
|
||||
'Failed to parse JSON: $e Delete corrupt file: ${file.path}',
|
||||
);
|
||||
await file.delete();
|
||||
rethrow;
|
||||
}
|
||||
|
||||
if (decoded is! Map) {
|
||||
debugPrint(
|
||||
'Failed to parse JSON: Not an object. Delete corrupt file: ${file.path}',
|
||||
);
|
||||
await file.delete();
|
||||
throw const FormatException('JSON is not an object.');
|
||||
}
|
||||
|
||||
return {
|
||||
for (final e
|
||||
in decoded.entries.where((x) => x.key is String && x.value is String))
|
||||
e.key as String: e.value as String,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> save(
|
||||
Map<String, String> data,
|
||||
Map<String, String> options,
|
||||
) async {
|
||||
final file = File(await _getJsonFilePath());
|
||||
final json = jsonEncode(data);
|
||||
final plainText = utf8.encode(json);
|
||||
|
||||
await using<FutureOr<void>>((alloc) async {
|
||||
final Pointer<Uint8> pPlainText = alloc(plainText.length);
|
||||
pPlainText.asTypedList(plainText.length).setAll(0, plainText);
|
||||
|
||||
// Specify size of the struct explicitly.
|
||||
final Pointer<CRYPT_INTEGER_BLOB> plainTextBlob =
|
||||
alloc.allocate(sizeOf<CRYPT_INTEGER_BLOB>());
|
||||
plainTextBlob.ref.cbData = plainText.length;
|
||||
plainTextBlob.ref.pbData = pPlainText;
|
||||
|
||||
// Specify size of the struct explicitly.
|
||||
final Pointer<CRYPT_INTEGER_BLOB> encryptedTextBlob =
|
||||
alloc.allocate(sizeOf<CRYPT_INTEGER_BLOB>());
|
||||
if (CryptProtectData(
|
||||
plainTextBlob,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0,
|
||||
encryptedTextBlob,
|
||||
) ==
|
||||
0) {
|
||||
throw WindowsException(
|
||||
GetLastError(),
|
||||
message: 'Failure on CryptProtectData()',
|
||||
);
|
||||
}
|
||||
|
||||
if (encryptedTextBlob.ref.pbData.address == NULL) {
|
||||
throw WindowsException(
|
||||
ERROR_OUTOFMEMORY,
|
||||
message: 'Failure on CryptProtectData()',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
final encryptedText = encryptedTextBlob.ref.pbData
|
||||
.asTypedList(encryptedTextBlob.ref.cbData);
|
||||
|
||||
// Loop to handle race condition.
|
||||
while (true) {
|
||||
try {
|
||||
await (await file.create(recursive: true))
|
||||
.writeAsBytes(encryptedText, flush: true);
|
||||
// If success, finish loop.
|
||||
break;
|
||||
} on FileSystemException catch (e) {
|
||||
// Another process has been deleted a file or parent directory
|
||||
// since previous File.create() call.
|
||||
// We will retry writing.
|
||||
debugPrint(
|
||||
'Reading file has been deleted by another process. $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (encryptedTextBlob.ref.pbData.address != NULL) {
|
||||
if (LocalFree(encryptedTextBlob.ref.pbData).address != NULL) {
|
||||
debugPrint(
|
||||
'save: Failed to LocalFree with: 0x${GetLastError().toHexString(32)}',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> clear(Map<String, String> options) async {
|
||||
final file = File(await _getJsonFilePath());
|
||||
if (await file.exists()) {
|
||||
try {
|
||||
await file.delete();
|
||||
} on FileSystemException catch (e) {
|
||||
// Another process has been deleted a file or parent directory
|
||||
// since previous File.exists() call.
|
||||
// We can ignore it.
|
||||
debugPrint(
|
||||
'Deleting file has been deleted by another process. $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
58
third_party/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart
vendored
Normal file
58
third_party/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
import 'package:flutter_secure_storage_platform_interface/flutter_secure_storage_platform_interface.dart';
|
||||
|
||||
/// A stub implementation to avoid extra transitive dependencies
|
||||
/// on non-Windows platforms including web.
|
||||
class FlutterSecureStorageWindows extends FlutterSecureStoragePlatform {
|
||||
/// Cannot be instantiated.
|
||||
FlutterSecureStorageWindows()
|
||||
: assert(false, 'Cannot instantiate this class.');
|
||||
|
||||
/// Registers this plugin.
|
||||
static void registerWith() {
|
||||
FlutterSecureStoragePlatform.instance = FlutterSecureStorageWindows();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> containsKey({
|
||||
required String key,
|
||||
required Map<String, String> options,
|
||||
}) =>
|
||||
Future.value(false);
|
||||
|
||||
@override
|
||||
Future<void> delete({
|
||||
required String key,
|
||||
required Map<String, String> options,
|
||||
}) =>
|
||||
Future.value();
|
||||
|
||||
@override
|
||||
Future<void> deleteAll({required Map<String, String> options}) =>
|
||||
Future.value();
|
||||
|
||||
@override
|
||||
Future<String?> read({
|
||||
required String key,
|
||||
required Map<String, String> options,
|
||||
}) =>
|
||||
Future.value();
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> readAll({required Map<String, String> options}) =>
|
||||
Future.value({});
|
||||
|
||||
@override
|
||||
Future<void> write({
|
||||
required String key,
|
||||
required String value,
|
||||
required Map<String, String> options,
|
||||
}) =>
|
||||
Future.value();
|
||||
|
||||
// @override
|
||||
// Future<bool> isCupertinoProtectedDataAvailable() => Future.value(true);
|
||||
//
|
||||
// @override
|
||||
// Stream<bool> get onCupertinoProtectedDataAvailabilityChanged =>
|
||||
// Stream.value(true);
|
||||
}
|
||||
30
third_party/flutter_secure_storage_windows/pubspec.yaml
vendored
Normal file
30
third_party/flutter_secure_storage_windows/pubspec.yaml
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
name: flutter_secure_storage_windows
|
||||
description: Windows implementation of flutter_secure_storage. Please use flutter_secure_storage instead of this package.
|
||||
repository: https://github.com/mogol/flutter_secure_storage
|
||||
version: 3.1.2
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <4.0.0'
|
||||
flutter: ">=2.0.0"
|
||||
|
||||
dependencies:
|
||||
ffi: ^2.0.0
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_secure_storage_platform_interface: ^1.1.1
|
||||
path: ^1.8.0
|
||||
path_provider: ^2.0.0
|
||||
win32: ^5.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
lint: ^2.0.0
|
||||
|
||||
flutter:
|
||||
plugin:
|
||||
implements: flutter_secure_storage
|
||||
platforms:
|
||||
windows:
|
||||
pluginClass: FlutterSecureStorageWindowsPlugin
|
||||
dartPluginClass: FlutterSecureStorageWindows
|
||||
1157
third_party/flutter_secure_storage_windows/test/unit_test.dart
vendored
Normal file
1157
third_party/flutter_secure_storage_windows/test/unit_test.dart
vendored
Normal file
File diff suppressed because it is too large
Load Diff
29
third_party/flutter_secure_storage_windows/windows/CMakeLists.txt
vendored
Normal file
29
third_party/flutter_secure_storage_windows/windows/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
set(PROJECT_NAME "flutter_secure_storage_windows")
|
||||
project(${PROJECT_NAME} LANGUAGES CXX)
|
||||
|
||||
# This value is used when generating builds using this plugin, so it must
|
||||
# not be changed
|
||||
set(PLUGIN_NAME "flutter_secure_storage_windows_plugin")
|
||||
|
||||
add_library(${PLUGIN_NAME} SHARED
|
||||
"flutter_secure_storage_windows_plugin.cpp"
|
||||
)
|
||||
apply_standard_settings(${PLUGIN_NAME})
|
||||
set_target_properties(${PLUGIN_NAME} PROPERTIES
|
||||
CXX_VISIBILITY_PRESET hidden)
|
||||
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
|
||||
target_include_directories(${PLUGIN_NAME} INTERFACE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)
|
||||
|
||||
# List of absolute paths to libraries that should be bundled with the plugin
|
||||
set(flutter_secure_storage_bundled_libraries
|
||||
""
|
||||
PARENT_SCOPE
|
||||
)
|
||||
if(NOT DEFINED STORAGE_PREFIX)
|
||||
add_compile_definitions(SECURE_STORAGE_KEY_PREFIX="${BINARY_NAME}_VGhpcyBpcyB0aGUgcHJlZml4IGZv_")
|
||||
else()
|
||||
add_compile_definitions(SECURE_STORAGE_KEY_PREFIX="${STORAGE_PREFIX}_VGhpcyBpcyB0aGUgcHJlZml4IGZv_")
|
||||
endif()
|
||||
941
third_party/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cpp
vendored
Normal file
941
third_party/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cpp
vendored
Normal file
@ -0,0 +1,941 @@
|
||||
#include "include/flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h"
|
||||
|
||||
// This must be included before many other Windows headers.
|
||||
#include <windows.h>
|
||||
#include <wincred.h>
|
||||
#include <ShlObj_core.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <direct.h>
|
||||
#include <bcrypt.h>
|
||||
|
||||
// For getPlatformVersion; remove unless needed for your plugin implementation.
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
#include <flutter/method_channel.h>
|
||||
#include <flutter/plugin_registrar_windows.h>
|
||||
#include <flutter/standard_method_codec.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
|
||||
#pragma comment(lib, "version.lib")
|
||||
#pragma comment(lib, "bcrypt.lib")
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class FlutterSecureStorageWindowsPlugin : public flutter::Plugin
|
||||
{
|
||||
public:
|
||||
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar);
|
||||
|
||||
FlutterSecureStorageWindowsPlugin();
|
||||
|
||||
virtual ~FlutterSecureStorageWindowsPlugin();
|
||||
|
||||
private:
|
||||
// Called when a method is called on this plugin's channel from Dart.
|
||||
void HandleMethodCall(
|
||||
const flutter::MethodCall<flutter::EncodableValue> &method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
|
||||
// Retrieves the value passed to the given param.
|
||||
std::optional<std::string> GetStringArg(
|
||||
const std::string ¶m,
|
||||
const flutter::EncodableMap *args);
|
||||
|
||||
// Derive the key for a value given a method argument map.
|
||||
std::optional<std::string> FlutterSecureStorageWindowsPlugin::GetValueKey(const flutter::EncodableMap *args);
|
||||
|
||||
// Removes prefix of the given storage key.
|
||||
//
|
||||
// The prefix (defined by ELEMENT_PREFERENCES_KEY_PREFIX) is added automatically when writing to storage,
|
||||
// to distinguish values that are written by this plugin from values that are not.
|
||||
std::string RemoveKeyPrefix(const std::string &key);
|
||||
|
||||
// Gets the string name for the given int error code
|
||||
std::string GetErrorString(const DWORD &error_code);
|
||||
// Get string name of ntstatus
|
||||
std::string NtStatusToString(const CHAR* operation, NTSTATUS status);
|
||||
|
||||
DWORD GetApplicationSupportPath(std::wstring& path);
|
||||
|
||||
std::wstring SanitizeDirString(std::wstring string);
|
||||
|
||||
bool PathExists(const std::wstring& path);
|
||||
|
||||
bool MakePath(const std::wstring& path);
|
||||
|
||||
PBYTE GetEncryptionKey();
|
||||
|
||||
// Stores the given value under the given key.
|
||||
void Write(const std::string &key, const std::string &val);
|
||||
|
||||
std::optional<std::string> Read(const std::string &key);
|
||||
|
||||
flutter::EncodableMap ReadAll();
|
||||
|
||||
void Delete(const std::string &key);
|
||||
|
||||
void DeleteAll();
|
||||
|
||||
bool ContainsKey(const std::string &key);
|
||||
};
|
||||
|
||||
const std::string ELEMENT_PREFERENCES_KEY_PREFIX = SECURE_STORAGE_KEY_PREFIX;
|
||||
const int ELEMENT_PREFERENCES_KEY_PREFIX_LENGTH = (sizeof SECURE_STORAGE_KEY_PREFIX) - 1;
|
||||
|
||||
std::wstring Utf8ToWide(const std::string& value) {
|
||||
if (value.empty()) {
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
const int size = MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, nullptr, 0);
|
||||
if (size <= 0) {
|
||||
throw GetLastError();
|
||||
}
|
||||
|
||||
std::wstring wide(static_cast<size_t>(size), L'\0');
|
||||
const int converted = MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, wide.data(), size);
|
||||
if (converted <= 0) {
|
||||
throw GetLastError();
|
||||
}
|
||||
wide.resize(static_cast<size_t>(converted - 1));
|
||||
return wide;
|
||||
}
|
||||
|
||||
std::string WideToUtf8(const wchar_t* value) {
|
||||
if (value == nullptr || value[0] == L'\0') {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
const int size = WideCharToMultiByte(CP_UTF8, 0, value, -1, nullptr, 0, nullptr, nullptr);
|
||||
if (size <= 0) {
|
||||
throw GetLastError();
|
||||
}
|
||||
|
||||
std::string utf8(static_cast<size_t>(size), '\0');
|
||||
const int converted = WideCharToMultiByte(CP_UTF8, 0, value, -1, utf8.data(), size, nullptr, nullptr);
|
||||
if (converted <= 0) {
|
||||
throw GetLastError();
|
||||
}
|
||||
utf8.resize(static_cast<size_t>(converted - 1));
|
||||
return utf8;
|
||||
}
|
||||
|
||||
std::wstring CredentialFilter() {
|
||||
return Utf8ToWide(ELEMENT_PREFERENCES_KEY_PREFIX + '*');
|
||||
}
|
||||
|
||||
static inline void rtrim(std::wstring& s) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](wchar_t ch) {
|
||||
return !std::isspace(ch);
|
||||
}).base(), s.end());
|
||||
}
|
||||
|
||||
// static
|
||||
void FlutterSecureStorageWindowsPlugin::RegisterWithRegistrar(
|
||||
flutter::PluginRegistrarWindows *registrar)
|
||||
{
|
||||
auto channel =
|
||||
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
|
||||
registrar->messenger(), "plugins.it_nomads.com/flutter_secure_storage",
|
||||
&flutter::StandardMethodCodec::GetInstance());
|
||||
|
||||
auto plugin = std::make_unique<FlutterSecureStorageWindowsPlugin>();
|
||||
|
||||
channel->SetMethodCallHandler(
|
||||
[plugin_pointer = plugin.get()](const auto &call, auto result)
|
||||
{
|
||||
plugin_pointer->HandleMethodCall(call, std::move(result));
|
||||
});
|
||||
|
||||
registrar->AddPlugin(std::move(plugin));
|
||||
}
|
||||
|
||||
FlutterSecureStorageWindowsPlugin::FlutterSecureStorageWindowsPlugin() {}
|
||||
|
||||
FlutterSecureStorageWindowsPlugin::~FlutterSecureStorageWindowsPlugin() {}
|
||||
|
||||
std::optional<std::string> FlutterSecureStorageWindowsPlugin::GetValueKey(const flutter::EncodableMap *args)
|
||||
{
|
||||
auto key = this->GetStringArg("key", args);
|
||||
if (key.has_value())
|
||||
return ELEMENT_PREFERENCES_KEY_PREFIX + key.value();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string FlutterSecureStorageWindowsPlugin::RemoveKeyPrefix(const std::string& key)
|
||||
{
|
||||
return key.substr(ELEMENT_PREFERENCES_KEY_PREFIX_LENGTH);
|
||||
}
|
||||
|
||||
std::optional<std::string> FlutterSecureStorageWindowsPlugin::GetStringArg(
|
||||
const std::string ¶m,
|
||||
const flutter::EncodableMap *args)
|
||||
{
|
||||
auto p = args->find(param);
|
||||
if (p == args->end())
|
||||
return std::nullopt;
|
||||
return std::get<std::string>(p->second);
|
||||
}
|
||||
|
||||
std::string FlutterSecureStorageWindowsPlugin::GetErrorString(const DWORD &error_code)
|
||||
{
|
||||
switch (error_code)
|
||||
{
|
||||
case ERROR_NO_SUCH_LOGON_SESSION:
|
||||
return "ERROR_NO_SUCH_LOGIN_SESSION";
|
||||
case ERROR_INVALID_FLAGS:
|
||||
return "ERROR_INVALID_FLAGS";
|
||||
case ERROR_BAD_USERNAME:
|
||||
return "ERROR_BAD_USERNAME";
|
||||
case SCARD_E_NO_READERS_AVAILABLE:
|
||||
return "SCARD_E_NO_READERS_AVAILABLE";
|
||||
case SCARD_E_NO_SMARTCARD:
|
||||
return "SCARD_E_NO_SMARTCARD";
|
||||
case SCARD_W_REMOVED_CARD:
|
||||
return "SCARD_W_REMOVED_CARD";
|
||||
case SCARD_W_WRONG_CHV:
|
||||
return "SCARD_W_WRONG_CHV";
|
||||
case ERROR_INVALID_PARAMETER:
|
||||
return "ERROR_INVALID_PARAMETER";
|
||||
default:
|
||||
return "UNKNOWN_ERROR";
|
||||
}
|
||||
}
|
||||
|
||||
std::string FlutterSecureStorageWindowsPlugin::NtStatusToString(const CHAR* operation, NTSTATUS status)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << operation << ", 0x" << std::hex << status;
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case 0xc0000000:
|
||||
oss << " (STATUS_SUCCESS)";
|
||||
break;
|
||||
case 0xC0000008:
|
||||
oss << " (STATUS_INVALID_HANDLE)";
|
||||
break;
|
||||
case 0xc000000d:
|
||||
oss << " (STATUS_INVALID_PARAMETER)";
|
||||
break;
|
||||
case 0xc00000bb:
|
||||
oss << " (STATUS_NOT_SUPPORTED)";
|
||||
break;
|
||||
case 0xC0000225:
|
||||
oss << " (STATUS_NOT_FOUND)";
|
||||
break;
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
DWORD FlutterSecureStorageWindowsPlugin::GetApplicationSupportPath(std::wstring &path)
|
||||
{
|
||||
std::wstring companyName;
|
||||
std::wstring productName;
|
||||
TCHAR nameBuffer[MAX_PATH + 1]{};
|
||||
char* infoBuffer;
|
||||
DWORD versionInfoSize;
|
||||
DWORD resVal;
|
||||
UINT queryLen;
|
||||
LPVOID queryVal;
|
||||
LPWSTR appdataPath;
|
||||
std::wostringstream stream;
|
||||
|
||||
SHGetKnownFolderPath(FOLDERID_RoamingAppData,KF_FLAG_DEFAULT,NULL,&appdataPath);
|
||||
|
||||
if (nameBuffer == NULL) {
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
resVal = GetModuleFileName(NULL,nameBuffer,MAX_PATH);
|
||||
if (resVal == 0) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
versionInfoSize = GetFileVersionInfoSize(nameBuffer, NULL);
|
||||
if (versionInfoSize != 0) {
|
||||
infoBuffer = (char*) calloc(versionInfoSize,sizeof(char));
|
||||
if (infoBuffer == NULL) {
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
if (GetFileVersionInfo(nameBuffer, 0, versionInfoSize, infoBuffer) == 0) {
|
||||
free(infoBuffer);
|
||||
infoBuffer = NULL;
|
||||
}
|
||||
else {
|
||||
|
||||
if (VerQueryValue(infoBuffer, TEXT("\\StringFileInfo\\040904e4\\CompanyName"), &queryVal, &queryLen) != 0) {
|
||||
companyName = SanitizeDirString(std::wstring((const TCHAR*)queryVal));
|
||||
}
|
||||
else if (VerQueryValue(infoBuffer, TEXT("\\StringFileInfo\\040904b0\\CompanyName"), &queryVal, &queryLen) != 0) {
|
||||
companyName = SanitizeDirString(std::wstring((const TCHAR*)queryVal));
|
||||
}
|
||||
else {
|
||||
companyName = L"placeholder_company";
|
||||
}
|
||||
if (VerQueryValue(infoBuffer, TEXT("\\StringFileInfo\\040904e4\\ProductName"), &queryVal, &queryLen) != 0) {
|
||||
productName = SanitizeDirString(std::wstring((const TCHAR*)queryVal));
|
||||
}
|
||||
else if (VerQueryValue(infoBuffer, TEXT("\\StringFileInfo\\040904b0\\ProductName"), &queryVal, &queryLen) != 0) {
|
||||
productName = SanitizeDirString(std::wstring((const TCHAR*)queryVal));
|
||||
}
|
||||
else {
|
||||
productName = L"placeholder_product";
|
||||
}
|
||||
}
|
||||
stream << appdataPath << "\\" << companyName << "\\" << productName;
|
||||
path = stream.str();
|
||||
}
|
||||
else {
|
||||
return GetLastError();
|
||||
}
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
std::wstring FlutterSecureStorageWindowsPlugin::SanitizeDirString(std::wstring string)
|
||||
{
|
||||
std::wstring illegalChars = L"\\/:?\"<>|";
|
||||
for (auto it = string.begin(); it < string.end(); ++it) {
|
||||
if (illegalChars.find(*it) != std::wstring::npos) {
|
||||
*it = L'_';
|
||||
}
|
||||
}
|
||||
rtrim(string);
|
||||
return string;
|
||||
}
|
||||
|
||||
bool FlutterSecureStorageWindowsPlugin::PathExists(const std::wstring& path)
|
||||
{
|
||||
struct _stat info;
|
||||
if (_wstat(path.c_str(), &info) != 0) {
|
||||
return false;
|
||||
}
|
||||
return (info.st_mode & _S_IFDIR) != 0;
|
||||
}
|
||||
|
||||
bool FlutterSecureStorageWindowsPlugin::MakePath(const std::wstring& path)
|
||||
{
|
||||
int ret = _wmkdir(path.c_str());
|
||||
if (ret == 0) {
|
||||
return true;
|
||||
}
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
{
|
||||
size_t pos = path.find_last_of('/');
|
||||
if (pos == std::wstring::npos)
|
||||
pos = path.find_last_of('\\');
|
||||
if (pos == std::wstring::npos)
|
||||
return false;
|
||||
if (!MakePath(path.substr(0, pos)))
|
||||
return false;
|
||||
}
|
||||
return 0 == _wmkdir(path.c_str());
|
||||
case EEXIST:
|
||||
return PathExists(path);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
PBYTE FlutterSecureStorageWindowsPlugin::GetEncryptionKey()
|
||||
{
|
||||
const size_t KEY_SIZE = 16;
|
||||
DWORD credError = 0;
|
||||
PBYTE AesKey;
|
||||
PCREDENTIALW pcred;
|
||||
std::wstring target_name = Utf8ToWide("key_" + ELEMENT_PREFERENCES_KEY_PREFIX);
|
||||
|
||||
AesKey = (PBYTE)HeapAlloc(GetProcessHeap(), 0, KEY_SIZE);
|
||||
if (NULL == AesKey) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ok = CredReadW(target_name.c_str(), CRED_TYPE_GENERIC, 0, &pcred);
|
||||
if (ok) {
|
||||
if (pcred->CredentialBlobSize != KEY_SIZE) {
|
||||
CredFree(pcred);
|
||||
CredDeleteW(target_name.c_str(), CRED_TYPE_GENERIC, 0);
|
||||
goto NewKey;
|
||||
}
|
||||
memcpy(AesKey, pcred->CredentialBlob, KEY_SIZE);
|
||||
CredFree(pcred);
|
||||
return AesKey;
|
||||
}
|
||||
credError = GetLastError();
|
||||
if (credError != ERROR_NOT_FOUND) {
|
||||
return NULL;
|
||||
}
|
||||
NewKey:
|
||||
if (BCryptGenRandom(NULL, AesKey, KEY_SIZE, BCRYPT_USE_SYSTEM_PREFERRED_RNG) != ERROR_SUCCESS) {
|
||||
return NULL;
|
||||
}
|
||||
CREDENTIALW cred = { 0 };
|
||||
cred.Type = CRED_TYPE_GENERIC;
|
||||
cred.TargetName = const_cast<LPWSTR>(target_name.c_str());
|
||||
cred.CredentialBlobSize = KEY_SIZE;
|
||||
cred.CredentialBlob = AesKey;
|
||||
cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
|
||||
|
||||
ok = CredWriteW(&cred, 0);
|
||||
if (!ok) {
|
||||
std::cerr << "Failed to write encryption key" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
return AesKey;
|
||||
}
|
||||
|
||||
void FlutterSecureStorageWindowsPlugin::HandleMethodCall(
|
||||
const flutter::MethodCall<flutter::EncodableValue> &method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
|
||||
{
|
||||
auto method = method_call.method_name();
|
||||
const auto *args = std::get_if<flutter::EncodableMap>(method_call.arguments());
|
||||
std::wstring path;
|
||||
if (GetApplicationSupportPath(path) != ERROR_SUCCESS) {
|
||||
result->Error("Exception occurred", "GetApplicationSupportPath");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (method == "write")
|
||||
{
|
||||
auto key = this->GetValueKey(args);
|
||||
auto val = this->GetStringArg("value", args);
|
||||
if (key.has_value())
|
||||
{
|
||||
if (val.has_value())
|
||||
this->Write(key.value(), val.value());
|
||||
else
|
||||
this->Delete(key.value());
|
||||
result->Success();
|
||||
}
|
||||
else
|
||||
{
|
||||
result->Error("Exception occurred", "write");
|
||||
}
|
||||
}
|
||||
else if (method == "read")
|
||||
{
|
||||
auto key = this->GetValueKey(args);
|
||||
if (key.has_value())
|
||||
{
|
||||
auto val = this->Read(key.value());
|
||||
if (val.has_value())
|
||||
result->Success(flutter::EncodableValue(val.value()));
|
||||
else
|
||||
result->Success();
|
||||
}
|
||||
else
|
||||
{
|
||||
result->Error("Exception occurred", "read");
|
||||
}
|
||||
}
|
||||
else if (method == "readAll")
|
||||
{
|
||||
auto creds = this->ReadAll();
|
||||
result->Success(flutter::EncodableValue(creds));
|
||||
}
|
||||
else if (method == "delete")
|
||||
{
|
||||
auto key = this->GetValueKey(args);
|
||||
if (key.has_value())
|
||||
{
|
||||
this->Delete(key.value());
|
||||
result->Success();
|
||||
}
|
||||
else
|
||||
{
|
||||
result->Error("Exception occurred", "delete");
|
||||
}
|
||||
}
|
||||
else if (method == "deleteAll")
|
||||
{
|
||||
this->DeleteAll();
|
||||
result->Success();
|
||||
}
|
||||
else if (method == "containsKey")
|
||||
{
|
||||
auto key = this->GetValueKey(args);
|
||||
if (key.has_value())
|
||||
{
|
||||
auto contains_key = this->ContainsKey(key.value());
|
||||
result->Success(flutter::EncodableValue(contains_key));
|
||||
}
|
||||
else
|
||||
{
|
||||
result->Error("Exception occurred", "containsKey");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result->NotImplemented();
|
||||
}
|
||||
}
|
||||
catch (DWORD e)
|
||||
{
|
||||
auto str_code = this->GetErrorString(e);
|
||||
result->Error("Exception encountered: " + str_code, method);
|
||||
}
|
||||
}
|
||||
|
||||
void FlutterSecureStorageWindowsPlugin::Write(const std::string &key, const std::string &val)
|
||||
{
|
||||
//The recommended size for AES-GCM IV is 12 bytes
|
||||
const DWORD NONCE_SIZE = 12;
|
||||
const DWORD KEY_SIZE = 16;
|
||||
|
||||
NTSTATUS status;
|
||||
BCRYPT_ALG_HANDLE algo = NULL;
|
||||
BCRYPT_KEY_HANDLE keyHandle = NULL;
|
||||
DWORD bytesWritten = 0,
|
||||
ciphertextSize = 0;
|
||||
PBYTE ciphertext = NULL,
|
||||
iv = (PBYTE)HeapAlloc(GetProcessHeap(), 0, NONCE_SIZE),
|
||||
encryptionKey = GetEncryptionKey();
|
||||
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo{};
|
||||
BCRYPT_AUTH_TAG_LENGTHS_STRUCT authTagLengths{};
|
||||
std::basic_ofstream<BYTE> fs;
|
||||
std::wstring appSupportPath;
|
||||
std::string error;
|
||||
|
||||
if (iv == NULL) {
|
||||
error = "IV HeapAlloc Failed";
|
||||
goto err;
|
||||
}
|
||||
if (encryptionKey == NULL) {
|
||||
error = "encryptionKey is NULL";
|
||||
goto err;
|
||||
}
|
||||
status = BCryptOpenAlgorithmProvider(&algo, BCRYPT_AES_ALGORITHM, NULL, 0);
|
||||
if (!BCRYPT_SUCCESS(status)) {
|
||||
error = NtStatusToString("BCryptOpenAlgorithmProvider", status);
|
||||
goto err;
|
||||
}
|
||||
status = BCryptSetProperty(algo, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0);
|
||||
if (!BCRYPT_SUCCESS(status)) {
|
||||
error = NtStatusToString("BCryptSetProperty", status);
|
||||
goto err;
|
||||
}
|
||||
status = BCryptGetProperty(algo, BCRYPT_AUTH_TAG_LENGTH, (PBYTE)&authTagLengths, sizeof(BCRYPT_AUTH_TAG_LENGTHS_STRUCT), &bytesWritten, 0);
|
||||
if (!BCRYPT_SUCCESS(status)) {
|
||||
error = NtStatusToString("BCryptGetProperty", status);
|
||||
goto err;
|
||||
}
|
||||
BCRYPT_INIT_AUTH_MODE_INFO(authInfo);
|
||||
authInfo.pbNonce = (PUCHAR)HeapAlloc(GetProcessHeap(), 0, NONCE_SIZE);
|
||||
if (authInfo.pbNonce == NULL) {
|
||||
error = "pbNonce HeapAlloc Failed";
|
||||
goto err;
|
||||
}
|
||||
authInfo.cbNonce = NONCE_SIZE;
|
||||
status = BCryptGenRandom(NULL, iv, authInfo.cbNonce, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
||||
if (!BCRYPT_SUCCESS(status)) {
|
||||
error = NtStatusToString("BCryptGenRandom", status);
|
||||
goto err;
|
||||
}
|
||||
//copy the original IV into the authInfo, we can't write the IV directly into the authInfo because it will change after calling BCryptEncrypt and we still need to write the IV to file
|
||||
memcpy(authInfo.pbNonce, iv, authInfo.cbNonce);
|
||||
//We do not use additional authenticated data
|
||||
authInfo.pbAuthData = NULL;
|
||||
authInfo.cbAuthData = 0;
|
||||
//Make space for the authentication tag
|
||||
authInfo.pbTag = (PUCHAR)HeapAlloc(GetProcessHeap(), 0, authTagLengths.dwMaxLength);
|
||||
if (authInfo.pbTag == NULL) {
|
||||
error = "pbTag HeapAlloc Failed";
|
||||
goto err;
|
||||
}
|
||||
authInfo.cbTag = authTagLengths.dwMaxLength;
|
||||
status = BCryptGenerateSymmetricKey(algo, &keyHandle, NULL, 0, encryptionKey, KEY_SIZE, 0);
|
||||
if (!BCRYPT_SUCCESS(status)) {
|
||||
error = NtStatusToString("BCryptGenerateSymmetricKey", status);
|
||||
goto err;
|
||||
}
|
||||
//First call to BCryptEncrypt to get size of ciphertext
|
||||
status = BCryptEncrypt(keyHandle, (PUCHAR)val.c_str(), (ULONG)val.length() + 1, (PVOID)&authInfo, NULL, 0, NULL, 0, &bytesWritten, 0);
|
||||
if (!BCRYPT_SUCCESS(status)) {
|
||||
error = NtStatusToString("BCryptEncrypt1", status);
|
||||
goto err;
|
||||
}
|
||||
ciphertextSize = bytesWritten;
|
||||
ciphertext = (PBYTE)HeapAlloc(GetProcessHeap(), 0, ciphertextSize);
|
||||
if (ciphertext == NULL) {
|
||||
error = "CipherText HeapAlloc failed";
|
||||
goto err;
|
||||
}
|
||||
//Actual encryption
|
||||
status = BCryptEncrypt(keyHandle, (PUCHAR)val.c_str(), (ULONG)val.length() + 1, (PVOID)&authInfo, NULL, 0, ciphertext, ciphertextSize, &bytesWritten, 0);
|
||||
if (!BCRYPT_SUCCESS(status)) {
|
||||
error = NtStatusToString("BCryptEncrypt2", status);
|
||||
goto err;
|
||||
}
|
||||
GetApplicationSupportPath(appSupportPath);
|
||||
if (!PathExists(appSupportPath)) {
|
||||
MakePath(appSupportPath);
|
||||
}
|
||||
fs = std::basic_ofstream<BYTE>(appSupportPath + L"\\" + std::wstring(key.begin(), key.end()) + L".secure", std::ios::binary | std::ios::trunc);
|
||||
if (!fs) {
|
||||
error = "Failed to open output stream";
|
||||
goto err;
|
||||
}
|
||||
fs.write(iv, NONCE_SIZE);
|
||||
fs.write(authInfo.pbTag, authInfo.cbTag);
|
||||
fs.write(ciphertext, ciphertextSize);
|
||||
fs.close();
|
||||
HeapFree(GetProcessHeap(), 0, iv);
|
||||
HeapFree(GetProcessHeap(), 0, encryptionKey);
|
||||
HeapFree(GetProcessHeap(), 0, authInfo.pbNonce);
|
||||
HeapFree(GetProcessHeap(), 0, authInfo.pbTag);
|
||||
HeapFree(GetProcessHeap(), 0, ciphertext);
|
||||
return;
|
||||
err:
|
||||
if (iv) {
|
||||
HeapFree(GetProcessHeap(), 0, iv);
|
||||
}
|
||||
if (encryptionKey) {
|
||||
HeapFree(GetProcessHeap(), 0, encryptionKey);
|
||||
}
|
||||
if (authInfo.pbNonce) {
|
||||
HeapFree(GetProcessHeap(), 0, authInfo.pbNonce);
|
||||
}
|
||||
if (authInfo.pbTag) {
|
||||
HeapFree(GetProcessHeap(), 0, authInfo.pbTag);
|
||||
}
|
||||
if (ciphertext) {
|
||||
HeapFree(GetProcessHeap(), 0, ciphertext);
|
||||
}
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
|
||||
std::optional<std::string> FlutterSecureStorageWindowsPlugin::Read(const std::string &key)
|
||||
{
|
||||
const DWORD NONCE_SIZE = 12;
|
||||
const DWORD KEY_SIZE = 16;
|
||||
|
||||
NTSTATUS status;
|
||||
BCRYPT_ALG_HANDLE algo = NULL;
|
||||
BCRYPT_KEY_HANDLE keyHandle = NULL;
|
||||
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo{};
|
||||
BCRYPT_AUTH_TAG_LENGTHS_STRUCT authTagLengths{};
|
||||
|
||||
PBYTE encryptionKey = GetEncryptionKey(),
|
||||
ciphertext = NULL,
|
||||
fileBuffer = NULL,
|
||||
plaintext = NULL;
|
||||
DWORD plaintextSize = 0,
|
||||
bytesWritten = 0,
|
||||
ciphertextSize = 0;
|
||||
std::wstring appSupportPath;
|
||||
std::basic_ifstream<BYTE> fs;
|
||||
std::streampos fileSize;
|
||||
std::optional<std::string> returnVal = std::nullopt;
|
||||
|
||||
if (encryptionKey == NULL) {
|
||||
std::cerr << "encryptionKey is NULL" << std::endl;
|
||||
goto cleanup;
|
||||
}
|
||||
GetApplicationSupportPath(appSupportPath);
|
||||
if (!PathExists(appSupportPath)) {
|
||||
MakePath(appSupportPath);
|
||||
}
|
||||
//Read full file into a buffer
|
||||
fs = std::basic_ifstream<BYTE>(appSupportPath + L"\\" + std::wstring(key.begin(), key.end()) + L".secure", std::ios::binary);
|
||||
if (!fs.good()) {
|
||||
//Backwards comp.
|
||||
PCREDENTIALW pcred;
|
||||
std::wstring target_name = Utf8ToWide(key);
|
||||
bool ok = CredReadW(target_name.c_str(), CRED_TYPE_GENERIC, 0, &pcred);
|
||||
if (ok)
|
||||
{
|
||||
auto val = std::string((char*)pcred->CredentialBlob);
|
||||
CredFree(pcred);
|
||||
returnVal = val;
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
fs.unsetf(std::ios::skipws);
|
||||
fs.seekg(0, std::ios::end);
|
||||
fileSize = fs.tellg();
|
||||
fs.seekg(0, std::ios::beg);
|
||||
fileBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), 0, fileSize);
|
||||
if (NULL == fileBuffer) {
|
||||
std::cerr << "fileBuffer HeapAlloc failed" << std::endl;
|
||||
goto cleanup;
|
||||
}
|
||||
fs.read(fileBuffer, fileSize);
|
||||
fs.close();
|
||||
|
||||
status = BCryptOpenAlgorithmProvider(&algo, BCRYPT_AES_ALGORITHM, NULL, 0);
|
||||
if (!BCRYPT_SUCCESS(status)) {
|
||||
std::cerr << NtStatusToString("BCryptOpenAlgorithmProvider", status) << std::endl;
|
||||
goto cleanup;
|
||||
}
|
||||
status = BCryptSetProperty(algo, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0);
|
||||
if (!BCRYPT_SUCCESS(status)) {
|
||||
std::cerr << NtStatusToString("BCryptOpenAlgorithmProvider", status) << std::endl;
|
||||
goto cleanup;
|
||||
}
|
||||
status = BCryptGetProperty(algo, BCRYPT_AUTH_TAG_LENGTH, (PBYTE)&authTagLengths, sizeof(BCRYPT_AUTH_TAG_LENGTHS_STRUCT), &bytesWritten, 0);
|
||||
if (!BCRYPT_SUCCESS(status)) {
|
||||
std::cerr << NtStatusToString("BCryptGetProperty", status) << std::endl;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
BCRYPT_INIT_AUTH_MODE_INFO(authInfo);
|
||||
authInfo.pbNonce = (PUCHAR)HeapAlloc(GetProcessHeap(), 0, NONCE_SIZE);
|
||||
if (authInfo.pbNonce == NULL) {
|
||||
std::cerr << "pbNonce HeapAlloc Failed" << std::endl;
|
||||
goto cleanup;
|
||||
}
|
||||
authInfo.cbNonce = NONCE_SIZE;
|
||||
//Check if file is at least long enough for iv and authentication tag
|
||||
if (fileSize <= static_cast<long long>(NONCE_SIZE) + authTagLengths.dwMaxLength) {
|
||||
std::cerr << "File is too small" << std::endl;
|
||||
goto cleanup;
|
||||
}
|
||||
authInfo.pbTag = (PUCHAR)HeapAlloc(GetProcessHeap(), 0, authTagLengths.dwMaxLength);
|
||||
if (authInfo.pbTag == NULL) {
|
||||
std::cerr << "pbTag HeapAlloc Failed" << std::endl;
|
||||
goto cleanup;
|
||||
}
|
||||
ciphertextSize = (DWORD)fileSize - NONCE_SIZE - authTagLengths.dwMaxLength;
|
||||
ciphertext = (PBYTE)HeapAlloc(GetProcessHeap(), 0, ciphertextSize);
|
||||
if (ciphertext == NULL) {
|
||||
std::cerr << "ciphertext HeapAlloc failed" << std::endl;
|
||||
goto cleanup;
|
||||
}
|
||||
//Copy different parts needed for decryption from filebuffer
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:6385)
|
||||
memcpy(authInfo.pbNonce, fileBuffer, NONCE_SIZE);
|
||||
#pragma warning(pop)
|
||||
memcpy(authInfo.pbTag, &fileBuffer[NONCE_SIZE], authTagLengths.dwMaxLength);
|
||||
memcpy(ciphertext, &fileBuffer[NONCE_SIZE + authTagLengths.dwMaxLength], ciphertextSize);
|
||||
authInfo.cbTag = authTagLengths.dwMaxLength;
|
||||
|
||||
status = BCryptGenerateSymmetricKey(algo, &keyHandle, NULL, 0, encryptionKey, KEY_SIZE, 0);
|
||||
if (!BCRYPT_SUCCESS(status)) {
|
||||
std::cerr << NtStatusToString("BCryptGenerateSymmetricKey", status) << std::endl;
|
||||
goto cleanup;
|
||||
}
|
||||
//First call is to determine size of plaintext
|
||||
status = BCryptDecrypt(keyHandle, ciphertext, ciphertextSize, (PVOID)&authInfo, NULL, 0, NULL, 0, &bytesWritten, 0);
|
||||
if (!BCRYPT_SUCCESS(status)) {
|
||||
std::cerr << NtStatusToString("BCryptDecrypt1", status) << std::endl;
|
||||
goto cleanup;
|
||||
}
|
||||
plaintextSize = bytesWritten;
|
||||
plaintext = (PBYTE)HeapAlloc(GetProcessHeap(), 0, plaintextSize);
|
||||
if (NULL == plaintext) {
|
||||
std::cerr << "plaintext HeapAlloc failed" << std::endl;
|
||||
goto cleanup;
|
||||
}
|
||||
//Actuual decryption
|
||||
status = BCryptDecrypt(keyHandle, ciphertext, ciphertextSize, (PVOID)&authInfo, NULL, 0, plaintext, plaintextSize, &bytesWritten, 0);
|
||||
if (!BCRYPT_SUCCESS(status)) {
|
||||
std::cerr << NtStatusToString("BCryptDecrypt2", status) << std::endl;
|
||||
goto cleanup;
|
||||
}
|
||||
returnVal = (char*)plaintext;
|
||||
cleanup:
|
||||
if (encryptionKey) {
|
||||
HeapFree(GetProcessHeap(), 0, encryptionKey);
|
||||
}
|
||||
if (ciphertext) {
|
||||
HeapFree(GetProcessHeap(), 0, ciphertext);
|
||||
}
|
||||
if (plaintext) {
|
||||
HeapFree(GetProcessHeap(), 0, plaintext);
|
||||
}
|
||||
if (fileBuffer) {
|
||||
HeapFree(GetProcessHeap(), 0, fileBuffer);
|
||||
}
|
||||
if (authInfo.pbNonce) {
|
||||
HeapFree(GetProcessHeap(), 0, authInfo.pbNonce);
|
||||
}
|
||||
if (authInfo.pbTag) {
|
||||
HeapFree(GetProcessHeap(), 0, authInfo.pbTag);
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
flutter::EncodableMap FlutterSecureStorageWindowsPlugin::ReadAll()
|
||||
{
|
||||
WIN32_FIND_DATA searchRes;
|
||||
HANDLE hFile;
|
||||
std::wstring appSupportPath;
|
||||
|
||||
GetApplicationSupportPath(appSupportPath);
|
||||
if (!PathExists(appSupportPath)) {
|
||||
MakePath(appSupportPath);
|
||||
}
|
||||
hFile = FindFirstFile((appSupportPath + L"\\*.secure").c_str(), &searchRes);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
return flutter::EncodableMap();
|
||||
}
|
||||
|
||||
flutter::EncodableMap creds;
|
||||
|
||||
do {
|
||||
std::wstring fileName(searchRes.cFileName);
|
||||
size_t pos = fileName.find(L".secure");
|
||||
fileName.erase(pos, 7);
|
||||
char* out = new char[fileName.length() + 1];
|
||||
size_t charsConverted = 0;
|
||||
wcstombs_s(&charsConverted, out, fileName.length() + 1, fileName.c_str(), fileName.length() + 1);
|
||||
std::optional<std::string> val = this->Read(out);
|
||||
auto key = this->RemoveKeyPrefix(out);
|
||||
if (val.has_value()) {
|
||||
creds[key] = val.value();
|
||||
continue;
|
||||
}
|
||||
} while (FindNextFile(hFile, &searchRes) != 0);
|
||||
|
||||
//Backwards comp.
|
||||
PCREDENTIALW* pcreds;
|
||||
DWORD cred_count = 0;
|
||||
|
||||
std::wstring credential_filter = CredentialFilter();
|
||||
bool ok = CredEnumerateW(credential_filter.c_str(), 0, &cred_count, &pcreds);
|
||||
if (!ok)
|
||||
{
|
||||
return creds;
|
||||
}
|
||||
for (DWORD i = 0; i < cred_count; i++)
|
||||
{
|
||||
auto pcred = pcreds[i];
|
||||
std::string target_name = WideToUtf8(pcred->TargetName);
|
||||
auto val = std::string((char*)pcred->CredentialBlob);
|
||||
auto key = this->RemoveKeyPrefix(target_name);
|
||||
//If the key exists then data was already read from a file, which implies that the data read from the credential system is outdated
|
||||
if (creds.find(key) == creds.end()) {
|
||||
creds[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
CredFree(pcreds);
|
||||
|
||||
return creds;
|
||||
}
|
||||
|
||||
void FlutterSecureStorageWindowsPlugin::Delete(const std::string &key)
|
||||
{
|
||||
std::wstring appSupportPath;
|
||||
GetApplicationSupportPath(appSupportPath);
|
||||
auto wstr = std::wstring(key.begin(), key.end());
|
||||
BOOL ok = DeleteFile((appSupportPath + L"\\" + wstr + L".secure").c_str());
|
||||
if (!ok) {
|
||||
DWORD error = GetLastError();
|
||||
if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//Backwards comp.
|
||||
ok = CredDeleteW(wstr.c_str(), CRED_TYPE_GENERIC, 0);
|
||||
if (!ok)
|
||||
{
|
||||
auto error = GetLastError();
|
||||
|
||||
// Silently ignore if we try to delete a key that doesn't exist
|
||||
if (error == ERROR_NOT_FOUND)
|
||||
return;
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
void FlutterSecureStorageWindowsPlugin::DeleteAll()
|
||||
{
|
||||
|
||||
WIN32_FIND_DATA searchRes;
|
||||
HANDLE hFile;
|
||||
std::wstring appSupportPath;
|
||||
|
||||
GetApplicationSupportPath(appSupportPath);
|
||||
if (!PathExists(appSupportPath)) {
|
||||
MakePath(appSupportPath);
|
||||
}
|
||||
hFile = FindFirstFile((appSupportPath + L"\\*.secure").c_str(), &searchRes);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
std::wstring fileName(searchRes.cFileName);
|
||||
BOOL ok = DeleteFile((appSupportPath + L"\\" + fileName).c_str());
|
||||
if (!ok) {
|
||||
DWORD error = GetLastError();
|
||||
if (error != ERROR_FILE_NOT_FOUND) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} while (FindNextFile(hFile, &searchRes) != 0);
|
||||
|
||||
//Backwards comp.
|
||||
PCREDENTIALW* pcreds;
|
||||
DWORD cred_count = 0;
|
||||
|
||||
std::wstring credential_filter = CredentialFilter();
|
||||
bool read_ok = CredEnumerateW(credential_filter.c_str(), 0, &cred_count, &pcreds);
|
||||
if (!read_ok)
|
||||
{
|
||||
auto error = GetLastError();
|
||||
if (error == ERROR_NOT_FOUND)
|
||||
// No credentials to delete
|
||||
return;
|
||||
throw error;
|
||||
}
|
||||
|
||||
for (DWORD i = 0; i < cred_count; i++)
|
||||
{
|
||||
auto pcred = pcreds[i];
|
||||
auto target_name = pcred->TargetName;
|
||||
|
||||
bool delete_ok = CredDeleteW(target_name, CRED_TYPE_GENERIC, 0);
|
||||
if (!delete_ok)
|
||||
{
|
||||
throw GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
CredFree(pcreds);
|
||||
}
|
||||
|
||||
bool FlutterSecureStorageWindowsPlugin::ContainsKey(const std::string &key)
|
||||
{
|
||||
std::wstring appSupportPath;
|
||||
GetApplicationSupportPath(appSupportPath);
|
||||
std::wstring wstr = std::wstring(key.begin(), key.end());
|
||||
if (INVALID_FILE_ATTRIBUTES == GetFileAttributes((appSupportPath + L"\\" + wstr + L".secure").c_str())) {
|
||||
//Backwards comp.
|
||||
PCREDENTIALW pcred;
|
||||
std::wstring target_name = Utf8ToWide(key);
|
||||
bool ok = CredReadW(target_name.c_str(), CRED_TYPE_GENERIC, 0, &pcred);
|
||||
if (ok) return true;
|
||||
|
||||
auto error = GetLastError();
|
||||
if (error == ERROR_NOT_FOUND)
|
||||
return false;
|
||||
throw error;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||
FlutterDesktopPluginRegistrarRef registrar)
|
||||
{
|
||||
FlutterSecureStorageWindowsPlugin::RegisterWithRegistrar(
|
||||
flutter::PluginRegistrarManager::GetInstance()
|
||||
->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
#ifndef FLUTTER_PLUGIN_FLUTTER_SECURE_STORAGE_WINDOWS_PLUGIN_H_
|
||||
#define FLUTTER_PLUGIN_FLUTTER_SECURE_STORAGE_WINDOWS_PLUGIN_H_
|
||||
|
||||
#include <flutter_plugin_registrar.h>
|
||||
|
||||
#ifdef FLUTTER_PLUGIN_IMPL
|
||||
#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||
FlutterDesktopPluginRegistrarRef registrar);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // FLUTTER_PLUGIN_FLUTTER_SECURE_STORAGE_WINDOWS_PLUGIN_H_
|
||||
Loading…
Reference in New Issue
Block a user