├── android
├── settings_aar.gradle
├── gradle.properties
├── app
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── chameleon.png
│ │ │ │ └── launch_background.xml
│ │ │ ├── xml
│ │ │ │ └── device_filter.xml
│ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ ├── java
│ │ │ └── tw
│ │ │ │ └── kgame
│ │ │ │ ├── crapto1
│ │ │ │ ├── Nonce.java
│ │ │ │ ├── Crypto1State.java
│ │ │ │ ├── Span.java
│ │ │ │ ├── MfKey.java
│ │ │ │ ├── Crypto1.java
│ │ │ │ └── Crapto1.java
│ │ │ │ └── chameleonminiapp
│ │ │ │ └── MainActivity.java
│ │ │ └── AndroidManifest.xml
│ └── build.gradle
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── build.gradle
└── settings.gradle
├── l10n.yaml
├── Crapto1Native
├── README.md
├── Crapto1Native.csproj
├── build.cmd
├── Library.cs
└── .gitignore
├── lib
├── main.dart
├── l10n
│ ├── app_zh.arb
│ ├── app_zh_CN.arb
│ ├── app_zh_TW.arb
│ ├── app_ja.arb
│ ├── app_en.arb
│ ├── app_localizations_ja.dart
│ ├── app_localizations_en.dart
│ ├── app_localizations_zh.dart
│ └── app_localizations.dart
├── views
│ ├── home
│ │ ├── deviceInfoDialog.dart
│ │ ├── homePage.dart
│ │ └── slotView.dart
│ └── settings
│ │ ├── languagePage.dart
│ │ └── settingsPage.dart
├── myApp.dart
├── services
│ ├── ffiService.dart
│ ├── settings.dart
│ ├── xmodem.dart
│ ├── chameleonClient.dart
│ └── crapto1.dart
└── view_models
│ └── slotViewModel.dart
├── .metadata
├── test
└── widget_test.dart
├── .gitignore
├── README.md
├── pubspec.yaml
└── LICENSE
/android/settings_aar.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/l10n.yaml:
--------------------------------------------------------------------------------
1 | arb-dir: lib/l10n
2 | template-arb-file: app_en.arb
3 |
--------------------------------------------------------------------------------
/Crapto1Native/README.md:
--------------------------------------------------------------------------------
1 | ## Environment Requirements
2 |
3 | - .NET 8 SDK
4 | - bflat tool
5 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | android.useAndroidX=true
2 | android.enableJetifier=true
3 | org.gradle.jvmargs=-Xmx1536M
4 | android.enableR8=true
5 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/chameleon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kgamecarter/ChameleonMiniApp/HEAD/android/app/src/main/res/drawable/chameleon.png
--------------------------------------------------------------------------------
/android/app/src/main/res/xml/device_filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/android/app/src/main/java/tw/kgame/crapto1/Nonce.java:
--------------------------------------------------------------------------------
1 | package tw.kgame.crapto1;
2 |
3 | public class Nonce {
4 | public long nt;
5 |
6 | public long nr;
7 |
8 | public long ar;
9 | }
10 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Jan 23 13:23:02 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
7 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'services/settings.dart';
3 | import 'myApp.dart';
4 |
5 | void main() async {
6 | WidgetsFlutterBinding.ensureInitialized();
7 | final settings = Settings();
8 | await settings.load();
9 | runApp(MyApp(settings: settings));
10 | }
11 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | rootProject.buildDir = '../build'
9 | subprojects {
10 | project.buildDir = "${rootProject.buildDir}/${project.name}"
11 | }
12 | subprojects {
13 | project.evaluationDependsOn(':app')
14 | }
15 |
16 | tasks.register("clean", Delete) {
17 | delete rootProject.buildDir
18 | }
19 |
--------------------------------------------------------------------------------
/Crapto1Native/Crapto1Native.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | True
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Crapto1Native/build.cmd:
--------------------------------------------------------------------------------
1 | dotnet publish
2 | rmdir /s /q obj
3 | bflat build --no-reflection --no-stacktrace-data --no-globalization --no-exception-messages -Os --no-pie --separate-symbols --os:linux --arch:arm64 --libc:bionic -r:bin\Release\net8.0\publish\Crapto1Sharp.dll
4 | del libCrapto1Native.so.dwo
5 |
6 | if not exist ..\android\app\src\main\jniLibs\arm64-v8a\ mkdir ..\android\app\src\main\jniLibs\arm64-v8a\
7 |
8 | move libCrapto1Native.so ..\android\app\src\main\jniLibs\arm64-v8a\libCrapto1Native.so
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | -
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Crapto1Native/Library.cs:
--------------------------------------------------------------------------------
1 | using Crapto1Sharp;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Crapto1Native;
5 |
6 | public static class Library
7 | {
8 | [UnmanagedCallersOnly(EntryPoint = "MfKey32")]
9 | public static unsafe ulong MfKey32(uint uid, int cnt, Nonce* nonces)
10 | {
11 | var arr = new Nonce[cnt];
12 | for (int i = 0; i < cnt; i++)
13 | arr[i] = nonces[i];
14 | return MfKey.MfKey32(uid, arr);
15 | }
16 |
17 | [UnmanagedCallersOnly(EntryPoint = "MfKey64")]
18 | public static ulong MfKey64(uint uid, uint nt, uint nr, uint ar, uint at)
19 | => MfKey.MfKey64(uid, nt, nr, ar, at);
20 | }
21 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | def flutterSdkPath = {
3 | def properties = new Properties()
4 | file("local.properties").withInputStream { properties.load(it) }
5 | def flutterSdkPath = properties.getProperty("flutter.sdk")
6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7 | return flutterSdkPath
8 | }
9 | settings.ext.flutterSdkPath = flutterSdkPath()
10 |
11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
12 |
13 | repositories {
14 | google()
15 | mavenCentral()
16 | gradlePluginPortal()
17 | }
18 | }
19 |
20 | plugins {
21 | id "dev.flutter.flutter-plugin-loader" version "1.0.0"
22 | id "com.android.application" version "8.6.0" apply false
23 | id "org.jetbrains.kotlin.android" version "2.1.0" apply false
24 | }
25 |
26 | include ":app"
27 |
--------------------------------------------------------------------------------
/android/app/src/main/java/tw/kgame/crapto1/Crypto1State.java:
--------------------------------------------------------------------------------
1 | package tw.kgame.crapto1;
2 |
3 | public class Crypto1State {
4 | public long odd;
5 | public long even;
6 |
7 | byte bit(long v, int n) {
8 | return (byte)(v >> n & 1);
9 | }
10 |
11 | byte beBit(long v, int n) {
12 | return bit(v, n ^ 24);
13 | }
14 |
15 | public Crypto1State(long odd, long even) {
16 | this.odd = odd;
17 | this.even = even;
18 | }
19 |
20 | public Crypto1State(long key) {
21 | for (int i = 47; i > 0; i -= 2)
22 | {
23 | odd = odd << 1 | bit(key, (i - 1) ^ 7);
24 | even = even << 1 | bit(key, i ^ 7);
25 | }
26 | }
27 |
28 | public long getLfsr() {
29 | long lfsr = 0;
30 | for (int i = 23; i >= 0; --i)
31 | {
32 | lfsr = lfsr << 1 | bit(odd, i ^ 3);
33 | lfsr = lfsr << 1 | bit(even, i ^ 3);
34 | }
35 | return lfsr;
36 | }
37 | }
--------------------------------------------------------------------------------
/lib/l10n/app_zh.arb:
--------------------------------------------------------------------------------
1 | {
2 | "apply": "套用",
3 | "attacking": "解密中",
4 | "button": "按鈕",
5 | "clear": "清除",
6 | "close": "關閉",
7 | "confirmClear": "確定要清除嗎?",
8 | "crapto1Implementation": "Crapto1 實作",
9 | "crapto1Dart": "Dart 單執行緒",
10 | "crapto1Java": "Java 多執行緒",
11 | "crapto1Online": "線上 (伺服器可能離線)",
12 | "crapto1Native": ".NET8 NativeAOT (僅限 ARM64)",
13 | "deviceInfo": "裝置資訊",
14 | "disconnect": "斷線",
15 | "download": "下載",
16 | "downloading": "下載中",
17 | "chameleonMiniApp": "Chameleon Mini App",
18 | "english": "英文",
19 | "generalSetting": "一般設定",
20 | "language": "語言",
21 | "longPressButton": "長按按鈕",
22 | "memorySize": "記憶體大小",
23 | "mfkey32": "解密",
24 | "mode": "模式",
25 | "notAvailable": "不可用",
26 | "refresh": "刷新",
27 | "reset": "重置",
28 | "slot": "槽位",
29 | "selectLanguage": "選擇語言",
30 | "settings": "設定",
31 | "traditionalChinese": "正體中文",
32 | "uid": "UID",
33 | "upload": "上傳",
34 | "usbDeviceNotFound": "找不到 USB 裝置",
35 | "usbDisconnected": "USB 已斷線",
36 | "systemDefault": "系統預設",
37 | "simplifiedChinese": "簡體中文",
38 | "japanese": "日文"
39 | }
40 |
--------------------------------------------------------------------------------
/lib/l10n/app_zh_CN.arb:
--------------------------------------------------------------------------------
1 | {
2 | "apply": "应用",
3 | "attacking": "解密中",
4 | "button": "按钮",
5 | "clear": "清除",
6 | "close": "关闭",
7 | "confirmClear": "确定要清除吗?",
8 | "crapto1Implementation": "Crapto1 实现",
9 | "crapto1Dart": "Dart 单线程",
10 | "crapto1Java": "Java 多线程",
11 | "crapto1Online": "在线 (服务器可能离线)",
12 | "crapto1Native": ".NET8 NativeAOT (仅限 ARM64)",
13 | "deviceInfo": "设备信息",
14 | "disconnect": "断开连接",
15 | "download": "下载",
16 | "downloading": "下载中",
17 | "chameleonMiniApp": "Chameleon Mini App",
18 | "english": "英语",
19 | "generalSetting": "通用设置",
20 | "language": "语言",
21 | "longPressButton": "长按按钮",
22 | "memorySize": "内存大小",
23 | "mfkey32": "解密",
24 | "mode": "模式",
25 | "notAvailable": "不可用",
26 | "refresh": "刷新",
27 | "reset": "重置",
28 | "slot": "卡槽",
29 | "selectLanguage": "选择语言",
30 | "settings": "设置",
31 | "traditionalChinese": "繁体中文",
32 | "uid": "UID",
33 | "upload": "上传",
34 | "usbDeviceNotFound": "未找到 USB 设备",
35 | "usbDisconnected": "USB 已断开",
36 | "systemDefault": "系统默认",
37 | "simplifiedChinese": "简体中文",
38 | "japanese": "日语"
39 | }
40 |
--------------------------------------------------------------------------------
/lib/l10n/app_zh_TW.arb:
--------------------------------------------------------------------------------
1 | {
2 | "apply": "套用",
3 | "attacking": "解密中",
4 | "button": "按鈕",
5 | "clear": "清除",
6 | "close": "關閉",
7 | "confirmClear": "確定要清除嗎?",
8 | "crapto1Implementation": "Crapto1 實作",
9 | "crapto1Dart": "Dart 單執行緒",
10 | "crapto1Java": "Java 多執行緒",
11 | "crapto1Online": "線上 (伺服器可能離線)",
12 | "crapto1Native": ".NET8 NativeAOT (僅限 ARM64)",
13 | "deviceInfo": "裝置資訊",
14 | "disconnect": "斷線",
15 | "download": "下載",
16 | "downloading": "下載中",
17 | "chameleonMiniApp": "Chameleon Mini App",
18 | "english": "英文",
19 | "generalSetting": "一般設定",
20 | "language": "語言",
21 | "longPressButton": "長按按鈕",
22 | "memorySize": "記憶體大小",
23 | "mfkey32": "解密",
24 | "mode": "模式",
25 | "notAvailable": "不可用",
26 | "refresh": "刷新",
27 | "reset": "重置",
28 | "slot": "槽位",
29 | "selectLanguage": "選擇語言",
30 | "settings": "設定",
31 | "traditionalChinese": "正體中文",
32 | "uid": "UID",
33 | "upload": "上傳",
34 | "usbDeviceNotFound": "找不到 USB 裝置",
35 | "usbDisconnected": "USB 已斷線",
36 | "systemDefault": "系統預設",
37 | "simplifiedChinese": "簡體中文",
38 | "japanese": "日文"
39 | }
40 |
--------------------------------------------------------------------------------
/lib/l10n/app_ja.arb:
--------------------------------------------------------------------------------
1 | {
2 | "apply": "適用",
3 | "attacking": "解読中",
4 | "button": "ボタン",
5 | "clear": "クリア",
6 | "close": "閉じる",
7 | "confirmClear": "クリアしてもよろしいですか?",
8 | "crapto1Implementation": "Crapto1 実装",
9 | "crapto1Dart": "Dart シングルスレッド",
10 | "crapto1Java": "Java マルチスレッド",
11 | "crapto1Online": "オンライン (サーバーオフラインの可能性あり)",
12 | "crapto1Native": ".NET8 NativeAOT (ARM64のみ)",
13 | "deviceInfo": "デバイス情報",
14 | "disconnect": "切断",
15 | "download": "ダウンロード",
16 | "downloading": "ダウンロード中",
17 | "chameleonMiniApp": "Chameleon Mini App",
18 | "english": "英語",
19 | "generalSetting": "一般設定",
20 | "language": "言語",
21 | "longPressButton": "ボタン長押し",
22 | "memorySize": "メモリサイズ",
23 | "mfkey32": "解読",
24 | "mode": "モード",
25 | "notAvailable": "利用不可",
26 | "refresh": "更新",
27 | "reset": "リセット",
28 | "slot": "スロット",
29 | "selectLanguage": "言語を選択",
30 | "settings": "設定",
31 | "traditionalChinese": "繁体字中国語",
32 | "uid": "UID",
33 | "upload": "アップロード",
34 | "usbDeviceNotFound": "USBデバイスが見つかりません",
35 | "usbDisconnected": "USBが切断されました",
36 | "systemDefault": "システムデフォルト",
37 | "simplifiedChinese": "簡体字中国語",
38 | "japanese": "日本語"
39 | }
40 |
--------------------------------------------------------------------------------
/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:chameleon_mini_app/views/home/homePage.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(HomePage());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/lib/views/home/deviceInfoDialog.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:chameleon_mini_app/l10n/app_localizations.dart';
3 |
4 | class DeviceInfoDialog extends StatelessWidget {
5 | const DeviceInfoDialog(this.version, this.rssi, {Key? key}) : super(key: key);
6 |
7 | final String version, rssi;
8 |
9 | void _reset(BuildContext context) {
10 | Navigator.of(context).pop('reset');
11 | }
12 |
13 | void _disconnect(BuildContext context) {
14 | Navigator.of(context).pop('disconnect');
15 | }
16 |
17 | void _close(BuildContext context) {
18 | Navigator.of(context).pop();
19 | }
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | return AlertDialog(
24 | title: Text(AppLocalizations.of(context)!.deviceInfo),
25 | content: Text('$version\nRSSI : $rssi'),
26 | actions: [
27 | TextButton(
28 | child: Text(AppLocalizations.of(context)!.reset),
29 | onPressed: () => _reset(context),
30 | ),
31 | TextButton(
32 | child: Text(AppLocalizations.of(context)!.disconnect),
33 | onPressed: () => _disconnect(context),
34 | ),
35 | TextButton(
36 | child: Text(AppLocalizations.of(context)!.close),
37 | onPressed: () => _close(context),
38 | ),
39 | ],
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/lib/l10n/app_en.arb:
--------------------------------------------------------------------------------
1 | {
2 | "apply": "Apply",
3 | "attacking": "Attacking",
4 | "button": "Button",
5 | "clear": "Clear",
6 | "close": "Close",
7 | "confirmClear": "Are you sure you want to clear?",
8 | "crapto1Implementation": "Crapto1 Implementation",
9 | "crapto1Dart": "Dart with Single-Thread",
10 | "crapto1Java": "Java with Multi-Thread",
11 | "crapto1Online": "Online (Server maybe offline)",
12 | "crapto1Native": ".NET8 NativeAOT (ARM64 only)",
13 | "deviceInfo": "Device Info",
14 | "disconnect": "Disconnect",
15 | "download": "Download",
16 | "downloading": "Downloading",
17 | "chameleonMiniApp": "Chameleon Mini App",
18 | "english": "English",
19 | "generalSetting": "General Setting",
20 | "language": "Language",
21 | "longPressButton": "Long Press Button",
22 | "memorySize": "Memory Size",
23 | "mfkey32": "mfkey32",
24 | "mode": "Mode",
25 | "notAvailable": "N/A",
26 | "refresh": "Refresh",
27 | "reset": "Reset",
28 | "slot": "Slot",
29 | "selectLanguage": "Select Language",
30 | "settings": "Settings",
31 | "traditionalChinese": "Traditional Chinese",
32 | "uid": "UID",
33 | "upload": "Upload",
34 | "usbDeviceNotFound": "USB device not found",
35 | "usbDisconnected": "USB Disconnected",
36 | "systemDefault": "System Default",
37 | "simplifiedChinese": "Simplified Chinese",
38 | "japanese": "Japanese"
39 | }
40 |
--------------------------------------------------------------------------------
/android/app/src/main/java/tw/kgame/crapto1/Span.java:
--------------------------------------------------------------------------------
1 | package tw.kgame.crapto1;
2 |
3 | import java.util.Arrays;
4 |
5 | public class Span {
6 | long[] array;
7 | int offset;
8 | int length;
9 |
10 | public Span(long[] array) {
11 | this(array, 0, array.length);
12 | }
13 |
14 | public Span(long[] array, int offset, int length) {
15 | this.array = array;
16 | this.offset = offset;
17 | this.length = length;
18 | }
19 |
20 | public long get(int i) {
21 | return array[i + offset];
22 | }
23 |
24 | public void set(int i, long v) {
25 | array[i + offset] = v;
26 | }
27 |
28 | public Span slice(int start) {
29 | return new Span(array, offset + start, length - start);
30 | }
31 |
32 | public Span slice(int start, int length) {
33 | return new Span(array, offset + start, length);
34 | }
35 |
36 | public int binarySearch() {
37 | int start = 0, stop = this.length - 1, mid;
38 | long val = this.get(stop) & 0xff000000L;
39 | while (start != stop)
40 | if (this.get(start + (mid = (stop - start) >> 1)) > val)
41 | stop = start + mid;
42 | else
43 | start += mid + 1;
44 | return start;
45 | }
46 |
47 | public void sort() {
48 | Arrays.sort(array, offset, offset + length);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/lib/myApp.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:chameleon_mini_app/l10n/app_localizations.dart';
3 |
4 | import 'services/settings.dart';
5 | import 'views/home/homePage.dart';
6 | import 'views/settings/settingsPage.dart';
7 | import 'views/settings/languagePage.dart';
8 |
9 | class MyApp extends StatelessWidget {
10 | final Settings settings;
11 |
12 | const MyApp({Key? key, required this.settings}) : super(key: key);
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return AnimatedBuilder(
17 | animation: settings,
18 | builder: (context, child) {
19 | return GestureDetector(
20 | onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
21 | child: MaterialApp(
22 | localizationsDelegates: AppLocalizations.localizationsDelegates,
23 | supportedLocales: AppLocalizations.supportedLocales,
24 | title: 'Chameleon Mini App',
25 | onGenerateTitle: (context) =>
26 | AppLocalizations.of(context)!.chameleonMiniApp,
27 | theme: ThemeData(
28 | colorScheme: ColorScheme.fromSeed(seedColor: Colors.lime),
29 | useMaterial3: true,
30 | ),
31 | home: HomePage(),
32 | routes: {
33 | SettingsPage.name: (BuildContext context) => new SettingsPage(),
34 | LanguagePage.name: (BuildContext context) => new LanguagePage(),
35 | },
36 | locale: settings.locale,
37 | ),
38 | );
39 | },
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/lib/services/ffiService.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ffi';
2 | import 'dart:io';
3 | import 'package:ffi/ffi.dart';
4 | import 'package:chameleon_mini_app/services/crapto1.dart';
5 |
6 | final class LibCrapto1Nonce extends Struct {
7 | @Uint32()
8 | external int nt;
9 | @Uint32()
10 | external int nr;
11 | @Uint32()
12 | external int ar;
13 | }
14 |
15 | final _crapto1Lib = Platform.isAndroid
16 | ? DynamicLibrary.open('libCrapto1Native.so')
17 | : DynamicLibrary.process();
18 | final _libCrapto1MfKey32 = _crapto1Lib.lookupFunction<
19 | Uint64 Function(Uint32, Uint32, Pointer),
20 | int Function(int, int, Pointer)>('MfKey32');
21 | final _libCrapto1MfKey64 = _crapto1Lib.lookupFunction<
22 | Uint64 Function(Uint32, Uint32, Uint32, Uint32, Uint32),
23 | int Function(int, int, int, int, int)>('MfKey64');
24 |
25 | Future mfKey32Native(int uid, List nonces) async {
26 | print('Native mfKey32');
27 | final key = using((arena) {
28 | final nativeNonces = arena(nonces.length);
29 | for (var i = 0; i < nonces.length; i++) {
30 | nativeNonces[i].nt = nonces[i].nt;
31 | nativeNonces[i].nr = nonces[i].nr;
32 | nativeNonces[i].ar = nonces[i].ar;
33 | }
34 | return _libCrapto1MfKey32(uid, nonces.length, nativeNonces);
35 | });
36 | return key.toRadixString(16).toUpperCase().padLeft(12, '0');
37 | }
38 |
39 | Future mfKey64Native(int uid, int nt, int nr, int ar, int at) async {
40 | print('Native mfKey64');
41 | final key = _libCrapto1MfKey64(uid, nt, nr, ar, at);
42 | return key.toRadixString(16).toUpperCase().padLeft(12, '0');
43 | }
44 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.lock
4 | *.log
5 | *.pyc
6 | *.swp
7 | .DS_Store
8 | .atom/
9 | .buildlog/
10 | .history
11 | .svn/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # Visual Studio Code related
20 | .vscode/
21 |
22 | # Flutter/Dart/Pub related
23 | **/doc/api/
24 | .dart_tool/
25 | .flutter-plugins
26 | .packages
27 | .pub-cache/
28 | .pub/
29 | build/
30 |
31 | # Android related
32 | **/android/**/gradle-wrapper.jar
33 | **/android/.gradle
34 | **/android/captures/
35 | **/android/gradlew
36 | **/android/gradlew.bat
37 | **/android/local.properties
38 | **/android/**/GeneratedPluginRegistrant.java
39 |
40 | # iOS/XCode related
41 | **/ios/**/*.mode1v3
42 | **/ios/**/*.mode2v3
43 | **/ios/**/*.moved-aside
44 | **/ios/**/*.pbxuser
45 | **/ios/**/*.perspectivev3
46 | **/ios/**/*sync/
47 | **/ios/**/.sconsign.dblite
48 | **/ios/**/.tags*
49 | **/ios/**/.vagrant/
50 | **/ios/**/DerivedData/
51 | **/ios/**/Icon?
52 | **/ios/**/Pods/
53 | **/ios/**/.symlinks/
54 | **/ios/**/profile
55 | **/ios/**/xcuserdata
56 | **/ios/.generated/
57 | **/ios/Flutter/App.framework
58 | **/ios/Flutter/Flutter.framework
59 | **/ios/Flutter/Generated.xcconfig
60 | **/ios/Flutter/app.flx
61 | **/ios/Flutter/app.zip
62 | **/ios/Flutter/flutter_assets/
63 | **/ios/ServiceDefinitions.json
64 | **/ios/Runner/GeneratedPluginRegistrant.*
65 |
66 | # Exceptions to above rules.
67 | !**/ios/**/default.mode1v3
68 | !**/ios/**/default.mode2v3
69 | !**/ios/**/default.pbxuser
70 | !**/ios/**/default.perspectivev3
71 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
72 | android/key.properties
73 | ios/Flutter/flutter_export_environment.sh
74 | .flutter-plugins-dependencies
75 | android/app/src/main/jniLibs/arm64-v8a/*.so
76 |
--------------------------------------------------------------------------------
/lib/views/settings/languagePage.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:chameleon_mini_app/l10n/app_localizations.dart';
3 |
4 | class LanguagePage extends StatefulWidget {
5 | static const String name = '/Settings';
6 |
7 | LanguagePage({Key? key}) : super(key: key);
8 |
9 | @override
10 | _LanguagePageState createState() => _LanguagePageState();
11 | }
12 |
13 | class _LanguagePageState extends State {
14 | Function() _pop(Object value) {
15 | return () => Navigator.of(context).pop(value);
16 | }
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return new Scaffold(
21 | appBar: new AppBar(
22 | title: new Text(AppLocalizations.of(context)!.selectLanguage),
23 | ),
24 | body: ListView(
25 | children: [
26 | ListTile(
27 | title: Text(AppLocalizations.of(context)!.systemDefault),
28 | subtitle: const Text('default'),
29 | onTap: _pop('default'),
30 | ),
31 | ListTile(
32 | title: Text(AppLocalizations.of(context)!.english),
33 | subtitle: const Text('en'),
34 | onTap: _pop(const Locale('en')),
35 | ),
36 | ListTile(
37 | title: Text(AppLocalizations.of(context)!.traditionalChinese),
38 | subtitle: const Text('zh-Hant-TW'),
39 | onTap: _pop(
40 | const Locale.fromSubtags(
41 | languageCode: "zh",
42 | scriptCode: "Hant",
43 | countryCode: "TW",
44 | ),
45 | ),
46 | ),
47 | ListTile(
48 | title: Text(AppLocalizations.of(context)!.simplifiedChinese),
49 | subtitle: const Text('zh-Hans-CN'),
50 | onTap: _pop(
51 | const Locale.fromSubtags(
52 | languageCode: "zh",
53 | scriptCode: "Hans",
54 | countryCode: "CN",
55 | ),
56 | ),
57 | ),
58 | ListTile(
59 | title: Text(AppLocalizations.of(context)!.japanese),
60 | subtitle: const Text('ja'),
61 | onTap: _pop(const Locale('ja')),
62 | ),
63 | ],
64 | ),
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/android/app/src/main/java/tw/kgame/crapto1/MfKey.java:
--------------------------------------------------------------------------------
1 | package tw.kgame.crapto1;
2 |
3 | import java.util.*;
4 | import java.util.concurrent.CopyOnWriteArrayList;
5 |
6 | import java.util.Spliterator;
7 | import java.util.Spliterators;
8 | import java.util.stream.*;
9 |
10 | public class MfKey {
11 | static long swapEndian(long x) {
12 | x = (x >> 8 & 0xff00ffL) | (x & 0xff00ffL) << 8;
13 | x &= 0xFFFFFFFFL;
14 | x = x >> 16 | x << 16;
15 | x &= 0xFFFFFFFFL;
16 | return x;
17 | }
18 |
19 | static long prngSuccessor(long x, int n) {
20 | x = swapEndian(x);
21 | while (n-- > 0)
22 | x = x >> 1 | ((x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) & 1) << 31;
23 | return swapEndian(x);
24 | }
25 |
26 | public static long mfKey32(long uid, List nonces) {
27 | Nonce nonce = nonces.get(0);
28 | nonces.remove(0);
29 | long p640 = prngSuccessor(nonce.nt, 64);
30 | System.out.println(Long.toHexString(p640));
31 | List list = Crapto1.lfsrRecovery32(nonce.ar ^ p640, 0);
32 | System.out.println(list.size());
33 | List keys = new CopyOnWriteArrayList<>();
34 | Spliterator sp = Spliterators.spliterator(list, Spliterator.CONCURRENT);
35 | StreamSupport.stream(sp, true).forEach(s -> {
36 | Crapto1 crapto1 = new Crapto1(s);
37 | crapto1.lfsrRollbackWord(0, false);
38 | crapto1.lfsrRollbackWord(nonce.nr, true);
39 | crapto1.lfsrRollbackWord(uid ^ nonce.nt, false);
40 | boolean allPass = true;
41 | Crypto1 crypto1 = new Crypto1(new Crypto1State(0, 0));
42 | for (Nonce n : nonces) {
43 | crypto1.state.odd = s.odd;
44 | crypto1.state.even = s.even;
45 | crypto1.crypto1Word(uid ^ n.nt, false);
46 | crypto1.crypto1Word(n.nr, true);
47 | long p641 = prngSuccessor(n.nt, 64);
48 | if (n.ar != (crypto1.crypto1Word(0, false) ^ p641)) {
49 | allPass = false;
50 | break;
51 | }
52 | }
53 | if (allPass)
54 | keys.add(s.getLfsr());
55 | });
56 | return keys.size() == 1 ? keys.get(0) : -1;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Chameleon Mini App
2 |
3 | A Android GUI for the ChameleonMini device.
4 |
5 | * **[Chameleon Mini App on Google Play](https://play.google.com/store/apps/details?id=tw.kgame.chameleonminiapp)**
6 |
7 | ## Features
8 |
9 | * **USB OTG Connection**: Seamlessly connect to your ChameleonMini RevE device.
10 | * **Slot Management**: Configure and control all 8 slots.
11 | * **Card Emulation**: View and modify UID, Memory Size, and Mode for each slot.
12 | * **Button Configuration**: Customize Button and Long Press Button actions.
13 | * **Data Transfer**: Upload and Download card dump files.
14 | * **Tools**: Includes `mfkey32` attack support.
15 | * **Device Info**: View firmware version and other device details.
16 | * **Localization**: Supports English and Traditional Chinese.
17 |
18 | ## Screenshots
19 |  
20 | 
21 |
22 | ## Requirements
23 |
24 | * ChameleonMini RevE
25 | * Android 4.4 or later
26 | * USB OTG cable
27 |
28 | ## Installation
29 |
30 | ### Google Play
31 | Download the app directly from the [Google Play Store](https://play.google.com/store/apps/details?id=tw.kgame.chameleonminiapp).
32 |
33 | ### Build from Source
34 | 1. Ensure you have the [Flutter SDK](https://flutter.dev/docs/get-started/install) installed.
35 | 2. Clone this repository.
36 | 3. Run `flutter pub get` to install dependencies.
37 | 4. Connect your Android device.
38 | 5. Run `flutter run` to build and install the app.
39 |
40 | ## Usage
41 |
42 | 1. Connect your ChameleonMini RevE to your Android phone using a USB OTG cable.
43 | 2. Open the Chameleon Mini App.
44 | 3. Grant USB permissions if prompted.
45 | 4. The app should automatically detect and connect to the device.
46 | 5. Swipe between tabs to manage different slots.
47 | 6. Use the menu to access Settings, Device Info, or perform actions like Upload/Download.
48 |
49 | ## ChameleonMini Compatible Firmware
50 |
51 | * [Origin Firmware](https://github.com/kgamecarter/ChameleonMiniApp/files/3639628/20170729.zip) *recommended
52 | * [ChameleonMini-rebooted](https://github.com/iceman1001/ChameleonMini-rebooted)
53 |
54 | ## Video
55 | [](https://youtu.be/WoU58GzxsAY)
56 |
57 | ## License
58 |
59 | This project is licensed under the terms of the LICENSE file found in the root of this repository.
60 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: chameleon_mini_app
2 | description: ChameleonMini RevE Android Client.
3 |
4 | # The following defines the version and build number for your application.
5 | # A version number is three numbers separated by dots, like 1.2.43
6 | # followed by an optional build number separated by a +.
7 | # Both the version and the builder number may be overridden in flutter
8 | # build by specifying --build-name and --build-number, respectively.
9 | # Read more about versioning at semver.org.
10 | version: 0.5.0+33
11 |
12 | environment:
13 | sdk: ^3.10.1
14 |
15 | dependencies:
16 | flutter:
17 | sdk: flutter
18 | flutter_localizations:
19 | sdk: flutter
20 | intl: ^0.20.2
21 |
22 | # The following adds the Cupertino Icons font to your application.
23 | # Use with the CupertinoIcons class for iOS style icons.
24 | cupertino_icons: ^1.0.8
25 | usb_serial: ^0.5.2
26 | shared_preferences: ^2.5.4
27 | flutter_file_dialog: ^3.0.3
28 | nfc_manager: ^4.1.1
29 |
30 | dev_dependencies:
31 | flutter_test:
32 | sdk: flutter
33 |
34 | # For information on the generic Dart part of this file, see the
35 | # following page: https://www.dartlang.org/tools/pub/pubspec
36 |
37 | # The following section is specific to Flutter.
38 | flutter:
39 | # The following line ensures that the Material Icons font is
40 | # included with your application, so that you can use the icons in
41 | # the material Icons class.
42 | uses-material-design: true
43 | generate: true
44 |
45 | # To add assets to your application, add an assets section, like this:
46 | # assets:
47 | # - images/a_dot_burr.jpeg
48 | # - images/a_dot_ham.jpeg
49 |
50 | # An image asset can refer to one or more resolution-specific "variants", see
51 | # https://flutter.io/assets-and-images/#resolution-aware.
52 |
53 | # For details regarding adding assets from package dependencies, see
54 | # https://flutter.io/assets-and-images/#from-packages
55 |
56 | # To add custom fonts to your application, add a fonts section here,
57 | # in this "flutter" section. Each entry in this list should have a
58 | # "family" key with the font family name, and a "fonts" key with a
59 | # list giving the asset and other descriptors for the font. For
60 | # example:
61 | # fonts:
62 | # - family: Schyler
63 | # fonts:
64 | # - asset: fonts/Schyler-Regular.ttf
65 | # - asset: fonts/Schyler-Italic.ttf
66 | # style: italic
67 | # - family: Trajan Pro
68 | # fonts:
69 | # - asset: fonts/TrajanPro.ttf
70 | # - asset: fonts/TrajanPro_Bold.ttf
71 | # weight: 700
72 | #
73 | # For details regarding fonts from package dependencies,
74 | # see https://flutter.io/custom-fonts/#from-packages
75 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
16 |
20 |
28 |
32 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
50 |
51 |
--------------------------------------------------------------------------------
/lib/services/settings.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:shared_preferences/shared_preferences.dart';
5 |
6 | enum Crapto1Implementation { Dart, Java, Online, Native }
7 |
8 | class Settings extends ChangeNotifier {
9 | static final Settings _instance = Settings._internal();
10 |
11 | factory Settings() {
12 | return _instance;
13 | }
14 |
15 | Settings._internal();
16 |
17 | SharedPreferences? _prefs;
18 |
19 | Locale? _locale;
20 | Locale? get locale => _locale;
21 |
22 | set locale(Locale? value) {
23 | if (_locale == value) return;
24 | _locale = value;
25 | _saveLocale();
26 | notifyListeners();
27 | }
28 |
29 | Crapto1Implementation? _crapto1Implementation;
30 | Crapto1Implementation? get crapto1Implementation => _crapto1Implementation;
31 |
32 | set crapto1Implementation(Crapto1Implementation? value) {
33 | if (value == null || _crapto1Implementation == value) return;
34 | _crapto1Implementation = value;
35 | _saveCrapto1Implementation();
36 | notifyListeners();
37 | }
38 |
39 | Future load() async {
40 | _prefs = await SharedPreferences.getInstance();
41 |
42 | // Load Locale
43 | final String? localeStr = _prefs?.getString('locale');
44 | if (localeStr == 'en') {
45 | _locale = const Locale('en');
46 | } else if (localeStr == 'zh_Hant_TW') {
47 | _locale = const Locale.fromSubtags(
48 | languageCode: "zh",
49 | scriptCode: "Hant",
50 | countryCode: "TW",
51 | );
52 | } else {
53 | _locale = null;
54 | }
55 |
56 | // Load Crapto1Implementation
57 | int? v = _prefs?.getInt('crapto1Implementation');
58 | if (v == null) {
59 | if (Platform.isAndroid) {
60 | if (Platform.version.contains('arm64')) {
61 | v = Crapto1Implementation.Native.index;
62 | } else {
63 | v = Crapto1Implementation.Java.index;
64 | }
65 | } else {
66 | v = Crapto1Implementation.Dart.index;
67 | }
68 | }
69 |
70 | if (v >= 0 && v < Crapto1Implementation.values.length) {
71 | _crapto1Implementation = Crapto1Implementation.values[v];
72 | } else {
73 | _crapto1Implementation = Crapto1Implementation.Dart;
74 | }
75 |
76 | notifyListeners();
77 | }
78 |
79 | Future _saveLocale() async {
80 | if (_prefs == null) return;
81 | if (_locale != null) {
82 | await _prefs!.setString('locale', _locale.toString());
83 | } else {
84 | await _prefs!.remove('locale');
85 | }
86 | }
87 |
88 | Future _saveCrapto1Implementation() async {
89 | if (_prefs == null || _crapto1Implementation == null) return;
90 | await _prefs!.setInt(
91 | 'crapto1Implementation',
92 | _crapto1Implementation!.index,
93 | );
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/lib/l10n/app_localizations_ja.dart:
--------------------------------------------------------------------------------
1 | // ignore: unused_import
2 | import 'package:intl/intl.dart' as intl;
3 | import 'app_localizations.dart';
4 |
5 | // ignore_for_file: type=lint
6 |
7 | /// The translations for Japanese (`ja`).
8 | class AppLocalizationsJa extends AppLocalizations {
9 | AppLocalizationsJa([String locale = 'ja']) : super(locale);
10 |
11 | @override
12 | String get apply => '適用';
13 |
14 | @override
15 | String get attacking => '解読中';
16 |
17 | @override
18 | String get button => 'ボタン';
19 |
20 | @override
21 | String get clear => 'クリア';
22 |
23 | @override
24 | String get close => '閉じる';
25 |
26 | @override
27 | String get confirmClear => 'クリアしてもよろしいですか?';
28 |
29 | @override
30 | String get crapto1Implementation => 'Crapto1 実装';
31 |
32 | @override
33 | String get crapto1Dart => 'Dart シングルスレッド';
34 |
35 | @override
36 | String get crapto1Java => 'Java マルチスレッド';
37 |
38 | @override
39 | String get crapto1Online => 'オンライン (サーバーオフラインの可能性あり)';
40 |
41 | @override
42 | String get crapto1Native => '.NET8 NativeAOT (ARM64のみ)';
43 |
44 | @override
45 | String get deviceInfo => 'デバイス情報';
46 |
47 | @override
48 | String get disconnect => '切断';
49 |
50 | @override
51 | String get download => 'ダウンロード';
52 |
53 | @override
54 | String get downloading => 'ダウンロード中';
55 |
56 | @override
57 | String get chameleonMiniApp => 'Chameleon Mini App';
58 |
59 | @override
60 | String get english => '英語';
61 |
62 | @override
63 | String get generalSetting => '一般設定';
64 |
65 | @override
66 | String get language => '言語';
67 |
68 | @override
69 | String get longPressButton => 'ボタン長押し';
70 |
71 | @override
72 | String get memorySize => 'メモリサイズ';
73 |
74 | @override
75 | String get mfkey32 => '解読';
76 |
77 | @override
78 | String get mode => 'モード';
79 |
80 | @override
81 | String get notAvailable => '利用不可';
82 |
83 | @override
84 | String get refresh => '更新';
85 |
86 | @override
87 | String get reset => 'リセット';
88 |
89 | @override
90 | String get slot => 'スロット';
91 |
92 | @override
93 | String get selectLanguage => '言語を選択';
94 |
95 | @override
96 | String get settings => '設定';
97 |
98 | @override
99 | String get traditionalChinese => '繁体字中国語';
100 |
101 | @override
102 | String get uid => 'UID';
103 |
104 | @override
105 | String get upload => 'アップロード';
106 |
107 | @override
108 | String get usbDeviceNotFound => 'USBデバイスが見つかりません';
109 |
110 | @override
111 | String get usbDisconnected => 'USBが切断されました';
112 |
113 | @override
114 | String get systemDefault => 'システムデフォルト';
115 |
116 | @override
117 | String get simplifiedChinese => '簡体字中国語';
118 |
119 | @override
120 | String get japanese => '日本語';
121 | }
122 |
--------------------------------------------------------------------------------
/lib/l10n/app_localizations_en.dart:
--------------------------------------------------------------------------------
1 | // ignore: unused_import
2 | import 'package:intl/intl.dart' as intl;
3 | import 'app_localizations.dart';
4 |
5 | // ignore_for_file: type=lint
6 |
7 | /// The translations for English (`en`).
8 | class AppLocalizationsEn extends AppLocalizations {
9 | AppLocalizationsEn([String locale = 'en']) : super(locale);
10 |
11 | @override
12 | String get apply => 'Apply';
13 |
14 | @override
15 | String get attacking => 'Attacking';
16 |
17 | @override
18 | String get button => 'Button';
19 |
20 | @override
21 | String get clear => 'Clear';
22 |
23 | @override
24 | String get close => 'Close';
25 |
26 | @override
27 | String get confirmClear => 'Are you sure you want to clear?';
28 |
29 | @override
30 | String get crapto1Implementation => 'Crapto1 Implementation';
31 |
32 | @override
33 | String get crapto1Dart => 'Dart with Single-Thread';
34 |
35 | @override
36 | String get crapto1Java => 'Java with Multi-Thread';
37 |
38 | @override
39 | String get crapto1Online => 'Online (Server maybe offline)';
40 |
41 | @override
42 | String get crapto1Native => '.NET8 NativeAOT (ARM64 only)';
43 |
44 | @override
45 | String get deviceInfo => 'Device Info';
46 |
47 | @override
48 | String get disconnect => 'Disconnect';
49 |
50 | @override
51 | String get download => 'Download';
52 |
53 | @override
54 | String get downloading => 'Downloading';
55 |
56 | @override
57 | String get chameleonMiniApp => 'Chameleon Mini App';
58 |
59 | @override
60 | String get english => 'English';
61 |
62 | @override
63 | String get generalSetting => 'General Setting';
64 |
65 | @override
66 | String get language => 'Language';
67 |
68 | @override
69 | String get longPressButton => 'Long Press Button';
70 |
71 | @override
72 | String get memorySize => 'Memory Size';
73 |
74 | @override
75 | String get mfkey32 => 'mfkey32';
76 |
77 | @override
78 | String get mode => 'Mode';
79 |
80 | @override
81 | String get notAvailable => 'N/A';
82 |
83 | @override
84 | String get refresh => 'Refresh';
85 |
86 | @override
87 | String get reset => 'Reset';
88 |
89 | @override
90 | String get slot => 'Slot';
91 |
92 | @override
93 | String get selectLanguage => 'Select Language';
94 |
95 | @override
96 | String get settings => 'Settings';
97 |
98 | @override
99 | String get traditionalChinese => 'Traditional Chinese';
100 |
101 | @override
102 | String get uid => 'UID';
103 |
104 | @override
105 | String get upload => 'Upload';
106 |
107 | @override
108 | String get usbDeviceNotFound => 'USB device not found';
109 |
110 | @override
111 | String get usbDisconnected => 'USB Disconnected';
112 |
113 | @override
114 | String get systemDefault => 'System Default';
115 |
116 | @override
117 | String get simplifiedChinese => 'Simplified Chinese';
118 |
119 | @override
120 | String get japanese => 'Japanese';
121 | }
122 |
--------------------------------------------------------------------------------
/android/app/src/main/java/tw/kgame/crapto1/Crypto1.java:
--------------------------------------------------------------------------------
1 | package tw.kgame.crapto1;
2 |
3 | public class Crypto1 {
4 | public static final long LF_POLY_ODD = 0x29CE5CL;
5 | public static final long LF_POLY_EVEN = 0x870804L;
6 |
7 | public Crypto1State state;
8 |
9 | public Crypto1() { }
10 |
11 | public Crypto1(Crypto1State state) { this.state = state; }
12 |
13 | static byte bit(long v, int n) {
14 | return (byte)(v >> n & 1);
15 | }
16 |
17 | static byte beBit(long v, int n) {
18 | return bit(v, n ^ 24);
19 | }
20 |
21 | public byte crypto1Bit(byte _in, boolean isEncrypted) {
22 | byte ret = filter(state.odd);
23 |
24 | long feedin = ret & (isEncrypted ? 1 : 0);
25 | feedin ^= _in != 0 ? 1 : 0;
26 | feedin ^= LF_POLY_ODD & state.odd;
27 | feedin ^= LF_POLY_EVEN & state.even;
28 | state.even = state.even << 1 | evenParity32(feedin);
29 |
30 | long x = state.odd;
31 | state.odd = state.even;
32 | state.even = x;
33 |
34 | return ret;
35 | }
36 |
37 | public short crypto1Byte(short _in, boolean isEncrypted) {
38 | short ret = 0;
39 |
40 | for (int i = 0; i < 8; ++i)
41 | ret |= (short)(crypto1Bit(bit(_in, i), isEncrypted) << i);
42 |
43 | return (short)(ret & 0xFF);
44 | }
45 |
46 | public long crypto1Word(long _in, boolean isEncrypted) {
47 | long ret = 0;
48 |
49 | for (int i = 0; i < 32; ++i)
50 | ret |= (long)crypto1Bit(beBit(_in, i), isEncrypted) << (i ^ 24);
51 |
52 | return ret & 0xFFFFFFFFL;
53 | }
54 |
55 | protected static byte filter(long x) {
56 | long f;
57 | f = 0xf22c0 >> (x & 0xf) & 16;
58 | f |= 0x6c9c0 >> (x >> 4 & 0xf) & 8;
59 | f |= 0x3c8b0 >> (x >> 8 & 0xf) & 4;
60 | f |= 0x1e458 >> (x >> 12 & 0xf) & 2;
61 | f |= 0x0d938 >> (x >> 16 & 0xf) & 1;
62 | return (byte)(0xEC57E80A >> f & 1);
63 | }
64 |
65 | static byte[] _oddintParity = new byte[] {
66 | 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
67 | 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
68 | 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
69 | 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
70 | 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
71 | 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
72 | 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
73 | 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
74 | 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
75 | 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
76 | 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
77 | 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
78 | 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
79 | 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
80 | 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
81 | 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1
82 | };
83 |
84 | protected static byte oddParity8(short x) { return _oddintParity[x & 0xFF]; }
85 |
86 | protected static byte evenParity8(short x) { return (byte)(_oddintParity[x & 0xFF] ^ 1); }
87 |
88 | protected static byte evenParity32(long x) {
89 | x ^= x >> 16;
90 | x ^= x >> 8;
91 | return evenParity8((short)(x & 0xFF));
92 | }
93 | }
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.android.application"
3 | id "kotlin-android"
4 | id "dev.flutter.flutter-gradle-plugin"
5 | }
6 |
7 | def localProperties = new Properties()
8 | def localPropertiesFile = rootProject.file('local.properties')
9 | if (localPropertiesFile.exists()) {
10 | localPropertiesFile.withReader('UTF-8') { reader ->
11 | localProperties.load(reader)
12 | }
13 | }
14 |
15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
16 | if (flutterVersionCode == null) {
17 | flutterVersionCode = '1'
18 | }
19 |
20 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
21 | if (flutterVersionName == null) {
22 | flutterVersionName = '1.0'
23 | }
24 |
25 | def keystorePropertiesFile = rootProject.file("key.properties")
26 | def keyExists = keystorePropertiesFile.exists()
27 | def keystoreProperties = new Properties()
28 | if (keyExists) {
29 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
30 | }
31 |
32 | android {
33 | namespace "tw.kgame.chameleonminiapp"
34 | compileSdkVersion 36
35 |
36 | lintOptions {
37 | disable 'InvalidPackage'
38 | }
39 |
40 | defaultConfig {
41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
42 | applicationId "tw.kgame.chameleonminiapp"
43 | minSdkVersion flutter.minSdkVersion
44 | targetSdkVersion 36
45 | versionCode flutterVersionCode.toInteger()
46 | versionName flutterVersionName
47 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
48 | }
49 |
50 | signingConfigs {
51 | release {
52 | if (keyExists) {
53 | keyAlias keystoreProperties['keyAlias']
54 | keyPassword keystoreProperties['keyPassword']
55 | storeFile file(keystoreProperties['storeFile'])
56 | storePassword keystoreProperties['storePassword']
57 | }
58 | }
59 | }
60 |
61 | buildTypes {
62 | debug {
63 | signingConfig signingConfigs.debug
64 | }
65 | release {
66 | signingConfig signingConfigs.release
67 | }
68 | }
69 |
70 | compileOptions {
71 | sourceCompatibility 1.8
72 | targetCompatibility 1.8
73 | }
74 |
75 | task dotnetPublish(type: Exec) {
76 | workingDir '../../Crapto1Native/'
77 | commandLine 'dotnet', 'publish'
78 | doLast {
79 | delete '../../Crapto1Native/obj'
80 | }
81 | }
82 |
83 | task buildBflat(type: Exec) {
84 | dependsOn dotnetPublish
85 | doFirst {
86 | file('src/main/jniLibs/arm64-v8a/').mkdirs()
87 | }
88 | workingDir '../../Crapto1Native/'
89 | commandLine 'bflat', 'build', '--no-reflection', '--no-stacktrace-data', '--no-globalization', '--no-exception-messages', '-Os', '--no-pie', '--separate-symbols', '--os:linux', '--arch:arm64', '--libc:bionic', '-r:bin/Release/net8.0/publish/Crapto1Sharp.dll', '-o:../android/app/src/main/jniLibs/arm64-v8a/libCrapto1Native.so'
90 | doLast {
91 | delete 'src/main/jniLibs/arm64-v8a/libCrapto1Native.so.dwo'
92 | }
93 | }
94 |
95 | preBuild.dependsOn(dotnetPublish, buildBflat)
96 | }
97 |
98 | flutter {
99 | source '../..'
100 | }
101 |
102 | dependencies {}
103 |
--------------------------------------------------------------------------------
/android/app/src/main/java/tw/kgame/chameleonminiapp/MainActivity.java:
--------------------------------------------------------------------------------
1 | package tw.kgame.chameleonminiapp;
2 |
3 | import java.util.*;
4 |
5 | import androidx.annotation.NonNull;
6 | import io.flutter.embedding.android.FlutterActivity;
7 | import io.flutter.embedding.engine.FlutterEngine;
8 | import io.flutter.plugin.common.BinaryMessenger;
9 | import io.flutter.plugin.common.MethodChannel;
10 | import io.flutter.plugins.GeneratedPluginRegistrant;
11 | import tw.kgame.crapto1.MfKey;
12 | import tw.kgame.crapto1.Nonce;
13 |
14 | public class MainActivity extends FlutterActivity {
15 |
16 | private static final String CHANNEL_MAIN = "tw.kgame.crapto1/main";
17 | private static final String CHANNEL_MFKEY = "tw.kgame.crapto1/mfkey";
18 | MethodChannel channelMfkey;
19 | MethodChannel channelMain;
20 |
21 | BinaryMessenger getBinaryMessenger(){
22 | return getFlutterEngine().getDartExecutor().getBinaryMessenger();
23 | }
24 |
25 | @Override
26 | public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
27 | GeneratedPluginRegistrant.registerWith(flutterEngine);
28 |
29 | channelMain = new MethodChannel(getBinaryMessenger(), CHANNEL_MAIN);
30 | channelMfkey = new MethodChannel(getBinaryMessenger(), CHANNEL_MFKEY);
31 | channelMfkey.setMethodCallHandler(
32 | (call, result) -> {
33 | System.out.println(call.method);
34 | @SuppressWarnings("unchecked")
35 | Map map = (Map)call.arguments;
36 | Object obj = map.get("uid");
37 | long uid = obj instanceof Long ? (long)obj : (long)(int)obj;
38 | @SuppressWarnings("unchecked")
39 | List