diff --git a/CHANGELOG b/CHANGELOG index 03829a01..b608510a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,149 +1,7 @@ -* 8ffeb4f3 - (HEAD -> release/3.5.1, tag: develop-145, origin/develop, develop) Merge pull request #157 from tangem/CSK-308-merge_master_to_develop +* 27ec40e8 - (HEAD -> release/3.6.0, tag: develop-149, tag: develop-148, origin/develop, develop) Merge pull request #161 from tangem/CSK-310-show_walletData_for_read_files |\ -| * 9531869f - Merge branch 'master' into CSK-308-merge_master_to_develop +| * 8cac08a8 - CSK-310 demo: show walletData for read files |/ -* 2a7544f2 - (tag: develop-144) Merge pull request #154 from tangem/CSK-307-fix_showing_response_for_ReadAllFiles_command -|\ -| * 2c1135e3 - CSK-307 fix showing response for ReadAllFiles command -|/ -* 8883159f - (tag: develop-143) Merge pull request #153 from tangem/CSK-304_fix-signCommand-npe -|\ -| * 143a1b87 - CSK-304 fixed signCommand npe -|/ -* d57c6f1c - (tag: develop-142) Merge pull request #152 from tangem/CSK-303_backup_batches -|\ -| * 033f7d00 - CSK-303: add another 'detached batch' -| * c593105b - Merge branch 'develop' of https://github.com/tangem/tangem-sdk-android into CSK-303_backup_batches -| |\ -| |/ -|/| -* | d5be2251 - (tag: develop-141) Merge pull request #151 from tangem/CSK-302_immutable-moshi-json-converter -|\ \ -| * | 9bae2150 - CSK-302 MoshiJsonConverter.INSTANCE is immutable now -|/ / -| * bf03ed3f - CSK-303: check batches during backup -|/ -* 9897c22b - (tag: develop-140) Merge pull request #150 from tangem/CSK-297_not_empty_wallets_error -|\ -| * 1b6fb3ef - CSK-297: change text for 10002 error (Extended length not supported) -| * 36bb71b2 - CSK-297: add text to ExtendedLengthNotSupported error -| * 0f761c4e - CSK-297: add text to Backup Error 'Not Empty Wallets' -|/ -* aa1d19ce - (tag: develop-139, tag: develop-138) Merge pull request #149 from tangem/CSK-301_change_id_formatter -|\ -| * 8c75aec3 - CSK-301: show last 4 digits in Card ID unless Luhn -|/ -* bf4cefbe - (tag: develop-137, tag: develop-136) Merge pull request #148 from tangem/CSK-300_translation-change-how -|\ -| * 614c7a90 - CSK-300 update russian language -|/ -* e4d1800a - (tag: develop-135) Merge pull request #146 from tangem/CSK-299_delete-all-wallets -|\ -| * ef4c0380 - Merge branch 'develop' into CSK-299_delete-all-wallets -| |\ -| |/ -|/| -* | 42ad4bc2 - (tag: develop-134) Merge pull request #147 from tangem/CSK-298_fix-hidden-button-on-access-code-request -|\ \ -| * | 292cdd18 - CSK-298 fixed hidden the forgot access code buttton on medium devices -| * | ccdec613 - CSK-298 fixed backup layout -|/ / -| * f0ec1cc5 - CSK-299 added the ability to delete all wallets through the demo app -|/ -* 304a66ff - (tag: develop-133) Merge pull request #140 from tangem/CSK-287_wrong-text -|\ -| * 814e2884 - Merge branch 'develop' into CSK-287_wrong-text -| |\ -| |/ -|/| -* | 49de1f12 - (tag: develop-132) Merge pull request #145 from tangem/CSK-296_add-ru-localization -|\ \ -| * \ 5e4a33a9 - Merge branch 'develop' into CSK-296_add-ru-localization -| |\ \ -| |/ / -|/| | -| * | b5c14e3c - CSK-296 added russian localization -| | * 8ab28a2c - Merge branch 'develop' into CSK-287_wrong-text -| | |\ -| |_|/ -|/| | -* | | bc9a3afe - (tag: develop-131) Merge pull request #144 from tangem/CSK-291_hide_access_code_input -|\ \ \ -| * | | ed123429 - CSK-291: hide PIN code in input field -|/ / / -* | | 4202aff8 - (tag: develop-130) Merge pull request #143 from tangem/CSK-295_fix-equals-for-derivationPath -|\ \ \ -| |/ / -|/| | -| * | 1a14e267 - CSK-292 fixed equals for the DerivationPath -|/ / -* | 3320403f - (tag: develop-129) Merge pull request #142 from tangem/CSK-294_fix_reset_pin_error_messages -|\ \ -| * \ 1af5dabd - Merge branch 'develop' into CSK-294_fix_reset_pin_error_messages -| |\ \ -| |/ / -|/| | -* | | 20e1a619 - (tag: develop-128) Merge pull request #141 from tangem/CSK-290_cancel_session_after_reset_pin -|\ \ \ -| * | | 1f346684 - CSK-291: make Access Code in ResetPin view visible by default -| * | | 12d0cc13 - CSK-290: cancel Session when ResetPin view is closed -|/ / / -| * | 2d3bfe89 - CSK-273: use one text for all SignResetPinTokenCommand errors -| * | 03bab4fb - CSK-292: check for Card ID in third step of ResetPin -| * | 426d8ce0 - CSK-294: show ResetPinWrongCard error when needed -|/ / -| * 77eaba27 - CSK-287 fixed wrong text; fixed start delay for viewDelegate tests -|/ -* 4261c813 - (tag: develop-127) Merge pull request #139 from tangem/CSK-285_antique-card-support -|\ -| * dd3f5987 - Merge remote-tracking branch 'origin/CSK-285_antique-card-support' into CSK-285_antique-card-support -| |\ -| | * 634fb52e - CSK-285 handle long hashes -| * | 4935c3b0 - CSK-282 handle long hashes -| |/ -| * 306eb7d2 - CSK-285 omit terminal keys for new cards -| * 7bbedd11 - CSK-285 added support for antique cards -|/ -* 16879308 - (tag: develop-126) Merge pull request #120 from tangem/CSK-271_fix-progressBar-color-on-nightMode-if-error-occurs -|\ -| * dbdde459 - Merge branch 'develop' into CSK-271_fix-progressBar-color-on-nightMode-if-error-occurs -| |\ -| |/ -|/| -* | 91d4a10c - (tag: develop-125) Merge pull request #138 from tangem/CSK-267_reset_codes -|\ \ -| * | d7066f4e - CSK-268: fix build -* | | 5bd80443 - (tag: develop-124) Merge pull request #137 from tangem/CSK-267_reset_codes -|\| | -| * | a472d8be - CSK-268: resolve merge conflicts -| * | 2c3e4bc5 - Merge branch 'develop' of https://github.com/tangem/tangem-sdk-android into CSK-267_reset_codes -| |\ \ -| |/ / -|/| | -* | | 255d3657 - (tag: develop-123) Merge pull request #136 from tangem/CSK-284_publick-file-not-read -|\ \ \ -| * | | bf254edf - CSK-282 clean debug lines -| * | | 48dee871 - CSK-282 fixed reading files -|/ / / -| * | 52690fa8 - CSK-268: complete reset codes feature -| * | 6b21eb1e - CSK-268: complete reset codes feature -| * | 88672d8c - CSK-267: implement reset codes basic logic -| | * 6b026f83 - CSK-271 updated with develop -| | * 62348410 - Merge branch 'develop' into CSK-271_fix-progressBar-color-on-nightMode-if-error-occurs -| | |\ -| |_|/ -|/| | -* | | 0d2ad492 - (tag: develop-122) Merge pull request #135 from tangem/CSK-241_update-files -|\ \ \ -| |/ / -|/| | -| * | e8acb9f8 - CSK-239 synced files to January 11, 2022 (v2) -| * | 84a52922 - CSK-241 fixed test -| * | e1014a60 - CSK-241 removed redundant throws -| * | 01a6523c - CSK-239 synced files to January 11, 2022 -| * | 8305d925 - CSK-241 update files -|/ / -* / db31c912 - (tag: develop-121) Merge pull request #134 from tangem/master - / -* 5986833a - CSK-271 added a rescan delay to better show how the viewDelegate works -* b9312357 - CSK-271 fixed progressBar color for the nightMode by replacing it with CircularProgressIndicator \ No newline at end of file +* fe58cc87 - (tag: develop-147) Merge pull request #160 from tangem/master +* cebbe740 - (tag: develop-146) Merge pull request #159 from tangem/CSK-309-write_named_files_support +* 57f05b40 - CSK-309 write named files supported \ No newline at end of file diff --git a/VERSION b/VERSION index d5c0c991..40c341bd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.5.1 +3.6.0 diff --git a/tangem-sdk-android-demo/src/main/java/com/tangem/tangem_demo/ui/BaseFragment.kt b/tangem-sdk-android-demo/src/main/java/com/tangem/tangem_demo/ui/BaseFragment.kt index e74eac70..a5c626de 100644 --- a/tangem-sdk-android-demo/src/main/java/com/tangem/tangem_demo/ui/BaseFragment.kt +++ b/tangem-sdk-android-demo/src/main/java/com/tangem/tangem_demo/ui/BaseFragment.kt @@ -31,7 +31,6 @@ import com.tangem.operations.attestation.AttestationTask import com.tangem.operations.files.FileHashHelper import com.tangem.operations.files.FileToWrite import com.tangem.operations.files.FileVisibility -import com.tangem.operations.files.NamedFile import com.tangem.operations.issuerAndUserData.WriteIssuerExtraDataCommand import com.tangem.operations.personalization.entities.CardConfig import com.tangem.tangem_demo.* @@ -232,7 +231,11 @@ abstract class BaseFragment : Fragment() { val counter = 1 val issuerData = CryptoUtils.generateRandomBytes(WriteIssuerExtraDataCommand.SINGLE_WRITE_SIZE * 5) val signatures = FileHashHelper.prepareHashes( - cardId, issuerData, counter, Personalization.issuer().dataKeyPair.privateKey + cardId = cardId, + fileData = issuerData, + fileCounter = counter, + fileName = null, + privateKey = Personalization.issuer().dataKeyPair.privateKey ) sdk.writeIssuerExtraData( @@ -293,15 +296,12 @@ abstract class BaseFragment : Fragment() { return@setCard } - result.data.forEach { - val namedFile = NamedFile(it.fileData) ?: return@forEach + result.data.forEach { file -> val detailInfo = mutableMapOf() - detailInfo["fileIndex"] = it.fileIndex - detailInfo["name"] = namedFile.name - detailInfo["fileData"] = namedFile.payload.toHexString() - namedFile.counter?.let { detailInfo["counter"] = it } - namedFile.signature?.let { detailInfo["signature"] = it.toHexString() } - Tlv.deserialize(namedFile.payload)?.let { + detailInfo["name"] = file.name ?: "" + detailInfo["fileData"] = file.fileData.toHexString() + detailInfo["fileIndex"] = file.fileIndex + Tlv.deserialize(file.fileData)?.let { val decoder = TlvDecoder(it) WalletDataDeserializer.deserialize(decoder)?.let { walletData -> detailInfo["walletData"] = jsonConverter.toMap(walletData) @@ -332,10 +332,13 @@ abstract class BaseFragment : Fragment() { protected fun writeUserFile() { val demoPayload = Utils.randomString(Utils.randomInt(15, 30)).toByteArray() - val demoData = NamedFile("User file", demoPayload).serialize() - val visibility: FileVisibility = FileVisibility.Private //let walletPublicKey = Data(hexString: "40D2D7CFEF2436C159CCC918B7833FCAC5CB6037A7C60C481E8CA50AF9EDC70B") - val file: FileToWrite = FileToWrite.ByUser(demoData, visibility, null) + val file: FileToWrite = FileToWrite.ByUser( + data = demoPayload, + fileName = "User file", + fileVisibility = FileVisibility.Public, + walletPublicKey = null + ) sdk.writeFiles(listOf(file), card?.cardId, initialMessage) { handleResult(it) @@ -348,16 +351,28 @@ abstract class BaseFragment : Fragment() { return } + val fileName = "Issuer file" val demoPayload = Utils.randomString(Utils.randomInt(15, 30)).toByteArray() - val demoData = NamedFile("Ownerfile", demoPayload).serialize() - val visibility: FileVisibility = FileVisibility.Private val counter = 1 //let walletPublicKey = Data(hexString: "40D2D7CFEF2436C159CCC918B7833FCAC5CB6037A7C60C481E8CA50AF9EDC70B") - val issuer = Personalization.issuer() - val fileHash = FileHashHelper.prepareHashes(cardId, demoData, counter, issuer.dataKeyPair.privateKey) + val fileHash = FileHashHelper.prepareHashes( + cardId = cardId, + fileData = demoPayload, + fileCounter = counter, + fileName = fileName, + privateKey = Personalization.issuer().dataKeyPair.privateKey + ) ifNotNullOr(fileHash.startingSignature, fileHash.finalizingSignature, { sSignature, fSignature -> - val file = FileToWrite.ByFileOwner(demoData, sSignature, fSignature, counter, visibility, null) + val file = FileToWrite.ByFileOwner( + data = demoPayload, + fileName = fileName, + startingSignature = sSignature, + finalizingSignature = fSignature, + counter = counter, + fileVisibility = FileVisibility.Public, + walletPublicKey = null + ) sdk.writeFiles(listOf(file)) { handleResult(it) } }, { showToast("Failed to sign data with issuer signature") diff --git a/tangem-sdk-core/src/main/java/com/tangem/TangemSdk.kt b/tangem-sdk-core/src/main/java/com/tangem/TangemSdk.kt index 3dfe8611..589df11a 100644 --- a/tangem-sdk-core/src/main/java/com/tangem/TangemSdk.kt +++ b/tangem-sdk-core/src/main/java/com/tangem/TangemSdk.kt @@ -491,6 +491,7 @@ class TangemSdk( * @param cardId: CID, Unique Tangem card ID number. * @param fileData: File data that will be written on card * @param fileCounter: A counter that protects issuer data against replay attack. + * @param fileName: Optional name of the file. * @param privateKey: Optional private key that will be used for signing files hashes. * If it is provided, then `FileHashData` will contain signed file signatures. * @return [FileHashData] with hashes to sign and signatures if [privateKey] was provided. @@ -499,9 +500,10 @@ class TangemSdk( cardId: String, fileData: ByteArray, fileCounter: Int, + fileName: String? = null, privateKey: ByteArray? = null ): FileHashData { - return FileHashHelper.prepareHashes(cardId, fileData, fileCounter, privateKey) + return FileHashHelper.prepareHashes(cardId, fileData, fileCounter, fileName, privateKey) } /** diff --git a/tangem-sdk-core/src/main/java/com/tangem/operations/files/File.kt b/tangem-sdk-core/src/main/java/com/tangem/operations/files/File.kt index 9fe18ce4..d16e7b2a 100644 --- a/tangem-sdk-core/src/main/java/com/tangem/operations/files/File.kt +++ b/tangem-sdk-core/src/main/java/com/tangem/operations/files/File.kt @@ -12,6 +12,7 @@ import com.tangem.common.tlv.TlvTag */ @JsonClass(generateAdapter = true) data class File( + val name: String?, val fileData: ByteArray, val fileIndex: Int, val fileSettings: FileSettings? @@ -39,7 +40,14 @@ data class File( operator fun invoke(response: ReadFileResponse): File? { if (response.size == null || response.settings == null) return null - return File(response.fileData, response.fileIndex, response.settings) + val namedFile = NamedFile(tlvData = response.fileData) + val (name, fileData) = if (namedFile == null) { + null to response.fileData + } else { + namedFile.name to namedFile.payload + } + + return File(name, fileData, response.fileIndex, response.settings) } } } @@ -102,14 +110,38 @@ data class NamedFile( */ sealed class FileToWrite { + val payload: ByteArray + get() = fileName?.let { + try { + NamedFile(it, data).serialize() + } catch (ex: Exception) { + throw IllegalArgumentException(ex) + } + } ?: data + + private val data: ByteArray + get() = when (this) { + is ByFileOwner -> (this as ByFileOwner).data + is ByUser -> (this as ByUser).data + } + + private val fileName: String? + get() = when (this) { + is ByFileOwner -> (this as ByFileOwner).fileName + is ByUser -> (this as ByUser).fileName + } + + /** * Write file protected by the user with security delay or user code if set * @property data: Data to write + * @property fileName: Optional name of the file. COS 4.0+ * @property fileVisibility: Optional visibility setting for the file. COS 4.0+ * @property walletPublicKey: Optional link to the card's wallet. COS 4.0+ */ data class ByUser( val data: ByteArray, + val fileName: String?, val fileVisibility: FileVisibility?, val walletPublicKey: ByteArray? ) : FileToWrite() { @@ -136,6 +168,7 @@ sealed class FileToWrite { /** * Write file protected by the file owner with two signatures and counter * @property data: Data to write + * @property fileName: Optional name of the file. COS 4.0+ * @property startingSignature: Starting signature of the file data. You can use `FileHashHelper` to generate * signatures or * use it as a reference to create the signature yourself @@ -148,6 +181,7 @@ sealed class FileToWrite { */ data class ByFileOwner( val data: ByteArray, + val fileName: String?, val startingSignature: ByteArray, val finalizingSignature: ByteArray, val counter: Int, diff --git a/tangem-sdk-core/src/main/java/com/tangem/operations/files/FileHashHelper.kt b/tangem-sdk-core/src/main/java/com/tangem/operations/files/FileHashHelper.kt index 1b9b7023..cd5dfed1 100644 --- a/tangem-sdk-core/src/main/java/com/tangem/operations/files/FileHashHelper.kt +++ b/tangem-sdk-core/src/main/java/com/tangem/operations/files/FileHashHelper.kt @@ -23,8 +23,11 @@ class FileHashHelper { cardId: String, fileData: ByteArray, fileCounter: Int, + fileName: String? = null, privateKey: ByteArray? = null ): FileHashData { + val fileData: ByteArray = fileName?.let { NamedFile(it, fileData).serialize() } ?: fileData + val startingHash = cardId.hexToBytes() + fileCounter.toByteArray(4) + fileData.size.toByteArray(2) val finalizingHash = cardId.hexToBytes() + fileData + fileCounter.toByteArray(4) diff --git a/tangem-sdk-core/src/main/java/com/tangem/operations/files/WriteFileCommand.kt b/tangem-sdk-core/src/main/java/com/tangem/operations/files/WriteFileCommand.kt index d44c7a19..a2931733 100644 --- a/tangem-sdk-core/src/main/java/com/tangem/operations/files/WriteFileCommand.kt +++ b/tangem-sdk-core/src/main/java/com/tangem/operations/files/WriteFileCommand.kt @@ -276,7 +276,7 @@ class WriteFileCommand private constructor( operator fun invoke(file: FileToWrite): WriteFileCommand = when (file) { is FileToWrite.ByFileOwner -> { WriteFileCommand( - data = file.data, + data = file.payload, startingSignature = file.startingSignature, finalizingSignature = file.finalizingSignature, counter = file.counter, @@ -286,7 +286,7 @@ class WriteFileCommand private constructor( } is FileToWrite.ByUser -> { WriteFileCommand( - data = file.data, + data = file.payload, fileVisibility = file.fileVisibility, walletPublicKey = file.walletPublicKey, ) diff --git a/tangem-sdk-core/src/test/java/com/tangem/common/JSONRPCTests.kt b/tangem-sdk-core/src/test/java/com/tangem/common/JSONRPCTests.kt index 4391f0e4..f2a1dc70 100644 --- a/tangem-sdk-core/src/test/java/com/tangem/common/JSONRPCTests.kt +++ b/tangem-sdk-core/src/test/java/com/tangem/common/JSONRPCTests.kt @@ -197,7 +197,12 @@ class JSONRPCTests { fun testFiles() { testMethod( "files/ReadFiles", listOf( - File("00AABBCCDD".hexToBytes(), 0, FileSettings(false, FileVisibility.Public)) + File( + name = null, + fileData = "00AABBCCDD".hexToBytes(), + fileIndex = 0, + fileSettings = FileSettings(false, FileVisibility.Public) + ) ) ) testMethod("files/DeleteFiles", SuccessResponse("c000111122223333")) @@ -220,18 +225,18 @@ class JSONRPCTests { val jsonMap = converter.toMap(readJson(name)) val jsonRequest = converter.toJson(jsonMap["request"]) val request: JSONRPCRequest = - assertDoesNotThrow("Json conversion failed to structure for $name") { - JSONRPCRequest(jsonRequest) - } + assertDoesNotThrow("Json conversion failed to structure for $name") { + JSONRPCRequest(jsonRequest) + } assertDoesNotThrow("Conversion to JSONRPC Failed. File: ${name}") { jsonRpcConverter.convert(request) } val jsonResponse: JSONRPCResponse? = - jsonMap["response"]?.let { converter.toJson(it).let { converter.fromJson(it) } } + jsonMap["response"]?.let { converter.toJson(it).let { converter.fromJson(it) } } val result: CompletionResult? = - response?.let { CompletionResult.Success(it) } + response?.let { CompletionResult.Success(it) } if (jsonResponse != null && result != null) { val jsonResponseMap = converter.toMap(jsonResponse) val resultJsonRpcResponse = when (result) { diff --git a/tangem-sdk-core/src/test/resources/jsonRpc/files/WriteFiles.json b/tangem-sdk-core/src/test/resources/jsonRpc/files/WriteFiles.json index f1d3bf0a..b7752fee 100644 --- a/tangem-sdk-core/src/test/resources/jsonRpc/files/WriteFiles.json +++ b/tangem-sdk-core/src/test/resources/jsonRpc/files/WriteFiles.json @@ -8,6 +8,10 @@ { "data": "AABBCCDDEEFF" }, + { + "data": "AABBCCDDEEFF", + "fileName": "File 1" + }, { "data": "00000000" },