diff --git a/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt b/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt index 31ca17f0dd..db7e155e92 100644 --- a/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt +++ b/app/src/main/java/com/tangem/ui/fragment/wallet/LoadedWalletFragment.kt @@ -30,6 +30,7 @@ import com.tangem.server_android.model.CardVerifyAndGetInfo import com.tangem.tangem_card.data.TangemCard import com.tangem.tangem_card.reader.CardProtocol import com.tangem.tangem_card.tasks.VerifyCardTask +import com.tangem.tangem_card.tasks.WriteIDCardTask import com.tangem.tangem_card.util.Util import com.tangem.tangem_sdk.android.reader.NfcReader import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD @@ -55,6 +56,8 @@ import kotlinx.android.synthetic.main.layout_tangem_card.* import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import java.io.InputStream +import java.nio.charset.StandardCharsets +import java.text.SimpleDateFormat import java.util.* import kotlin.concurrent.timerTask @@ -525,6 +528,19 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte else updateViews() + // TODO - remove, only for test&demo reason! + // TODO - begin + if( cardProtocol.card.isIDCard) + { + if( cardProtocol.card.hasIDCardData() ) + { + Toast.makeText(activity, "ID card: ${cardProtocol.card.idCardData.fullName}", Toast.LENGTH_SHORT).show() + }else{ + Toast.makeText(activity, "Empty ID card", Toast.LENGTH_SHORT).show() + } + } + // TODO - end + mpSecondScanSound.start() } } else { @@ -789,6 +805,65 @@ class LoadedWalletFragment : BaseFragment(), NavigationResultListener, NfcAdapte val uid = tag?.id val sUID = Util.byteArrayToHexString(uid) if (ctx.card.uid != sUID || cardProtocol != null) { + + //TODO - remove, only for test + //TODO - begin + if( ctx.card.isIDCard && !ctx.card.hasIDCardData() ) + { + val idCardData=TangemCard.IDCardData() + idCardData.fullName="John Doe" + idCardData.birthday="1980-1-1" + idCardData.gender="M" + val dt= Date() + idCardData.issueDate= SimpleDateFormat ("yyyy-MM-dd", Locale.US).format(dt) + dt.year = dt.year + 10 + idCardData.expireDate=SimpleDateFormat("yyyy-MM-dd", Locale.US).format(dt) + idCardData.trustedAddress="GCOQLQT32RVH2MTWQGK5K3GIGT3STYG5323KMNTTFHIU2T5TFOGR4KDT" // ask D.Baturin + idCardData.photo=Util.hexToBytes("FFD8FFE000104A46494600010101012C012C0000FFDB0043000A07070807060A0808080B0A0A0B0E18100E0D0D0E1D15161118231F2524221F2221262B372F26293429212230413134393B3E3E3E252E4449433C48373D3E3BFFDB0043010A0B0B0E0D0E1C10101C3B2822283B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3B3BFFC000110800C8007903012200021101031101FFC4001C0000020203010100000000000000000000000706080103050402FFC4003F100001030301050505060406020300000001020304000511060712213141135161718114223291A115235262B1C1428292D10816333472A2175393D2E1FFC4001801000301010000000000000000000000000002030104FFC4002111000202030100020301000000000000000001021103213112132232415142FFDA000C03010002110311003F007351451400562B35A83ED29F53097505D424294807DE4839C123B8E0FCA8016BB4CD712AC97FB642B72FDF8AA12A4273C179C84A0F811BC7D45306CF768B7BB547B9425EFB0FA3793DE3BC1F10720D579D75297335C5E1D5924A64A9B19EE4FBA3F4AECECDB5B7F96AE2604E708B64B57BC4F2617C82FC8F23E87A54164FB6C771D0FBA2BE50B4AD0149214923208E44566AE219A28A2800A28A2800A28A2800A28A2800A28A2803E5440049E02ABEFF009F26C5DA0C9D44CA94B69C74B6A673C16C03809F900478D580791DAB2B6F38DF494E7CEAA9BAD165E5B4AF89B5149F307151CADAAA1A28ECEB35B0FEAB9D322381C8D31424B4B1D52B483F30720F8835C4A3248009381CBC28AE76ED9444C7496D2AEDA65B44375227C04F04B2E2B0A6C7E45741E07879533AD7B55D2B7040ED662A0B879A2520A47F50C8FAD2028A78E468571459E6F53D85E46FB77AB7A93DE24A3FBD706EBB54D2D6DCA5B98A9CE0FE18A8DE1FD4709FAD57EC03D07CAB34CF3333C967B4E5F19D47638D7561B53687C1FBB5104A48241071E55EE932E3C3614FCA7DB61A4FC4E38A0948F5355F6C1B43BBE9BB02ED36F6D8CA9E538979D05451903202797319E3DFCAB8574BD5CEF6FF006D739CF4B5E78768AC84F90E43D053FCAA8CF2588B36AFB46A0B9C8856A7952BD99016E3C9410D8C9C0009E679F2E1C2BBB4BED8EDA7D8B4A393D69C393DE2A07F227DD4FD778FAD306A916DAB6630A28A298C31466B54A694FC575943CB654B414A5C47C4824731E2290978D5FAE2C976936C977B901E8EB2927711850E8A1EEF22307D69252F26A564CEFF00B49BCE92D48FDB6E36D8F2E383DA30EB6A2D296D9E5DE091C41F2A52DD65353AEF32630D29A6A43EB752DA8E4A428938FAD6FBBDFEEB7E5B4BBACD5CA532084296948290798E0077573AB9A53B2895051582A006490078D7C7B4320E3B44FCE9526CD3651520B268ABA6A0483024DB5448CEE19C82BF54A72457A6E9B37D51698AECA760B6FB2CA0AD6A61E4A8800649C7034530B22D45789570E3EEB7F335F22E0BEADA4F91A7F8A467A47BE83E15E76A634E1C1F70F8D7A291C5AE9A9D8D7B0ED76D36BB544B6AACB2D0DC6692D02D3885E70319C1C73A6258AFE9BEB1DBB76DB8446F190A96C86F7BCB8E4D56C813E55B26B53613CA62432ADE42D38C83EB4E2D15B556EF1218B5DE582CCD748436F3292A43A7C40E293F4F2ABC277A62490C8A2B159AB8862951B68B75B0B712E224B2D5C53F765927DF79BEFC7E53D4F426999738D2E5C25330E7182EAB876C9682D491E00F0CF8F1A40EBEB55AECB75EC5ABFBD75B82944CB5BC527B33D0139E2AF0E953C9C1A3D22F5A24C8EC123032A3CBBAB7020F2391E15BA2C11739B1E0938F68790D820711BCA033F5AE68D5EC77C3970E15C2F32D31E1467A5BEAE4DB482A3F21D2A52C6C8F5A3CD05FD9496F3C92E486D27E59A7BE9ED3569D236AF6580D25A42465E7DC237DC3F894AFDB90AC2F58E986DEEC57A86D895E71BA6523FBD59E47FE5095FD10917671AA23DFE0439B6A931DB7E4210A7D1EF21033C4EF24903866AC9169B2D762520B653BBBA78E538C63E55861F664B21E8EF21D695C96DA82927D452FB5D6D43EC49E6C7608E275D490859DD2A4B4A3FC21238A95E1C878F2A46DCDD1BA479E06C32C2D2D6B9B3A649CA894A1B21B4A46780EA4FCC56ABEEC36D521ADFB24C7A23C07FA7215DA215EB8C8FAD781AD21B56D449126E17E55B82F88695254D91FC8D8C0A1CD27B58D3A3B7817D55C528E3D9A649709FE57471F4AAF99FF004CB42E351E8FBDE977B72E509686C9C21E4FBCDABC94387EF5E78A9712C80E1C9E80F414D381B4C45E62CBD2FACA10B7497DB5305F282942564606FA4F141CE0E797952CD68536B5217F12490AF31CEA7372AA66C523E69F7B33B169F896166E76B57B5497D387A4B806FA55D518FE1C1E9D79E4D2523E9BD473612AE10EC52DF86919ED529F8C0E6523991E40D31F64F11C5155CAC3770EC65283770B74B6F716D9E8A041209EE381919071D371C5A7612636EB358159AE810876D3F53BDA6748BAEC3514CD96B11E3A8734920E543C40071E24579F44681B6E9EB434E4B88D49BA3C90B92FBC80B2147894A73C80E5E278D7236C984CCD2AE3BFEDD370FBDCF2E68E7E80D320F33E751CADF068A10FB53B4C5B56AF262212DA2530878B6848012AC949C01DFBB9F9D462D17362CD798572928538DC5792E96D3CD653C401EB8A97ED817BDACDB4FE086D8FAA8D426CD6A3A8355DBACFBC52890F250B23984F3511E80D4A11F5219BA44D615A358ED764AA6CE99EC1670B3B8307B3183C908CFBE47551F9F4AF5DE767DB39D33B912F5A9673531C00E13BAA207794A50703CE9CD162B16F84DC68ACA5A61840436DA0704A40E005548BD5CA45DEF32EE129654F48754B513D38F01E4070F4AEBAA264F275B6FDB305C7BEE9DBBA6E165998DD71232DAB23805A738F250EA3A72AEF6C36CAD4D72E5A9660EDE507BB1696BE25248DE5ABCCEF019F3EFAD5616C9FF0F574F6FE2CEF3AA8FBDD3DF4EEE3F9F35AF613A8988F26769F7D610B92A1223E7F89406149F3C007D0D0074F52EDBBEC8D46F5BADF6B6E5468AE169D756E9495A81C2B7703000391939CD31B4E5FE16A7B247BAC1512D3C38A55F121438149F106AB56BBB1CAB06AFB846908210E3CA759591C16DA89208EFE783E20D3736131E533A3E53AF052587A6294CE7AE1290A23C3231E8680241AFB41C1D636A5E1B437736924C6918C1CFE051EA93F4E755CD80F34F3B0DE4143CD28A0A55CC107047A1AB7279555FD6FD89DA4DE7D9F1B9ED4BCE3F163DEFF00B66926AE2CD5D2C6DAA208369870C0C7B3B086FE490297D15B1A676EC23C61D9C5BEC52B5B63E10BC28938FF009209FE6358D9AEBF125B458AF2F80F21388D21C57FA807F0289EA0723D478F3F88529BD5FB6F6675BD41E83648A52A7D1C50A5614381EBEF2CE3BF74D47174690D8159AC0ACD748847B5B694635869D76D8EB9D93A141C61DC6771C1C8F910483E06A0D17556BDD22C2605F74BBD7669801089914925491C8920107D403DF4DAAC1AC693E8595A3575FD7A9750BD725C572265296C30E1CA91BA3183E39CD78ECD25FD35A82D1A95D654A862491BC3AEEE02C79EEAABEF51C77226A3B8B0F496E53A990BED1D6F3BAA51393CFC4E3D29A7A2B4BDBB516CB99B7DC992B69F7DD752A4F0536ADE202927A1E15CD19799146AD0C5833A2DCA135321BE87E3BC90A6DC41C850A5DDD7629619B7B76E7EDF222C57165D7632027033C4E147E11E87151FFF00C6DAEF4BBEBFF2B5F7B48CA39DD4BDD913FF00242BDD27C73515D6E35D4211A2EA6B9BCEFB58514474C90A07040C94A78733C2BA1493E13A247AF351C7BF983A0B46321D8ADAD285167E070A79241EA91CCABA919E993D0D47B227A1DAA04ED32E94DD203490E84AB74BEB1C77D27A2B3D3A8C75E732D13A12D9A420A16D35DA5C1D6C07E439C559C64A53DC9CFCFAD4AAA32C8EF43A889DB6ED3AD5706D169DA1D8D0B9118E0BEE460BC1EF5208CA4F7E39F753021EBED12DC34262DF6DECB084E10D85767BA3B827031F2AF6DE74C593502026ED6D625103016A4E163C943047CE92BB50D176BD2371B649B7B0E88127783AD29D2AC292412013C46527E9548E452D18D5135D5BB67B6468AB89A68AA7CD706EA1EECC86DB27A8CF151EE0063F4A80CDD0D3ECFA3D3A8AEFBE89F2E5A53D8B87DE4A141472AFCC4E0E3A0F3A72E9DD17A5ECA96A65A6D8D071690B4487097178232082ACE3D315AF5E69995AAACCCC18D25A8FD9BE1E5A9C04E40491818F3A9CF25E91A908BB346B5CC9E98F769CEC261CE01F4341C4A4FE6191C3C45582D17A6E1699B0B70E1A98782CEFAA4328DDEDF3C94789CF0F1C776290B61F6190B7ED53FB3684C012CCB50FF6EF0CEE927F01CEEABCC1E94E6D9DE96BE69A82A6AE972438CAC6510D037C327A90B3FA018ADC46489AD158ACD5C50A8D6BBD4A9D33A69F94850F6B7BEE62A7BD67AFA0C9F4A92D41B6A3A818B1D89296D0DAAE52829A8CB29054D24FC6B07A70C0F323BA964E91A8439254A2A512493924F326AC2ECF59EC7425A138C6F31BFF00D4A27F7AAF237411BD9DDEB8EEAB31A762A6169CB6C54AD2B0D456D2149E4AF747115C654E8D26F5AA7EDADB7D9ADABE2DB1D82549F0C9715F4A7252727FDCFF0088C8CB74E12B537BB9F16303EB54C7D62C871F3E3DF59AC0E559A98C14BADB6C44BFA25A7F1EF4798820F8282927F6A62D2EB6D935B8FA25B8C4FBF265A02478241513FA7CE9E1F9215F092E8394A99A12CAF2CE55EC88493DFBBEEFED5DD75412DA94790493F4AE0E828AB85A12CCCB80857B225641E9BDEF7EF5EBD513D36BD2F729AA38ECA32F77FE44607D48A57D3570AD8A3BCA51E849A766CAB5A7DAF6FF00B127BB99B111F74A51E2F343F529E47C307BE92206001DD5E9B7CF936AB8313E1BA5A911D616DA8743FD8F235B097960D596A6B35C4D29A9236A8B1B370630959F75E6B3C5A58E63F71E04576EBAD3B247CA88032780155B3596A273536A491394486527B28E83FC2D83C3D4F127CE9F7AC2E3F65692BA4D070A6E32820FE623753F522AB3018E1DD51CCFF43C4CD3E76617B4DDB4830C2D797E01F677013C7747141FE9E1E8690D5DDD21AAA4E93BB898D23B561C1B9219CE37D3E1DC474FFF006B9C72C6528B6C76D936BBCDA757C2202D85A1A59CF25A49520F8E4647A577646D92C0DC7DE8F1273CEE3820A12819F1564FE94B2D57AB2E1ABA6076684B6C3790CC647C280799F127BE9E32A7663563CB4BEAEB5EACB737260BE80F1482EC62A1DA34AEA08EEEE3C8D77307B8FCAAA608AA69C0E47794D287220E08F222BD2A9B7B5A77177896A477190B23E59A6A8BE3336593BDEA8B2E9D8EA76E97165820706F7B79C578048E26945DA4FDB16B76886571EC9031BC09E286C9C9C9FC6BC63872C7852FC420A5153AE2964F3F1F5A9468BD52EE8FBA990D33DAC6753B8FB20E37873041EF1FDE8B8C79D0A6CB148425B42508484A523000E400E94ABDAF6A76D486F4EC570294141D9641F871F0A3CFA9F4AF8BFED8FB786A62C509D61D58C191237728FF008A46727C4FCA960E38E3CEADD756A71C5A8A94B51C9513CC935318F9A28A2B00636CD6DFAAECD3A3DCE2DB1C91699C901EDD751EF273C16015734FE9914EAE3E1503D8F5C7DAF46988A56550A42DBC7E557BC3F53F2A9ED76415224FA2F76CD70F66D28C4249C2A6494823BD281BC7EBBB490A636DA6E3DBEA285012ACA62C6DF50EE52CFF00648A5CD73E4772291E05145153342B5B921B68E16AC1EEC56CAF9536851CA90924778AD55FB035A65B0A380B03CC62B766B52E3B2B182D8F4E15AA3EF32FA982494E32934F49AD196CF551451533428A28A0028A283C050032B62971EC6F770B7295812180EA47E641C1FA2BE94E7CD577D1E6569BD79685CB41692FA90013F0ADB75380A07A8E23E5561BDEEEAEAC4FEB44E5D14ACE8E7B5DEB6B85F26A94DD9848DC694382A4A5184809EE4F0E2AF9782BA404894E840C2438A091DC327156A5C09663AB7404A508380060000555351DE515779CD4F2248D8B31451454470A28A2800A31451400514514005145140051451400E6D2F6585ADB6690187CF6732DEA5B6C4948CA995A5594F98C6EE454DF7F50FF00E9B77FF22FFF00AD2F762170CB374B6A95F0A90FA0798293FA0A6BD76436AC93E9E4B9AFB3B5CB5FE16167FEA6AAEC3892273A962334A75D282ADD4F3C049513E8013566F512FB3D37735E71BB0DD3FF0043506D9368F4C1B3AAF539A1ED13DBDD692A1F0327F7573F2C52CE3E9A46A7484C03919A2BD57582AB65DA64058C18CFADAF92881F4C5796B95940A28A2800A28A2800A28A2800A28A2803D56CB6C8BBDC1B811021521EC86D2B504EF9033BA09E1938E15F1360CBB74A5459B19D8CFA3E26DD4949AF88D25E8729A951D5B8F32B0E36A1D140E47E95635B8D65D75A6A24B9B09A92CC9682D3BC3DE6D5D40573041C8E1DD548414918DD0A2D935C3D8B5CB0D156133195B27CF1BC3EA9FAD3F37877D2AA4ECAE6586FB0EF1A7A47B5371A421D319E2038003C40572570CF3C7AD357753DD57C69A54C9B354B8ACCE88EC5909DF65E414389CFC493C08AD8942508094A425206001C80AFBAC550C2BFED56DDEC1AEA4B813BA898DA1F1E646E9FAA6A1D4DADB75BB2D5AEE694FC2A5C759F31BC9FD15503D21A4A66ADBA88AC65B8CDE15224638369F0EF51E83F6AE49C7ED48A27A38C224930CCC0C39ECC970345EDD3BA16464273DF8AD5565D5A4ED074C2B4EA6304C12DEE6E8F8B3F8F3F8B3C73DF55EF50D865E9BBD3F6C9832A6CE50B0301C41E4A1E7F439144F1F904ECE6515BE14291719CCC288D175F7D610DA075269FB6ED9D5958D22DD866C74C827EF1D7C7059748E2A49E631C8780AC841C8D6E8AF4738E0327A0A91EAAD1772D2A63BB2125D8B21092979238256464A15DC471C778AEF3BB31B95A758DB23A92A976C7E52712529F8520EF10B1D0E0791FA53A2E16F897582EC29CC25F8EF2775685F223F63E34F1C769D8AE4557A9AECE34546D5AEDC173CBA88CC361085B6AC10E2B883E3800F0F115E4D73A1A56919BDA2379FB6BCAFB97C8E293F817DC7C7AD37B67164FB1346C36DC4EEBF247B43BDF957103D13814421F6A66B7A14FAAB66B7AD39BF21A419F053C7B7653EF207E74F31E6323CAA5BB16BF769165D89D5E4B47DA18C9FE13C163D0E0FF0035353151D73455ADBD431EFB6F47B0CD6944B8591843E9230A0A4F2E3DE3AF7D51429DA16F448AB358159AA8A1451450047B5BE9B56A9D3ABB6B4E21A77B66DC42D7C9385713FD2555EDD3F6083A6ED4D5BA037BADA38A967E2715D54A3D49A28ACA5760752A21B43D1C9D5566DF8E948B8C50551D5CB7FBD04F71E9DC71E34514356A80E1ECA345AADB14DFAE2C944B7D2531DB58C169BEA48E855FA79D32A8A2B22A91AC3145145318689B062DC62391263087D874616DAC642856F4A424000600E94514019A28A2800A28A2803FFFD9") + + val writeTask=WriteIDCardTask(ctx.card, cardProtocol!!.reader,App.localStorage, App.pinStorage, idCardData, ctx.card.issuer.privateDataKey, + object: CardProtocol.Notifications{ + override fun onReadProgress(cardProtocol: CardProtocol?, progress: Int) { + LOG.i("WriteIDCardTask","onReadProgress: ${progress}%") + } + override fun onReadWait(msec: Int) { + } + override fun onReadStart(cardProtocol: CardProtocol?) { + LOG.i("WriteIDCardTask","onReadStart") + rlProgressBar?.post { rlProgressBar?.visibility = View.VISIBLE } + + } + override fun onReadCancel() { + LOG.i("WriteIDCardTask","onReadCancel") + rlProgressBar?.postDelayed({ + try { + rlProgressBar?.visibility = View.GONE + } catch (e: Exception) { + e.printStackTrace() + } + }, 500) + + } + override fun onReadFinish(cardProtocol: CardProtocol?) { + LOG.i("WriteIDCardTask","onReadFinish") + rlProgressBar?.postDelayed({ + try { + rlProgressBar?.visibility = View.GONE + } catch (e: Exception) { + e.printStackTrace() + } + }, 500) + + } + override fun onReadAfterRequest() { + } + override fun onReadBeforeRequest(timeout: Int) { + } + }) + writeTask.start() + return + } + //TODO end (activity as MainActivity).nfcManager.ignoreTag(isoDep.tag) return } diff --git a/build.gradle b/build.gradle index c4edca4c38..dde59cbded 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.3.60' + ext.kotlin_version = '1.3.61' repositories { google() jcenter() diff --git a/tangem-card-old/src/main/java/com/tangem/tangem_card/data/TangemCard.java b/tangem-card-old/src/main/java/com/tangem/tangem_card/data/TangemCard.java index 8fe19575d6..0fad9c1874 100644 --- a/tangem-card-old/src/main/java/com/tangem/tangem_card/data/TangemCard.java +++ b/tangem-card-old/src/main/java/com/tangem/tangem_card/data/TangemCard.java @@ -1,9 +1,14 @@ package com.tangem.tangem_card.data; import com.tangem.tangem_card.reader.CardProtocol; +import com.tangem.tangem_card.reader.ProductMask; import com.tangem.tangem_card.reader.SettingsMask; +import com.tangem.tangem_card.reader.TLV; +import com.tangem.tangem_card.reader.TLVList; import com.tangem.tangem_card.util.Util; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Date; import java.util.EnumSet; @@ -288,6 +293,28 @@ public class TangemCard { return ""; } + private byte[] issuerDataEx; + private byte[] issuerDataExSignature; + private int issuerDataExCounter; + + public byte[] getIssuerDataEx() { + return issuerDataEx; + } + + public byte[] getIssuerDataExSignature() { + return issuerDataExSignature; + } + + public int getIssuerDataExCounter() { + return issuerDataExCounter; + } + + public void setIssuerDataEx(byte[] value, byte[] signature, int counter) { + issuerDataEx = value; + issuerDataExSignature = signature; + issuerDataExCounter = counter; + } + private int pauseBeforePIN2 = 0; public void setPauseBeforePIN2(int value) { @@ -579,6 +606,79 @@ public class TangemCard { this.UID = UID; } + public int productMask = ProductMask.Note; + + public void setProductMask(int productMask) { + this.productMask = productMask; + } + + public boolean isIDCard() { + try { + return (productMask & ProductMask.IdCard) != 0; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + TLVList tlvIDCardData = null; + + public boolean hasIDCardData() { + if (tlvIDCardData == null) { + if (issuerDataEx == null) return false; + try { + tlvIDCardData = TLVList.fromBytes(issuerDataEx); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + return tlvIDCardData.hasTag(TLV.Tag.TAG_FullName) && tlvIDCardData.hasTag(TLV.Tag.TAG_Birthday) && tlvIDCardData.hasTag(TLV.Tag.TAG_Gender) && tlvIDCardData.hasTag(TLV.Tag.TAG_Photo) + && tlvIDCardData.hasTag(TLV.Tag.TAG_IssueDate) && tlvIDCardData.hasTag(TLV.Tag.TAG_ExpireDate) && tlvIDCardData.hasTag(TLV.Tag.TAG_TrustedAddress); + } + + public static class IDCardData + { + public String fullName; + public String birthday; + public String gender; + public byte[] photo; + public String issueDate; + public String expireDate; + public String trustedAddress; + + public TLVList toTLVList() { + TLVList tlvIDCardData=new TLVList(); + tlvIDCardData.add(new TLV(TLV.Tag.TAG_FullName,fullName.getBytes(StandardCharsets.UTF_8))); + tlvIDCardData.add(new TLV(TLV.Tag.TAG_Birthday,birthday.getBytes(StandardCharsets.UTF_8))); + tlvIDCardData.add(new TLV(TLV.Tag.TAG_Gender, gender.getBytes(StandardCharsets.UTF_8))); + tlvIDCardData.add(new TLV(TLV.Tag.TAG_Photo, photo)); + tlvIDCardData.add(new TLV(TLV.Tag.TAG_IssueDate,issueDate.getBytes(StandardCharsets.UTF_8))); + tlvIDCardData.add(new TLV(TLV.Tag.TAG_ExpireDate,expireDate.getBytes(StandardCharsets.UTF_8))); + tlvIDCardData.add(new TLV(TLV.Tag.TAG_TrustedAddress,trustedAddress.getBytes(StandardCharsets.UTF_8))); + return tlvIDCardData; + } + + public static IDCardData fromTLVList(TLVList tlvIDCardData) + { + IDCardData result=new IDCardData(); + result.fullName=tlvIDCardData.getTLV(TLV.Tag.TAG_FullName).getAsString(); + result.birthday=tlvIDCardData.getTLV(TLV.Tag.TAG_Birthday).getAsString(); + result.gender=tlvIDCardData.getTLV(TLV.Tag.TAG_Gender).getAsString(); + result.photo=tlvIDCardData.getTLV(TLV.Tag.TAG_Photo).Value; + result.issueDate=tlvIDCardData.getTLV(TLV.Tag.TAG_IssueDate).getAsString(); + result.expireDate=tlvIDCardData.getTLV(TLV.Tag.TAG_ExpireDate).getAsString(); + result.trustedAddress=tlvIDCardData.getTLV(TLV.Tag.TAG_TrustedAddress).getAsString(); + return result; + } + } + + public IDCardData getIDCardData() + { + if( !hasIDCardData() ) return null; + return IDCardData.fromTLVList(tlvIDCardData); + } private byte[] offlineBalance; @@ -638,164 +738,6 @@ public class TangemCard { this.tagSignature = tagSignature; } -// public Bundle getAsBundle() { -// Bundle B = new Bundle(); -// saveToBundle(B); -// return B; -// } -// -// public void saveToBundle(Bundle B) { -// try { -// B.putString("UID", UID); -// B.putByteArray("CID", CID); -// B.putString("PIN", PIN); -// B.putString("PIN2", PIN2.name()); -// B.putString("Status", status.name()); -// B.putString("Blockchain", blockchainID); -// B.putString("BlockchainName", blockchainName); -// B.putInt("TokensDecimal", tokensDecimal); -// B.putString("TokenSymbol", tokenSymbol); -// B.putString("ContractAddress", contractAddress); -// if (dtPersonalization != null) B.putLong("dtPersonalization", dtPersonalization.getTime()); -// B.putInt("RemainingSignatures", remainingSignatures); -// B.putInt("MaxSignatures", maxSignatures); -// B.putInt("Health", health); -// if (settingsMask != null) B.putInt("settingsMask", settingsMask); -// B.putInt("pauseBeforePIN2", pauseBeforePIN2); -// if (signingMethod != null) B.putString("signingMethod", signingMethod.name()); -// if (manufacturer != null) B.putString("Manufacturer", manufacturer.name()); -// if (encryptionMode != null) B.putString("EncryptionMode", encryptionMode.name()); -// if (issuer != null) B.putString("Issuer", issuer.getID()); -// if (issuerPublicDataKey != null) B.putByteArray("IssuerPublicDataKey", issuerPublicDataKey); -// if (firmwareVersion != null) B.putString("FirmwareVersion", firmwareVersion); -// if (batch != null) B.putString("Batch", batch); -// B.putBoolean("ManufacturerConfirmed", manufacturerConfirmed); -// B.putBoolean("CardPublicKeyValid", isCardPublicKeyValid()); -// B.putByteArray("CardPublicKey", getCardPublicKey()); -// -// B.putInt("SignedHashes", getSignedHashes()); -// B.putBoolean("WalletPublicKeyValid", isWalletPublicKeyValid()); -// if (pbWalletKey != null) -// B.putByteArray("PublicKey", pbWalletKey); -// if (pbWalletKeyRar != null) -// B.putByteArray("PublicKeyRar", pbWalletKeyRar); -// -// if (getOfflineBalance() != null) B.putByteArray("OfflineBalance", getOfflineBalance()); -// -// if (getDenomination() != null) B.putByteArray("Denomination", getDenomination()); -// if (getDenominationText() != null) B.putString("DenominationText", getDenominationText()); -// -// if (getIssuerData() != null && getIssuerDataSignature() != null) { -// B.putByteArray("IssuerData", getIssuerData()); -// B.putByteArray("IssuerDataSignature", getIssuerDataSignature()); -// B.putBoolean("NeedWriteIssuerData", getNeedWriteIssuerData()); -// } -// -// if (codeConfirmed != null) -// B.putBoolean("codeConfirmed", codeConfirmed); -// -// if (codeConfirmed != null) -// B.putBoolean("codeConfirmed", codeConfirmed); -// -// if (onlineVerified != null) -// B.putBoolean("onlineVerified", onlineVerified); -// -// if (onlineValidated != null) -// B.putBoolean("onlineValidated", onlineValidated); -// -// if (codeConfirmed != null) -// B.putBoolean("codeConfirmed", codeConfirmed); -// -// if (codeConfirmed != null) -// B.putBoolean("codeConfirmed", codeConfirmed); -// -// if (onlineVerified != null) -// B.putBoolean("onlineVerified", onlineVerified); -// -// if (onlineValidated != null) -// B.putBoolean("onlineValidated", onlineValidated); -// } catch (Exception e) { -// Log.e("Can't save to bundle ", e.getMessage()); -// } -// -// } -// -// public void loadFromBundle(Bundle B) { -// UID = B.getString("UID"); -// CID = B.getByteArray("CID"); -// PIN = B.getString("PIN"); -// PIN2 = PIN2_Mode.valueOf(B.getString("PIN2")); -// status = Status.valueOf(B.getString("Status")); -// blockchainID = B.getString("Blockchain"); -// tokensDecimal = B.getInt("TokensDecimal", 18); -// tokenSymbol = B.getString("TokenSymbol", ""); -// contractAddress = B.getString("ContractAddress", ""); -// if (B.containsKey("BlockchainName")) -// blockchainName = B.getString("BlockchainName", ""); -// if (B.containsKey("dtPersonalization")) { -// dtPersonalization = new Date(B.getLong("dtPersonalization")); -// } -// remainingSignatures = B.getInt("RemainingSignatures"); -// maxSignatures = B.getInt("MaxSignatures"); -// health = B.getInt("health"); -// if (B.containsKey("settingsMask")) settingsMask = B.getInt("settingsMask"); -// pauseBeforePIN2 = B.getInt("pauseBeforePIN2"); -// if (B.containsKey("signingMethod")) -// signingMethod = SigningMethod.valueOf(B.getString("signingMethod")); -// if (B.containsKey("Manufacturer")) -// manufacturer = Manufacturer.valueOf(B.getString("Manufacturer")); -// manufacturerConfirmed = B.getBoolean("ManufacturerConfirmed"); -// if (B.containsKey("EncryptionMode")) -// encryptionMode = EncryptionMode.valueOf(B.getString("EncryptionMode")); -// else -// encryptionMode = null; -// -// if (B.containsKey("SignedHashes")) setSignedHashes(B.getInt("SignedHashes")); -// -// if (B.containsKey("Issuer")) issuer = Issuer.FindIssuer(B.getString("Issuer")); -// if (B.containsKey("IssuerPublicDataKey")) -// issuerPublicDataKey = B.getByteArray("IssuerPublicDataKey"); -// -// if (B.containsKey("FirmwareVersion")) firmwareVersion = B.getString("FirmwareVersion"); -// if (B.containsKey("Batch")) batch = B.getString("Batch"); -// -// cardPublicKeyValid = B.getBoolean("CardPublicKeyValid"); -// if (B.containsKey("CardPublicKey")) setCardPublicKey(B.getByteArray("CardPublicKey")); -// -// if (B.containsKey("OfflineBalance")) setOfflineBalance(B.getByteArray("OfflineBalance")); -// else clearOfflineBalance(); -// -// if (B.containsKey("Denomination") && B.containsKey("DenominationText")) { -// setDenomination(B.getByteArray("Denomination"), B.getString("DenominationText")); -// } else if (B.containsKey("Denomination")) { -// setDenomination(B.getByteArray("Denomination"), "N/A"); -// } else clearDenomination(); -// -// if (B.containsKey("IssuerData") && B.containsKey("IssuerDataSignature")) -// setIssuerData(B.getByteArray("IssuerData"), B.getByteArray("IssuerDataSignature")); -// else setIssuerData(null, null); -// -// if (B.containsKey("NeedWriteIssuerData")) -// setNeedWriteIssuerData(B.getBoolean("NeedWriteIssuerData")); -// -// walletPublicKeyValid = B.getBoolean("WalletPublicKeyValid"); -// if (B.containsKey("PublicKey")) { -// pbWalletKey = B.getByteArray("PublicKey"); -// } -// if (B.containsKey("PublicKeyRar")) { -// pbWalletKeyRar = B.getByteArray("PublicKeyRar"); -// } -// -// if (B.containsKey("codeConfirmed")) -// codeConfirmed = B.getBoolean("codeConfirmed"); -// -// if (B.containsKey("onlineVerified")) -// onlineVerified = B.getBoolean("onlineVerified"); -// -// if (B.containsKey("onlineValidated")) -// onlineValidated = B.getBoolean("onlineValidated"); -// } - public enum EncryptionMode { None((byte) 0x0), Fast((byte) 0x1), Strong((byte) 0x2); diff --git a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/CardProtocol.java b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/CardProtocol.java index 94e1a7b799..518318a219 100644 --- a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/CardProtocol.java +++ b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/CardProtocol.java @@ -1,5 +1,6 @@ package com.tangem.tangem_card.reader; +import com.tangem.tangem_card.data.Issuer; import com.tangem.tangem_card.data.Manufacturer; import com.tangem.tangem_card.data.TangemCard; import com.tangem.tangem_card.util.Log; @@ -17,6 +18,7 @@ import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.ECGenParameterSpec; +import java.util.Arrays; import javax.crypto.KeyAgreement; @@ -99,6 +101,11 @@ public class CardProtocol { private NfcReader mIsoDep; + public NfcReader getReader() + { + return mIsoDep; + } + public static final String DefaultPIN = "000000"; public static final String DefaultPIN2 = "000"; @@ -1095,6 +1102,12 @@ public class CardProtocol { if (issuerData == null || issuerDataSignature == null) throw new TangemException("Invalid answer format (GetIssuerData)"); + if( issuerData.Value.length==0 && issuerDataSignature.Value.length==0 ) + { + mCard.setIssuerData(null, null); + return new TLVList(); + } + ByteArrayOutputStream bsDataToVerify = new ByteArrayOutputStream(); bsDataToVerify.write(mCard.getCID()); bsDataToVerify.write(issuerData.Value); @@ -1117,6 +1130,185 @@ public class CardProtocol { } } + /** + * Series of GET_ISSUER_DATA command to read extra data and verify issuer signature of returned extra data + * See [1] 3.3, 8.7 + * This command returns Issuer_Extra_Data data block and its issuer’s signature. + * + * @return TLVList with issuerData (if success read and verify) + * @throws Exception - if something went wrong + */ + public TLVList run_GetIssuerDataEx(Notifications notifications) throws Exception { + boolean needReadIssuerDataCounter = (readResult.getTagAsInt(TLV.Tag.TAG_SettingsMask) & SettingsMask.ProtectIssuerDataAgainstReplay) != 0; + TLV issuerDataSignature = null; + TLV issuerDataCounter = null; + TLV issuerDataSize = null; + ByteArrayOutputStream bsIssuerDataEx = new ByteArrayOutputStream(); + do { + + CommandApdu rqApdu = StartPrepareCommand(INS.GetIssuerData); + rqApdu.addTLV_U8(TLV.Tag.TAG_Mode, 0x01); + rqApdu.addTLV_U16(TLV.Tag.TAG_Offset, bsIssuerDataEx.size()); + + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, false); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + TLV issuerData = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Issuer_Data); + if (issuerData != null && issuerData.Value != null) + bsIssuerDataEx.write(issuerData.Value); + issuerDataSignature = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Issuer_Data_Signature); + issuerDataCounter = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Issuer_Data_Counter); + if (issuerDataSize == null) + issuerDataSize = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Size); + } else { + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + if (issuerDataSize != null && issuerDataSize.getAsInt() != 0) { + if (notifications != null) + notifications.onReadProgress(this, (int) Math.round(bsIssuerDataEx.size() * 100.0 / issuerDataSize.getAsInt())); + Log.i(logTag, String.format("Read %d/%d bytes", bsIssuerDataEx.size(), issuerDataSize.getAsInt())); + } + + } while ((issuerDataSignature == null) || (needReadIssuerDataCounter && (issuerDataCounter == null))); + boolean protectIssuerDataAgainstReplay = (readResult.getTagAsInt(TLV.Tag.TAG_SettingsMask) & SettingsMask.ProtectIssuerDataAgainstReplay) != 0; + + if (notifications != null) notifications.onReadProgress(this, 100); + + if (bsIssuerDataEx.size() > 0 && issuerDataSignature != null && issuerDataSignature.Value != null) { + ByteArrayOutputStream bsDataToVerify = new ByteArrayOutputStream(); + bsDataToVerify.write(mCard.getCID()); + bsDataToVerify.write(bsIssuerDataEx.toByteArray()); + if (protectIssuerDataAgainstReplay) { + bsDataToVerify.write(issuerDataCounter.Value); + } + try { + if (CardCrypto.VerifySignature(mCard.getIssuerPublicDataKey(), bsDataToVerify.toByteArray(), issuerDataSignature.Value)) { + mCard.setIssuerDataEx(bsIssuerDataEx.toByteArray(), issuerDataSignature.Value, issuerDataCounter.getAsInt()); + return TLVList.fromBytes(bsIssuerDataEx.toByteArray()); + } else { + throw new TangemException("Invalid issuer data read (signature verification failed)"); + } + } catch (Exception e) { + e.printStackTrace(); + throw new TangemException("Invalid issuer data read"); + } + } else { + mCard.setIssuerDataEx(null, null, issuerDataCounter.getAsInt()); + return null; + } + } + + /** + * Series of WRITE_ISSUER_DATA command to write extra data: + * 1. start write procedure + * 2. series of writing data blocks + * 3. finalize write procedure + * This command make signatures with provided issuer privateDataKey + * - to start write procedure (sign CID|Counter|Size) + * - to finalize write procedure (sign CID|dataEx|Counter) + * See [1] 3.3, 8.8 + * + * @throws Exception - if something went wrong + */ + public void run_WriteIssuerDataEx(Notifications notifications, byte[] dataEx, byte[] issuerPrivateDataKey) throws Exception { + boolean needCounter = readResult.getTLV(TLV.Tag.TAG_SettingsMask) != null && (readResult.getTagAsInt(TLV.Tag.TAG_SettingsMask) & SettingsMask.ProtectIssuerDataAgainstReplay) != 0; + + Log.i(logTag, "WriteIssuerDataEx - Start"); + + int issuerDataCounter = 0; + ByteArrayOutputStream osDataToSign = new ByteArrayOutputStream(); + osDataToSign.write(mCard.getCID()); + if (needCounter) { + issuerDataCounter = mCard.getIssuerDataExCounter() + 1; + osDataToSign.write(Util.intToByteArray4(issuerDataCounter)); + } + osDataToSign.write(Util.intToByteArray2(dataEx.length)); + + byte[] issuerDataSignature = CardCrypto.Signature(issuerPrivateDataKey, osDataToSign.toByteArray()); + + CommandApdu rqApdu = StartPrepareCommand(INS.WriteIssuerData); + rqApdu.addTLV_U8(TLV.Tag.TAG_Mode, 0x01); + if (needCounter) { + rqApdu.addTLV_U32(TLV.Tag.TAG_Issuer_Data_Counter, issuerDataCounter); + } + rqApdu.addTLV_U16(TLV.Tag.TAG_Size, dataEx.length); + rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data_Signature, issuerDataSignature); + + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, true); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + } else { + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + + if (notifications != null) + notifications.onReadProgress(this, 10); + + + Log.i(logTag, "WriteIssuerDataEx - Send data"); + int offset = 0; + int partSize = 1524; + while (offset < dataEx.length) { + + if (dataEx.length - offset < partSize) { + partSize = dataEx.length - offset; + } + byte[] part = Arrays.copyOfRange(dataEx, offset, offset + partSize); + + rqApdu = StartPrepareCommand(INS.WriteIssuerData); + rqApdu.addTLV_U8(TLV.Tag.TAG_Mode, 0x02); + rqApdu.addTLV_U16(TLV.Tag.TAG_Offset, offset); + rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data, part); + + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + rspApdu = SendAndReceive(rqApdu, true); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + } else { + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + + offset += partSize; + if (notifications != null) + notifications.onReadProgress(this, 10+(int) Math.round(offset * 80.0 / dataEx.length)); + } + + Log.i(logTag, "WriteIssuerDataEx - Finalize"); + + osDataToSign = new ByteArrayOutputStream(); + osDataToSign.write(mCard.getCID()); + osDataToSign.write(dataEx); + if (needCounter) { + osDataToSign.write(Util.intToByteArray4(issuerDataCounter)); + } + issuerDataSignature = CardCrypto.Signature(issuerPrivateDataKey, osDataToSign.toByteArray()); + + rqApdu = StartPrepareCommand(INS.WriteIssuerData); + rqApdu.addTLV_U8(TLV.Tag.TAG_Mode, 0x03); + rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data_Signature, issuerDataSignature); + + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + rspApdu = SendAndReceive(rqApdu, true); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + } else { + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + if (notifications != null) + notifications.onReadProgress(this, 100); + } + + /** * Execute consecutive READ commands with increasing encryption level from EncryptionMode.None to EncryptionMode.Strong, see {@link TangemCard.EncryptionMode} * If card requires stricter encryption level it returns SW.NEED_ENCRYPTION status word diff --git a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/ProductMask.java b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/ProductMask.java new file mode 100644 index 0000000000..15516edaab --- /dev/null +++ b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/ProductMask.java @@ -0,0 +1,25 @@ +package com.tangem.tangem_card.reader; + +/** + * Created by dvol on 15.07.2019. + */ + +public class ProductMask { + public static final int Note = 0x0001; + public static final int Tag = 0x0002; + public static final int IdCard = 0x0004; + + public static String getDescription(int iValue) { + StringBuilder sb = new StringBuilder(); + sb.append("["); + if ((iValue & ProductMask.Note) != 0) sb.append("Note, "); + if ((iValue & ProductMask.Tag) != 0) + sb.append("Tag, "); + if ((iValue & ProductMask.IdCard) != 0) + sb.append("IdCard, "); + + if (sb.length() > 1) sb.delete(sb.length() - 2, sb.length()); + sb.append("]"); + return sb.toString(); + } +} diff --git a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/SettingsMask.java b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/SettingsMask.java index 81470d9586..0513936d3d 100644 --- a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/SettingsMask.java +++ b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/SettingsMask.java @@ -29,8 +29,16 @@ public class SettingsMask { public static final int DisablePrecomputedNDEF = 0x00010000; + public static final int SkipSecurityDelayIfValidatedByIssuer = 0x00020000; + public static final int SkipCheckPIN2andCVCIfValidatedByIssuer = 0x00040000; public static final int SkipSecurityDelayIfValidatedByLinkedTerminal = 0x00080000; + public static final int RestrictOverwriteIssuerDataEx = 0x00100000; + + public static final int RequireTermTxSignature = 0x01000000; + public static final int RequireTermCertSignature = 0x02000000; + public static final int CheckPIN3onCard = 0x04000000; + public static String getDescription(int iValue) { StringBuilder sb=new StringBuilder(); sb.append("["); @@ -63,6 +71,24 @@ public class SettingsMask { if ((iValue & SettingsMask.SkipSecurityDelayIfValidatedByLinkedTerminal) != 0) sb.append("SkipSecurityDelayIfValidatedByLinkedTerminal, "); + + if ((iValue & SettingsMask.SkipSecurityDelayIfValidatedByIssuer) != 0) + sb.append("SkipSecurityDelayIfValidatedByIssuer, "); + if ((iValue & SettingsMask.SkipCheckPIN2andCVCIfValidatedByIssuer) != 0) + sb.append("SkipCheckPIN2andCVCIfValidatedByIssuer, "); + + if ((iValue & SettingsMask.RestrictOverwriteIssuerDataEx) != 0) + sb.append("RestrictOverwriteIssuerDataEx, "); + + if ((iValue & SettingsMask.RequireTermTxSignature) != 0) + sb.append("RequireTermTxSignature, "); + + if ((iValue & SettingsMask.RequireTermCertSignature) != 0) + sb.append("RequireTermCertSignature, "); + + if ((iValue & SettingsMask.CheckPIN3onCard) != 0) + sb.append("CheckPIN3onCard, "); + if (sb.length() > 1) sb.delete(sb.length() - 2, sb.length()); sb.append("]"); return sb.toString(); diff --git a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLV.java b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLV.java index 4188e0ca54..4e704fe1f0 100644 --- a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLV.java +++ b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLV.java @@ -44,15 +44,27 @@ public class TLV { TAG_Session_Key_B(0x1B), TAG_Pause(0x1C), + TAG_PIN3(0x1D), + TAG_NewPIN3(0x1E), + TAG_CrEx_Key(0x1F), + TAG_Manufacture_ID(0x20), TAG_Manufacturer_Signature(0x21), + TAG_Mode(0x23), + TAG_Offset(0x24), + TAG_Size(0x25), + + TAG_User_Data(0x2A), + TAG_User_ProtectedData(0x2B), + TAG_Issuer_Data_PublicKey(0x30), TAG_Issuer_Transaction_PublicKey(0x31), TAG_Issuer_Data(0x32), TAG_Issuer_Data_Signature(0x33), TAG_Issuer_Transaction_Signature(0x34), TAG_Issuer_Data_Counter(0x35), + TAG_Acquirer_PublicKey(0x37), TAG_IsActivated(0x3A), TAG_ActivationSeed(0x3B), @@ -65,12 +77,38 @@ public class TLV { TAG_TrOut_Hash(0x50), TAG_TrOut_HashSize(0x51), TAG_TrOut_Raw(0x52), + TAG_TrOut_Amount(0x53), + + TAG_Terminal_PaymentFlowVersion(0x54), + TAG_Terminal_Certificate(0x55), + // hash of extra data to transaction (for SIGN_HASH_EXTERNAL) + TAG_TrOut_Extra_Hash(0x56), + TAG_Terminal_Transaction_Signature(0x57), + TAG_Terminal_Certificate_Id(0x5A), + TAG_Terminal_Certificate_Param(0x5B), + TAG_Terminal_Certificate_PublicKey(0x5C), + TAG_Terminal_Certificate_Signature(0x5D), TAG_Wallet_PublicKey(0x60), TAG_Signature(0x61), TAG_RemainingSignatures(0x62), TAG_SignedHashes(0x63), + TAG_CheckWalletCounter(0x64), + + TAG_CrEx_Salt(0x65), + TAG_CrEx_Message(0x66), + TAG_CrEx_CryptedMessage(0x67), + TAG_CrEx_HMAC(0x68), + + TAG_CrEx_RequirePIN3(0x69), + TAG_TrOut_Extra_Signature(0x6A), + TAG_SignCommand_ChecksPassed(0x6B), + + TAG_Wallet_PrivateKey(0x70), + TAG_Card_PrivateKey(0x71), + TAG_Block_Reason(0x72), + TAG_Firmware(0x80), TAG_Batch(0x81), TAG_ManufactureDateTime(0x82), @@ -79,6 +117,10 @@ public class TLV { TAG_Manufacturer_PublicKey(0x85), TAG_CardID_Manufacturer_Signature(0x86), + TAG_ProductMask(0x8A), + + TAG_PinLessFloorLimit(0x90), + TAG_Token_Symbol(0xA0), TAG_Token_Contract_Address(0xA1), TAG_Token_Decimal(0xA2), @@ -87,6 +129,14 @@ public class TLV { TAG_LastSign_Date(0xC2), TAG_DenominationText(0xC3), + TAG_FullName (0xD0), + TAG_Birthday (0xD1), + TAG_Photo (0xD2), + TAG_Gender(0xD3), + TAG_IssueDate (0xD4), + TAG_ExpireDate(0xD5), + TAG_TrustedAddress (0xD6), + TAG_Terminal_IsLinked(0x58), TAG_Terminal_PublicKey(0x5C), TAG_Terminal_TransactionSignature(0x57); @@ -172,7 +222,7 @@ public class TLV { } public String getAsString() { - if( Value.length==0 ) return ""; + if (Value.length == 0) return ""; if (Value[Value.length - 1] == 0) { String s1 = new String(Arrays.copyOfRange(Value, 0, Value.length - 1), Charset.forName("utf-8")); @@ -214,18 +264,29 @@ public class TLV { return String.format("%s[]: [[NULL]]", tag.name()); } case TAG_SettingsMask: { - StringBuilder sb=new StringBuilder(); - if( Value!=null ) { + StringBuilder sb = new StringBuilder(); + if (Value != null) { try { int iValue = Util.byteArrayToInt(Value); return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value), SettingsMask.getDescription(iValue)); - } - catch (Exception e) - { + } catch (Exception e) { e.printStackTrace(); return String.format("%s[%d]: %s", tag.name(), Value.length, Util.bytesToHex(Value)); } - }else{ + } else { + return String.format("%s[]: [[NULL]]", tag.name()); + } + } + case TAG_ProductMask: { + if (Value != null) { + try { + int iValue = Util.byteArrayToInt(Value); + return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value), ProductMask.getDescription(iValue)); + } catch (Exception e) { + e.printStackTrace(); + return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value)); + } + } else { return String.format("%s[]: [[NULL]]", tag.name()); } } diff --git a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLVList.java b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLVList.java index 40c36a777b..8f7ef8875f 100644 --- a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLVList.java +++ b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLVList.java @@ -85,4 +85,8 @@ public class TLVList extends ArrayList { while (tlv != null); return tlvList; } + + public boolean hasTag(TLV.Tag tag) { + return getTLV(tag)!=null; + } } diff --git a/tangem-card-old/src/main/java/com/tangem/tangem_card/tasks/CustomReadCardTask.java b/tangem-card-old/src/main/java/com/tangem/tangem_card/tasks/CustomReadCardTask.java index 453ecd2b28..d0f5274e93 100644 --- a/tangem-card-old/src/main/java/com/tangem/tangem_card/tasks/CustomReadCardTask.java +++ b/tangem-card-old/src/main/java/com/tangem/tangem_card/tasks/CustomReadCardTask.java @@ -7,6 +7,7 @@ import com.tangem.tangem_card.data.external.PINsProvider; import com.tangem.tangem_card.reader.CardCrypto; import com.tangem.tangem_card.reader.CardProtocol; import com.tangem.tangem_card.reader.NfcReader; +import com.tangem.tangem_card.reader.ProductMask; import com.tangem.tangem_card.reader.TLV; import com.tangem.tangem_card.reader.TLVException; import com.tangem.tangem_card.reader.TLVList; @@ -137,6 +138,12 @@ public class CustomReadCardTask extends Thread { mCard.setBatch(tlvCardData.getTLV(TLV.Tag.TAG_Batch).getAsHexString()); + if( tlvCardData.hasTag(TLV.Tag.TAG_ProductMask)) { + mCard.setProductMask(tlvCardData.getTagAsInt(TLV.Tag.TAG_ProductMask)); + }else{ + mCard.setProductMask(ProductMask.Note); + } + TLV tokenSymbol = tlvCardData.getTLV(TLV.Tag.TAG_Token_Symbol); TLV contractAddress = tlvCardData.getTLV(TLV.Tag.TAG_Token_Contract_Address); TLV tokens_decimal = tlvCardData.getTLV(TLV.Tag.TAG_Token_Decimal); diff --git a/tangem-card-old/src/main/java/com/tangem/tangem_card/tasks/VerifyCardTask.java b/tangem-card-old/src/main/java/com/tangem/tangem_card/tasks/VerifyCardTask.java index 57cb73c822..4b70353a3b 100644 --- a/tangem-card-old/src/main/java/com/tangem/tangem_card/tasks/VerifyCardTask.java +++ b/tangem-card-old/src/main/java/com/tangem/tangem_card/tasks/VerifyCardTask.java @@ -46,6 +46,10 @@ public class VerifyCardTask extends CustomReadCardTask { } mNotifications.onReadProgress(protocol, 90); + if( protocol.getCard().isIDCard() ) + { + protocol.run_GetIssuerDataEx(mNotifications); + } } } \ No newline at end of file diff --git a/tangem-card-old/src/main/java/com/tangem/tangem_card/tasks/WriteIDCardTask.java b/tangem-card-old/src/main/java/com/tangem/tangem_card/tasks/WriteIDCardTask.java new file mode 100644 index 0000000000..9329607641 --- /dev/null +++ b/tangem-card-old/src/main/java/com/tangem/tangem_card/tasks/WriteIDCardTask.java @@ -0,0 +1,36 @@ +package com.tangem.tangem_card.tasks; + +import com.tangem.tangem_card.data.TangemCard; +import com.tangem.tangem_card.data.external.CardDataSubstitutionProvider; +import com.tangem.tangem_card.data.external.PINsProvider; +import com.tangem.tangem_card.reader.CardProtocol; +import com.tangem.tangem_card.reader.NfcReader; + +/** + * Created by dvol on 19.01.2020. + */ + +public class WriteIDCardTask extends CustomReadCardTask { + public static final String TAG = WriteIDCardTask.class.getSimpleName(); + + private TangemCard.IDCardData idCardData; + private byte[] issuerPrivateDataKey; + public WriteIDCardTask(TangemCard card, NfcReader reader, CardDataSubstitutionProvider cardDataSubstitutionProvider, PINsProvider pinsProvider, TangemCard.IDCardData idCardData, byte[] issuerPrivateDataKey, CardProtocol.Notifications notifications) { + super(card, reader, cardDataSubstitutionProvider, pinsProvider, notifications); + this.idCardData=idCardData; + this.issuerPrivateDataKey=issuerPrivateDataKey; + } + + @Override + public void run_Task() throws Exception { + mNotifications.onReadProgress(protocol, 0); + if (isCancelled) return; + + if( !protocol.getCard().isIDCard() ) + throw new Exception("It's not an IDCard!"); + + protocol.run_WriteIssuerDataEx(mNotifications,idCardData.toTLVList().toBytes(),issuerPrivateDataKey); + mNotifications.onReadProgress(protocol, 100); + } + +} \ No newline at end of file diff --git a/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/data/TangemCardExtensions.kt b/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/data/TangemCardExtensions.kt index 9c6356dd4e..2f0cd24013 100644 --- a/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/data/TangemCardExtensions.kt +++ b/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/data/TangemCardExtensions.kt @@ -61,6 +61,13 @@ fun TangemCard.loadFromBundle(B: Bundle) { setIssuerData(B.getByteArray("IssuerData"), B.getByteArray("IssuerDataSignature")) else setIssuerData(null, null) + if (B.containsKey("IssuerDataEx") && B.containsKey("IssuerDataExSignature") && B.containsKey("IssuerDataExCounter")) + setIssuerDataEx(B.getByteArray("IssuerDataEx"), B.getByteArray("IssuerDataExSignature"), B.getInt("IssuerDataExCounter")) + else setIssuerDataEx(null, null, 0) + + if( B.containsKey("ProductMask") ) + setProductMask(B.getInt("ProductMask")) + if (B.containsKey("NeedWriteIssuerData")) needWriteIssuerData = B.getBoolean("NeedWriteIssuerData") @@ -149,6 +156,14 @@ fun TangemCard.saveToBundle(B: Bundle) { B.putBoolean("NeedWriteIssuerData", needWriteIssuerData) } + if (issuerDataEx != null && issuerDataExSignature != null) { + B.putByteArray("IssuerDataEx", issuerDataEx) + B.putByteArray("IssuerDataExSignature", issuerDataExSignature) + B.putInt("IssuerDataExCounter", issuerDataExCounter) + } + + B.putInt("ProductMask",productMask) + if (isCodeConfirmed != null) B.putBoolean("codeConfirmed", isCodeConfirmed)