Skip to content

Commit

Permalink
feat(core): Add secure storage module
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 committed Mar 6, 2024
1 parent a474b2f commit bd7a66e
Show file tree
Hide file tree
Showing 162 changed files with 14,643 additions and 31 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/celest_core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,33 @@ jobs:
- name: Format
working-directory: packages/celest_core
run: dart format --set-exit-if-changed .
test:
needs: analyze_and_format
runs-on: macos-xl
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: Setup Android
uses: ReactiveCircus/android-emulator-runner@6b0df4b0efb23bb0ec63d881db79aefbc976e4b2 # 2.30.1
with:
api-level: 34
- 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: Test (Example)
working-directory: packages/celest_core/example
run: |
for OS in "sdk iPhone macos"; do
flutter test -d $OS integration_test/secure_storage_test.dart
done
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"files.associations": {
"__locale": "cpp",
"locale": "cpp"
}
}
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 `Keychain` interface for secure storage of sensitive data.

## 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
13 changes: 13 additions & 0 deletions packages/celest_core/example/android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java

# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks
66 changes: 66 additions & 0 deletions packages/celest_core/example/android/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}

android {
namespace "dev.celest.celest_core_example"
compileSdk flutter.compileSdkVersion
ndkVersion flutter.ndkVersion

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = '1.8'
}

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

defaultConfig {
applicationId "dev.celest.celest_core_example"
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}

buildTypes {
release {
signingConfig signingConfigs.debug
}
}
}

flutter {
source '../..'
}

dependencies {
// These dependencies are only present so that `jnigen` will work.
// Applications should not include these.
implementation 'androidx.security:security-crypto:[1.1.0-alpha04,)'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
Loading

0 comments on commit bd7a66e

Please sign in to comment.