Skip to content

Commit

Permalink
Feature/debug mode sharing functionality (eu-digital-green-certificat…
Browse files Browse the repository at this point in the history
…es#200)

* - add DCC anonymize functionality;

* - create zip from dcc and version files;

* - add all L1 policy files to zip;

* - add all L2 policy files to zip;

* - remove unused files;

* - code cleanup;
- refactor sharing functionality;

* - add image to l3 level zip;

* - add all required files for debug mode;

* - formatting;

* - remove unused code;

* - add share functionality for zip files;

* - update payload files;

* - resolve merge conflicts;

* - updated sharing functionality based on debug mode level;

* - remove unused dependency;
  • Loading branch information
MykhailoNester authored Sep 6, 2021
1 parent 132b372 commit fa49177
Show file tree
Hide file tree
Showing 22 changed files with 828 additions and 60 deletions.
11 changes: 10 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 0 additions & 7 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@
android:authorities="${applicationId}.workmanager-init"
android:exported="false"
tools:node="remove" />

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
</application>

</manifest>
20 changes: 16 additions & 4 deletions app/src/main/java/dgca/verifier/app/android/CodeReaderFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,20 @@ class CodeReaderFragment : BindingFragment<FragmentCodeReaderBinding>(),
val certificateModel: CertificateModel? = bundle.getParcelable(CERTIFICATE_MODEL_KEY)
val hcert: String? = bundle.getString(HCERT_KEY)
val ruleValidationResultModelsContainer: RuleValidationResultModelsContainer? = bundle.getParcelable(
RULE_VALIDATION_RESULT_MODELS_CONTAINER_KEY)
RULE_VALIDATION_RESULT_MODELS_CONTAINER_KEY
)
val isDebugModeEnabled = bundle.getBoolean(IS_DEBUG_MODE_ENABLED)
val debugData: DebugData? = bundle.getParcelable(DEBUG_DATA)

if (standardizedVerificationResult != null) {
showVerificationResult(standardizedVerificationResult, certificateModel, hcert, ruleValidationResultModelsContainer, isDebugModeEnabled)
showVerificationResult(
standardizedVerificationResult,
certificateModel,
hcert,
ruleValidationResultModelsContainer,
isDebugModeEnabled,
debugData
)
}
}
}
Expand All @@ -132,7 +142,8 @@ class CodeReaderFragment : BindingFragment<FragmentCodeReaderBinding>(),
certificateModel: CertificateModel?,
hcert: String?,
ruleValidationResultModelsContainer: RuleValidationResultModelsContainer?,
isDebugModeEnabled: Boolean
isDebugModeEnabled: Boolean,
debugData: DebugData?
) {
findNavController().navigateUp()
binding.barcodeScanner.pause()
Expand All @@ -141,7 +152,8 @@ class CodeReaderFragment : BindingFragment<FragmentCodeReaderBinding>(),
standardizedVerificationResult,
certificateModel,
hcert,
ruleValidationResultModelsContainer
ruleValidationResultModelsContainer,
debugData
)
} else {
CodeReaderFragmentDirections.actionCodeReaderFragmentToVerificationResultFragment(
Expand Down
44 changes: 44 additions & 0 deletions app/src/main/java/dgca/verifier/app/android/Event.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* ---license-start
* eu-digital-green-certificates / dgca-verifier-app-android
* ---
* Copyright (C) 2021 T-Systems International GmbH and all other contributors
* ---
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ---license-end
*
* Created by mykhailo.nester on 06/09/2021, 09:01
*/

package dgca.verifier.app.android

/**
* Used as a wrapper for data that is exposed via a LiveData that represents an event.
*/
open class Event<out T>(private val content: T) {

var hasBeenHandled = false
private set // Allow external read but not write

/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* ---license-start
* eu-digital-green-certificates / dgca-verifier-app-android
* ---
* Copyright (C) 2021 T-Systems International GmbH and all other contributors
* ---
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ---license-end
*
* Created by mykhailo.nester on 31/08/2021, 14:05
*/

package dgca.verifier.app.android.anonymization

import dgca.verifier.app.android.model.CertificateModel
import dgca.verifier.app.android.settings.debug.mode.DebugModeState
import javax.inject.Inject

class AnonymizationManager @Inject constructor() {

fun anonymizeDcc(certificateModel: CertificateModel, state: DebugModeState = DebugModeState.LEVEL_1): CertificateModel {
return when (state) {
DebugModeState.LEVEL_1 -> l1Anonymization(certificateModel)
DebugModeState.LEVEL_2 -> l2Anonymization(certificateModel)
else -> certificateModel
}
}

private fun l1Anonymization(certificateModel: CertificateModel): CertificateModel {
val person = certificateModel.person

val dobAnonymizePart = certificateModel.dateOfBirth.substring(certificateModel.dateOfBirth.indexOf("-") + 1).anonymize()
val dateOfBirth = certificateModel.dateOfBirth.replaceAfter("-", dobAnonymizePart)

val vaccinations = certificateModel.vaccinations?.map {
it.copy(certificateIdentifier = it.certificateIdentifier.anonymizeCi())
}
val tests = certificateModel.tests?.map {
val dateTime = it.dateTimeOfCollection
val timePart = dateTime.substring(dateTime.indexOf("T") + 1).anonymize()
val dateTimeOfCollectionAnonymize = dateTime.replaceAfter("T", timePart)

it.copy(
dateTimeOfCollection = dateTimeOfCollectionAnonymize,
certificateIdentifier = it.certificateIdentifier.anonymizeCi()
)
}

val recovery = certificateModel.recoveryStatements?.map {
it.copy(certificateIdentifier = it.certificateIdentifier.anonymizeCi())
}

return certificateModel.copy(
person = certificateModel.person.copy(
standardisedFamilyName = person.standardisedFamilyName.anonymize(),
familyName = person.familyName?.anonymize(),
standardisedGivenName = person.standardisedGivenName?.anonymize(),
givenName = person.givenName?.anonymize(),
),
dateOfBirth = dateOfBirth,
vaccinations = vaccinations,
tests = tests,
recoveryStatements = recovery
)
}

private fun l2Anonymization(certificateModel: CertificateModel): CertificateModel {
val person = certificateModel.person

val dobAnonymizePart = certificateModel.dateOfBirth.substring(certificateModel.dateOfBirth.indexOf("-") + 1).anonymize()
val dateOfBirth = certificateModel.dateOfBirth.replaceAfter("-", dobAnonymizePart)

return certificateModel.copy(
person = certificateModel.person.copy(
standardisedFamilyName = person.standardisedFamilyName.anonymize(),
familyName = person.familyName?.anonymize(),
standardisedGivenName = person.standardisedGivenName?.anonymize(),
givenName = person.givenName?.anonymize(),
),
dateOfBirth = dateOfBirth
)
}

}

fun String.anonymize(): String {
val strBuilder = StringBuilder()
forEach {
val charReplaced = when (it.category) {
CharCategory.LOWERCASE_LETTER -> "x"
CharCategory.TITLECASE_LETTER, CharCategory.UPPERCASE_LETTER -> "X"
CharCategory.MODIFIER_LETTER -> "M"
CharCategory.OTHER_LETTER -> "R"
CharCategory.COMBINING_SPACING_MARK -> "S"
CharCategory.ENCLOSING_MARK, CharCategory.NON_SPACING_MARK -> "s"
CharCategory.DECIMAL_DIGIT_NUMBER -> if ("[0-9]".toRegex().matches(it.toString())) "9" else "8"
CharCategory.OTHER_NUMBER -> "2"
CharCategory.LETTER_NUMBER -> "1"
CharCategory.CURRENCY_SYMBOL, CharCategory.MODIFIER_SYMBOL, CharCategory.MATH_SYMBOL, CharCategory.OTHER_SYMBOL -> "@"

else -> {
when {
"\\p{P}".toRegex().matches(it.toString()) -> {
when {
"\\u002D".toRegex().matches(it.toString()) -> "-"
"\\u002E".toRegex().matches(it.toString()) -> "."
"\\u002C".toRegex().matches(it.toString()) -> ","
it.category == CharCategory.DASH_PUNCTUATION -> "="
it.category == CharCategory.FINAL_QUOTE_PUNCTUATION || it.category == CharCategory.START_PUNCTUATION ||
it.category == CharCategory.INITIAL_QUOTE_PUNCTUATION || it.category == CharCategory.END_PUNCTUATION -> "Q"
else -> "!"
}
}
"\\p{Z}".toRegex().matches(it.toString()) -> {
when {
"\\u0020".toRegex().matches(it.toString()) -> " "
it.category == CharCategory.SPACE_SEPARATOR -> "_"
it.category == CharCategory.LINE_SEPARATOR || it.category == CharCategory.PARAGRAPH_SEPARATOR -> "N"
else -> "?"
}
}
else -> "Q"
}
}
}

strBuilder.append(charReplaced)
}

return strBuilder.toString()
}

fun String.anonymizeCi(): String {
val ciPart = substring(lastIndexOf(":") + 1)
val ciAnonymize = "[A-Za-z0-9]".toRegex().replace(ciPart, "X")
return replaceAfterLast(":", ciAnonymize)
}
55 changes: 55 additions & 0 deletions app/src/main/java/dgca/verifier/app/android/di/QrCodeModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* ---license-start
* eu-digital-green-certificates / dgca-verifier-app-android
* ---
* Copyright (C) 2021 T-Systems International GmbH and all other contributors
* ---
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ---license-end
*
* Created by Mykhailo Nester on 4/23/21 9:48 AM
*/

package dgca.verifier.app.android.di

import com.google.zxing.MultiFormatWriter
import com.journeyapps.barcodescanner.BarcodeEncoder
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dgca.verifier.app.android.verification.detailed.qr.DefaultQrCodeConverter
import dgca.verifier.app.android.verification.detailed.qr.QrCodeConverter
import javax.inject.Singleton

/**
* Provide QR decoder functionality for injection.
*/
@ExperimentalUnsignedTypes
@InstallIn(SingletonComponent::class)
@Module
object QrCodeModule {

@Singleton
@Provides
fun provideMultiFormatWriter(): MultiFormatWriter = MultiFormatWriter()

@Singleton
@Provides
fun provideBarcodeEncoder(): BarcodeEncoder = BarcodeEncoder()

@Singleton
@Provides
fun provideQrCodeConverter(multiFormatWriter: MultiFormatWriter, barcodeEncoder: BarcodeEncoder): QrCodeConverter =
DefaultQrCodeConverter(multiFormatWriter, barcodeEncoder)
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,14 @@
package dgca.verifier.app.android.network

import android.os.Build
import dgca.verifier.app.android.BackportUtils
import dgca.verifier.app.android.utils.sha256
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import java.io.IOException
import java.net.HttpURLConnection.HTTP_BAD_REQUEST
import java.security.MessageDigest

class HeaderInterceptor : Interceptor {

Expand Down Expand Up @@ -70,23 +69,4 @@ private fun Response.toRuleResponse(expectedSha256: String): Response = if (this
newResponse.build()
} else {
this
}

private fun String.sha256(): String {
val sb = StringBuilder()
try {
val digest: MessageDigest = MessageDigest.getInstance("SHA-256")
digest.update(this.toByteArray())
val byteData: ByteArray = digest.digest()
for (x in byteData) {
val str = Integer.toHexString(BackportUtils.byteToUnsignedInt(x))
if (str.length < 2) {
sb.append('0')
}
sb.append(str)
}
} catch (e: Exception) {
e.printStackTrace()
}
return sb.toString()
}
Loading

0 comments on commit fa49177

Please sign in to comment.