Skip to content

Commit

Permalink
feat: v0.1.2 | Add support for DG11 and DG12
Browse files Browse the repository at this point in the history
  • Loading branch information
li0ard committed Aug 29, 2024
1 parent 50b2dab commit ab73ee6
Show file tree
Hide file tree
Showing 12 changed files with 325 additions and 8 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
<br><br>
<img src="https://github.com/li0ard/tsemrtd/actions/workflows/test.yml/badge.svg" />
<img src="https://jsr.io/badges/@li0ard/tsemrtd" />
<img src="https://img.shields.io/badge/-alpha-34D058" />
<br>
<img src="https://img.shields.io/github/license/li0ard/tsemrtd" />
<img src="https://img.shields.io/badge/-alpha-orange" />
<br>
<hr>
</p>
Expand All @@ -26,7 +28,7 @@ npx jsr add @li0ard/tsemrtd # for npm
## Usage

```ts
import { DG1 } from "tsemrtd"
import { DG1 } from "@li0ard/tsemrtd"

let file = await Bun.file("EF_DG1.bin").bytes()
let data = DG1.load(Buffer.from(file))
Expand All @@ -43,6 +45,5 @@ console.log(data)
- [x] DG3
- [x] DG5
- [x] DG7
- [ ] DG11
- [ ] DG12
- [ ] DG16
- [x] DG11
- [x] DG12
2 changes: 1 addition & 1 deletion jsr.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@li0ard/tsemrtd",
"version": "0.1.1",
"version": "0.1.2",
"exports": "./src/index.ts",
"publish": {
"include": [
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "tsemrtd",
"module": "index.ts",
"version": "0.1.1",
"version": "0.1.2",
"type": "module",
"author": "li0ard",
"repository": {
Expand Down
52 changes: 52 additions & 0 deletions src/consts/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,56 @@ export interface DecodedFingerprint extends AbstractBioTemplate {
nrOfRepresention: number,
/** Name of finger/part of palm */
fingerType: FingerType
}

/** Decoded EF.DG11 datagroup */
export interface DecodedAdditionalPersonalData {
/** Full name of document holder */
nameOfHolder: string,
/** Other names of document holder */
otherNames: string[],
/** Personal number */
personalNumber: string,
/** Full date of birth (CCYYMMDD) */
fullDateOfBirth: number,
/** Place of birth */
placeOfBirth: string[],
/** Permanent residence address */
permanentAddress: string[],
/** Phone number */
telephone: string,
/** Profession */
profession: string,
/** Position */
title: string,
/** Personal resume */
personalSummary: string,
/** Proof of citizenship. Image described by ISO/IEC 10918 */
proofOfCitizenship: Buffer,
/** Numbers of other valid TDs */
otherValidTDNumbers: string[],
/** Information about detention */
custodyInformation: string
}

/** Decoded EF.DG12 datagroup */
export interface DecodedAdditionalDocumentData {
/** Full date of issue (YYYYMMDD) */
dateOfIssue: number,
/** Issuing authority */
issuingAuthority: string,
/** Name of another person, formatted according to the rules of Doc 9303 */
namesOfOtherPersons: string[],
/** Endorsements and observations */
endorsements: string,
/** Tax and exit requirements */
taxAndExitReqs: string,
/** Image of front of document. Image described by ISO/IEC 10918 */
imageOfFront: Buffer,
/** Image of rear of document. Image described by ISO/IEC 10918 */
imageOfRear: Buffer,
/** Date and time of document personalization (YYYYMMDDHHMMSS) */
dateOfPersonalization: number,
/** Serial number of personalization system */
personalizationNumber: string
}
119 changes: 119 additions & 0 deletions src/dg11.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import TLV from "node-tlv"
import { Enums, Interfaces } from "./index";

/**
* Class for working with DG11 (Additional personal data)
*/
export class DG11 {
/**
* Get additional personal data
* @param data Data of EF.DG11 file
*/
static load(data: string | Buffer): Interfaces.DecodedAdditionalPersonalData {
const FULL_NAME_TAG = 0x5F0E;
const OTHER_NAME_TAG = 0x5F0F;
const OTHER_NAME_ARRAY_TAG = 0xA0;
const PERSONAL_NUMBER_TAG = 0x5F10;

// In 'CCYYMMDD' format.
const FULL_DATE_OF_BIRTH_TAG = 0x5F2B;

// Fields separated by '<'
const PLACE_OF_BIRTH_TAG = 0x5F11;

// Fields separated by '<'
const PERMANENT_ADDRESS_TAG = 0x5F42;
const TELEPHONE_TAG = 0x5F12;
const PROFESSION_TAG = 0x5F13;
const TITLE_TAG = 0x5F14;
const PERSONAL_SUMMARY_TAG = 0x5F15;

// Compressed image per ISO/IEC 10918
const PROOF_OF_CITIZENSHIP_TAG = 0x5F16;

// Separated by '<'
const OTHER_VALID_TD_NUMBERS_TAG = 0x5F17;
const CUSTODY_INFORMATION_TAG = 0x5F18;

let nameOfHolder: string = "",
otherNames: string[] = [],
personalNumber: string = "",
fullDateOfBirth: number = 0,
placeOfBirth: string[] = [],
permanentAddress: string[] = [],
telephone: string = "",
profession: string = "",
title: string = "",
personalSummary: string = "",
proofOfCitizenship: Buffer = Buffer.from(""),
otherValidTDNumbers: string[] = [],
custodyInformation: string = "";


let tlv = TLV.parse(data)
if(parseInt(tlv.tag, 16) != Enums.TAGS.DG11) throw new Error(`Invalid DG1 tag "0x${tlv.tag}", expected 0x${Enums.TAGS.DG11.toString(16)}`);

for(let i of tlv.child) {
switch(parseInt(i.tag, 16)) {
case FULL_NAME_TAG:
nameOfHolder = i.bValue.toString("utf-8")
break;
case PERSONAL_NUMBER_TAG:
personalNumber = i.bValue.toString("utf-8")
break;
case OTHER_NAME_ARRAY_TAG:
for(let j of i.child) {
if(parseInt(j.tag, 16) == OTHER_NAME_TAG) {
otherNames.push(j.bValue.toString("utf-8"))
}
}
break;
case FULL_DATE_OF_BIRTH_TAG:
fullDateOfBirth = parseInt(i.bValue.toString("hex"))
break;
case PLACE_OF_BIRTH_TAG:
placeOfBirth = i.bValue.toString("utf-8").split("<")
break;
case PERMANENT_ADDRESS_TAG:
permanentAddress = i.bValue.toString("utf-8").split("<")
break;
case TELEPHONE_TAG:
telephone = i.bValue.toString("utf-8")
break
case PROFESSION_TAG:
profession = i.bValue.toString("utf-8")
break;
case TITLE_TAG:
title = i.bValue.toString("utf-8")
break;
case PERSONAL_SUMMARY_TAG:
personalSummary = i.bValue.toString("utf-8")
break;
case PROOF_OF_CITIZENSHIP_TAG:
proofOfCitizenship = i.bValue
break;
case OTHER_VALID_TD_NUMBERS_TAG:
otherValidTDNumbers = i.bValue.toString("utf-8").split("<")
break;
case CUSTODY_INFORMATION_TAG:
custodyInformation = i.bValue.toString("utf-8")
break;
}
}
return {
nameOfHolder,
otherNames,
personalNumber,
fullDateOfBirth,
placeOfBirth,
permanentAddress,
telephone,
profession,
title,
personalSummary,
proofOfCitizenship,
otherValidTDNumbers,
custodyInformation
}
}
}
93 changes: 93 additions & 0 deletions src/dg12.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import TLV from "node-tlv"
import { Enums, Interfaces } from "./index";

/**
* Class for working with DG12 (Additional document data)
*/
export class DG12 {
/**
* Get additional document data
* @param data Data of EF.DG12 file
*/
static load(data: string | Buffer): Interfaces.DecodedAdditionalDocumentData {
const ISSUING_AUTHORITY_TAG = 0x5F19;

// yyyymmdd
const DATE_OF_ISSUE_TAG = 0x5F26;

// formatted per ICAO 9303 rules
const NAME_OF_OTHER_PERSON_ARRAY_TAG = 0xA0;
const NAME_OF_OTHER_PERSON_TAG = 0x5F1A;
const ENDORSEMENTS_AND_OBSERVATIONS_TAG = 0x5F1B;
const TAX_OR_EXIT_REQUIREMENTS_TAG = 0x5F1C;

// Image per ISO/IEC 10918
const IMAGE_OF_FRONT_TAG = 0x5F1D;
const IMAGE_OF_REAR_TAG = 0x5F1E;

// yyyymmddhhmmss
const DATE_AND_TIME_OF_PERSONALIZATION = 0x5F55;
const PERSONALIZATION_SYSTEM_SERIAL_NUMBER_TAG = 0x5F56;

let dateOfIssue: number = 0,
issuingAuthority: string = "",
namesOfOtherPersons: string[] = [],
endorsements: string = "",
taxAndExitReqs: string = "",
imageOfFront: Buffer = Buffer.from(""),
imageOfRear: Buffer = Buffer.from(""),
dateOfPersonalization: number = 0,
personalizationNumber: string = ""

let tlv = TLV.parse(data)
if(parseInt(tlv.tag, 16) != Enums.TAGS.DG12) throw new Error(`Invalid DG1 tag "0x${tlv.tag}", expected 0x${Enums.TAGS.DG12.toString(16)}`);

for(let i of tlv.child) {
switch(parseInt(i.tag, 16)) {
case ISSUING_AUTHORITY_TAG:
issuingAuthority = i.bValue.toString("utf-8")
break;
case DATE_OF_ISSUE_TAG:
dateOfIssue = parseInt(i.bValue.toString("hex"))
break;
case NAME_OF_OTHER_PERSON_ARRAY_TAG:
for(let j of i.child) {
if(parseInt(j.tag, 16) == NAME_OF_OTHER_PERSON_TAG) {
namesOfOtherPersons.push(j.bValue.toString("utf-8"))
}
}
break;
case ENDORSEMENTS_AND_OBSERVATIONS_TAG:
endorsements = i.bValue.toString("utf-8")
break;
case TAX_OR_EXIT_REQUIREMENTS_TAG:
taxAndExitReqs = i.bValue.toString("utf-8")
break;
case IMAGE_OF_FRONT_TAG:
imageOfFront = i.bValue;
break;
case IMAGE_OF_REAR_TAG:
imageOfRear = i.bValue;
break;
case DATE_AND_TIME_OF_PERSONALIZATION:
dateOfPersonalization = parseInt(i.bValue.toString("hex"))
break;
case PERSONALIZATION_SYSTEM_SERIAL_NUMBER_TAG:
personalizationNumber = i.bValue.toString("utf-8")
break;
}
}

return {
dateOfIssue,
issuingAuthority,
namesOfOtherPersons,
endorsements,
taxAndExitReqs,
imageOfFront,
imageOfRear,
dateOfPersonalization,
personalizationNumber
}
}
}
10 changes: 10 additions & 0 deletions src/dg2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import { Enums, Interfaces } from "./index";
* Class for working with DG2 (Face)
*/
export class DG2 {
/**
* Extract int from buffer
* @param data Buffer
* @param start Offset
* @param end Offset+length
*/
private extractContent(data: Buffer, start: number, end: number): number {
if (end - start == 1) {
return data.subarray(start, end).readInt8();
Expand All @@ -13,6 +19,10 @@ export class DG2 {
}
return data.subarray(start,end).readInt32BE()
}
/**
* Read Biometric data block
* @param tlv
*/
readBDB(tlv: TLV): Interfaces.DecodedImage {
if(parseInt(tlv.tag, 16) != 0x7f60) throw new Error(`Invalid object tag "0x${tlv.tag}", expected 0x7f60`);

Expand Down
10 changes: 10 additions & 0 deletions src/dg3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import { Enums, Interfaces } from "./index";
* @experimental
*/
export class DG3 {
/**
* Extract int from buffer
* @param data Buffer
* @param start Offset
* @param end Offset+length
*/
private extractContent(data: Buffer, start: number, end: number): number {
if (end - start == 1) {
return data.subarray(start, end).readUInt8();
Expand All @@ -17,6 +23,10 @@ export class DG3 {
}
return parseInt(data.subarray(start,end).toString("hex"), 16)
}
/**
* Read Biometric data block
* @param tlv
*/
readBDB(tlv: TLV): Interfaces.DecodedFingerprint {
if(parseInt(tlv.tag, 16) != 0x7f60) throw new Error(`Invalid object tag "0x${tlv.tag}", expected 0x7f60`);
let sbh = tlv.child[0]
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ export { DG2 } from "./dg2"
export { DG3 } from "./dg3"
export { DG5 } from "./dg5"
export { DG7 } from "./dg7"
export { DG11 } from "./dg11"
export { DG12 } from "./dg12"
export * as Enums from "./consts/enums"
export * as Interfaces from "./consts/interfaces"
Binary file added tests/dgs/EF_DG11.bin
Binary file not shown.
Binary file added tests/dgs/EF_DG12.bin
Binary file not shown.
Loading

0 comments on commit ab73ee6

Please sign in to comment.