├── .gitignore
├── CHANGELOG.md
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── Makefile
├── README.md
├── proton-authenticator-mobile
├── Cargo.toml
├── android
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── gradle.properties
│ ├── gradle
│ │ ├── libs.versions.toml
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── lib
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ └── AndroidManifest.xml
│ ├── libTest
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ └── test
│ │ │ └── kotlin
│ │ │ └── proton
│ │ │ └── android
│ │ │ └── authenticator
│ │ │ ├── AuthenticatorCryptoTest.kt
│ │ │ ├── AuthenticatorLoggerTest.kt
│ │ │ ├── MobileAuthenticatorClientTest.kt
│ │ │ ├── SyncOperationCheckerTest.kt
│ │ │ ├── TestUtils.kt
│ │ │ └── TotpGeneratorTest.kt
│ ├── libTestApp
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── ic_launcher-playstore.png
│ │ │ ├── java
│ │ │ └── proton
│ │ │ │ └── authenticator
│ │ │ │ └── android
│ │ │ │ └── benchmark
│ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ ├── drawable
│ │ │ ├── ic_launcher_background.xml
│ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── layout
│ │ │ └── main_activity.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ └── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ └── settings.gradle.kts
├── bindgen.rs
├── build.rs
├── iOS
│ └── AuthenticatorRustCore
│ │ ├── .gitignore
│ │ ├── .swiftpm
│ │ └── xcode
│ │ │ └── package.xcworkspace
│ │ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ ├── Package.swift
│ │ └── README.md
├── src
│ ├── authenticator.rs
│ ├── authenticator.udl
│ ├── benchmark.rs
│ ├── benchmark.udl
│ ├── crypto.rs
│ ├── crypto.udl
│ ├── entry.rs
│ ├── generator.rs
│ ├── generator.udl
│ ├── import.rs
│ ├── import.udl
│ ├── issuer_mapper.rs
│ ├── issuer_mapper.udl
│ ├── lib.rs
│ ├── log.rs
│ ├── log.udl
│ ├── namespace.udl
│ ├── operations.rs
│ ├── operations.udl
│ ├── ordering.rs
│ └── ordering.udl
└── uniffi.toml
├── proton-authenticator-web
├── Cargo.toml
├── asm.js
├── package.json
├── src
│ ├── common.rs
│ ├── entry.rs
│ ├── lib.rs
│ ├── log.rs
│ └── worker
│ │ ├── client.rs
│ │ ├── crypto.rs
│ │ ├── generator.rs
│ │ ├── import.rs
│ │ ├── issuer.rs
│ │ ├── mod.rs
│ │ ├── operations.rs
│ │ └── ordering.rs
└── test
│ ├── .gitignore
│ ├── README.md
│ ├── bun.lockb
│ ├── package.json
│ ├── proton-authenticator-web-worker-generator.spec.ts
│ ├── proton-authenticator-web-worker-importer.spec.ts
│ ├── proton-authenticator-web-worker-operations.spec.ts
│ ├── proton-authenticator-web-worker.spec.ts
│ └── tsconfig.json
├── proton-authenticator
├── Cargo.toml
├── build.rs
├── proto
│ ├── authenticator_entry.proto
│ └── google_authenticator.proto
├── resources
│ ├── issuerInfos.txt
│ └── issuerManualOverrides.txt
├── src
│ ├── client.rs
│ ├── crypto.rs
│ ├── entry
│ │ ├── create.rs
│ │ ├── exporter.rs
│ │ ├── gen
│ │ │ ├── authenticator_entry.rs
│ │ │ └── mod.rs
│ │ ├── mod.rs
│ │ ├── serializer.rs
│ │ └── update.rs
│ ├── generator.rs
│ ├── issuer_mapper.rs
│ ├── lib.rs
│ ├── log.rs
│ ├── operations.rs
│ ├── ordering.rs
│ ├── parser
│ │ ├── aegis
│ │ │ ├── db.rs
│ │ │ ├── encrypted.rs
│ │ │ ├── json.rs
│ │ │ ├── mod.rs
│ │ │ └── txt.rs
│ │ ├── bitwarden
│ │ │ ├── csv.rs
│ │ │ ├── json.rs
│ │ │ └── mod.rs
│ │ ├── ente
│ │ │ ├── mod.rs
│ │ │ └── txt.rs
│ │ ├── google
│ │ │ ├── gen
│ │ │ │ ├── google_authenticator.rs
│ │ │ │ └── mod.rs
│ │ │ └── mod.rs
│ │ ├── lastpass
│ │ │ ├── json.rs
│ │ │ └── mod.rs
│ │ ├── mod.rs
│ │ ├── proton_authenticator
│ │ │ └── mod.rs
│ │ └── twofas
│ │ │ ├── mod.rs
│ │ │ └── parser.rs
│ ├── steam
│ │ └── mod.rs
│ ├── test_utils.rs
│ └── utils.rs
└── test_data
│ └── authenticator
│ ├── 2fas
│ ├── decrypted.2fas
│ ├── decrypted_ios.2fas
│ ├── encrypted.2fas
│ └── password
│ ├── aegis
│ ├── aegis-json-encrypted-test.json
│ ├── aegis-json-unencrypted.json
│ ├── aegis-txt.txt
│ └── password
│ ├── bitwarden
│ ├── bitwarden.csv
│ ├── bitwarden.json
│ └── bitwarden_sample.csv
│ ├── ente
│ └── plain.txt
│ └── lastpass
│ ├── lastpass.json
│ └── lastpass_ios_export.json
├── proton-pass-common
├── 2faDomains.txt
├── Cargo.toml
├── benches
│ ├── card_detector.rs
│ └── password_scorer.rs
├── build.rs
├── eff_large_wordlist.txt
├── passwords.txt
├── src
│ ├── alias_prefix.rs
│ ├── creditcard
│ │ ├── detector.rs
│ │ └── mod.rs
│ ├── domain.rs
│ ├── email.rs
│ ├── file
│ │ ├── associations.rs
│ │ ├── mod.rs
│ │ └── sanitize_filename.rs
│ ├── host.rs
│ ├── invite.rs
│ ├── lib.rs
│ ├── login.rs
│ ├── passkey
│ │ ├── generate.rs
│ │ ├── mod.rs
│ │ ├── parser
│ │ │ ├── cvs.rs
│ │ │ ├── ebay.rs
│ │ │ ├── equal_sign.rs
│ │ │ ├── mod.rs
│ │ │ ├── paypal.rs
│ │ │ ├── sanitize.rs
│ │ │ └── swissid.rs
│ │ ├── passkey_handling.rs
│ │ ├── protonpasskey.rs
│ │ ├── protonpasskeydeserializer.rs
│ │ ├── protonpasskeyserializer.rs
│ │ └── resolve.rs
│ ├── password
│ │ ├── analyzer.rs
│ │ ├── mod.rs
│ │ ├── password_generator.rs
│ │ └── scorer.rs
│ ├── qr.rs
│ ├── twofa.rs
│ └── wifi.rs
├── test_data
│ └── file_format
│ │ ├── pgpkey.private
│ │ ├── pgpkey.pub
│ │ ├── sample-unclosed.svg
│ │ ├── sample.avi
│ │ ├── sample.docx
│ │ ├── sample.garbage
│ │ ├── sample.ics
│ │ ├── sample.jpg
│ │ ├── sample.mp3
│ │ ├── sample.mp4
│ │ ├── sample.pages
│ │ ├── sample.pdf
│ │ ├── sample.png
│ │ ├── sample.rar
│ │ ├── sample.svg
│ │ ├── sample.txt
│ │ ├── sample.wav
│ │ ├── sample.xlsx
│ │ └── sample.zip
└── tests
│ ├── alias_prefix_valid.rs
│ ├── credit_card_detector.rs
│ ├── domain.rs
│ ├── email_valid.rs
│ ├── file.rs
│ ├── passkey.rs
│ ├── password_scorer.rs
│ └── twofa_valid.rs
├── proton-pass-derive
├── Cargo.toml
└── src
│ └── lib.rs
├── proton-pass-mobile
├── Cargo.toml
├── android
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── lib
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ └── AndroidManifest.xml
│ └── settings.gradle.kts
├── bindgen.rs
├── build.rs
├── iOS
│ └── PassRustCore
│ │ ├── .gitignore
│ │ ├── .swiftpm
│ │ └── xcode
│ │ │ └── package.xcworkspace
│ │ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ ├── Package.swift
│ │ └── README.md
├── src
│ ├── alias.rs
│ ├── alias.udl
│ ├── creditcard.rs
│ ├── creditcard.udl
│ ├── domain.rs
│ ├── domain.udl
│ ├── email.rs
│ ├── email.udl
│ ├── file.rs
│ ├── file.udl
│ ├── host.rs
│ ├── host.udl
│ ├── invite.rs
│ ├── invite.udl
│ ├── lib.rs
│ ├── login.rs
│ ├── login.udl
│ ├── namespace.udl
│ ├── passkey.rs
│ ├── passkey.udl
│ ├── password.rs
│ ├── password.udl
│ ├── qr.rs
│ ├── qr.udl
│ ├── totp.rs
│ ├── totp.udl
│ ├── twofa.rs
│ ├── twofa.udl
│ ├── wifi.rs
│ └── wifi.udl
└── uniffi.toml
├── proton-pass-totp
├── Cargo.toml
├── benches
│ └── totp_generation.rs
├── src
│ ├── algorithm.rs
│ ├── error.rs
│ ├── lib.rs
│ ├── queries.rs
│ ├── sanitizer.rs
│ └── totp.rs
└── tests
│ ├── edent.rs
│ └── totp.rs
├── proton-pass-web
├── Cargo.toml
├── package.json
├── src
│ ├── common.rs
│ ├── lib.rs
│ ├── password
│ │ ├── mod.rs
│ │ └── password.rs
│ ├── ui
│ │ ├── creditcard.rs
│ │ ├── file.rs
│ │ ├── login.rs
│ │ ├── mod.rs
│ │ └── wifi.rs
│ └── worker
│ │ ├── mod.rs
│ │ ├── passkey.rs
│ │ └── totp.rs
└── test
│ ├── .gitignore
│ ├── README.md
│ ├── bun.lockb
│ ├── package.json
│ ├── proton-pass-web-password.spec.ts
│ ├── proton-pass-web-ui.spec.ts
│ ├── proton-pass-web-worker.spec.ts
│ └── tsconfig.json
├── release.toml
├── rustfmt.toml
└── tools
├── custom2faDomains.txt
├── excluded2faDomains.txt
├── generate2FADomains.py
├── generateCommonPasswordList.py
└── icon_fetcher
├── Cargo.toml
├── config.toml
└── src
└── main.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 | .idea/
3 | /target
4 | .cargo/
5 |
6 | # Pass mobile
7 | proton-pass-mobile/android/lib/**/*.kt
8 | proton-pass-mobile/iOS/headers
9 | proton-pass-mobile/iOS/frameworks
10 | proton-pass-mobile/iOS/PassRustCore/*.xcframework
11 | proton-pass-mobile/src/proton
12 | proton-pass-mobile/src/common.udl
13 | proton-pass-mobile/src/*.h
14 | proton-pass-mobile/src/*.swift
15 | proton-pass-mobile/src/*.modulemap
16 | proton-pass-mobile/iOS/**/*.swift
17 | proton-pass-mobile/pkg
18 |
19 | proton-pass-web/**/pkg
20 | proton-pass-web/dist
21 |
22 | # Authenticator
23 | proton-authenticator-mobile/android/lib/**/*.kt
24 | proton-authenticator-mobile/iOS/headers
25 | proton-authenticator-mobile/iOS/frameworks
26 | proton-authenticator-mobile/iOS/AuthenticatorRustCore/*.xcframework
27 | proton-authenticator-mobile/src/proton
28 | proton-authenticator-mobile/src/common.udl
29 | proton-authenticator-mobile/src/*.h
30 | proton-authenticator-mobile/src/*.swift
31 | proton-authenticator-mobile/src/*.modulemap
32 | proton-authenticator-mobile/iOS/**/*.swift
33 | proton-authenticator-mobile/pkg
34 |
35 | proton-authenticator-web/**/pkg
36 | proton-authenticator-web/dist
37 |
38 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | resolver = "1"
3 | members = [
4 | "proton-authenticator",
5 | "proton-authenticator-mobile",
6 | "proton-authenticator-web",
7 | "proton-pass-common",
8 | "proton-pass-mobile",
9 | "proton-pass-web",
10 | "proton-pass-derive",
11 | "proton-pass-totp",
12 | "tools/icon_fetcher"
13 | ]
14 |
15 | [workspace.dependencies]
16 | criterion = { version = "0.5.1", features = ["html_reports"] }
17 | js-sys = "0.3.77"
18 | serde = { version = "1.0", features = ["derive"] }
19 | serde_json = "1.0"
20 | tokio = { version = "1.44.1", features = ["full"] }
21 | tsify-next = { version = "0.5.5", features = ["wasm-bindgen", "js", "json"] }
22 | wasm-bindgen = "0.2.100"
23 | wasm-bindgen-futures = "0.4.50"
24 |
25 | [profile.release]
26 | opt-level = "s" # Tell `rustc` to optimize for small code size.
27 | strip = true
28 |
--------------------------------------------------------------------------------
/proton-authenticator-mobile/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "proton-authenticator-mobile"
3 | version = "0.21.1"
4 | edition = "2021"
5 |
6 | # Flags for cargo-release
7 | [package.metadata.release]
8 | pre-release-replacements = [
9 | # Yes, we are relying on the number of whitespaces
10 | {file="android/lib/build.gradle.kts", search=" version = .*", replace=" version = \"{{version}}\""}
11 | ]
12 |
13 |
14 | [lib]
15 | doctest = false
16 | crate-type = ["cdylib", "staticlib"]
17 | name = "proton_authenticator_common_mobile"
18 |
19 | [dependencies]
20 | proton-authenticator = { path = "../proton-authenticator" }
21 | proton-pass-derive = { path = "../proton-pass-derive" }
22 |
23 | tokio.workspace = true
24 |
25 | chrono = "0.4.40"
26 | uniffi = { version = "0.29.2" }
27 |
28 | [build-dependencies]
29 | uniffi = { version = "0.29.2", features = [ "build" ] }
30 |
31 | [[bin]]
32 | name = "uniffi-bindgen"
33 | path = "./bindgen.rs"
34 | required-features = [ "uniffi/cli" ]
35 |
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 | *.so
17 |
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | alias(libs.plugins.gradlePlugin.application) apply false
4 | alias(libs.plugins.gradlePlugin.kotlin.android) apply false
5 | alias(libs.plugins.gradlePlugin.kotlin.jvm) apply false
6 | alias(libs.plugins.gradlePlugin.kotlin.serialization) apply false
7 | alias(libs.plugins.gradlePlugin.kotlinx.atomicfu) apply false
8 | alias(libs.plugins.gradlePlugin.library) apply false
9 | alias(libs.plugins.gradlePlugin.maven.publish) apply false
10 | }
11 |
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
2 | android.useAndroidX=true
3 | kotlin.code.style=official
4 | android.nonTransitiveRClass=true
5 |
6 | # --- MAVEN PUBLISHING CONFIG
7 | RELEASE_SIGNING_ENABLED=true
8 |
9 | # or when publishing to https://s01.oss.sonatype.org
10 | SONATYPE_HOST=S01
11 |
12 | POM_NAME=Proton Authenticator Common
13 | POM_DESCRIPTION=A shared library for Proton Authenticator clients that implements common operations.
14 | POM_URL=https://github.com/ProtonPass
15 | POM_INCEPTION_YEAR=2023
16 | POM_LICENSE_NAME=GNU GENERAL PUBLIC LICENSE, Version 3.0
17 | POM_LICENSE_URL=https://www.gnu.org/licenses/gpl-3.0.en.html
18 | POM_LICENSE_DIST=repo
19 | POM_DEVELOPER_ID=opensource@proton.me
20 | POM_DEVELOPER_NAME=Open Source Proton
21 | POM_DEVELOPER_URL=opensource@proton.me
22 |
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/protonpass/proton-pass-common/8d5de37e2defe2a2a5e037df2ef555db83e82496/proton-authenticator-mobile/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Sep 20 13:19:37 CEST 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/lib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/lib/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/lib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/libTest/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | jniLibs/
3 | src/main/kotlin/**/*.kt
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/libTest/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.gradlePlugin.kotlin.jvm)
3 | alias(libs.plugins.gradlePlugin.kotlinx.atomicfu)
4 | alias(libs.plugins.gradlePlugin.kotlin.serialization)
5 | }
6 |
7 | kotlin {
8 | jvmToolchain(17)
9 | }
10 |
11 | sourceSets {
12 | main {
13 | // Make sure jniLibs is treated as resources, so it is on the classpath
14 | resources {
15 | srcDir("src/main/jniLibs")
16 | }
17 | }
18 | }
19 |
20 | tasks.test {
21 | systemProperty("jna.library.path", file("src/main/jniLibs").absolutePath)
22 |
23 | testLogging {
24 | events("PASSED", "SKIPPED", "FAILED") // which events to display
25 | showStandardStreams = true // show standard out/err for each test
26 | exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
27 | }
28 | }
29 |
30 | dependencies {
31 | implementation(libs.kotlinx.serialization.json)
32 | implementation(libs.kotlinx.coroutines.core)
33 | implementation(libs.jna)
34 | implementation(libs.okio)
35 |
36 | testImplementation(libs.coroutines.test)
37 | testImplementation(libs.kotlinx.coroutines.core)
38 | testImplementation(libs.kotlinx.datetime)
39 | testImplementation(libs.kotlinTest)
40 | testImplementation(libs.junit)
41 | testImplementation(libs.truth)
42 | testImplementation(libs.turbine)
43 | }
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/libTest/src/test/kotlin/proton/android/authenticator/AuthenticatorCryptoTest.kt:
--------------------------------------------------------------------------------
1 | package proton.android.authenticator
2 |
3 | import com.google.common.truth.Truth.assertThat
4 | import org.junit.Test
5 | import uniffi.proton_authenticator_common_mobile.AuthenticatorCrypto
6 | import uniffi.proton_authenticator_common_mobile.AuthenticatorMobileClient
7 |
8 | class AuthenticatorCryptoTest {
9 |
10 | private val crypto = AuthenticatorCrypto()
11 |
12 | @Test
13 | fun `can generate encryption key`() {
14 | val key = crypto.generateKey()
15 | assertThat(key.size).isEqualTo(32)
16 | }
17 |
18 | @Test
19 | fun `can encrypt and decrypt`() {
20 | val client = AuthenticatorMobileClient()
21 | val label1 = "MYLABEL1"
22 | val label2 = "MYLABEL2"
23 |
24 | val entry1 = client.entryFromUri(entryUriWithLabel(label1))
25 | val entry2 = client.entryFromUri(entryUriWithLabel(label2))
26 |
27 | val key = crypto.generateKey()
28 | val encrypted = crypto.encryptManyEntries(listOf(entry1, entry2), key)
29 |
30 | assertThat(encrypted.size).isEqualTo(2)
31 |
32 | val decrypted = crypto.decryptManyEntries(encrypted, key)
33 | assertThat(decrypted.size).isEqualTo(2)
34 |
35 | assertThat(decrypted[0].name).isEqualTo(label1)
36 | assertThat(decrypted[1].name).isEqualTo(label2)
37 | }
38 |
39 | @Test
40 | fun `generateKey generates different keys`() {
41 | val key1 = crypto.generateKey()
42 | val key2 = crypto.generateKey()
43 |
44 | assertThat(key1).isNotEqualTo(key2)
45 | }
46 |
47 | private fun entryUriWithLabel(label: String): String =
48 | "otpauth://totp/$label?secret=MYSECRET&issuer=MYISSUER&algorithm=SHA256&digits=8&period=15"
49 | }
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/libTest/src/test/kotlin/proton/android/authenticator/AuthenticatorLoggerTest.kt:
--------------------------------------------------------------------------------
1 | package proton.android.authenticator
2 |
3 | import com.google.common.truth.Truth.assertThat
4 | import org.junit.Test
5 | import uniffi.proton_authenticator_common_mobile.AuthenticatorLogLevel
6 | import uniffi.proton_authenticator_common_mobile.AuthenticatorLogger
7 | import uniffi.proton_authenticator_common_mobile.emitLog
8 | import uniffi.proton_authenticator_common_mobile.registerAuthenticatorLogger
9 |
10 | class AuthenticatorLoggerTest {
11 |
12 | @Test
13 | fun `can register a logger`() {
14 | val loggedMessages = mutableListOf>()
15 | registerAuthenticatorLogger(object : AuthenticatorLogger {
16 | override fun log(level: AuthenticatorLogLevel, message: String) {
17 | loggedMessages.add(level to message)
18 | }
19 | })
20 |
21 | val messages = listOf(
22 | "trace message",
23 | "debug message",
24 | "info message",
25 | "warn message",
26 | "error message"
27 | )
28 |
29 | val levels = listOf(
30 | AuthenticatorLogLevel.TRACE,
31 | AuthenticatorLogLevel.DEBUG,
32 | AuthenticatorLogLevel.INFO,
33 | AuthenticatorLogLevel.WARN,
34 | AuthenticatorLogLevel.ERROR,
35 | )
36 |
37 | for ((message, level) in messages.zip(levels)) {
38 | emitLog(level, message)
39 | }
40 |
41 | assertThat(loggedMessages.size).isEqualTo(messages.size)
42 |
43 | for (i in messages.indices) {
44 | val loggedMessage = loggedMessages[i]
45 | assertThat(loggedMessage.first).isEqualTo(levels[i])
46 | assertThat(loggedMessage.second).isEqualTo(messages[i])
47 | }
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/libTest/src/test/kotlin/proton/android/authenticator/SyncOperationCheckerTest.kt:
--------------------------------------------------------------------------------
1 | package proton.android.authenticator
2 |
3 | import com.google.common.truth.Truth.assertThat
4 | import org.junit.Test
5 | import uniffi.proton_authenticator_common_mobile.LocalEntry
6 | import uniffi.proton_authenticator_common_mobile.LocalEntryState
7 | import uniffi.proton_authenticator_common_mobile.OperationType
8 | import uniffi.proton_authenticator_common_mobile.RemoteEntry
9 | import uniffi.proton_authenticator_common_mobile.SyncOperationChecker
10 |
11 | class SyncOperationCheckerTest {
12 |
13 | private val instance = SyncOperationChecker()
14 |
15 | @Test
16 | fun `can handle empty lists`() {
17 | val res = instance.calculateOperations(emptyList(), emptyList())
18 | assertThat(res).isEmpty()
19 | }
20 |
21 | @Test
22 | fun `does not return anything in case no differences`() {
23 | val entry = TestUtils.getEntry1()
24 | val remote = listOf(RemoteEntry(remoteId = "REMOTE_ID", entry = entry, modifyTime = NOW))
25 | val local = listOf(LocalEntry(
26 | entry = entry,
27 | state = LocalEntryState.SYNCED,
28 | modifyTime = NOW,
29 | localModifyTime = null
30 | ))
31 | val res = instance.calculateOperations(remote, local)
32 | assertThat(res).isEmpty()
33 | }
34 |
35 | @Test
36 | fun `remote entry not present in local returns upsert`() {
37 | val entry = TestUtils.getEntry1()
38 | val remoteId = "REMOTE_ID"
39 | val remote = listOf(RemoteEntry(remoteId = remoteId, entry = entry, modifyTime = NOW))
40 | val res = instance.calculateOperations(remote, emptyList())
41 |
42 | assertThat(res.size).isEqualTo(1)
43 | assertThat(res[0].entry).isEqualTo(entry)
44 | assertThat(res[0].remoteId).isEqualTo(remoteId)
45 | assertThat(res[0].operation).isEqualTo(OperationType.UPSERT)
46 | }
47 |
48 | @Test
49 | fun `local entry pending to be pushed not present in remote returns push`() {
50 | val entry = TestUtils.getEntry1()
51 | val local = listOf(LocalEntry(
52 | entry = entry,
53 | state = LocalEntryState.PENDING_SYNC,
54 | modifyTime = NOW,
55 | localModifyTime = null
56 | ))
57 | val res = instance.calculateOperations(emptyList(), local)
58 |
59 | assertThat(res.size).isEqualTo(1)
60 | assertThat(res[0].entry).isEqualTo(entry)
61 | assertThat(res[0].remoteId).isNull()
62 | assertThat(res[0].operation).isEqualTo(OperationType.PUSH)
63 | }
64 |
65 | companion object {
66 | private val NOW = 1_700_000_000L
67 | }
68 |
69 | }
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/libTest/src/test/kotlin/proton/android/authenticator/TestUtils.kt:
--------------------------------------------------------------------------------
1 | package proton.android.authenticator
2 |
3 | import uniffi.proton_authenticator_common_mobile.AuthenticatorEntryModel
4 | import uniffi.proton_authenticator_common_mobile.AuthenticatorMobileClient
5 |
6 | object TestUtils {
7 |
8 | fun getEntry1(): AuthenticatorEntryModel {
9 | val client = AuthenticatorMobileClient()
10 | return client.entryFromUri("otpauth://totp/MYLABEL?secret=MYSECRET&issuer=MYISSUER&algorithm=SHA256&digits=8&period=15")
11 | }
12 |
13 | fun getEntry2(): AuthenticatorEntryModel {
14 | val client = AuthenticatorMobileClient()
15 | return client.entryFromUri("otpauth://totp/MYLABEL?secret=MYSECRET123&issuer=MYISSUER&algorithm=SHA256&digits=8&period=15")
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/libTestApp/.gitignore:
--------------------------------------------------------------------------------
1 | build/
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/libTestApp/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.gradlePlugin.application)
3 | alias(libs.plugins.gradlePlugin.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace = "proton.android.authenticator.benchmark"
8 | compileSdk = libs.versions.compileSdk.get().toInt()
9 |
10 | defaultConfig {
11 | minSdk = libs.versions.minSdk.get().toInt()
12 |
13 | proguardFiles(
14 | getDefaultProguardFile("proguard-android-optimize.txt"),
15 | "proguard-rules.pro"
16 | )
17 | }
18 |
19 | compileOptions {
20 | sourceCompatibility = JavaVersion.VERSION_17
21 | targetCompatibility = JavaVersion.VERSION_17
22 | }
23 |
24 | kotlinOptions {
25 | jvmTarget = JavaVersion.VERSION_17.toString()
26 | }
27 | }
28 |
29 | dependencies {
30 | implementation(projects.lib)
31 |
32 | implementation(libs.kotlinx.coroutines.core)
33 | implementation(libs.kotlinx.datetime)
34 | }
35 |
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/libTestApp/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/libTestApp/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/protonpass/proton-pass-common/8d5de37e2defe2a2a5e037df2ef555db83e82496/proton-authenticator-mobile/android/libTestApp/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/proton-authenticator-mobile/android/libTestApp/src/main/java/proton/authenticator/android/benchmark/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package proton.authenticator.android.benchmark
2 |
3 | import android.app.Activity
4 | import android.os.Bundle
5 | import android.widget.Button
6 | import proton.android.authenticator.benchmark.R
7 | import proton.android.authenticator.commonrust.AuthenticatorMobileClient
8 | import proton.android.authenticator.commonrust.MobileTotpBenchmark
9 |
10 | class MainActivity: Activity() {
11 |
12 | override fun onCreate(savedInstanceState: Bundle?) {
13 | super.onCreate(savedInstanceState)
14 | setContentView(R.layout.main_activity)
15 |
16 | findViewById