Skip to content

Commit

Permalink
feat(core): Add secure storage module (#58)
Browse files Browse the repository at this point in the history
Adds a secure storage interface which uses FFI bindings to the
platform-specific Keychain implementation.
  • Loading branch information
dnys1 authored Mar 6, 2024
1 parent a474b2f commit d6cbcb9
Show file tree
Hide file tree
Showing 162 changed files with 13,961 additions and 34 deletions.
57 changes: 57 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
## GITATTRIBUTES
#
# Details per file setting:
# text These files should be normalized (i.e. convert CRLF to LF).
# binary These files are binary and should be left untouched.
#
# Reference: https://git-scm.com/docs/gitattributes
# GitHub Linguist: https://github.com/github-linguist/linguist/blob/master/docs/overrides.md
######################################################################

# Auto detect text files and perform LF normalization
* text=auto

# Always perform LF normalization
*.dart text
*.gradle text
*.go text
*.html text
*.java text
*.js text
*.json text linguist-language=JSON-with-Comments
*.md text
*.sh text
*.ts text
*.txt text
*.xml text
*.yaml text

# Make sure that these Windows files always have CRLF line endings at checkout
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf
*.rc text eol=crlf
*.sln text eol=crlf
*.props text eol=crlf
*.vcxproj text eol=crlf
*.vcxproj.filters text eol=crlf
# Including templates
*.sln.tmpl text eol=crlf
*.props.tmpl text eol=crlf
*.vcxproj.tmpl text eol=crlf

## Platform files generated by Flutter during `flutter create`
**/example/android/** linguist-generated
**/example/ios/** linguist-generated
**/example/linux/** linguist-generated
**/example/macos/** linguist-generated
**/example/windows/** linguist-generated
## Exclude flutter generated web files without excluding web files from
## non-flutter dart example apps
**/example/web/icons/** linguist-generated
**/example/web/*.json linguist-generated
**/example/web/*.png linguist-generated

## Generated Dart files
**/*.g.dart linguist-generated
**/*.ffi.dart linguist-generated
71 changes: 68 additions & 3 deletions .github/workflows/celest_core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,79 @@ jobs:
steps:
- name: Git Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # 4.1.1
- name: Setup Dart
uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 # 1.6.2
- name: Setup Flutter
uses: subosito/flutter-action@62f096cacda5168a3bd7b95793373be14fa4fbaf # 2.13.0
with:
cache: true
- name: Get Packages
working-directory: packages/celest_core
run: dart pub get
- name: Analyze
working-directory: packages/celest_core
run: dart analyze --fatal-infos --fatal-warnings
run: dart analyze
- name: Format
working-directory: packages/celest_core
run: dart format --set-exit-if-changed .
test_darwin:
needs: analyze_and_format
runs-on: macos-latest-xlarge
timeout-minutes: 20
steps:
- name: Git Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # 4.1.1
- name: Setup Flutter
uses: subosito/flutter-action@62f096cacda5168a3bd7b95793373be14fa4fbaf # 2.13.0
with:
cache: true
- name: Get Packages
working-directory: packages/celest_core
run: dart pub get
- name: Test
working-directory: packages/celest_core
run: dart test
- name: Get Packages (Example)
working-directory: packages/celest_core/example
run: flutter pub get
- name: Setup iOS Simulator
run: |
RUNTIME=$(xcrun simctl list runtimes | grep 'iOS 17' | tail -n 1 | cut -d' ' -f 7)
echo "Using runtime: $RUNTIME"
xcrun simctl create ios 'iPhone 15 Pro Max' $RUNTIME
echo "Booting simulator"
xcrun simctl boot ios
echo "Booted simulator"
- name: Test (iOS)
working-directory: packages/celest_core/example
run: flutter test -d ios integration_test/secure_storage_test.dart
- name: Test (macOS)
working-directory: packages/celest_core/example
run: flutter test -d macos integration_test/secure_storage_test.dart
test_android:
needs: analyze_and_format
runs-on:
group: public
labels: linux
timeout-minutes: 15
steps:
- name: Git Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # 4.1.1
- name: Setup Flutter
uses: subosito/flutter-action@62f096cacda5168a3bd7b95793373be14fa4fbaf # 2.13.0
with:
cache: true
- name: Get Packages
working-directory: packages/celest_core/example
run: flutter pub get
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Test (Android)
uses: ReactiveCircus/android-emulator-runner@6b0df4b0efb23bb0ec63d881db79aefbc976e4b2 # 2.30.1
with:
# Matches `package:jni` compileSdkVersion
# https://github.com/dart-lang/native/blob/001910c9f40d637cb25c19bb500fb89cebdf7450/pkgs/jni/android/build.gradle#L57C23-L57C25
api-level: 31
arch: x86_64
script: cd packages/celest_core/example && flutter test -d emulator integration_test/secure_storage_test.dart
4 changes: 4 additions & 0 deletions packages/celest_core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.2.2-wip

- Adds `SecureStorage` interface for storage of sensitive data in the platform keychain.

## 0.2.1

- Overrides `toString` for `CloudException` types.
Expand Down
8 changes: 8 additions & 0 deletions packages/celest_core/android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*.iml
.gradle
local.properties
.idea/
.DS_Store
build
captures
.cxx
62 changes: 62 additions & 0 deletions packages/celest_core/android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
group 'dev.celest.celest_core'
version '1.0-SNAPSHOT'

buildscript {
ext.kotlin_version = '1.7.21'
repositories {
google()
mavenCentral()
}

dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

rootProject.allprojects {
repositories {
google()
mavenCentral()
}
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
// Conditional for compatibility with AGP <4.2.
if (project.android.hasProperty("namespace")) {
namespace 'dev.celest.celest_core'
}

compileSdk 31

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = '1.8'
}

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}

defaultConfig {
minSdkVersion 21
consumerProguardFiles 'consumer-rules.pro'
}

buildTypes {
release {
minifyEnabled false
}
}
}

dependencies {
implementation 'androidx.security:security-crypto:[1.1.0-alpha04,)'
}
1 change: 1 addition & 0 deletions packages/celest_core/android/consumer-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-keep class dev.celest.celest_core.** { *; }
6 changes: 6 additions & 0 deletions packages/celest_core/android/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
rootProject.name = 'celest_core'
dependencyResolutionManagement {
repositories {
google()
}
}
3 changes: 3 additions & 0 deletions packages/celest_core/android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dev.celest.celest_core">
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package dev.celest.celest_core

import android.annotation.SuppressLint
import android.app.Activity
import android.content.SharedPreferences
import androidx.annotation.Keep
import androidx.annotation.Nullable
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey

// TODO(dnys1): Exclude from backup:
// - https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences
// - https://developer.android.com/guide/topics/data/autobackup#IncludingFiles
@Keep
class CelestSecureStorage(private val mainActivity: Activity, private val scope: String) {

private val sharedPreferences: SharedPreferences by lazy {
val masterKey = MasterKey.Builder(mainActivity)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val sharedPreferences = EncryptedSharedPreferences.create(
mainActivity,
scope,
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
)
sharedPreferences
}

private val editor: SharedPreferences.Editor
get() = sharedPreferences.edit()

fun write(dataKey: String, value: String?) {
with(editor) {
putString(dataKey, value)
apply()
}
}

fun read(dataKey: String): String? = sharedPreferences.getString(dataKey, null)

fun delete(dataKey: String): String? {
val current = read(dataKey)
with(editor) {
remove(dataKey)
apply()
}
return current
}

fun clear() {
with(editor) {
clear()
apply()
}
}

}
43 changes: 43 additions & 0 deletions packages/celest_core/example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/

# Symbolication related
app.*.symbols

# Obfuscation related
app.*.map.json

# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
45 changes: 45 additions & 0 deletions packages/celest_core/example/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "7482962148e8d758338d8a28f589f317e1e42ba4"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: android
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: ios
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: linux
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: macos
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: web
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
- platform: windows
create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4
base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4

# User provided section

# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
3 changes: 3 additions & 0 deletions packages/celest_core/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# celest_core_example

A new Flutter project.
1 change: 1 addition & 0 deletions packages/celest_core/example/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:flutter_lints/flutter.yaml
Loading

0 comments on commit d6cbcb9

Please sign in to comment.