UNPKG

@ntlab/identity-fingerprint

Version:

Fingerprint identity acquisition and identification

255 lines (243 loc) 10.2 kB
/** * The MIT License (MIT) * * Copyright (c) 2023-2025 Toha <tohenk@yahoo.com> * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ const { Identity } = require('@ntlab/identity'); const path = require('path'); const util = require('util'); const JSZip = require('jszip'); const debug = require('debug')('identity:fingerprint'); class FingerprintId extends Identity { VERSION = 'FPIDENTITY-1.0' init() { super.init(); this.ready = false; this.id = 'FP'; this.proxyServerId = 'FPIDENTITY'; this.enrollWithSamples = typeof this.options.enrollWithSamples !== 'undefined' ? this.options.enrollWithSamples : true; this.workerOptions = { worker: path.join(__dirname, 'worker'), maxWorks: 160, } try { this.dp = require('@ntlab/dplib'); if (this.dp.init(this.options.dpInitOptions || {})) { this.getIdentifier(); this.featuresLen = this.dp.getFeaturesLen(); this.ready = true; } } catch (err) { if (err.code === 'ERR_DLOPEN_FAILED') { console.error('Fingerprint identity is unavailable, DigitalPersona U.are.U SDK not installed!'); } else { console.error(err); } } } finalize() { if (this.dp) { this.dp.exit(); } } getCommands() { return { [Identity.MODE_ALL]: { 'self-test': data => this.VERSION, 'connect': data => this.ready, }, [Identity.MODE_BRIDGE]: { 'required-features': data => { return this.featuresLen; }, 'set-options': data => { if (undefined !== data.enrollWithSamples) { this.enrollWithSamples = data.enrollWithSamples; return true; } return false; }, 'acquire': data => { this.doOp(this.ID_ACQUIRE); return true; }, 'enroll': data => { this.doOp(this.ID_ENROLL); return true; }, 'stop': data => { this.doOp(this.ID_STOP); return true; }, }, [Identity.MODE_VERIFIER]: { 'identify': async (data) => { return await this.fingerIdentify(data.feature, data.workid); }, 'count-template': data => { return {count: this.getIdentifier().count()}; }, 'reg-template': data => { if (data.id && data.template) { if (data.force && this.getIdentifier().has(data.id)) { this.getIdentifier().remove(data.id); } const success = this.getIdentifier().add(data.id, data.template); debug(`Register template ${data.id} [${success ? 'OK' : 'FAIL'}]`); if (success) { return {id: data.id}; } } }, 'unreg-template': data => { if (data.id) { const success = this.getIdentifier().remove(data.id); debug(`Unregister template ${data.id} [${success ? 'OK' : 'FAIL'}]`); if (success) { return {id: data.id}; } } }, 'has-template': data => { if (data.id) { const success = this.getIdentifier().has(data.id); if (success) { return {id: data.id}; } } }, 'clear-template': data => { this.getIdentifier().clear(); return true; } } } } doOp(op) { if (this.ready) { const stopAcquire = callback => { this.setStatus('Stopping acquire'); if (this.dp.isAcquiring()) { this.dp.stopAcquire(() => { this.setStatus('Acquire stopped'); callback(); }); } else { this.setStatus('Stop not required'); callback(); } } const startAcquire = () => { this.setStatus('Starting acquire'); if (op === this.ID_ENROLL) { this.fingers = []; } let xstatus = null; this.dp.startAcquire(op === this.ID_ENROLL ? true : false, (status, data) => { switch (status) { case 'disconnected': if (xstatus != status) { xstatus = status; this.setStatus('Connect fingerprint reader', true); this.sendMessage(this.getPrefix(op === this.ID_ENROLL ? 'enroll-status' : 'acquire-status'), {status: status}); } break; case 'connected': if (xstatus != status) { xstatus = status; this.setStatus('Swipe your finger', true); this.sendMessage(this.getPrefix(op === this.ID_ENROLL ? 'enroll-status' : 'acquire-status'), {status: status}); } break; case 'error': this.setStatus('Error occured, try again', true); this.sendMessage(this.getPrefix(op === this.ID_ENROLL ? 'enroll-status' : 'acquire-status'), {status: status}); break; case 'complete': if (op === this.ID_ENROLL) { this.setStatus('Enroll completed', true); this.fingers.push(data); this.sendMessage(this.getPrefix('enroll-complete'), {data: data}); } else { this.setStatus('Acquire completed', true); stopAcquire(() => { this.sendMessage(this.getPrefix('acquire-complete'), {data: data}); }); } break; case 'enrolled': this.setStatus('Enroll finished', true); stopAcquire(() => { const zip = new JSZip(); zip.file('TMPL', data); if (this.enrollWithSamples) { for (let i = 0; i < this.fingers.length; i++) { zip.file(util.format('S%s', i + 1), this.fingers[i]); } } zip.generateAsync({type: 'nodebuffer'}) .then(buffer => { this.setStatus('Notify for enroll finished'); this.sendMessage(this.getPrefix('enroll-finished'), {template: buffer}); }) ; }); break; } }); } // main operation stopAcquire(() => { if (op !== this.ID_STOP) { startAcquire(); } }); } } normalize(data) { if (typeof data === 'string') { const buff = new Uint8Array(data.length); for (let i = 0; i < data.length; i++) { buff[i] = data.charCodeAt(i); } data = buff; } if (data instanceof Uint8Array || Buffer.isBuffer(data)) { data = data.buffer; } return data; } async fingerIdentify(feature, workid) { return await this.getIdentifier().identify(this.fixWorkId(workid), feature); } fixWorkId(workid) { if (!workid) { workid = Identity.genId(); } return workid; } onreset() { this.doCmd(this.getPrefix('clear-template')); } } module.exports = FingerprintId;