diff --git a/chameleonultragui/android/.gitignore b/chameleonultragui/android/.gitignore
index 6f568019..55afd919 100644
--- a/chameleonultragui/android/.gitignore
+++ b/chameleonultragui/android/.gitignore
@@ -7,7 +7,7 @@ gradle-wrapper.jar
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
-# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks
diff --git a/chameleonultragui/android/app/build.gradle b/chameleonultragui/android/app/build.gradle
index 8caef025..58d87ded 100644
--- a/chameleonultragui/android/app/build.gradle
+++ b/chameleonultragui/android/app/build.gradle
@@ -1,30 +1,10 @@
-def localProperties = new Properties()
-def localPropertiesFile = rootProject.file('local.properties')
-if (localPropertiesFile.exists()) {
- localPropertiesFile.withReader('UTF-8') { reader ->
- localProperties.load(reader)
- }
-}
-
-def flutterRoot = localProperties.getProperty('flutter.sdk')
-if (flutterRoot == null) {
- throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
-}
-
-def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
-if (flutterVersionCode == null) {
- flutterVersionCode = '1'
+plugins {
+ id "com.android.application"
+ id "kotlin-android"
+ // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
+ id "dev.flutter.flutter-gradle-plugin"
}
-def flutterVersionName = localProperties.getProperty('flutter.versionName')
-if (flutterVersionName == null) {
- flutterVersionName = '1.0'
-}
-
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
-
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
@@ -32,31 +12,28 @@ if (keystorePropertiesFile.exists()) {
}
android {
- compileSdkVersion Math.max(flutter.compileSdkVersion, 34)
- ndkVersion flutter.ndkVersion
+ namespace = "io.chameleon.ultra"
+ compileSdk = flutter.compileSdkVersion
+ ndkVersion = flutter.ndkVersion
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
- jvmTarget = '1.8'
- }
-
- sourceSets {
- main.java.srcDirs += 'src/main/kotlin'
+ jvmTarget = JavaVersion.VERSION_1_8
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
- applicationId "io.chameleon.ultra"
+ applicationId = "io.chameleon.ultra"
// You can update the following values to match your application needs.
- // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
- minSdkVersion 21
- targetSdkVersion Math.max(flutter.targetSdkVersion, 34)
- versionCode flutterVersionCode.toInteger()
- versionName flutterVersionName
+ // For more information, see: https://flutter.dev/to/review-gradle-config.
+ minSdk = flutter.minSdkVersion
+ targetSdk = flutter.targetSdkVersion
+ versionCode = flutter.versionCode
+ versionName = flutter.versionName
}
signingConfigs {
@@ -66,19 +43,20 @@ android {
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
- }
- buildTypes {
- release {
- if (keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']).exists() : false) {
- println "Info: upload-keystore.jks found. Signing for release."
- signingConfig signingConfigs.release
- }
- else {
- println "Warning: upload-keystore.jks not found. Signing for debug."
- signingConfig signingConfigs.debug
- }
- }
- }
+ }
+
+ buildTypes {
+ release {
+ if (keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']).exists() : false) {
+ println "Info: upload-keystore.jks found. Signing for release."
+ signingConfig signingConfigs.release
+ }
+ else {
+ println "Warning: upload-keystore.jks not found. Signing for debug."
+ signingConfig signingConfigs.debug
+ }
+ }
+ }
externalNativeBuild {
cmake {
@@ -88,10 +66,9 @@ android {
}
flutter {
- source '../..'
+ source = "../.."
}
dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.polidea.rxandroidble2:rxandroidble:1.17.2"
-}
+}
\ No newline at end of file
diff --git a/chameleonultragui/android/build.gradle b/chameleonultragui/android/build.gradle
index 674e96f4..d2ffbffa 100644
--- a/chameleonultragui/android/build.gradle
+++ b/chameleonultragui/android/build.gradle
@@ -1,16 +1,3 @@
-buildscript {
- ext.kotlin_version = '1.9.0'
- repositories {
- google()
- mavenCentral()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:7.2.0'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- }
-}
-
allprojects {
repositories {
google()
@@ -18,12 +5,12 @@ allprojects {
}
}
-rootProject.buildDir = '../build'
+rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
- project.evaluationDependsOn(':app')
+ project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {
diff --git a/chameleonultragui/android/gradle.properties b/chameleonultragui/android/gradle.properties
index 94adc3a3..25971708 100644
--- a/chameleonultragui/android/gradle.properties
+++ b/chameleonultragui/android/gradle.properties
@@ -1,3 +1,3 @@
-org.gradle.jvmargs=-Xmx1536M
+org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
diff --git a/chameleonultragui/android/gradle/wrapper/gradle-wrapper.properties b/chameleonultragui/android/gradle/wrapper/gradle-wrapper.properties
index 3c472b99..e1ca574e 100644
--- a/chameleonultragui/android/gradle/wrapper/gradle-wrapper.properties
+++ b/chameleonultragui/android/gradle/wrapper/gradle-wrapper.properties
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
diff --git a/chameleonultragui/android/settings.gradle b/chameleonultragui/android/settings.gradle
index 44e62bcf..264db3c0 100644
--- a/chameleonultragui/android/settings.gradle
+++ b/chameleonultragui/android/settings.gradle
@@ -1,11 +1,25 @@
-include ':app'
+pluginManagement {
+ def flutterSdkPath = {
+ def properties = new Properties()
+ file("local.properties").withInputStream { properties.load(it) }
+ def flutterSdkPath = properties.getProperty("flutter.sdk")
+ assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+ return flutterSdkPath
+ }()
-def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
-def properties = new Properties()
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
-assert localPropertiesFile.exists()
-localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
-def flutterSdkPath = properties.getProperty("flutter.sdk")
-assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
-apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "7.3.0" apply false
+ id "org.jetbrains.kotlin.android" version "2.0.21" apply false
+}
+
+include ":app"
diff --git a/chameleonultragui/ios/Flutter/AppFrameworkInfo.plist b/chameleonultragui/ios/Flutter/AppFrameworkInfo.plist
index 9625e105..7c569640 100644
--- a/chameleonultragui/ios/Flutter/AppFrameworkInfo.plist
+++ b/chameleonultragui/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 11.0
+ 12.0
diff --git a/chameleonultragui/ios/Podfile b/chameleonultragui/ios/Podfile
index 17b3c5a1..842bb4a7 100644
--- a/chameleonultragui/ios/Podfile
+++ b/chameleonultragui/ios/Podfile
@@ -1,4 +1,4 @@
-platform :ios, '11.0'
+platform :ios, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
diff --git a/chameleonultragui/ios/Podfile.lock b/chameleonultragui/ios/Podfile.lock
new file mode 100644
index 00000000..8e02bf6b
--- /dev/null
+++ b/chameleonultragui/ios/Podfile.lock
@@ -0,0 +1,214 @@
+PODS:
+ - DKImagePickerController/Core (4.3.9):
+ - DKImagePickerController/ImageDataManager
+ - DKImagePickerController/Resource
+ - DKImagePickerController/ImageDataManager (4.3.9)
+ - DKImagePickerController/PhotoGallery (4.3.9):
+ - DKImagePickerController/Core
+ - DKPhotoGallery
+ - DKImagePickerController/Resource (4.3.9)
+ - DKPhotoGallery (0.0.19):
+ - DKPhotoGallery/Core (= 0.0.19)
+ - DKPhotoGallery/Model (= 0.0.19)
+ - DKPhotoGallery/Preview (= 0.0.19)
+ - DKPhotoGallery/Resource (= 0.0.19)
+ - SDWebImage
+ - SwiftyGif
+ - DKPhotoGallery/Core (0.0.19):
+ - DKPhotoGallery/Model
+ - DKPhotoGallery/Preview
+ - SDWebImage
+ - SwiftyGif
+ - DKPhotoGallery/Model (0.0.19):
+ - SDWebImage
+ - SwiftyGif
+ - DKPhotoGallery/Preview (0.0.19):
+ - DKPhotoGallery/Model
+ - DKPhotoGallery/Resource
+ - SDWebImage
+ - SwiftyGif
+ - DKPhotoGallery/Resource (0.0.19):
+ - SDWebImage
+ - SwiftyGif
+ - file_picker (0.0.1):
+ - DKImagePickerController/PhotoGallery
+ - Flutter
+ - file_saver (0.0.1):
+ - Flutter
+ - Flutter (1.0.0)
+ - GoogleDataTransport (9.4.1):
+ - GoogleUtilities/Environment (~> 7.7)
+ - nanopb (< 2.30911.0, >= 2.30908.0)
+ - PromisesObjC (< 3.0, >= 1.2)
+ - GoogleMLKit/BarcodeScanning (6.0.0):
+ - GoogleMLKit/MLKitCore
+ - MLKitBarcodeScanning (~> 5.0.0)
+ - GoogleMLKit/MLKitCore (6.0.0):
+ - MLKitCommon (~> 11.0.0)
+ - GoogleToolboxForMac/Defines (4.2.1)
+ - GoogleToolboxForMac/Logger (4.2.1):
+ - GoogleToolboxForMac/Defines (= 4.2.1)
+ - "GoogleToolboxForMac/NSData+zlib (4.2.1)":
+ - GoogleToolboxForMac/Defines (= 4.2.1)
+ - GoogleUtilities/Environment (7.13.3):
+ - GoogleUtilities/Privacy
+ - PromisesObjC (< 3.0, >= 1.2)
+ - GoogleUtilities/Logger (7.13.3):
+ - GoogleUtilities/Environment
+ - GoogleUtilities/Privacy
+ - GoogleUtilities/Privacy (7.13.3)
+ - GoogleUtilities/UserDefaults (7.13.3):
+ - GoogleUtilities/Logger
+ - GoogleUtilities/Privacy
+ - GoogleUtilitiesComponents (1.1.0):
+ - GoogleUtilities/Logger
+ - GTMSessionFetcher/Core (3.5.0)
+ - MLImage (1.0.0-beta5)
+ - MLKitBarcodeScanning (5.0.0):
+ - MLKitCommon (~> 11.0)
+ - MLKitVision (~> 7.0)
+ - MLKitCommon (11.0.0):
+ - GoogleDataTransport (< 10.0, >= 9.4.1)
+ - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1)
+ - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)"
+ - GoogleUtilities/UserDefaults (< 8.0, >= 7.13.0)
+ - GoogleUtilitiesComponents (~> 1.0)
+ - GTMSessionFetcher/Core (< 4.0, >= 3.3.2)
+ - MLKitVision (7.0.0):
+ - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1)
+ - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)"
+ - GTMSessionFetcher/Core (< 4.0, >= 3.3.2)
+ - MLImage (= 1.0.0-beta5)
+ - MLKitCommon (~> 11.0)
+ - mobile_scanner (5.1.1):
+ - Flutter
+ - GoogleMLKit/BarcodeScanning (~> 6.0.0)
+ - nanopb (2.30910.0):
+ - nanopb/decode (= 2.30910.0)
+ - nanopb/encode (= 2.30910.0)
+ - nanopb/decode (2.30910.0)
+ - nanopb/encode (2.30910.0)
+ - package_info_plus (0.4.5):
+ - Flutter
+ - path_provider_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+ - permission_handler_apple (9.3.0):
+ - Flutter
+ - PromisesObjC (2.4.0)
+ - Protobuf (3.27.2)
+ - reactive_ble_mobile (0.0.1):
+ - Flutter
+ - Protobuf (~> 3.5)
+ - SwiftProtobuf (~> 1.0)
+ - recovery (0.0.1):
+ - Flutter
+ - SDWebImage (5.19.4):
+ - SDWebImage/Core (= 5.19.4)
+ - SDWebImage/Core (5.19.4)
+ - shared_preferences_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+ - SwiftProtobuf (1.27.1)
+ - SwiftyGif (5.4.5)
+ - url_launcher_ios (0.0.1):
+ - Flutter
+ - wakelock_plus (0.0.1):
+ - Flutter
+
+DEPENDENCIES:
+ - file_picker (from `.symlinks/plugins/file_picker/ios`)
+ - file_saver (from `.symlinks/plugins/file_saver/ios`)
+ - Flutter (from `Flutter`)
+ - mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`)
+ - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
+ - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
+ - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
+ - reactive_ble_mobile (from `.symlinks/plugins/reactive_ble_mobile/ios`)
+ - recovery (from `./recovery.podspec`)
+ - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
+ - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
+ - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
+
+SPEC REPOS:
+ trunk:
+ - DKImagePickerController
+ - DKPhotoGallery
+ - GoogleDataTransport
+ - GoogleMLKit
+ - GoogleToolboxForMac
+ - GoogleUtilities
+ - GoogleUtilitiesComponents
+ - GTMSessionFetcher
+ - MLImage
+ - MLKitBarcodeScanning
+ - MLKitCommon
+ - MLKitVision
+ - nanopb
+ - PromisesObjC
+ - Protobuf
+ - SDWebImage
+ - SwiftProtobuf
+ - SwiftyGif
+
+EXTERNAL SOURCES:
+ file_picker:
+ :path: ".symlinks/plugins/file_picker/ios"
+ file_saver:
+ :path: ".symlinks/plugins/file_saver/ios"
+ Flutter:
+ :path: Flutter
+ mobile_scanner:
+ :path: ".symlinks/plugins/mobile_scanner/ios"
+ package_info_plus:
+ :path: ".symlinks/plugins/package_info_plus/ios"
+ path_provider_foundation:
+ :path: ".symlinks/plugins/path_provider_foundation/darwin"
+ permission_handler_apple:
+ :path: ".symlinks/plugins/permission_handler_apple/ios"
+ reactive_ble_mobile:
+ :path: ".symlinks/plugins/reactive_ble_mobile/ios"
+ recovery:
+ :path: "./recovery.podspec"
+ shared_preferences_foundation:
+ :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
+ url_launcher_ios:
+ :path: ".symlinks/plugins/url_launcher_ios/ios"
+ wakelock_plus:
+ :path: ".symlinks/plugins/wakelock_plus/ios"
+
+SPEC CHECKSUMS:
+ DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
+ DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
+ file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
+ file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
+ Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
+ GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
+ GoogleMLKit: 97ac7af399057e99182ee8edfa8249e3226a4065
+ GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8
+ GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
+ GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe
+ GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
+ MLImage: 1824212150da33ef225fbd3dc49f184cf611046c
+ MLKitBarcodeScanning: 10ca0845a6d15f2f6e911f682a1998b68b973e8b
+ MLKitCommon: afec63980417d29ffbb4790529a1b0a2291699e1
+ MLKitVision: e858c5f125ecc288e4a31127928301eaba9ae0c1
+ mobile_scanner: 8564358885a9253c43f822435b70f9345c87224f
+ nanopb: 438bc412db1928dac798aa6fd75726007be04262
+ package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
+ path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
+ permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
+ PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
+ Protobuf: fb2c13674723f76ff6eede14f78847a776455fa2
+ reactive_ble_mobile: 9ce6723d37ccf701dbffd202d487f23f5de03b4c
+ recovery: 2514f094eb85cb1660b8a85b6937a4473b1db89d
+ SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d
+ shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
+ SwiftProtobuf: b109bd17979d7993a84da14b1e1fdd8b0ded934a
+ SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
+ url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
+ wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
+
+PODFILE CHECKSUM: 4e2832874d7b4657aae7c8959ebc9b6aee0be9ad
+
+COCOAPODS: 1.15.2
diff --git a/chameleonultragui/ios/Runner.xcodeproj/project.pbxproj b/chameleonultragui/ios/Runner.xcodeproj/project.pbxproj
index f82455f3..ed62fb45 100644
--- a/chameleonultragui/ios/Runner.xcodeproj/project.pbxproj
+++ b/chameleonultragui/ios/Runner.xcodeproj/project.pbxproj
@@ -366,7 +366,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -448,7 +448,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -497,7 +497,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
diff --git a/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
index d36b1fab..53611299 100644
--- a/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -1,122 +1,122 @@
{
"images" : [
{
- "size" : "20x20",
- "idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
- "scale" : "2x"
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "20x20"
},
{
- "size" : "20x20",
- "idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
- "scale" : "3x"
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "20x20"
},
{
- "size" : "29x29",
- "idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
- "scale" : "1x"
+ "idiom" : "iphone",
+ "scale" : "1x",
+ "size" : "29x29"
},
{
- "size" : "29x29",
- "idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
- "scale" : "2x"
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "29x29"
},
{
- "size" : "29x29",
- "idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
- "scale" : "3x"
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "29x29"
},
{
- "size" : "40x40",
- "idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
- "scale" : "2x"
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "40x40"
},
{
- "size" : "40x40",
- "idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
- "scale" : "3x"
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "40x40"
},
{
- "size" : "60x60",
- "idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
- "scale" : "2x"
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
},
{
- "size" : "60x60",
- "idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
- "scale" : "3x"
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
},
{
- "size" : "20x20",
- "idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
- "scale" : "1x"
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "20x20"
},
{
- "size" : "20x20",
- "idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
- "scale" : "2x"
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "20x20"
},
{
- "size" : "29x29",
- "idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
- "scale" : "1x"
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "29x29"
},
{
- "size" : "29x29",
- "idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
- "scale" : "2x"
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "29x29"
},
{
- "size" : "40x40",
- "idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
- "scale" : "1x"
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "40x40"
},
{
- "size" : "40x40",
- "idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
- "scale" : "2x"
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "40x40"
},
{
- "size" : "76x76",
- "idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
- "scale" : "1x"
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "76x76"
},
{
- "size" : "76x76",
- "idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
- "scale" : "2x"
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "76x76"
},
{
- "size" : "83.5x83.5",
- "idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
- "scale" : "2x"
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "83.5x83.5"
},
{
- "size" : "1024x1024",
- "idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
- "scale" : "1x"
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
}
],
"info" : {
- "version" : 1,
- "author" : "xcode"
+ "author" : "xcode",
+ "version" : 1
}
}
diff --git a/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png
deleted file mode 100644
index e93a8a86..00000000
Binary files a/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png and /dev/null differ
diff --git a/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png
deleted file mode 100644
index 74901e14..00000000
Binary files a/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png and /dev/null differ
diff --git a/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png
deleted file mode 100644
index 8fadf1c3..00000000
Binary files a/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png and /dev/null differ
diff --git a/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png
deleted file mode 100644
index 1cdd9269..00000000
Binary files a/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png and /dev/null differ
diff --git a/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png
deleted file mode 100644
index bc6f6b53..00000000
Binary files a/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png and /dev/null differ
diff --git a/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png
deleted file mode 100644
index 3c7238a2..00000000
Binary files a/chameleonultragui/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png and /dev/null differ
diff --git a/chameleonultragui/lib/bridge/chameleon.dart b/chameleonultragui/lib/bridge/chameleon.dart
index cc215b87..1268e048 100644
--- a/chameleonultragui/lib/bridge/chameleon.dart
+++ b/chameleonultragui/lib/bridge/chameleon.dart
@@ -70,6 +70,8 @@ enum ChameleonCommand {
mf1CheckKey(2007),
mf1ReadBlock(2008),
mf1WriteBlock(2009),
+ mf1ManipulateValueBlock(2011),
+ mf1CheckKeysOfSectors(2012), // not implemented
hf14ARawCommand(2010),
// lf commands
@@ -96,6 +98,19 @@ enum ChameleonCommand {
mf1GetWriteMode(4016),
mf1SetWriteMode(4017),
+ mf0NtagGetUidMagicMode(4019),
+ mf0NtagSetUidMagicMode(4020),
+ mf0NtagReadEmuPageData(4021),
+ mf0NtagWriteEmuPageData(4022),
+ mf0NtagGetVersionData(4023),
+ mf0NtagSetVersionData(4024),
+ mf0NtagGetSignatureData(4025),
+ mf0NtagSetSignatureData(4026),
+ mf0NtagGetCounterData(4027),
+ mf0NtagSetCounterData(4028),
+ mf0NtagResetAuthCount(4029),
+ mf0NtagGetPageCount(4030),
+
// read slot info
mf1GetBlockData(4008),
mf1GetAntiCollData(4018),
@@ -115,9 +130,15 @@ enum TagType {
mifare1K(1001),
mifare2K(1002),
mifare4K(1003),
+ ntag210(1107),
+ ntag212(1108),
ntag213(1100),
ntag215(1101),
- ntag216(1102);
+ ntag216(1102),
+ ultralight(1103),
+ ultralightC(1104),
+ ultralight11(1105),
+ ultralight21(1106);
const TagType(this.value);
final int value;
@@ -334,6 +355,15 @@ class DeviceSettings {
this.key = ""});
}
+enum MifareClassicValueBlockOperator {
+ decrement(0xC0),
+ increment(0xC1),
+ restore(0xC2);
+
+ const MifareClassicValueBlockOperator(this.value);
+ final int value;
+}
+
// Some ChatGPT magic
// Nobody knows how it works
@@ -1148,6 +1178,31 @@ class ChameleonCommunicator {
return commands;
}
+ Future manipulateValueBlock(
+ int srcBlock,
+ int srcKeyType,
+ Uint8List srcKey,
+ MifareClassicValueBlockOperator op,
+ int value,
+ int dstBlock,
+ int dstKeyType,
+ Uint8List dstKey) async {
+ await sendCmd(ChameleonCommand.mf1ManipulateValueBlock,
+ data: Uint8List.fromList([
+ srcKeyType,
+ srcBlock,
+ ...srcKey,
+ op.value,
+ value >> 24,
+ value >> 16 & 0xFF,
+ value >> 8 & 0xFF,
+ value & 0xFF,
+ dstKeyType,
+ dstBlock,
+ ...dstKey
+ ]));
+ }
+
Future send14ARaw(Uint8List data,
{int respTimeoutMs = 100,
int? bitLen,
@@ -1188,4 +1243,69 @@ class ChameleonCommunicator {
])))!
.data;
}
+
+ Future mf0GetMagicMode() async {
+ return (await sendCmd(ChameleonCommand.mf0NtagGetUidMagicMode))!.data[0] ==
+ 1;
+ }
+
+ Future mf0SetMagicMode(bool enabled) async {
+ await sendCmd(ChameleonCommand.mf0NtagSetUidMagicMode,
+ data: Uint8List.fromList([enabled ? 1 : 0]));
+ }
+
+ Future mf0EmulatorReadPages(int from, int count) async {
+ return (await sendCmd(ChameleonCommand.mf0NtagReadEmuPageData,
+ data: Uint8List.fromList([from, count])))!
+ .data;
+ }
+
+ Future mf0EmulatorWritePages(int from, Uint8List data) async {
+ await sendCmd(ChameleonCommand.mf0NtagWriteEmuPageData,
+ data: Uint8List.fromList([from, data.length >> 2, ...data]));
+ }
+
+ Future mf0EmulatorGetVersionData() async {
+ return (await sendCmd(ChameleonCommand.mf0NtagGetVersionData))!.data;
+ }
+
+ Future mf0EmulatorSetVersionData(Uint8List data) async {
+ await sendCmd(ChameleonCommand.mf0NtagSetVersionData,
+ data: Uint8List.fromList([...data]));
+ }
+
+ Future mf0EmulatorGetSignatureData() async {
+ return (await sendCmd(ChameleonCommand.mf0NtagGetSignatureData))!.data;
+ }
+
+ Future mf0EmulatorSetSignatureData(Uint8List data) async {
+ await sendCmd(ChameleonCommand.mf0NtagSetSignatureData,
+ data: Uint8List.fromList([...data]));
+ }
+
+ Future mf0ResetAuthCount() async {
+ return (await sendCmd(ChameleonCommand.mf0NtagResetAuthCount))!.data[0];
+ }
+
+ Future mf0EmulatorGetPageCount() async {
+ return (await sendCmd(ChameleonCommand.mf0NtagGetPageCount))!.data[0];
+ }
+
+ Future<(int, bool)> mf0EmulatorGetCounterData(int index) async {
+ Uint8List data = (await sendCmd(ChameleonCommand.mf0NtagGetCounterData,
+ data: Uint8List.fromList([index])))!
+ .data;
+ return (((data[0] << 16) | (data[1] << 8) | data[2]), data[3] == 0xBD);
+ }
+
+ Future mf0EmulatorSetCounterData(
+ int index, int value, bool resetTearing) async {
+ await sendCmd(ChameleonCommand.mf0NtagSetCounterData,
+ data: Uint8List.fromList([
+ index | ((resetTearing ? 1 : 0) << 7),
+ (value >> 16) & 0xFF,
+ (value >> 8) & 0xFF,
+ value & 0xFF
+ ]));
+ }
}
diff --git a/chameleonultragui/lib/gui/component/card_list.dart b/chameleonultragui/lib/gui/component/card_list.dart
index 68710386..55009981 100644
--- a/chameleonultragui/lib/gui/component/card_list.dart
+++ b/chameleonultragui/lib/gui/component/card_list.dart
@@ -1,6 +1,5 @@
import 'package:chameleonultragui/bridge/chameleon.dart';
import 'package:chameleonultragui/helpers/general.dart';
-import 'package:chameleonultragui/helpers/mifare_classic/general.dart';
import 'package:chameleonultragui/sharedprefsprovider.dart';
import 'package:flutter/material.dart';
@@ -157,10 +156,7 @@ class CardSearchDelegate extends SearchDelegate {
color: card.color),
title: Text(card.name),
subtitle: Text(
- chameleonTagToString(card.tag) +
- ((chameleonTagSaveCheckForMifareClassicEV1(card))
- ? " EV1"
- : ""),
+ chameleonCardToString(card),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
diff --git a/chameleonultragui/lib/gui/component/card_recovery.dart b/chameleonultragui/lib/gui/component/mifare/classic.dart
similarity index 97%
rename from chameleonultragui/lib/gui/component/card_recovery.dart
rename to chameleonultragui/lib/gui/component/mifare/classic.dart
index 11f96528..fb2ffd1e 100644
--- a/chameleonultragui/lib/gui/component/card_recovery.dart
+++ b/chameleonultragui/lib/gui/component/mifare/classic.dart
@@ -19,22 +19,22 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
-class CardRecovery extends StatefulWidget {
+class MifareClassicHelper extends StatefulWidget {
final HFCardInfo hfInfo;
final MifareClassicInfo mfcInfo;
final bool allowSave;
- const CardRecovery(
+ const MifareClassicHelper(
{super.key,
required this.hfInfo,
required this.mfcInfo,
this.allowSave = true});
@override
- State createState() => CardRecoveryState();
+ State createState() => CardReaderState();
}
-class CardRecoveryState extends State {
+class CardReaderState extends State {
String dumpName = "";
bool skipDefaultDictionary = false;
@@ -47,7 +47,7 @@ class CardRecoveryState extends State {
);
}
- Future saveHFCard({bool bin = false, bool skipDump = false}) async {
+ Future saveCard({bool bin = false, bool skipDump = false}) async {
var appState = Provider.of(context, listen: false);
List cardDump = [];
@@ -396,7 +396,7 @@ class CardRecoveryState extends State {
actions: [
ElevatedButton(
onPressed: () async {
- await saveHFCard();
+ await saveCard();
if (context.mounted) {
Navigator.pop(context);
}
@@ -420,7 +420,7 @@ class CardRecoveryState extends State {
const SizedBox(width: 8),
ElevatedButton(
onPressed: () async {
- await saveHFCard(bin: true);
+ await saveCard(bin: true);
},
child: Text(localizations.save_as(".bin")),
),
diff --git a/chameleonultragui/lib/gui/component/mifare/ultralight.dart b/chameleonultragui/lib/gui/component/mifare/ultralight.dart
new file mode 100644
index 00000000..614af6a1
--- /dev/null
+++ b/chameleonultragui/lib/gui/component/mifare/ultralight.dart
@@ -0,0 +1,269 @@
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:chameleonultragui/gui/component/error_message.dart';
+import 'package:chameleonultragui/gui/page/read_card.dart';
+import 'package:chameleonultragui/helpers/general.dart';
+import 'package:chameleonultragui/helpers/mifare_ultralight/general.dart';
+import 'package:chameleonultragui/main.dart';
+import 'package:chameleonultragui/sharedprefsprovider.dart';
+import 'package:collection/collection.dart';
+import 'package:file_picker/file_picker.dart';
+import 'package:file_saver/file_saver.dart';
+import 'package:flutter/material.dart';
+
+// Localizations
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+import 'package:provider/provider.dart';
+
+enum MifareUltralightState { none, read, save }
+
+class MifareUltralightHelper extends StatefulWidget {
+ final HFCardInfo hfInfo;
+ final bool allowSave;
+
+ const MifareUltralightHelper(
+ {super.key, required this.hfInfo, this.allowSave = true});
+
+ @override
+ State createState() => CardReaderState();
+}
+
+class CardReaderState extends State {
+ TextEditingController keyController = TextEditingController();
+ MifareUltralightState state = MifareUltralightState.none;
+ final GlobalKey formKey = GlobalKey();
+ List cardData = [];
+ String version = "";
+ String signature = "";
+ String dumpName = "";
+ String error = "";
+ double progress = -1;
+
+ Future readCard({bool withPassword = false}) async {
+ var appState = Provider.of(context, listen: false);
+ var localizations = AppLocalizations.of(context)!;
+ Uint8List? pack;
+ setState(() {
+ cardData = [];
+ error = "";
+ state = MifareUltralightState.read;
+ });
+
+ for (var page = 0;
+ page < mfUltralightGetPagesCount(widget.hfInfo.type);
+ page++) {
+ if (withPassword) {
+ pack = await appState.communicator!.send14ARaw(
+ Uint8List.fromList([0x1B, ...hexToBytes(keyController.text)]),
+ keepRfField: true);
+ if (pack.length < 2) {
+ setState(() {
+ state = MifareUltralightState.none;
+ error = localizations.invalid_password;
+ });
+ return;
+ }
+ }
+
+ Uint8List pageData = await appState.communicator!
+ .send14ARaw(Uint8List.fromList([0x30, page]));
+ if (pageData.isNotEmpty) {
+ cardData.add(Uint8List.fromList(pageData.slice(0, 4).toList()));
+ } else {
+ cardData.add(Uint8List(0));
+ }
+
+ setState(() {
+ progress = page / mfUltralightGetPagesCount(widget.hfInfo.type);
+ });
+ }
+
+ version =
+ bytesToHexSpace(await mfUltralightGetVersion(appState.communicator!));
+ signature =
+ bytesToHexSpace(await mfUltralightGetSignature(appState.communicator!));
+
+ // Save password to dump if was used
+ int passwordPage = mfUltralightGetPasswordPage(widget.hfInfo.type);
+ if (passwordPage != 0 && withPassword) {
+ cardData[passwordPage] = hexToBytes(keyController.text);
+ for (var byte = 0; byte < pack!.length; byte++) {
+ cardData[passwordPage + 1][byte] = pack[byte];
+ }
+ }
+
+ setState(() {
+ error = "";
+ state = MifareUltralightState.save;
+ });
+ }
+
+ Future saveCard({bool bin = false}) async {
+ var appState = Provider.of(context, listen: false);
+
+ List cardDump = [];
+ var localizations = AppLocalizations.of(context)!;
+ for (var page = 0;
+ page < mfUltralightGetPagesCount(widget.hfInfo.type);
+ page++) {
+ if (cardData[page].isEmpty) {
+ cardDump.addAll(Uint8List(4));
+ } else {
+ cardDump.addAll(cardData[page]);
+ }
+ }
+
+ if (bin) {
+ try {
+ await FileSaver.instance.saveAs(
+ name: widget.hfInfo.uid.replaceAll(" ", ""),
+ bytes: Uint8List.fromList(cardDump),
+ ext: 'bin',
+ mimeType: MimeType.other);
+ } on UnimplementedError catch (_) {
+ String? outputFile = await FilePicker.platform.saveFile(
+ dialogTitle: '${localizations.output_file}:',
+ fileName: '${widget.hfInfo.uid.replaceAll(" ", "")}.bin',
+ );
+
+ if (outputFile != null) {
+ var file = File(outputFile);
+ await file.writeAsBytes(Uint8List.fromList(cardDump));
+ }
+ }
+ } else {
+ var tags = appState.sharedPreferencesProvider.getCards();
+ tags.add(CardSave(
+ uid: widget.hfInfo.uid,
+ sak: hexToBytes(widget.hfInfo.sak)[0],
+ atqa: hexToBytes(widget.hfInfo.atqa),
+ name: dumpName,
+ tag: widget.hfInfo.type,
+ data: cardData,
+ extraData: CardSaveExtra(
+ ultralightSignature: hexToBytes(signature),
+ ultralightVersion: hexToBytes(version),
+ ),
+ ats: (widget.hfInfo.ats != localizations.no)
+ ? hexToBytes(widget.hfInfo.ats)
+ : Uint8List(0)));
+ appState.sharedPreferencesProvider.setCards(tags);
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ var localizations = AppLocalizations.of(context)!;
+
+ return Column(
+ children: [
+ const SizedBox(height: 16),
+ if (state == MifareUltralightState.none) ...[
+ Form(
+ key: formKey,
+ autovalidateMode: AutovalidateMode.onUserInteraction,
+ child: Column(
+ children: [
+ TextFormField(
+ controller: keyController,
+ decoration: InputDecoration(
+ labelText: localizations.key,
+ hintMaxLines: 4,
+ hintText: localizations.enter_something(
+ localizations.ultralight_key_prompt)),
+ validator: (String? value) {
+ if (value!.isNotEmpty && !isValidHexString(value)) {
+ return localizations.must_be_valid_hex;
+ }
+
+ if (value.length != 8) {
+ return localizations.must_be(4, localizations.key);
+ }
+
+ return null;
+ },
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 8),
+ Row(children: [
+ Expanded(
+ child: TextButton(
+ onPressed: () async => {await readCard(withPassword: true)},
+ child: Text(localizations.read_with_key),
+ ),
+ ),
+ Expanded(
+ child: TextButton(
+ onPressed: () async => {await readCard(withPassword: false)},
+ child: Text(localizations.read_without_key),
+ ),
+ ),
+ ]),
+ ],
+ if (error != "") ...[
+ const SizedBox(height: 16),
+ ErrorMessage(errorMessage: error),
+ ],
+ if (state == MifareUltralightState.read) ...[
+ LinearProgressIndicator(value: progress),
+ const SizedBox(height: 8)
+ ],
+ if (state == MifareUltralightState.save)
+ Center(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ ElevatedButton(
+ onPressed: () async {
+ await showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return AlertDialog(
+ title: Text(localizations.enter_name_of_card),
+ content: TextField(
+ onChanged: (value) {
+ setState(() {
+ dumpName = value;
+ });
+ },
+ ),
+ actions: [
+ ElevatedButton(
+ onPressed: () async {
+ await saveCard();
+ if (context.mounted) {
+ Navigator.pop(context);
+ }
+ },
+ child: Text(localizations.ok),
+ ),
+ ElevatedButton(
+ onPressed: () {
+ Navigator.pop(
+ context); // Close the modal without saving
+ },
+ child: Text(localizations.cancel),
+ ),
+ ],
+ );
+ },
+ );
+ },
+ child: Text(localizations.save),
+ ),
+ const SizedBox(width: 8),
+ ElevatedButton(
+ onPressed: () async {
+ await saveCard(bin: true);
+ },
+ child: Text(localizations.save_as(".bin")),
+ ),
+ ])),
+ ],
+ );
+ }
+}
diff --git a/chameleonultragui/lib/gui/menu/card_edit.dart b/chameleonultragui/lib/gui/menu/card_edit.dart
index 7d6c0107..f2758723 100644
--- a/chameleonultragui/lib/gui/menu/card_edit.dart
+++ b/chameleonultragui/lib/gui/menu/card_edit.dart
@@ -1,4 +1,4 @@
-import 'package:chameleonultragui/helpers/ntag/general.dart';
+import 'package:chameleonultragui/helpers/mifare_ultralight/general.dart';
import 'package:flutter/material.dart';
import 'package:chameleonultragui/helpers/general.dart';
import 'package:chameleonultragui/sharedprefsprovider.dart';
@@ -27,6 +27,8 @@ class CardEditMenuState extends State {
TextEditingController sakController = TextEditingController();
TextEditingController atqaController = TextEditingController();
TextEditingController atsController = TextEditingController();
+ TextEditingController ultralightVersionController = TextEditingController();
+ TextEditingController ultralightSignatureController = TextEditingController();
Color pickerColor = Colors.deepOrange;
Color currentColor = Colors.deepOrange;
final GlobalKey _formKey = GlobalKey();
@@ -39,6 +41,10 @@ class CardEditMenuState extends State {
sakController.text = bytesToHexSpace(u8ToBytes(widget.tagSave.sak));
atqaController.text = bytesToHexSpace(widget.tagSave.atqa);
atsController.text = bytesToHexSpace(widget.tagSave.ats);
+ ultralightVersionController.text =
+ bytesToHexSpace(widget.tagSave.extraData.ultralightVersion);
+ ultralightSignatureController.text =
+ bytesToHexSpace(widget.tagSave.extraData.ultralightSignature);
nameController.text = widget.tagSave.name;
pickerColor = widget.tagSave.color;
currentColor = widget.tagSave.color;
@@ -130,17 +136,8 @@ class CardEditMenuState extends State {
const SizedBox(height: 8),
DropdownButton(
value: selectedType,
- items: [
- TagType.mifare1K,
- TagType.mifare2K,
- TagType.mifare4K,
- TagType.mifareMini,
- TagType.ntag213,
- TagType.ntag215,
- TagType.ntag216,
- TagType.em410X,
- TagType.unknown
- ].map>((TagType type) {
+ items: getTagTypes()
+ .map>((TagType type) {
return DropdownMenuItem(
value: type,
child: Text(
@@ -149,7 +146,7 @@ class CardEditMenuState extends State {
);
}).toList(),
onChanged: (TagType? newValue) {
- if (newValue != TagType.unknown && !isNTAG(newValue!)) {
+ if (newValue! != TagType.unknown) {
setState(() {
selectedType = newValue;
});
@@ -252,6 +249,44 @@ class CardEditMenuState extends State {
}
return null;
}),
+ if (isMifareUltralight(selectedType)) ...[
+ const SizedBox(height: 20),
+ TextFormField(
+ controller: ultralightVersionController,
+ decoration: InputDecoration(
+ labelText: localizations.ultralight_version,
+ hintText: localizations.enter_something(
+ localizations.ultralight_version)),
+ validator: (value) {
+ if (value!.replaceAll(" ", "").length % 2 !=
+ 0) {
+ return localizations.must_be_valid_hex;
+ }
+
+ if (value.isNotEmpty &&
+ value.replaceAll(" ", "").length != 16 &&
+ isMifareUltralight(selectedType)) {
+ return localizations.must_be(
+ 8, localizations.ultralight_version);
+ }
+
+ return null;
+ }),
+ const SizedBox(height: 20),
+ TextFormField(
+ controller: ultralightSignatureController,
+ decoration: InputDecoration(
+ labelText: localizations.ultralight_signature,
+ hintText: localizations.enter_something(
+ localizations.ultralight_signature)),
+ validator: (value) {
+ if (value!.replaceAll(" ", "").length % 2 !=
+ 0) {
+ return localizations.must_be_valid_hex;
+ }
+ return null;
+ }),
+ ]
]))
]),
)
@@ -280,6 +315,12 @@ class CardEditMenuState extends State {
: hexToBytes(sakController.text)[0],
atqa: hexToBytes(atqaController.text),
uid: bytesToHexSpace(hexToBytes(uidController.text)),
+ extraData: CardSaveExtra(
+ ultralightSignature:
+ hexToBytes(ultralightSignatureController.text),
+ ultralightVersion:
+ hexToBytes(ultralightVersionController.text),
+ ),
tag: selectedType,
data: widget.tagSave.data,
color: currentColor,
diff --git a/chameleonultragui/lib/gui/menu/card_view.dart b/chameleonultragui/lib/gui/menu/card_view.dart
index 723af9c3..5e8560bd 100644
--- a/chameleonultragui/lib/gui/menu/card_view.dart
+++ b/chameleonultragui/lib/gui/menu/card_view.dart
@@ -2,6 +2,7 @@ import 'package:chameleonultragui/bridge/chameleon.dart';
import 'package:chameleonultragui/gui/menu/card_edit.dart';
import 'package:chameleonultragui/gui/menu/dictionary_export.dart';
import 'package:chameleonultragui/helpers/mifare_classic/general.dart';
+import 'package:chameleonultragui/helpers/mifare_ultralight/general.dart';
import 'package:flutter/material.dart';
import 'package:chameleonultragui/helpers/general.dart';
import 'package:chameleonultragui/sharedprefsprovider.dart';
@@ -61,66 +62,102 @@ class CardViewMenuState extends State {
),
],
),
- ...(chameleonTagToFrequency(widget.tagSave.tag) == TagFrequency.hf)
- ? [
- Row(
- children: [
- Text(
- "${localizations.sak}: ${widget.tagSave.sak == 0 ? localizations.unavailable : bytesToHex(u8ToBytes(widget.tagSave.sak))}"),
- IconButton(
- onPressed: () async {
- ClipboardData data = ClipboardData(
- text: widget.tagSave.sak == 0
- ? localizations.unavailable
- : bytesToHex(u8ToBytes(widget.tagSave.sak)));
- await Clipboard.setData(data);
- },
- icon: const Icon(Icons.copy),
- ),
- ],
- ),
- Row(
- children: [
- Text(
- "${localizations.atqa}: ${widget.tagSave.atqa.isNotEmpty ? bytesToHexSpace(widget.tagSave.atqa) : localizations.unavailable}"),
- IconButton(
- onPressed: () async {
- ClipboardData data = ClipboardData(
- text: widget.tagSave.atqa.isNotEmpty
- ? bytesToHex(widget.tagSave.atqa)
- : localizations.unavailable);
- await Clipboard.setData(data);
- },
- icon: const Icon(Icons.copy),
- ),
- ],
- ),
- if (isMifareClassic(widget.tagSave.tag))
- Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- const SizedBox(height: 8),
- ElevatedButton(
- onPressed: (mfClassicGetKeysFromDump(
- widget.tagSave.data)
- .isNotEmpty)
- ? () async {
- List keys =
- mfClassicGetKeysFromDump(
- widget.tagSave.data);
- await showDialog(
- context: context,
- builder: (BuildContext context) {
- return DictionaryExportMenu(keys: keys);
- },
- );
- }
- : null,
- child: Text(localizations.export_to_dictionary)),
- ],
- )
- ]
- : [],
+ if (chameleonTagToFrequency(widget.tagSave.tag) ==
+ TagFrequency.hf) ...[
+ Row(
+ children: [
+ Text(
+ "${localizations.sak}: ${bytesToHex(u8ToBytes(widget.tagSave.sak))}"),
+ IconButton(
+ onPressed: () async {
+ ClipboardData data = ClipboardData(
+ text: widget.tagSave.sak == 0
+ ? localizations.unavailable
+ : bytesToHex(u8ToBytes(widget.tagSave.sak)));
+ await Clipboard.setData(data);
+ },
+ icon: const Icon(Icons.copy),
+ ),
+ ],
+ ),
+ Row(
+ children: [
+ Text(
+ "${localizations.atqa}: ${widget.tagSave.atqa.isNotEmpty ? bytesToHexSpace(widget.tagSave.atqa) : localizations.unavailable}"),
+ IconButton(
+ onPressed: () async {
+ ClipboardData data = ClipboardData(
+ text: widget.tagSave.atqa.isNotEmpty
+ ? bytesToHex(widget.tagSave.atqa)
+ : localizations.unavailable);
+ await Clipboard.setData(data);
+ },
+ icon: const Icon(Icons.copy),
+ ),
+ ],
+ ),
+ if (isMifareClassic(widget.tagSave.tag))
+ Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const SizedBox(height: 8),
+ ElevatedButton(
+ onPressed: (mfClassicGetKeysFromDump(widget.tagSave.data)
+ .isNotEmpty)
+ ? () async {
+ List keys =
+ mfClassicGetKeysFromDump(widget.tagSave.data);
+ await showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return DictionaryExportMenu(keys: keys);
+ },
+ );
+ }
+ : null,
+ child: Text(localizations.export_to_dictionary)),
+ ],
+ ),
+ if (isMifareUltralight(widget.tagSave.tag))
+ Column(mainAxisAlignment: MainAxisAlignment.center, children: [
+ Row(
+ children: [
+ Text(
+ "${localizations.ultralight_version}: ${widget.tagSave.extraData.ultralightVersion.isNotEmpty ? bytesToHexSpace(widget.tagSave.extraData.ultralightVersion) : localizations.unavailable}"),
+ IconButton(
+ onPressed: () async {
+ ClipboardData data = ClipboardData(
+ text: widget.tagSave.extraData.ultralightVersion
+ .isNotEmpty
+ ? bytesToHexSpace(
+ widget.tagSave.extraData.ultralightVersion)
+ : localizations.unavailable);
+ await Clipboard.setData(data);
+ },
+ icon: const Icon(Icons.copy),
+ ),
+ ],
+ ),
+ Row(
+ children: [
+ Text(
+ "${localizations.ultralight_signature}: ${widget.tagSave.extraData.ultralightSignature.isNotEmpty ? bytesToHexSpace(widget.tagSave.extraData.ultralightSignature) : localizations.unavailable}"),
+ IconButton(
+ onPressed: () async {
+ ClipboardData data = ClipboardData(
+ text: widget.tagSave.extraData.ultralightVersion
+ .isNotEmpty
+ ? bytesToHexSpace(
+ widget.tagSave.extraData.ultralightVersion)
+ : localizations.unavailable);
+ await Clipboard.setData(data);
+ },
+ icon: const Icon(Icons.copy),
+ ),
+ ],
+ ),
+ ])
+ ],
],
)),
actions: [
@@ -178,7 +215,8 @@ class CardViewMenuState extends State {
var confirm = await showDialog(
context: context,
builder: (BuildContext context) {
- return ConfirmDeletionMenu(thingBeingDeleted: widget.tagSave.name);
+ return ConfirmDeletionMenu(
+ thingBeingDeleted: widget.tagSave.name);
},
);
diff --git a/chameleonultragui/lib/gui/menu/slot_edit.dart b/chameleonultragui/lib/gui/menu/slot_edit.dart
index 5858047d..b91f3de8 100644
--- a/chameleonultragui/lib/gui/menu/slot_edit.dart
+++ b/chameleonultragui/lib/gui/menu/slot_edit.dart
@@ -1,7 +1,6 @@
import 'package:chameleonultragui/bridge/chameleon.dart';
import 'package:chameleonultragui/gui/component/toggle_buttons.dart';
import 'package:chameleonultragui/helpers/mifare_classic/general.dart';
-import 'package:chameleonultragui/helpers/ntag/general.dart';
import 'package:flutter/material.dart';
import 'package:chameleonultragui/helpers/general.dart';
import 'package:provider/provider.dart';
@@ -159,7 +158,7 @@ class SlotEditMenuState extends State {
);
}).toList(),
onChanged: (TagType? newValue) {
- if (newValue != TagType.unknown && !isNTAG(newValue!)) {
+ if (newValue! != TagType.unknown) {
setState(() {
selectedType = newValue;
});
diff --git a/chameleonultragui/lib/gui/page/read_card.dart b/chameleonultragui/lib/gui/page/read_card.dart
index 81b86bf4..16fa6b30 100644
--- a/chameleonultragui/lib/gui/page/read_card.dart
+++ b/chameleonultragui/lib/gui/page/read_card.dart
@@ -1,9 +1,11 @@
import 'package:chameleonultragui/bridge/chameleon.dart';
-import 'package:chameleonultragui/gui/component/card_recovery.dart';
+import 'package:chameleonultragui/gui/component/mifare/classic.dart';
import 'package:chameleonultragui/gui/component/error_message.dart';
+import 'package:chameleonultragui/gui/component/mifare/ultralight.dart';
import 'package:chameleonultragui/helpers/general.dart';
import 'package:chameleonultragui/helpers/mifare_classic/general.dart';
import 'package:chameleonultragui/helpers/mifare_classic/recovery.dart';
+import 'package:chameleonultragui/helpers/mifare_ultralight/general.dart';
import 'package:chameleonultragui/main.dart';
import 'package:chameleonultragui/sharedprefsprovider.dart';
import 'package:chameleonultragui/connector/serial_abstract.dart';
@@ -32,6 +34,7 @@ class HFCardInfo {
String atqa;
String tech;
String ats;
+ TagType type;
bool cardExist;
HFCardInfo(
@@ -40,6 +43,7 @@ class HFCardInfo {
this.atqa = '',
this.tech = '',
this.ats = '',
+ this.type = TagType.unknown,
this.cardExist = true});
}
@@ -105,11 +109,14 @@ class ReadCardPageState extends State {
CardData card = await appState.communicator!.scan14443aTag();
bool isMifareClassic = false;
+ TagType type = TagType.unknown;
MifareClassicType mifareClassicType = MifareClassicType.none;
try {
isMifareClassic = await appState.communicator!.detectMf1Support();
- mifareClassicType = await mfClassicGetType(appState.communicator!);
+ if (isMifareClassic) {
+ mifareClassicType = await mfClassicGetType(appState.communicator!);
+ }
} catch (_) {}
bool isMifareClassicEV1 = isMifareClassic
@@ -126,6 +133,16 @@ class ReadCardPageState extends State {
});
}
+ if (!isMifareClassic) {
+ Uint8List version =
+ await mfUltralightGetVersion(appState.communicator!);
+ if (version.length == 8) {
+ type = mfUltralightGetType(version);
+ }
+ } else {
+ type = mfClassicGetChameleonTagType(mifareClassicType);
+ }
+
setState(() {
hfInfo.uid = bytesToHexSpace(card.uid);
hfInfo.sak = card.sak.toRadixString(16).padLeft(2, '0').toUpperCase();
@@ -133,14 +150,14 @@ class ReadCardPageState extends State {
hfInfo.ats = (card.ats.isNotEmpty)
? bytesToHexSpace(card.ats)
: localizations.no;
+ hfInfo.type = type;
mfcInfo.isEV1 = isMifareClassicEV1;
mfcInfo.type = mifareClassicType;
mfcInfo.state = (mfcInfo.type != MifareClassicType.none)
? MifareClassicState.checkKeys
: MifareClassicState.none;
- hfInfo.tech = isMifareClassic
- ? "Mifare Classic ${mfClassicGetName(mfcInfo.type)}${isMifareClassicEV1 ? " EV1" : ""}"
- : localizations.other;
+ hfInfo.tech =
+ chameleonTagToString(type) + (isMifareClassicEV1 ? " EV1" : "");
});
} catch (_) {
setState(() {
@@ -341,8 +358,10 @@ class ReadCardPageState extends State {
child: Text(localizations.save_only_uid),
),
],
- if (mfcInfo.type != MifareClassicType.none)
- CardRecovery(mfcInfo: mfcInfo, hfInfo: hfInfo)
+ if (isMifareClassic(hfInfo.type))
+ MifareClassicHelper(mfcInfo: mfcInfo, hfInfo: hfInfo),
+ if (isMifareUltralight(hfInfo.type))
+ MifareUltralightHelper(hfInfo: hfInfo)
],
),
),
diff --git a/chameleonultragui/lib/gui/page/saved_cards.dart b/chameleonultragui/lib/gui/page/saved_cards.dart
index 8988ff85..54e2d512 100644
--- a/chameleonultragui/lib/gui/page/saved_cards.dart
+++ b/chameleonultragui/lib/gui/page/saved_cards.dart
@@ -478,11 +478,7 @@ class SavedCardsPageState extends State {
: Icons.wifi,
iconColor: tag.color,
firstLine: tag.name.isEmpty ? "⠀" : tag.name,
- secondLine: chameleonTagToString(tag.tag) +
- ((chameleonTagSaveCheckForMifareClassicEV1(
- tag))
- ? " EV1"
- : ""),
+ secondLine: chameleonCardToString(tag),
itemIndex: index,
onPressed: () {
showDialog(
diff --git a/chameleonultragui/lib/gui/page/settings.dart b/chameleonultragui/lib/gui/page/settings.dart
index 0f635ee8..f94f1800 100644
--- a/chameleonultragui/lib/gui/page/settings.dart
+++ b/chameleonultragui/lib/gui/page/settings.dart
@@ -35,7 +35,7 @@ const localeNameMap = {
"it": "Italiano",
"ja": "日本語",
"ko": "한국어",
- "nl": "Dutch",
+ "nl": "Nederlands",
"ar": "العربية ",
"tr": "Türkçe",
"pl": "Polski",
@@ -155,7 +155,8 @@ class SettingsMainPageState extends State {
selectedValue:
appState.sharedPreferencesProvider.getTheme().index,
onChange: (int index) async {
- appState.sharedPreferencesProvider.setTheme(ThemeMode.values[index]);
+ appState.sharedPreferencesProvider
+ .setTheme(ThemeMode.values[index]);
appState.changesMade();
}),
const SizedBox(height: 10),
@@ -224,11 +225,9 @@ class SettingsMainPageState extends State {
},
items: AppLocalizations.supportedLocales.map((locale) {
return DropdownMenuItem(
- value: locale.toLanguageTag(),
- child: Text(
- localeNameMap[locale.toLanguageTag()] ?? "Unknown"
- )
- );
+ value: locale.toLanguageTag(),
+ child: Text(localeNameMap[locale.toLanguageTag()] ??
+ "Unknown"));
}).toList(),
),
),
@@ -242,8 +241,8 @@ class SettingsMainPageState extends State {
),
const SizedBox(width: 5),
Switch(
- value: appState.sharedPreferencesProvider
- .getConfirmDelete(),
+ value:
+ appState.sharedPreferencesProvider.getConfirmDelete(),
onChanged: (value) async {
appState.sharedPreferencesProvider
.setConfirmDelete(value);
diff --git a/chameleonultragui/lib/gui/page/slot_manager.dart b/chameleonultragui/lib/gui/page/slot_manager.dart
index 38caa98e..d2a5fa58 100644
--- a/chameleonultragui/lib/gui/page/slot_manager.dart
+++ b/chameleonultragui/lib/gui/page/slot_manager.dart
@@ -5,6 +5,7 @@ import 'package:chameleonultragui/gui/component/card_list.dart';
import 'package:chameleonultragui/gui/menu/slot_settings.dart';
import 'package:chameleonultragui/helpers/general.dart';
import 'package:chameleonultragui/helpers/mifare_classic/general.dart';
+import 'package:chameleonultragui/helpers/mifare_ultralight/general.dart';
import 'package:chameleonultragui/main.dart';
import 'package:chameleonultragui/sharedprefsprovider.dart';
import 'package:flutter/material.dart';
@@ -207,6 +208,52 @@ class SlotManagerPageState extends State {
await appState.communicator!.saveSlotData();
appState.changesMade();
refreshSlot(gridPosition);
+ } else if (isMifareUltralight(card.tag)) {
+ close(context, card.name);
+ setUploadState(0);
+
+ await appState.communicator!.setReaderDeviceMode(false);
+ await appState.communicator!
+ .enableSlot(gridPosition, TagFrequency.hf, true);
+ await appState.communicator!.activateSlot(gridPosition);
+ await appState.communicator!.setSlotType(gridPosition, card.tag);
+ await appState.communicator!.setDefaultDataToSlot(gridPosition, card.tag);
+ var cardData = CardData(
+ uid: hexToBytes(card.uid),
+ atqa: card.atqa,
+ sak: card.sak,
+ ats: card.ats);
+ await appState.communicator!.setMf1AntiCollision(cardData);
+
+ for (var page = 0; page < mfUltralightGetPagesCount(card.tag); page++) {
+ await appState.communicator!
+ .mf0EmulatorWritePages(page, card.data[page]);
+
+ setUploadState(
+ (page / mfUltralightGetPagesCount(card.tag) * 100).round());
+
+ await asyncSleep(1);
+ }
+
+ if (card.extraData.ultralightVersion.isNotEmpty) {
+ await appState.communicator!
+ .mf0EmulatorSetVersionData(card.extraData.ultralightVersion);
+ }
+
+ if (card.extraData.ultralightSignature.isNotEmpty) {
+ await appState.communicator!
+ .mf0EmulatorSetSignatureData(card.extraData.ultralightSignature);
+ }
+
+ setUploadState(100);
+
+ await appState.communicator!.setSlotTagName(
+ gridPosition,
+ (card.name.isEmpty) ? localizations.no_name : card.name,
+ TagFrequency.hf);
+ await appState.communicator!.saveSlotData();
+ appState.changesMade();
+ refreshSlot(gridPosition);
} else {
appState.log!.e("Can't write this card type yet.");
close(context, card.name);
diff --git a/chameleonultragui/lib/gui/page/write_card.dart b/chameleonultragui/lib/gui/page/write_card.dart
index 6d29d97d..fd106e72 100644
--- a/chameleonultragui/lib/gui/page/write_card.dart
+++ b/chameleonultragui/lib/gui/page/write_card.dart
@@ -280,6 +280,12 @@ class WriteCardPageState extends State {
@override
Widget build(BuildContext context) {
var localizations = AppLocalizations.of(context)!;
+ var typeLocalization = {
+ 'gen1': localizations.gen1,
+ 'gen2': localizations.gen2,
+ 'gen3': localizations.gen3,
+ 't55xx': localizations.t55xx,
+ };
return Scaffold(
appBar: AppBar(
@@ -345,7 +351,8 @@ class WriteCardPageState extends State {
(AbstractWriteHelper helperClass) {
return DropdownMenuItem(
value: helperClass,
- child: Text(helperClass.name),
+ child:
+ Text(typeLocalization[helperClass.name]!),
);
}).toList(),
onChanged: (AbstractWriteHelper? helperClass) {
diff --git a/chameleonultragui/lib/helpers/general.dart b/chameleonultragui/lib/helpers/general.dart
index b3be87f6..a7360300 100644
--- a/chameleonultragui/lib/helpers/general.dart
+++ b/chameleonultragui/lib/helpers/general.dart
@@ -3,6 +3,7 @@ import 'dart:io';
import 'dart:typed_data';
import 'package:chameleonultragui/bridge/chameleon.dart';
import 'package:chameleonultragui/connector/serial_abstract.dart';
+import 'package:chameleonultragui/helpers/mifare_classic/general.dart';
import 'package:chameleonultragui/main.dart';
import 'package:chameleonultragui/sharedprefsprovider.dart';
import 'package:file_picker/file_picker.dart';
@@ -117,17 +118,39 @@ String chameleonTagToString(TagType tag) {
return "Mifare Classic 4K";
} else if (tag == TagType.em410X) {
return "EM410X";
+ } else if (tag == TagType.ntag210) {
+ return "NTAG210";
+ } else if (tag == TagType.ntag212) {
+ return "NTAG212";
} else if (tag == TagType.ntag213) {
return "NTAG213";
} else if (tag == TagType.ntag215) {
return "NTAG215";
} else if (tag == TagType.ntag216) {
return "NTAG216";
+ } else if (tag == TagType.ultralight) {
+ return "Ultralight";
+ } else if (tag == TagType.ultralightC) {
+ return "Ultralight C";
+ } else if (tag == TagType.ultralight11) {
+ return "Ultralight EV1 (20)";
+ } else if (tag == TagType.ultralight21) {
+ return "Ultralight EV1 (41)";
} else {
return "Unknown";
}
}
+String chameleonCardToString(CardSave card) {
+ String name = chameleonTagToString(card.tag);
+
+ if (chameleonTagSaveCheckForMifareClassicEV1(card)) {
+ name += " EV1";
+ }
+
+ return name;
+}
+
TagType numberToChameleonTag(int type) {
if (type == TagType.mifareMini.value) {
return TagType.mifareMini;
@@ -139,17 +162,49 @@ TagType numberToChameleonTag(int type) {
return TagType.mifare4K;
} else if (type == TagType.em410X.value) {
return TagType.em410X;
+ } else if (type == TagType.ntag210.value) {
+ return TagType.ntag210;
+ } else if (type == TagType.ntag212.value) {
+ return TagType.ntag212;
} else if (type == TagType.ntag213.value) {
return TagType.ntag213;
} else if (type == TagType.ntag215.value) {
return TagType.ntag215;
} else if (type == TagType.ntag216.value) {
return TagType.ntag216;
+ } else if (type == TagType.ultralight.value) {
+ return TagType.ultralight;
+ } else if (type == TagType.ultralight11.value) {
+ return TagType.ultralight11;
+ } else if (type == TagType.ultralight21.value) {
+ return TagType.ultralight21;
+ } else if (type == TagType.ultralightC.value) {
+ return TagType.ultralightC;
} else {
return TagType.unknown;
}
}
+List getTagTypes() {
+ return [
+ TagType.mifare1K,
+ TagType.mifare2K,
+ TagType.mifare4K,
+ TagType.mifareMini,
+ TagType.em410X,
+ TagType.ultralight,
+ TagType.ultralightC,
+ TagType.ultralight11,
+ TagType.ultralight21,
+ TagType.ntag210,
+ TagType.ntag212,
+ TagType.ntag213,
+ TagType.ntag215,
+ TagType.ntag216,
+ TagType.unknown
+ ];
+}
+
TagType getTagTypeByValue(int value) {
return TagType.values.firstWhere((element) => element.value == value,
orElse: () => TagType.unknown);
@@ -303,9 +358,15 @@ List getTagTypeByFrequency(TagFrequency frequency) {
TagType.mifare2K,
TagType.mifare4K,
TagType.mifareMini,
+ TagType.ntag210,
+ TagType.ntag212,
TagType.ntag213,
TagType.ntag215,
TagType.ntag216,
+ TagType.ultralight,
+ TagType.ultralightC,
+ TagType.ultralight11,
+ TagType.ultralight21
];
} else if (frequency == TagFrequency.lf) {
return [TagType.em410X];
diff --git a/chameleonultragui/lib/helpers/mifare_classic/write/base.dart b/chameleonultragui/lib/helpers/mifare_classic/write/base.dart
index ba94827b..32cd4cd4 100644
--- a/chameleonultragui/lib/helpers/mifare_classic/write/base.dart
+++ b/chameleonultragui/lib/helpers/mifare_classic/write/base.dart
@@ -1,7 +1,7 @@
import 'dart:typed_data';
import 'package:chameleonultragui/bridge/chameleon.dart';
-import 'package:chameleonultragui/gui/component/card_recovery.dart';
+import 'package:chameleonultragui/gui/component/mifare/classic.dart';
import 'package:chameleonultragui/gui/page/read_card.dart';
import 'package:chameleonultragui/helpers/general.dart';
import 'package:chameleonultragui/helpers/mifare_classic/general.dart';
@@ -18,7 +18,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
-class BaseMifareClassicMagicCardHelper extends AbstractWriteHelper {
+class BaseMifareClassicWriteHelper extends AbstractWriteHelper {
late MifareClassicRecovery recovery;
late MifareClassicType type;
late bool isEV1;
@@ -29,7 +29,7 @@ class BaseMifareClassicMagicCardHelper extends AbstractWriteHelper {
@override
bool get autoDetect => true;
- BaseMifareClassicMagicCardHelper(super.communicator,
+ BaseMifareClassicWriteHelper(super.communicator,
{required this.recovery,
this.type = MifareClassicType.m1k,
this.isEV1 = false});
@@ -92,9 +92,16 @@ class BaseMifareClassicMagicCardHelper extends AbstractWriteHelper {
}
@override
- Future writeData(CardSave card, dynamic update) async {
+ Future writeData(
+ CardSave card, Function(int writeProgress) update) async {
List data = card.data;
+ try {
+ await communicator.scan14443aTag();
+ } catch (e) {
+ return false;
+ }
+
if (data.isEmpty || data[0].isEmpty) {
if (data.isEmpty) {
data = [Uint8List(0)];
@@ -215,6 +222,12 @@ class BaseMifareClassicMagicCardHelper extends AbstractWriteHelper {
setState(() {
mfcInfo!.recovery = getExtraData()[0];
});
+ } else {
+ setState(() {
+ hfInfo!.cardExist = false;
+ });
+
+ return;
}
setState(() {
@@ -250,7 +263,7 @@ class BaseMifareClassicMagicCardHelper extends AbstractWriteHelper {
future: (hfInfo != null) ? Future.value([]) : prepareMifareClassic(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (hfInfo != null && mfcInfo != null && mfcInfo!.recovery != null) {
- return CardRecovery(
+ return MifareClassicHelper(
hfInfo: hfInfo!, mfcInfo: mfcInfo!, allowSave: false);
} else if (hfInfo != null &&
mfcInfo != null &&
diff --git a/chameleonultragui/lib/helpers/mifare_classic/write/gen1.dart b/chameleonultragui/lib/helpers/mifare_classic/write/gen1.dart
index 941a1579..08b890d5 100644
--- a/chameleonultragui/lib/helpers/mifare_classic/write/gen1.dart
+++ b/chameleonultragui/lib/helpers/mifare_classic/write/gen1.dart
@@ -2,13 +2,13 @@ import 'dart:typed_data';
import 'package:chameleonultragui/helpers/mifare_classic/write/base.dart';
-class MifareClassicGen1WriteHelper extends BaseMifareClassicMagicCardHelper {
+class MifareClassicGen1WriteHelper extends BaseMifareClassicWriteHelper {
MifareClassicGen1WriteHelper(super.communicator, {required super.recovery});
@override
- String get name => "Gen1";
+ String get name => "gen1";
- static String get staticName => "Gen1";
+ static String get staticName => "gen1";
@override
Future isMagic(dynamic data) async {
diff --git a/chameleonultragui/lib/helpers/mifare_classic/write/gen2.dart b/chameleonultragui/lib/helpers/mifare_classic/write/gen2.dart
index 5361b259..2a2a75fb 100644
--- a/chameleonultragui/lib/helpers/mifare_classic/write/gen2.dart
+++ b/chameleonultragui/lib/helpers/mifare_classic/write/gen2.dart
@@ -6,14 +6,14 @@ import 'package:chameleonultragui/helpers/mifare_classic/recovery.dart';
import 'package:chameleonultragui/helpers/mifare_classic/write/base.dart';
import 'package:chameleonultragui/sharedprefsprovider.dart';
-class MifareClassicGen2WriteHelper extends BaseMifareClassicMagicCardHelper {
+class MifareClassicGen2WriteHelper extends BaseMifareClassicWriteHelper {
List failedBlocks = [];
MifareClassicGen2WriteHelper(super.communicator, {required super.recovery});
@override
- String get name => "Gen2 / Generic";
+ String get name => "gen2";
- static String get staticName => "Gen2 / Generic";
+ static String get staticName => "gen2";
@override
Future isMagic(dynamic data) async {
@@ -99,11 +99,18 @@ class MifareClassicGen2WriteHelper extends BaseMifareClassicMagicCardHelper {
}
@override
- Future writeData(CardSave card, dynamic update) async {
+ Future writeData(
+ CardSave card, Function(int writeProgress) update) async {
List data = card.data;
List cleanSectors = List.generate(40, (index) => false);
failedBlocks = [];
+ try {
+ await communicator.scan14443aTag();
+ } catch (e) {
+ return false;
+ }
+
if (data.isEmpty || data[0].isEmpty) {
if (data.isEmpty) {
data = [Uint8List(0)];
diff --git a/chameleonultragui/lib/helpers/mifare_classic/write/gen3.dart b/chameleonultragui/lib/helpers/mifare_classic/write/gen3.dart
index 2931ba15..e1d585bc 100644
--- a/chameleonultragui/lib/helpers/mifare_classic/write/gen3.dart
+++ b/chameleonultragui/lib/helpers/mifare_classic/write/gen3.dart
@@ -11,9 +11,9 @@ class MifareClassicGen3WriteHelper extends MifareClassicGen2WriteHelper {
MifareClassicGen3WriteHelper(super.communicator, {required super.recovery});
@override
- String get name => "Gen3";
+ String get name => "gen3";
- static String get staticName => "Gen3";
+ static String get staticName => "gen3";
@override
Future isMagic(dynamic data) async {
diff --git a/chameleonultragui/lib/helpers/mifare_ultralight/general.dart b/chameleonultragui/lib/helpers/mifare_ultralight/general.dart
new file mode 100644
index 00000000..416e2252
--- /dev/null
+++ b/chameleonultragui/lib/helpers/mifare_ultralight/general.dart
@@ -0,0 +1,100 @@
+import 'package:chameleonultragui/bridge/chameleon.dart';
+import 'package:chameleonultragui/helpers/general.dart';
+import 'package:flutter/services.dart';
+
+bool isMifareUltralight(TagType type) {
+ return [
+ TagType.ntag210,
+ TagType.ntag212,
+ TagType.ntag213,
+ TagType.ntag215,
+ TagType.ntag216,
+ TagType.ultralight,
+ TagType.ultralightC,
+ TagType.ultralight11,
+ TagType.ultralight21
+ ].contains(type);
+}
+
+Future mfUltralightGetVersion(
+ ChameleonCommunicator communicator) async {
+ return await communicator.send14ARaw(Uint8List.fromList([0x60]));
+}
+
+Future mfUltralightGetSignature(
+ ChameleonCommunicator communicator) async {
+ Uint8List signature =
+ await communicator.send14ARaw(Uint8List.fromList([0x3C, 0x00]));
+ if (bytesToHex(signature) == bytesToHex(Uint8List(32))) {
+ return Uint8List(0);
+ }
+ return signature;
+}
+
+TagType mfUltralightGetType(Uint8List version) {
+ // TODO: detect ultralightC/ntag210/ntag212
+ if (version[6] == 0x0B || version[6] == 0x00) {
+ return TagType.ultralight11;
+ } else if (version[6] == 0x0E) {
+ return TagType.ultralight21;
+ } else if (version[6] == 0x0F) {
+ return TagType.ntag213;
+ } else if (version[6] == 0x11) {
+ return TagType.ntag215;
+ } else if (version[6] == 0x13) {
+ return TagType.ntag216;
+ }
+
+ return TagType.ultralight;
+}
+
+// https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf
+// https://www.nxp.com/docs/en/data-sheet/MF0ULX1.pdf
+// https://www.nxp.com/docs/en/data-sheet/NTAG210_212.pdf
+// https://www.nxp.com/docs/en/data-sheet/NTAG213_215_216.pdf
+
+int mfUltralightGetPagesCount(TagType type) {
+ if (type == TagType.ultralight) {
+ return 16;
+ } else if (type == TagType.ultralightC) {
+ return 48;
+ } else if (type == TagType.ultralight11) {
+ return 20;
+ } else if (type == TagType.ultralight21) {
+ return 41;
+ } else if (type == TagType.ntag210) {
+ return 20;
+ } else if (type == TagType.ntag212) {
+ return 41;
+ } else if (type == TagType.ntag213) {
+ return 45;
+ } else if (type == TagType.ntag215) {
+ return 135;
+ } else if (type == TagType.ntag216) {
+ return 231;
+ }
+ return 0;
+}
+
+int mfUltralightGetPasswordPage(TagType type) {
+ if (type == TagType.ultralight) {
+ return 0; // Don't have password support
+ } else if (type == TagType.ultralightC) {
+ return 0; // 44 to 47, custom logic required for Ultralight C
+ } else if (type == TagType.ultralight11) {
+ return 18;
+ } else if (type == TagType.ultralight21) {
+ return 39;
+ } else if (type == TagType.ntag210) {
+ return 18;
+ } else if (type == TagType.ntag212) {
+ return 39;
+ } else if (type == TagType.ntag213) {
+ return 43;
+ } else if (type == TagType.ntag215) {
+ return 133;
+ } else if (type == TagType.ntag216) {
+ return 229;
+ }
+ return 0;
+}
diff --git a/chameleonultragui/lib/helpers/mifare_ultralight/write/base.dart b/chameleonultragui/lib/helpers/mifare_ultralight/write/base.dart
new file mode 100644
index 00000000..74cf7e7d
--- /dev/null
+++ b/chameleonultragui/lib/helpers/mifare_ultralight/write/base.dart
@@ -0,0 +1,173 @@
+import 'dart:typed_data';
+
+import 'package:chameleonultragui/gui/page/read_card.dart';
+import 'package:chameleonultragui/helpers/general.dart';
+import 'package:chameleonultragui/helpers/write.dart';
+import 'package:chameleonultragui/sharedprefsprovider.dart';
+import 'package:flutter/material.dart';
+
+// Localizations
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+
+class BaseMifareUltralightWriteHelper extends AbstractWriteHelper {
+ HFCardInfo? hfInfo;
+ List failedBlocks = [];
+
+ @override
+ bool get autoDetect => false;
+
+ @override
+ String get name => "gen2";
+
+ static String get staticName => "gen2";
+ TextEditingController keyController = TextEditingController();
+ String? key;
+
+ BaseMifareUltralightWriteHelper(super.communicator);
+
+ @override
+ List getAvailableMethods() {
+ return [
+ BaseMifareUltralightWriteHelper(communicator),
+ ];
+ }
+
+ @override
+ List getAvailableMethodsByPriority() {
+ return [BaseMifareUltralightWriteHelper(communicator)];
+ }
+
+ @override
+ Widget getWriteWidget(BuildContext context, setState) {
+ var localizations = AppLocalizations.of(context)!;
+ final GlobalKey formKey = GlobalKey();
+
+ return Row(children: [
+ Form(
+ key: formKey,
+ autovalidateMode: AutovalidateMode.onUserInteraction,
+ child: Expanded(
+ child: Column(
+ children: [
+ TextFormField(
+ controller: keyController,
+ decoration: InputDecoration(
+ labelText: localizations.key,
+ hintMaxLines: 4,
+ hintText: localizations
+ .enter_something(localizations.ultralight_key_prompt)),
+ validator: (String? value) {
+ if (value!.isNotEmpty && !isValidHexString(value)) {
+ return localizations.must_be_valid_hex;
+ }
+
+ if (value.length != 8) {
+ return localizations.must_be(4, localizations.key);
+ }
+
+ return null;
+ },
+ )
+ ],
+ ))),
+ TextButton(
+ onPressed: () => {
+ setState(() {
+ key = keyController.text;
+ })
+ },
+ child: Text(localizations.next),
+ ),
+ TextButton(
+ onPressed: () => {
+ setState(() {
+ key = "";
+ })
+ },
+ child: Text(localizations.no_key),
+ )
+ ]);
+ }
+
+ @override
+ Future isCompatible(CardSave card) async {
+ return true;
+ }
+
+ @override
+ Future isMagic(data) async {
+ return false;
+ }
+
+ @override
+ bool isReady() {
+ return key != null;
+ }
+
+ @override
+ bool writeWidgetSupported() {
+ return true;
+ }
+
+ @override
+ Future reset() async {
+ failedBlocks = [];
+ key = null;
+ }
+
+ @override
+ Future writeData(
+ CardSave card, Function(int writeProgress) update) async {
+ int totalBlocks = card.data.length;
+
+ if (!await communicator.isReaderDeviceMode()) {
+ await communicator.setReaderDeviceMode(true);
+ }
+
+ try {
+ await communicator.scan14443aTag();
+ } catch (e) {
+ return false;
+ }
+
+ if (key!.isNotEmpty) {
+ Uint8List pack = await communicator.send14ARaw(
+ Uint8List.fromList([0x1B, ...hexToBytes(key!)]),
+ keepRfField: true);
+ if (pack.length < 2) {
+ return false;
+ }
+ }
+
+ for (var block = 0; block < totalBlocks; block++) {
+ Uint8List write = await communicator.send14ARaw(
+ Uint8List.fromList([0xA2, block, ...card.data[block]]),
+ keepRfField: true,
+ checkResponseCrc: false,
+ autoSelect: block == 0 || block == 3);
+ if (write.isEmpty || write[0] != 0x0A || block == 2) {
+ await communicator.send14ARaw(Uint8List(1)); // reset
+
+ if (key!.isNotEmpty) {
+ await communicator.send14ARaw(
+ Uint8List.fromList([0x1B, ...hexToBytes(key!)]),
+ keepRfField: true);
+ }
+
+ if (block > 2) {
+ // block is not UID
+ failedBlocks.add(block);
+ }
+ }
+
+ update((block / totalBlocks * 100).round());
+ }
+
+ return failedBlocks.isEmpty;
+ }
+
+ @override
+ List getFailedBlocks() {
+ return failedBlocks;
+ }
+}
diff --git a/chameleonultragui/lib/helpers/ntag/general.dart b/chameleonultragui/lib/helpers/ntag/general.dart
deleted file mode 100644
index f0cb5c5a..00000000
--- a/chameleonultragui/lib/helpers/ntag/general.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-import 'package:chameleonultragui/bridge/chameleon.dart';
-
-bool isNTAG(TagType type) {
- return [TagType.ntag213, TagType.ntag215, TagType.ntag216].contains(type);
-}
diff --git a/chameleonultragui/lib/helpers/t55xx/write/base.dart b/chameleonultragui/lib/helpers/t55xx/write/base.dart
index 98be8207..e8b937a8 100644
--- a/chameleonultragui/lib/helpers/t55xx/write/base.dart
+++ b/chameleonultragui/lib/helpers/t55xx/write/base.dart
@@ -14,9 +14,9 @@ class BaseT55XXCardHelper extends AbstractWriteHelper {
bool get autoDetect => true;
@override
- String get name => "T55XX";
+ String get name => "t55xx";
- static String get staticName => "T55XX";
+ static String get staticName => "t55xx";
TextEditingController newKeyController = TextEditingController();
TextEditingController currentKeyController = TextEditingController();
String currentKey = "";
@@ -157,7 +157,8 @@ class BaseT55XXCardHelper extends AbstractWriteHelper {
}
@override
- Future writeData(CardSave card, update) async {
+ Future writeData(
+ CardSave card, Function(int writeProgress) update) async {
await communicator.writeEM410XtoT55XX(
hexToBytes(card.uid), hexToBytes(newKey), [hexToBytes(currentKey)]);
var newCard = await communicator.readEM410X();
diff --git a/chameleonultragui/lib/helpers/write.dart b/chameleonultragui/lib/helpers/write.dart
index 1a242aa4..b07f0e9a 100644
--- a/chameleonultragui/lib/helpers/write.dart
+++ b/chameleonultragui/lib/helpers/write.dart
@@ -2,6 +2,8 @@ import 'package:chameleonultragui/bridge/chameleon.dart';
import 'package:chameleonultragui/helpers/mifare_classic/general.dart';
import 'package:chameleonultragui/helpers/mifare_classic/recovery.dart';
import 'package:chameleonultragui/helpers/mifare_classic/write/base.dart';
+import 'package:chameleonultragui/helpers/mifare_ultralight/general.dart';
+import 'package:chameleonultragui/helpers/mifare_ultralight/write/base.dart';
import 'package:chameleonultragui/helpers/t55xx/write/base.dart';
import 'package:chameleonultragui/main.dart';
import 'package:chameleonultragui/sharedprefsprovider.dart';
@@ -41,11 +43,15 @@ abstract class AbstractWriteHelper {
static AbstractWriteHelper? getClassByCardType(
TagType type, ChameleonGUIState appState, void Function() update) {
- if (chameleonTagTypeGetMfClassicType(type) != MifareClassicType.none) {
- return BaseMifareClassicMagicCardHelper(appState.communicator!,
+ if (isMifareClassic(type)) {
+ return BaseMifareClassicWriteHelper(appState.communicator!,
recovery: MifareClassicRecovery(appState: appState, update: update));
}
+ if (isMifareUltralight(type)) {
+ return BaseMifareUltralightWriteHelper(appState.communicator!);
+ }
+
if (type == TagType.em410X) {
return BaseT55XXCardHelper(appState.communicator!);
}
@@ -53,7 +59,7 @@ abstract class AbstractWriteHelper {
return null; // writing is not supported
}
- Future writeData(CardSave card, dynamic update);
+ Future writeData(CardSave card, Function(int writeProgress) update);
Widget getWriteWidget(BuildContext context, dynamic setState);
@@ -66,5 +72,6 @@ abstract class AbstractWriteHelper {
}
@override
- bool operator ==(dynamic other) => other != null && name == other.name;
+ bool operator ==(Object other) =>
+ other is AbstractWriteHelper && name == other.name;
}
diff --git a/chameleonultragui/lib/l10n/app_en.arb b/chameleonultragui/lib/l10n/app_en.arb
index ca5d6677..5691c2ba 100644
--- a/chameleonultragui/lib/l10n/app_en.arb
+++ b/chameleonultragui/lib/l10n/app_en.arb
@@ -281,5 +281,16 @@
"key": "Key",
"t55xx_key_prompt": "current T55XX key. Default CU key is 20206666",
"t55xx_new_key_prompt": "new T55XX key if you want to change it",
- "new_key": "New key"
+ "new_key": "New key",
+ "ultralight_key_prompt": "Ultralight key (HEX, 4 bytes)",
+ "read_with_key": "Read with key",
+ "read_without_key": "Read without key",
+ "invalid_password": "Invalid password",
+ "ultralight_version": "Ultralight version",
+ "ultralight_signature": "Ultralight signature",
+ "no_key": "No key",
+ "gen1": "Gen1",
+ "gen2": "Gen2 / Generic",
+ "gen3": "Gen3",
+ "t55xx": "T55XX"
}
\ No newline at end of file
diff --git a/chameleonultragui/lib/recovery/bindings.dart b/chameleonultragui/lib/recovery/bindings.dart
index 0ae8e442..42689d43 100644
--- a/chameleonultragui/lib/recovery/bindings.dart
+++ b/chameleonultragui/lib/recovery/bindings.dart
@@ -30,7 +30,7 @@ class Recovery {
ffi.Pointer darkside(
ffi.Pointer data,
- ffi.Pointer keyCount,
+ ffi.Pointer keyCount,
) {
return _darkside(
data,
@@ -41,10 +41,10 @@ class Recovery {
late final _darksidePtr = _lookup<
ffi.NativeFunction<
ffi.Pointer Function(
- ffi.Pointer, ffi.Pointer)>>('darkside');
+ ffi.Pointer, ffi.Pointer)>>('darkside');
late final _darkside = _darksidePtr.asFunction<
ffi.Pointer Function(
- ffi.Pointer, ffi.Pointer)>();
+ ffi.Pointer, ffi.Pointer)>();
ffi.Pointer nested(
ffi.Pointer data,
diff --git a/chameleonultragui/lib/recovery/recovery.dart b/chameleonultragui/lib/recovery/recovery.dart
index adf59b19..565aa3d0 100644
--- a/chameleonultragui/lib/recovery/recovery.dart
+++ b/chameleonultragui/lib/recovery/recovery.dart
@@ -254,7 +254,7 @@ Future _helperIsolateSendPort = () async {
pointer.ref.items = itemPointer;
pointer.ref.count = i;
- Pointer count = calloc();
+ Pointer count = calloc();
count.value = 0;
List keys = [];
final Pointer result = _bindings.darkside(pointer, count);
diff --git a/chameleonultragui/lib/sharedprefsprovider.dart b/chameleonultragui/lib/sharedprefsprovider.dart
index 6ec88e3a..cf781d1c 100644
--- a/chameleonultragui/lib/sharedprefsprovider.dart
+++ b/chameleonultragui/lib/sharedprefsprovider.dart
@@ -75,6 +75,7 @@ class CardSave {
String name;
TagType tag;
List data;
+ CardSaveExtra extraData;
Color color;
factory CardSave.fromJson(String json) {
@@ -86,6 +87,7 @@ class CardSave {
final ats = List.from((data['ats'] ?? []) as List);
final name = data['name'] as String;
final tag = getTagTypeByValue(data['tag']);
+ final extraData = CardSaveExtra.import(data['extra'] ?? {});
final color =
data['color'] == null ? Colors.deepOrange : hexToColor(data['color']);
List tagData = (data['data'] as List)
@@ -100,6 +102,7 @@ class CardSave {
tag: tag,
data: tagData,
color: color,
+ extraData: extraData,
ats: Uint8List.fromList(ats),
atqa: Uint8List.fromList(atqa));
}
@@ -115,23 +118,63 @@ class CardSave {
'tag': tag.value,
'color': colorToHex(color),
'data': data.map((data) => data.toList()).toList(),
+ 'extra': extraData.export(),
});
}
- CardSave(
- {String? id,
- required this.uid,
- required this.name,
- required this.tag,
- int? sak,
- Uint8List? atqa,
- Uint8List? ats,
- this.color = Colors.deepOrange,
- this.data = const []})
- : id = id ?? const Uuid().v4(),
+ CardSave({
+ String? id,
+ required this.uid,
+ required this.name,
+ required this.tag,
+ int? sak,
+ Uint8List? atqa,
+ Uint8List? ats,
+ CardSaveExtra? extraData,
+ this.color = Colors.deepOrange,
+ this.data = const [],
+ }) : id = id ?? const Uuid().v4(),
sak = sak ?? 0,
atqa = atqa ?? Uint8List(0),
- ats = ats ?? Uint8List(0);
+ ats = ats ?? Uint8List(0),
+ extraData = extraData ?? CardSaveExtra();
+}
+
+class CardSaveExtra {
+ Uint8List ultralightSignature;
+ Uint8List ultralightVersion;
+
+ factory CardSaveExtra.import(Map data) {
+ List readBytes(Map data, String key) {
+ return List.from(
+ data[key] != null ? data[key] as List : []);
+ }
+
+ final ultralightSignature = readBytes(data, 'ultralightSignature');
+ final ultralightVersion = readBytes(data, 'ultralightVersion');
+
+ return CardSaveExtra(
+ ultralightSignature: Uint8List.fromList(ultralightSignature),
+ ultralightVersion: Uint8List.fromList(ultralightVersion));
+ }
+
+ Map export() {
+ Map json = {};
+
+ if (ultralightSignature.isNotEmpty) {
+ json['ultralightSignature'] = ultralightSignature;
+ }
+
+ if (ultralightVersion.isNotEmpty) {
+ json['ultralightVersion'] = ultralightVersion;
+ }
+
+ return json;
+ }
+
+ CardSaveExtra({Uint8List? ultralightSignature, Uint8List? ultralightVersion})
+ : ultralightSignature = ultralightSignature ?? Uint8List(0),
+ ultralightVersion = ultralightVersion ?? Uint8List(0);
}
class SharedPreferencesProvider extends ChangeNotifier {
diff --git a/chameleonultragui/macos/Podfile.lock b/chameleonultragui/macos/Podfile.lock
new file mode 100644
index 00000000..82a6aa0e
--- /dev/null
+++ b/chameleonultragui/macos/Podfile.lock
@@ -0,0 +1,79 @@
+PODS:
+ - file_saver (0.0.1):
+ - FlutterMacOS
+ - flutter_libserialport (0.0.1):
+ - FlutterMacOS
+ - libserialport
+ - FlutterMacOS (1.0.0)
+ - libserialport (0.1.1)
+ - mobile_scanner (5.1.1):
+ - FlutterMacOS
+ - package_info_plus (0.0.1):
+ - FlutterMacOS
+ - path_provider_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+ - recovery (0.0.1):
+ - FlutterMacOS
+ - shared_preferences_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+ - url_launcher_macos (0.0.1):
+ - FlutterMacOS
+ - wakelock_plus (0.0.1):
+ - FlutterMacOS
+
+DEPENDENCIES:
+ - file_saver (from `Flutter/ephemeral/.symlinks/plugins/file_saver/macos`)
+ - flutter_libserialport (from `Flutter/ephemeral/.symlinks/plugins/flutter_libserialport/macos`)
+ - FlutterMacOS (from `Flutter/ephemeral`)
+ - mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos`)
+ - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
+ - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
+ - recovery (from `./recovery.podspec`)
+ - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
+ - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
+ - wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`)
+
+SPEC REPOS:
+ trunk:
+ - libserialport
+
+EXTERNAL SOURCES:
+ file_saver:
+ :path: Flutter/ephemeral/.symlinks/plugins/file_saver/macos
+ flutter_libserialport:
+ :path: Flutter/ephemeral/.symlinks/plugins/flutter_libserialport/macos
+ FlutterMacOS:
+ :path: Flutter/ephemeral
+ mobile_scanner:
+ :path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos
+ package_info_plus:
+ :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
+ path_provider_foundation:
+ :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
+ recovery:
+ :path: "./recovery.podspec"
+ shared_preferences_foundation:
+ :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
+ url_launcher_macos:
+ :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
+ wakelock_plus:
+ :path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos
+
+SPEC CHECKSUMS:
+ file_saver: 44e6fbf666677faf097302460e214e977fdd977b
+ flutter_libserialport: d1a505fd4b05785f7b50cc7c93b361937ea1c5bd
+ FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
+ libserialport: 1cb25e66ef3c92a8e59c2ea3820302c3fa2268cd
+ mobile_scanner: 1efac1e53c294b24e3bb55bcc7f4deee0233a86b
+ package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
+ path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
+ recovery: 3dd1b9b85ac8e7f017ef8f6a3caea297d8559924
+ shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
+ url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399
+ wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269
+
+PODFILE CHECKSUM: 58eeb33b8e458bd5d682f8fe36f300c258f4d19a
+
+COCOAPODS: 1.15.2
diff --git a/chameleonultragui/macos/Runner.xcodeproj/project.pbxproj b/chameleonultragui/macos/Runner.xcodeproj/project.pbxproj
index 4a4a6a24..b708c77e 100644
--- a/chameleonultragui/macos/Runner.xcodeproj/project.pbxproj
+++ b/chameleonultragui/macos/Runner.xcodeproj/project.pbxproj
@@ -203,7 +203,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
- LastUpgradeCheck = 1430;
+ LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
33CC10EC2044A3C60003C045 = {
diff --git a/chameleonultragui/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/chameleonultragui/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index 2331cca6..e2803976 100644
--- a/chameleonultragui/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/chameleonultragui/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
3.0.0'
@@ -33,7 +33,10 @@ dependencies:
path:
async:
- flutter_libserialport: ^0.4.0
+ flutter_libserialport:
+ git:
+ url: https://github.com/snabble/flutter_libserialport.git # FIXME: When https://github.com/jpnurmi/flutter_libserialport/pull/109 merged, switch back to proper package
+ ref: 0.5.0
provider: ^6.0.5
logger: ^2.0.1
convert: ^3.1.1
diff --git a/chameleonultragui/src/crapto1.c b/chameleonultragui/src/crapto1.c
index 70195e07..6603be40 100644
--- a/chameleonultragui/src/crapto1.c
+++ b/chameleonultragui/src/crapto1.c
@@ -340,7 +340,9 @@ uint8_t lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb)
uint32_t t;
s->odd &= 0xffffff;
- t = s->odd, s->odd = s->even, s->even = t;
+ t = s->odd;
+ s->odd = s->even;
+ s->even = t;
out = s->even & 1;
out ^= LF_POLY_EVEN & (s->even >>= 1);
diff --git a/chameleonultragui/src/mfkey.c b/chameleonultragui/src/mfkey.c
index b04cf310..6b7fc857 100644
--- a/chameleonultragui/src/mfkey.c
+++ b/chameleonultragui/src/mfkey.c
@@ -23,7 +23,7 @@ int compare_uint64(const void *a, const void *b)
}
// create the intersection (common members) of two sorted lists. Lists are terminated by -1. Result will be in list1. Number of elements is returned.
-uint32_t intersection(uint64_t *listA, uint64_t *listB)
+uint64_t intersection(uint64_t *listA, uint64_t *listB)
{
if (listA == NULL || listB == NULL)
return 0;
diff --git a/chameleonultragui/src/mfkey.h b/chameleonultragui/src/mfkey.h
index 4f974fe6..773728d0 100644
--- a/chameleonultragui/src/mfkey.h
+++ b/chameleonultragui/src/mfkey.h
@@ -18,6 +18,6 @@
uint32_t nonce2key(uint32_t uid, uint32_t nt, uint32_t nr, uint32_t ar, uint64_t par_info, uint64_t ks_info, uint64_t **keys);
int compare_uint64(const void *a, const void *b);
-uint32_t intersection(uint64_t *listA, uint64_t *listB);
+uint64_t intersection(uint64_t *listA, uint64_t *listB);
#endif
diff --git a/chameleonultragui/src/recovery.c b/chameleonultragui/src/recovery.c
index 0a2a5001..817e3a1e 100644
--- a/chameleonultragui/src/recovery.c
+++ b/chameleonultragui/src/recovery.c
@@ -49,11 +49,11 @@ typedef struct
uint32_t endPos;
} RecPar;
-FFI_PLUGIN_EXPORT uint64_t *darkside(Darkside *data, uint32_t *outputKeyCount)
+FFI_PLUGIN_EXPORT uint64_t *darkside(Darkside *data, uint64_t *outputKeyCount)
{
uint32_t uid = data->uid;
uint32_t count = 0, i = 0;
- uint32_t keycount = 0;
+ uint64_t keycount = 0;
uint64_t *keylist = NULL, *last_keylist = NULL;
DarksideParam *dps = calloc(1, sizeof(DarksideParam) * data->count);
uint64_t *keys = malloc(sizeof(uint64_t) * 256);
@@ -95,7 +95,7 @@ FFI_PLUGIN_EXPORT uint64_t *darkside(Darkside *data, uint32_t *outputKeyCount)
continue;
}
}
- uint8_t key_tmp[6] = {0};
+
if (keycount > 0)
{
no_key_recover = false;
@@ -261,7 +261,6 @@ static void nested_recover(RecPar *rp)
uint64_t *nested_run(NtpKs1 *pNK, uint32_t sizePNK, uint32_t authuid, uint32_t *keyCount, uint32_t *outputKeyCount)
{
*keyCount = 0;
- uint32_t i;
RecPar *pRPs = malloc(sizeof(RecPar));
if (pRPs == NULL)
@@ -364,9 +363,9 @@ FFI_PLUGIN_EXPORT uint64_t *nested(Nested *data, uint32_t *outputKeyCount)
FFI_PLUGIN_EXPORT uint64_t *static_nested(StaticNested *data, uint32_t *outputKeyCount)
{
NtpKs1 *pNK = NULL;
- uint32_t i, m;
+ uint32_t i;
uint32_t j = 0;
- uint32_t nt1, nt2, nttest, ks1, dist;
+ uint32_t nt1, nt2, nttest, ks1, dist = 0;
uint32_t authuid = data->uid;
uint8_t type = (uint8_t)data->key_type; // target key type
@@ -470,4 +469,4 @@ FFI_PLUGIN_EXPORT uint64_t mfkey32(Mfkey32 *data)
free(s);
return UINT64_MAX;
-}
\ No newline at end of file
+}
diff --git a/chameleonultragui/src/recovery.h b/chameleonultragui/src/recovery.h
index b3cd5368..f41a6642 100644
--- a/chameleonultragui/src/recovery.h
+++ b/chameleonultragui/src/recovery.h
@@ -64,7 +64,7 @@ typedef struct
uint32_t ar1_enc; // second encrypted reader response
} Mfkey32;
-FFI_PLUGIN_EXPORT uint64_t *darkside(Darkside *data, uint32_t *keyCount);
+FFI_PLUGIN_EXPORT uint64_t *darkside(Darkside *data, uint64_t *keyCount);
FFI_PLUGIN_EXPORT uint64_t *nested(Nested *data, uint32_t *keyCount);