Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Грешка приликом читања личне карте за странце #30

Open
X3KT0 opened this issue May 10, 2024 · 21 comments
Labels
bug Something isn't working

Comments

@X3KT0
Copy link

X3KT0 commented May 10, 2024

Графички Баш Челик одма улази у циклус "читање-грешка".
CLI верзия каже "Error saving document: reading card: reading card: reading document 0 file: parsing file header: invalid length" па се завршава.
Исти баш челик са истим хардвер читачом ради са картицом здравственог осигурања на истом рачунару (онда проблем није у хардверу).
Исти хардвер читач добро ради са проблематичном картом на другом рачунару со Виндовзом (онда проблем није у карти).
ЛК има на себи сертификате.
ATR је 3bff9400008131804380318065b0850201f3120fff82900079
Издата је у априлу 2023.

Немам држављанске карте, онда не могу да проверим са њом.

@ubavic
Copy link
Owner

ubavic commented May 10, 2024

Preći ću na engleski, pošto mislim da ćemo se tako lakše razumeti:

First, thank you for bringing this up, and thank you for very detailed description of the problem. I suppose you have ID card for foreigners, and it has a slightly different structure of data on card. As BasCelik is developed completely independently from official apps, there are some edge cases like this that I didn't cover. Unfortunately, I don't have access to ID cards for foreigners on which I could test, so I can't resolve bug, even if I am pretty sure what is happening.

You can try to fix bug yourself (I can introduce you to the code and the workflow), or maybe we could meet for a coffee so I could see what is exactly happening (if you are in Belgrade).

@ubavic ubavic added the bug Something isn't working label May 10, 2024
@X3KT0
Copy link
Author

X3KT0 commented May 10, 2024

Thank you for the answer.
I'm not in Belgrade, but sometimes go there.
Will contact you by email before that (I found your email address on your website).

@ubavic
Copy link
Owner

ubavic commented May 10, 2024

I will answer here your question from #29 about reading from smart cards (because that issue isn't a bug, a this really is).

There is no way (AFAIK) you can read all data from smart card. Smart card behaves like small filesystem, and data is divided into 'files'. Each file has an address, and you must send a request with the exact address to be able to read that file.

On Serbian ID cards, data is stored in 4 files.

Because the Serbian vehicle card has the same ATR as the Serbian ID card, Bas Celik tries to distinct these two documents by reading DOCUMENT_FILE on address 0f02. This file is present on ID cards but not on vehicle cards. But, it seems that cards for foreigners don't have this file, and therefore your card is wrongly processed as a vehicle card.

Please, in card.go, on line 41, change

tempIdCard := Gemalto{smartCard: sc}
if tempIdCard.testGemalto() {
	card = Gemalto{smartCard: sc}
} else {
	card = VehicleCard{smartCard: sc}
}

to

card = Gemalto{smartCard: sc}

This will force the program to process your card as an ID card.

Your card will be processed inside readIdCard form idCard.go. You can add there fmt.Printf to see raw data. That function will fail because your card don't have file on the address 0f02. You can modify this function to see if other files are present. For example, replace the whole function with this:

func readIdCard[Id IdDocument](card Id) (*document.IdDocument, error) {
	doc := document.IdDocument{}

	rsp, err := card.readFile(DOCUMENT_FILE_LOC, false)
	if err != nil {
		fmt.Println("reading document file:", err)
	} else {
		fields, err := parseTLV(rsp)
		if err != nil {
			fmt.Println(err)
		} else {
			assignField(fields, 1546, &doc.DocumentNumber)
			assignField(fields, 1547, &doc.DocumentType)
			assignField(fields, 1548, &doc.DocumentSerialNumber)
			assignField(fields, 1549, &doc.IssuingDate)
			assignField(fields, 1550, &doc.ExpiryDate)
			assignField(fields, 1551, &doc.IssuingAuthority)
			localization.FormatDate(&doc.IssuingDate)
			localization.FormatDate(&doc.ExpiryDate)
		}
	}

	rsp, err = card.readFile(PERSONAL_FILE_LOC, false)
	if err != nil {
		fmt.Println("reading personal file:", err)
	} else {
		fields, err := parseTLV(rsp)
		if err != nil {
			fmt.Println(err)
		} else {
			assignField(fields, 1558, &doc.PersonalNumber)
			assignField(fields, 1559, &doc.Surname)
			assignField(fields, 1560, &doc.GivenName)
			assignField(fields, 1561, &doc.ParentGivenName)
			assignField(fields, 1562, &doc.Sex)
			assignField(fields, 1563, &doc.PlaceOfBirth)
			assignField(fields, 1564, &doc.CommunityOfBirth)
			assignField(fields, 1565, &doc.StateOfBirth)
			assignField(fields, 1566, &doc.DateOfBirth)
			localization.FormatDate(&doc.DateOfBirth)
		}
	}

	rsp, err = card.readFile(RESIDENCE_FILE_LOC, false)
	if err != nil {
		fmt.Println("reading residence file:", err)
	} else {
		fields, err := parseTLV(rsp)
		if err != nil {
			fmt.Println(err)
		} else {
			assignField(fields, 1568, &doc.State)
			assignField(fields, 1569, &doc.Community)
			assignField(fields, 1570, &doc.Place)
			assignField(fields, 1571, &doc.Street)
			assignField(fields, 1572, &doc.AddressNumber)
			assignField(fields, 1573, &doc.AddressLetter)
			assignField(fields, 1574, &doc.AddressEntrance)
			assignField(fields, 1575, &doc.AddressFloor)
			assignField(fields, 1578, &doc.AddressApartmentNumber)
			assignField(fields, 1580, &doc.AddressDate)
			localization.FormatDate(&doc.AddressDate)
		}
	}

	rsp, err = card.readFile(PHOTO_FILE_LOC, true)
	if err != nil {
		fmt.Println("reading photo file:", err)
	} else {
		doc.Portrait, _, err = image.Decode(bytes.NewReader(rsp))
		if err != nil {
			fmt.Println("decoding photo file:", err)
		}
	}

	return &doc, nil
}

If you don't conclude anything from this, then we need to peek into the official app to see how it communicates with foreigners cards.

Btw, I suppose that you know how to compile go programs. Compiling Bas Celik should not be a problem on Linux. If you want to skip building the whole GUI version you can do:

go build -tags "cli"  .

@X3KT0
Copy link
Author

X3KT0 commented May 10, 2024

Thank you for the help!
I managed to compile it with the patches you gave. Actually, with the first one only, because after applying it, the program stops with Error saving document: reading card: initializing card: initializing card: response not OK

I think we have two possible ways: either I take my card and go to Belgrade; or I can install linux on a machine with the cardreader plugged in and give you an ssh access to it (if you like the idea, of course).

@ubavic
Copy link
Owner

ubavic commented May 10, 2024

Hm, interesting. It seem that ID cards for foreigners have different initialization sequence. This complicates things

Very cool idea with ssh :) We can try that, but I am afraid I will have to test your card with the official app. Then I will be able to see exactly what data is transferred to the card.

BTW, if you are in NS, or somewhere near BG, I can drop by there.

Feel free to send me a mail when you have time

@RodiAda
Copy link

RodiAda commented Oct 1, 2024

HI! I am in Belgrade and I have the TRP card )) If you want we can meet and debug the issue ))

@loskutov
Copy link
Contributor

loskutov commented Oct 3, 2024

The response for the initialization request seems to be OK (0x90 0x00) when [0xA0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00] is used as AID (also used with vehicle cards).
However, it then fails reading the file (the response for "select file" is 0x6A 0x86, "Incorrect P1 or P2 parameter"). Trying the APDU formats used for medical/vehicle cards for file selection leads to the same result.

@ubavic
Copy link
Owner

ubavic commented Oct 4, 2024

@loskutov Thanks for information

@X3KT0 @RodiAda @loskutov I just released v2 branch which among other things brings support for foreigner cards. Could you compile this branch and test your cards with it?

Edit: If you have PDFs of your cards made by state app, please compare it with PDF created with Baš Čelik. It should have same values listed.

@loskutov
Copy link
Contributor

loskutov commented Oct 4, 2024

Works like a charm for me!

The only difference I see is the address format: with the state app, it's entitled as "Prebivalište i adresa stana" and the value is like "BEOGRAD, AREA, NEKA ULICA 001", whilst with Baš Čelik it's just "Prebivalište" and the value format is "NEKA ULICA 001, AREA, BEOGRAD".
The match is also not pixel-perfect in terms of word wraps but I guess it's not really supposed to be otherwise.

@ubavic
Copy link
Owner

ubavic commented Oct 5, 2024

Great, glad it works now :)

The difference in the address field label was already noted in #6. But, once again, I tested this today with two IDs and the official app produces PDFs with "Prebivalište". It seems that the label depends on some other field or maybe card release date. I am not sure. The Field AddressLabel looks suspicious, but I still haven't found a card with this field present.

I will change the order in the address field, that is a confirmed bug :) I usually strive to produce pixel perfect PDFs.

@RodiAda
Copy link

RodiAda commented Oct 7, 2024

All works fine with my card, but when I generate a PDF I have a small issues with it. Please see pictures attached.

SCR-20241007-nxhi SCR-20241007-nxey

@ubavic
Copy link
Owner

ubavic commented Oct 7, 2024

Thanks for the information.

The second picture is an obvious problem, but I am not sure what the problem is with the first one? The bottom line of the letters seems cuted off?

@RodiAda
Copy link

RodiAda commented Oct 7, 2024

Yes, letters are a bit cuted off on a first one

ubavic added a commit that referenced this issue Oct 18, 2024
ubavic added a commit that referenced this issue Oct 18, 2024
@ubavic
Copy link
Owner

ubavic commented Nov 29, 2024

Not sure what happened with letter cut-off. All other issues should be resolved in the new version v2.0.0.

Thank you all, especially you Rodion :)

@ubavic ubavic closed this as completed Nov 29, 2024
@X3KT0
Copy link
Author

X3KT0 commented Dec 3, 2024

Thank you for the amazing work! It now reads my PRP card! :)

Nevertheless, there are differences in presentation (see pictures attached; the brownish one is for official Čelik app, I checked with the latest 1.4.1.1, and the white one is made with Baš Čelik).

I believe the PRP (stalno nastanjenje; lična karta stranca) card can be distinguished from a regular Serbian citizen (lična karta) card by its JMBG/EBS which in case of a foreigner has 06 in its code field DD: AABBCCCDDEEEF where AABBCCC is a birthdate. Otherwise, PRP's EBS is identical to JMBG including the method of calculating its checksum (the F field here).

Sorry for the late response.

Снимок экрана в 2024-12-03 08-14-37
Снимок экрана в 2024-12-03 08-14-59

@ubavic ubavic reopened this Dec 3, 2024
@ubavic
Copy link
Owner

ubavic commented Dec 7, 2024

Hi @X3KT0, I just pushed a fix to the main branch (commit aa80f37f). Can you pull it and test again? Also, can you compare the rest of fields on the page and note at the bottom of the page?

@RodiAda Please, can you check once again with this new fix? Just want to check that I didn't mess up print for RP cards :)

@X3KT0
Copy link
Author

X3KT0 commented Dec 14, 2024

The "Osnov boravka" field is empty with both CLI and GUI versions of Baš Čelik, but contains "Stalno nastanjenje" with the official app. I think the official app can guess the status (or the difference between citizenship and PRP) using the two-digit field right after the DDMMYYY birthdate fields (that field is 06 in my case).

Also thank you very much for pointing to a text of the note at the bottom of page. There ARE differences which went completely unnoticed by me. I am attaching an original look for you to see ends of lines. Note "karatkera" (sic!) in the beginning of the fourth line!
IMG_20241214_141914

@ubavic
Copy link
Owner

ubavic commented Dec 21, 2024

Note "karatkera" (sic!) in the beginning of the fourth line!

Nice catch! I added updated note to Foreigner ID print.

I am a little bit confused. You say that you have RP, but note at the and of page suggests it is a foreigner ID. Also, the documentation says that FID doesn't have populated field Purpose Of Stay but Status Of Foreigner.

It seems that label on the print by the official app is misleading: it should be Status stranca not Osnov boravka

Can you try to export yout ID to a json file, and see if Stalno nastanjenje appears in any field (note that face picture is encoded at the start of the json, text data is at the end of it):

./bas-celik -json id.json

@X3KT0
Copy link
Author

X3KT0 commented Dec 22, 2024

No, there is no "Stalno" in the JSON.

A part of the JSON after "portrait" is:

"DocRegNo":"0000XXXXXX","DocumentType":"IF","IssuingDate":"XX
.XX.XXXX.","ExpiryDate":"XX.XX.XXXX.","IssuingAuthority":"PU U NOVOM SADU","DocumentSerialNumber":"IF0000XXXXX","ChipSerialNumber":"LS000XXXXX","DocumentName":"","PersonalNumber":"XXYYZZZ060026",
"Surname":"XXPREZIMEXX","GivenName":"XXIMEXX","ParentGivenName":"","Sex":"M","PlaceOfBirth":"","CommunityOfBirth":"","StateOfBirth":"RUSKA FEDERACIJA","StateOfBirthCode":"RUS","DateOfBirt
h":"XX.YY.ZZZZ.","StatusOfForeigner":"","NationalityFull":"RUSKA FEDERACIJA","PurposeOfStay":"","ENote":"","State":"SRBIJA","Community":"XXOPŠTINAXX","Place":"XXMESTOXX","Street":"XXULICAXX","Ho
useNumber":"XXX","HouseLetter":"","Entrance":"","Floor":"","ApartmentNumber":"","AddressDate":"10.0x
.202x.","AddressLabel":""}

And yes, I have an RP, and of course I am still a foreigner, not a citizen.

Na slučaj ako ne znaš, Srbija ima dva "nivoa" dugotrajne dozvole za strance: privremeni boravak, koji je definisan u članu 40 Zakona o strancima Srbije, i stalno nastanjenje, koje je definisano u članu 67 istog Zakona. Plastične kartice za onih ko ima privremeni boravak se počeli da izdaju tek od februara ove godine. Njihova kartica se zove nešto kao "Dozvola za boravak i rad" ili slično. Pre toga smo imali samo nalepnicu u pasošu.

Sa druge strane, plastična kartica (koja u ovom slučaju se zove "Lična karta stranca", kao i moja, na slici) za onih, koji su već dobili stalno nastanjenje, je bila odavno. Ne znam tačno, od koliko davno, ali ja sam takvu video već, mislim, u 19. godištu.
IMG_20230715_152437~2

@ubavic
Copy link
Owner

ubavic commented Dec 23, 2024

Hvala na dostavljenim informacijama.

Stvar je u tome što kartica koju si skenirao nije boravišna dozvola (residence permit - RP) već lična karta za strance (identity foreigner - IF) što se vidi i u JSON-u, ali i na samoj ličnoj karti.

RP kartice, odnosno boravišne dozvole, su već implementirane u Baš Čeliku, i od nekoliko korisnika sam dobio potvrdu da se PDF podudara sa PDF-om iz zvanične aplikacije.

Nisam uspeo da pronađem logiku u zvaničnoj aplikaciji za dodelu Osnova boravka za IF kartice. Ako želiš, možeš još i da pokušaš da probaš da izlistaš sve informacije zapisane u datotekama i da tu pokušaš da nađeš nisku Stalno nastanjenje. Na primer, za ID_PERSONAL_FILE to izgleda ovako (i slično treba uraditi za ostale datoteke):

func parseIdPersonalFile(data []byte, doc *document.IdDocument) error {
	fields, err := tlv.ParseTLV(data)
	if err != nil {
		return err
	}

	for k, v := range fields {
		fmt.Printf("%d %s\n", k, string(v))
	}

	tlv.AssignField(fields, 1558, &doc.PersonalNumber)
	tlv.AssignField(fields, 1559, &doc.Surname)
	tlv.AssignField(fields, 1560, &doc.GivenName)
	tlv.AssignField(fields, 1561, &doc.ParentGivenName)
	tlv.AssignField(fields, 1562, &doc.Sex)
	tlv.AssignField(fields, 1563, &doc.PlaceOfBirth)
	tlv.AssignField(fields, 1564, &doc.CommunityOfBirth)
	tlv.AssignField(fields, 1565, &doc.StateOfBirth)
	tlv.AssignField(fields, 1566, &doc.DateOfBirth)
	tlv.AssignField(fields, 1567, &doc.StateOfBirthCode)
	tlv.AssignField(fields, 1583, &doc.NationalityFull)
	tlv.AssignField(fields, 1683, &doc.PurposeOfStay)
	tlv.AssignField(fields, 1684, &doc.ENote)
	localization.FormatDate(&doc.DateOfBirth)

	return nil
}

Ako i ovo ne uspe, jedini moj zaključak bi bio da postoji neki bug u karticama, koji zvanična aplikacija na neki način prevazilazi.

@X3KT0
Copy link
Author

X3KT0 commented Dec 24, 2024

Po zakonu, ima jedan jedini osnov za stalno nastanjenje: N godina na privremenom boravku (član 67(2) Zakona o strancima). N može (moglo je) da se razlikuje u zavisnosti od osnova privremenog boravka, ali to ne menja sam princip.

Onda, kako to vidim: kad čitamo karticu, koja izgleda kao lična karta (A NE boravišna dozvola!):

if (JMBG =~ /.......06..../) {
  status = stranac;
  osnov = "Stalno nastanjenje";
 ...
} else {
  status = državljanin;
 ...
}

Al' dobro, nije to hitno niti jako bitno, imam Windows kući za sada, a od proleća, verovatno, biće više takvih kartica da možes da sakupiš neku statistiku.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants