@li0ard/tsemrtd
Version:
simple library for eMRTD. supports browsers, node, bun and more!
131 lines (130 loc) • 5.13 kB
JavaScript
import { TLV } from "@li0ard/tinytlv";
import { Enums, Interfaces, Schemas } from "./index";
import { AsnConvert } from "@peculiar/asn1-schema";
/**
* Class for working with DG2 (Face)
*/
export class DG2 {
/**
* Extract int from Uint8Array
* @param data Uint8Array
* @param start Offset
* @param end Offset+length
*/
extractContent(data, start, end) {
if (end - start === 1) {
return data[start];
}
else if (end - start < 4) {
return (data[start] << 8) | data[start + 1];
}
return (data[start] << 24) | (data[start + 1] << 16) | (data[start + 2] << 8) | data[start + 3];
}
/**
* Read Biometric data block
* @hidden
* @param tlv
*/
readBDB(tlv) {
if (parseInt(tlv.tag, 16) != 0x7f60)
throw new Error(`Invalid object tag "0x${tlv.tag}", expected 0x7f60`);
let sbh = AsnConvert.parse(tlv.childs[0].toBytes(), Schemas.SBH);
let firstBlock = tlv.childs[1];
if (parseInt(firstBlock.tag, 16) != 0x5f2e && parseInt(firstBlock.tag, 16) != 0x7f2e)
throw new Error(`Invalid object tag "0x${tlv.tag}", expected 0x5f2e or 0x7f2e`);
let data = firstBlock.byteValue;
if (this.extractContent(data, 0, 4) != 0x46414300)
throw new Error("Biometric data block is invalid");
let offset = 4;
if (this.extractContent(data, offset, offset + 4) != 0x30313000)
throw new Error("Version of Biometric data is not valid");
offset += 4;
let lengthOfRecord = this.extractContent(data, offset, offset + 4);
offset += 4;
let numberOfFacialImages = this.extractContent(data, offset, offset + 2);
offset += 2;
if (numberOfFacialImages > 1)
console.warn("[DG2] The record contains more than 1 image.");
let facialRecordDataLength = this.extractContent(data, offset, offset + 4);
offset += 4;
let nrFeaturePoints = this.extractContent(data, offset, offset + 2);
offset += 2;
let gender = this.extractContent(data, offset, offset + 1);
offset += 1;
let eyeColor = this.extractContent(data, offset, offset + 1);
offset += 1;
let hairColor = this.extractContent(data, offset, offset + 1);
offset += 1;
let featureMask = this.extractContent(data, offset, offset + 3);
offset += 3;
let expression = this.extractContent(data, offset, offset + 2);
offset += 2;
let poseAngle = this.extractContent(data, offset, offset + 3);
offset += 3;
let poseAngleUncertainty = this.extractContent(data, offset, offset + 3);
offset += 3;
offset += nrFeaturePoints * 8;
let faceImageType = this.extractContent(data, offset, offset + 1);
offset += 1;
let _imageDataType = this.extractContent(data, offset, offset + 1);
offset += 1;
let imageWidth = this.extractContent(data, offset, offset + 2);
offset += 2;
let imageHeight = this.extractContent(data, offset, offset + 2);
offset += 2;
let imageColorSpace = this.extractContent(data, offset, offset + 1);
offset += 1;
let sourceType = this.extractContent(data, offset, offset + 1);
offset += 1;
let deviceType = this.extractContent(data, offset, offset + 2);
offset += 2;
let quality = this.extractContent(data, offset, offset + 2);
offset += 2;
let imageEnd = facialRecordDataLength - 20 - (nrFeaturePoints * 8) - 12;
let imageData = data.subarray(offset, offset + imageEnd);
return {
sbh,
lengthOfRecord,
numberOfFacialImages,
facialRecordDataLength,
nrFeaturePoints,
gender,
eyeColor,
hairColor,
featureMask,
expression,
poseAngle,
poseAngleUncertainty,
faceImageType,
imageType: _imageDataType,
imageWidth,
imageHeight,
imageColorSpace,
sourceType,
deviceType,
quality,
imageData
};
}
/**
* Get image of face and meta info
* @param data Data of EF.DG2 file
*/
static load(data) {
let tlv = TLV.parse(data);
if (parseInt(tlv.tag, 16) != Enums.TAGS.DG2)
throw new Error(`Invalid DG2 tag "0x${tlv.tag}", expected 0x${Enums.TAGS.DG2.toString(16)}`);
let bigt = tlv.childs[0];
if (parseInt(bigt.tag, 16) != 0x7f61)
throw new Error(`Invalid object tag "0x${bigt.tag}", expected 0x7f61`);
let bict = bigt.childs[0];
if (parseInt(bict.tag, 16) != 0x02)
throw new Error(`Invalid object tag "0x${bict.tag}", expected 0x02`);
let bitCount = parseInt(bigt.childs[0].value, 16);
let results = [];
for (let i = 0; i < bitCount; i++) {
results.push(new DG2().readBDB(bigt.childs[i + 1]));
}
return results;
}
}