nfc-pcsc icon indicating copy to clipboard operation
nfc-pcsc copied to clipboard

RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to write outside buffer bounds

Open Franx0 opened this issue 5 years ago • 8 comments

Hi @pokusew , here I go again :) Last day I notice that after make a transmit command to get default key from an ultralight C tag, when I trigger the read method, I'm getting the following error from my nodeJS server.

UnhandledPromiseRejectionWarning: RangeError [ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to write outside buffer bounds
warning.js:25
    at boundsError (internal/buffer.js:48:11)
    at Buffer.readUInt16BE (internal/buffer.js:220:5)
    at /dir/node_modules/nfc-pcsc/dist/Reader.js:500:42
    at Generator.next (<anonymous>)
    at step (/dir/node_modules/nfc-pcsc/dist/Reader.js:18:191)
    at dir/node_modules/nfc-pcsc/dist/Reader.js:18:361

It's like I exceeded the offSet of Buffer :S

The flow is like:

  1. Connect
  2. Tap tag
  3. Transmit to get tag token
  4. Authenticate tag
  5. Read (and here I get the error)

If I skip the number 3 step I can read the tag without any problem.

Thanks ;)

Franx0 avatar Jan 10 '19 18:01 Franx0

Hi @Franx0,

could you please post the whole code? It would make the debugging easier. Thanks. 🙂

Also please ensure that you are using the latest nfc-pcsc version (currently, 0.7.0).

pokusew avatar Jan 10 '19 22:01 pokusew

For sure @pokusew , here you can check a kind of approach, the code it's a little bit complex in real app:

read(params) {
    this._authenticate().then((authenticated) => {
        if(authenticated)
            return this._read(params);
        else
            return 'whatever'
    });
};

async _authenticate() {
	let pass = this._authKey();
	console.log(pass)
		
	let authenticated = false;

	if(pass == 6300) {
		throw new Error('Unable to get default token');
	} else {
		authenticated = this._writePassword(pass);
	}
		
	if(authenticated) 
		return authenticated;
        else
                throw new Error('Your are not authenticated');
};

async _authKey() {
    try { 
        const apdu = Buffer.from([0xff, 0x00, 0x00, 0x00, 0x04, 0xd4, 0x42, 0x1a, 0x00]);
        const buffer = await this.reader.transmit(apdu, 14); 
        return buffer.toString('hex');
    } catch(error) {
        throw error;
    };
};

async _read(params) {
	const data = await this.reader.read(this.config.credit, 4)
	const response = ConvertBase.hex2dec(data, 2);

	if(response === 6300)
		this.status = { data: response, code: "bad", message: "Operation failure" };
	else
		this.status = { data: response, code: "ok", message: "response ok" };

	return this.status;
};

It's something related to promises buffer offSet and the Promise.all([promises]) approach in Reader class but I'm lost and I can not figure out which is the real issue and how to solve it.

Node version -> v11.6.0 nfc-pscs version -> v0.6.4

I'm going to try upgrading version to v0.7.0

Thanks ;)

PD:

Debugging in version v0.7.0 discover that you controlled the error with:

if (response.length < 2) { <<- here the error occurs in v0.7.0
      throw new _errors.ReadError(_errors.OPERATION_FAILED, `Read operation failed: Invalid response 
      length ${response.length}. Expected minimal length is 2 bytes.`);
}

const statusCode = response.slice(-2).readUInt16BE(0); <<- here the error occurred in v0.6.4

So the issue comes when a previos transmit is done and then I try to read, I'm getting a response like that:

Uint8Array(1) [254]

In this code side:

try {
      response = await this.transmit(packet, length + 2);
      this.logger.debug('response received', response);
} catch (err) {
      throw new _errors.ReadError(null, null, err);
}

 if (response.length < 2) {
      throw new _errors.ReadError(_errors.OPERATION_FAILED, `Read operation failed: Invalid response length ${response.length}. Expected minimal length is 2 bytes.`);
}

Franx0 avatar Jan 10 '19 23:01 Franx0

@Franx0 Thanks. 👍

What is the value of this.config.credit in the _read method here?

console.log(this.config.credit); // <- add this line and send me the output
const data = await this.reader.read(this.config.credit, 4);

Could you print it using console.log(this.config.credit) and send me the output? Thanks.

pokusew avatar Jan 11 '19 00:01 pokusew

@Franx0 Thanks.

What is the value of this.config.credit in the _read method here?

console.log(this.config.credit); // <- add this line and send me the output
const data = await this.reader.read(this.config.credit, 4);

Could you print it using console.log(this.config.credit) and send me the output? Thanks.

9 for example, any number between 7-22 that we can use to write/read data and need to be authenticated first 😉 Its a custom config object from my side to manage tag pages.

Franx0 avatar Jan 11 '19 08:01 Franx0

Hi @Franx0,

thanks for the reply. Sorry for the delay.

The only explanation I can think of is that the authentication sequence fails (invalid password/token, malformed command) and then the card gets possibly into the IDLE state and needs to be reset in order to send any commands. The statement you wrote in the first post, would support that:

If I skip the number 3 step I can read the tag without any problem.

Could you please start the NFC with the logger and send me the complete output? 🙂

So that we can check 🔍 that all transmitted data (commands and responses) match the expectations.

You can start the NFC with the logger like this: Please upgrade to the latest nfc-pcsc version, 0.7.1 which features a more detailed debug logs.

function log() {
    console.log(...arguments);
}

const logger = {
    log: log,
    debug: log,
    info: log,
    warn: log,
    error: log,
};

const nfc = new NFC(logger); // <- pass the logger here when initializing NFC

Thank you. 🙂

pokusew avatar Jan 14 '19 05:01 pokusew

hi @pokusew as you can see in the following log, there is something weird because the log return a 'transmit response receive' message but this is the flow:

Transmit command to return default token from tag starts:

1. status
2. Object {state: 1769506, atr: Uint8Array(20)}
3. changes 65584
4. trying to connect CONNECT_MODE_CARD 2
5. card inserted
6. Uint8Array(20) [59, 143, 128, 1, 128, 79, 12, 160, …]
7. connected
8. Object {type: 2, protocol: 2}
9. handling tag
10. processing ISO 14443-3 tag
11. transmitting
12. 12
13. transmit response received
14. 9
15. ACS ACR1222 3S PICC Reader 00 00  card 044f4ada794c80 inserted
16. transmitting
17. 14
18. transmit response received
19. 14 <<--(here I'm getting the right response from auth command 1Ah)
20. reading data from card <<-(here is starting reading transmit)
21. transmitting
22. 6
23. Uint8Array(1) [254]
24. 1
(node:31900) UnhandledPromiseRejectionWarning: ReadError: Read operation failed: Invalid response length 1. Expected minimal length is 2 bytes.
warning.js:25
    at ACR122Reader.read (dir/node_modules/nfc-pcsc/dist/Reader.js:422:13)
    at process.internalTickCallback (internal/process/next_tick.js:77:7)

Digging in log I discover that I have the right response in

PD: I figure out that 'trying to connect CONNECT_MODE_CARD 2' is the default behaviour of the lib in that process, isn't it? Cause I'm using the CONNECT_MODE_DIRECT protocol instead of CONNECT_MODE_CARD

Thanks for all your support :100:

Franx0 avatar Jan 14 '19 06:01 Franx0

Hi @Franx0,

could you please post a complete uncensored version of the console output? Every bit plays a role and without the full logs and cannot help much. 🙁

Also replace the previous logger with the improved version below:

function log() {
    console.log('[DEBUG]', ...arguments); // we can better distinguish debug logs in the output
}

const logger = {
    log: log,
    debug: log,
    info: log,
    warn: log,
    error: log,
};

Thanks. 🙂

Card handling process: When a card is detected, the nfc-pcsc will automatically try to connect to it in CONNECT_MODE_CARD. The previous connection will be dropped. When the connection is successfully initiated, the card event is emitted (or eventually when autoProcessing is enabled, by default it is, the command to get card's UID will be transmitted, and only then a the card event containing card's uid will be emitted). Hope it makes it a bit clearer.


Here is the example how the output should look like (this is basic example with the debug logging):

[DEBUG] new reader detected ACS ACR122U PICC Interface
ACS ACR122U PICC Interface  device attached
[DEBUG] status { state: 18, atr: <Buffer > }
[DEBUG] changes 18
[DEBUG] card removed
[DEBUG] status { state: 34,
  atr:
   <Buffer 3b 8f 80 01 80 4f 0c a0 00 00 03 06 03 00 03 00 00 00 00 68> }
[DEBUG] changes 48
[DEBUG] card inserted <Buffer 3b 8f 80 01 80 4f 0c a0 00 00 03 06 03 00 03 00 00 00 00 68>
[DEBUG] trying to connect CONNECT_MODE_CARD 2
[DEBUG] connected { type: 2, protocol: 2 }
[DEBUG] handling tag { atr:
   <Buffer 3b 8f 80 01 80 4f 0c a0 00 00 03 06 03 00 03 00 00 00 00 68>,
  standard: 'TAG_ISO_14443_3',
  type: 'TAG_ISO_14443_3' }
[DEBUG] processing ISO 14443-3 tag { atr:
   <Buffer 3b 8f 80 01 80 4f 0c a0 00 00 03 06 03 00 03 00 00 00 00 68>,
  standard: 'TAG_ISO_14443_3',
  type: 'TAG_ISO_14443_3' }
[DEBUG] transmitting <Buffer ff ca 00 00 00> 12
[DEBUG] status { state: 290,
  atr:
   <Buffer 3b 8f 80 01 80 4f 0c a0 00 00 03 06 03 00 03 00 00 00 00 68> }
[DEBUG] changes 256
[DEBUG] transmit response received <Buffer 04 a4 25 3a 9c 4a 84 90 00> 9
ACS ACR122U PICC Interface  card detected { atr:
   <Buffer 3b 8f 80 01 80 4f 0c a0 00 00 03 06 03 00 03 00 00 00 00 68>,
  standard: 'TAG_ISO_14443_3',
  type: 'TAG_ISO_14443_3',
  uid: '04a4253a9c4a84' }
[DEBUG] status { state: 18, atr: <Buffer > }
[DEBUG] changes 304
[DEBUG] card removed
ACS ACR122U PICC Interface  card removed { atr:
   <Buffer 3b 8f 80 01 80 4f 0c a0 00 00 03 06 03 00 03 00 00 00 00 68>,
  standard: 'TAG_ISO_14443_3',
  type: 'TAG_ISO_14443_3',
  uid: '04a4253a9c4a84' }
[DEBUG] trying to disconnect { type: 2, protocol: 2 }
[DEBUG] disconnected

pokusew avatar Jan 14 '19 21:01 pokusew

Hi @pokusew Sorry for the delay, these days I was researching about my own code and nfc-pcsc lib to figure out what was happening ;)

Here you have my log:

nfc_1    | new reader detected ACS ACR1222 3S PICC Reader 00 00
nfc_1    | Initialized ACS ACR1222 3S PICC Reader 00 00
nfc_1    | ACS ACR1222 3S PICC Reader 00 00  device attached
nfc_1    | trying to connect CONNECT_MODE_DIRECT 3
<---This is weird, why having only one device connected NFC detect 4 readers (00, 01, 02, 03) only 00 is valid--->
nfc_1    | new reader detected ACS ACR1222 3S PICC Reader 00 01
nfc_1    | new reader detected ACS ACR1222 3S PICC Reader 00 02
nfc_1    | new reader detected ACS ACR1222 3S PICC Reader 00 03
nfc_1    | connected { type: 3, protocol: 0 }
nfc_1    | status { state: 2555922, atr: <Buffer > }
nfc_1    | changes 2555922
nfc_1    | card removed
nfc_1    | trying to disconnect { type: 3, protocol: 0 }
nfc_1    | status { state: 65554, atr: <Buffer > }
nfc_1    | changes 65554
nfc_1    | card removed
nfc_1    | status { state: 65554, atr: <Buffer > }
nfc_1    | changes 65554
nfc_1    | card removed
nfc_1    | status { state: 65554, atr: <Buffer > }
nfc_1    | changes 65554
nfc_1    | card removed
nfc_1    | disconnected
nfc_1    | status { state: 2621474,
nfc_1    |   atr:
nfc_1    |    <Buffer 3b 8f 80 01 80 4f 0c a0 00 00 03 06 03 00 03 00 00 00 00 68> }
nfc_1    | changes 983088
nfc_1    | card inserted <Buffer 3b 8f 80 01 80 4f 0c a0 00 00 03 06 03 00 03 00 00 00 00 68>
nfc_1    | trying to connect CONNECT_MODE_CARD 2
nfc_1    | connected { type: 2, protocol: 2 }
nfc_1    | handling tag { atr:
nfc_1    |    <Buffer 3b 8f 80 01 80 4f 0c a0 00 00 03 06 03 00 03 00 00 00 00 68>,
nfc_1    |   standard: 'TAG_ISO_14443_3',
nfc_1    |   type: 'TAG_ISO_14443_3' }
nfc_1    | processing ISO 14443-3 tag { atr:
nfc_1    |    <Buffer 3b 8f 80 01 80 4f 0c a0 00 00 03 06 03 00 03 00 00 00 00 68>,
nfc_1    |   standard: 'TAG_ISO_14443_3',
nfc_1    |   type: 'TAG_ISO_14443_3' }
nfc_1    | transmitting <Buffer ff ca 00 00 00> 12
nfc_1    | transmit response received <Buffer 04 4f 4a da 79 4c 80 90 00> 9
nfc_1    | ACS ACR1222 3S PICC Reader 00 00  card 044f4ada794c80 inserted { atr:
nfc_1    |    <Buffer 3b 8f 80 01 80 4f 0c a0 00 00 03 06 03 00 03 00 00 00 00 68>,
nfc_1    |   standard: 'TAG_ISO_14443_3',
nfc_1    |   type: 'TAG_ISO_14443_3',
nfc_1    |   uid: '044f4ada794c80' }
nfc_1    | transmitting <Buffer ff 00 00 00 04 d4 42 1a 00> 14
nfc_1    | transmit response received <Buffer d5 43 00 af 00 9f 2b 32 be 3c 10 3b 90 00> 14
nfc_1    | transmitting <Buffer ff 00 00 00 13 d4 42 af 0d 69 12 61 41 db fa fe 56 f0 21 92 c9 b5 96 7d> 23
nfc_1    | transmit response received <Buffer d5 43 02 90 00> 5
nfc_1    | transmitting <Buffer ff 00 00 00 04 d4 42 1a 00> 14
nfc_1    | transmit response received <Buffer d5 43 00 af fe a2 34 43 0b 75 09 ee 90 00> 14
<---To make this transmit possible after a transmit that return wrong credentials casue of pass auth, it's neccessary to write a setTimeout of 2000ms --->
nfc_1    | transmitting <Buffer ff 00 00 00 13 d4 42 af ab 90 04 2e a1 71 c7 0d 21 69 8c 0c 22 3c 6a 01> 24
nfc_1    | transmit response received <Buffer d5 43 00 00 aa 59 b4 b9 9b c9 82 c1 90 00> 14
nfc_1    | reading data from card { atr:
nfc_1    |    <Buffer 3b 8f 80 01 80 4f 0c a0 00 00 03 06 03 00 03 00 00 00 00 68>,
nfc_1    |   standard: 'TAG_ISO_14443_3',
nfc_1    |   type: 'TAG_ISO_14443_3',
nfc_1    |   uid: '044f4ada794c80' }
nfc_1    | transmitting <Buffer ff b0 00 07 04> 6
nfc_1    | transmit response received <Buffer 00 00 14 50 90 00> 6
nfc_1    | data <Buffer 00 00 14 50>

As you can see in the log comments it's neccessary to execute a second transmit in auth inside of a setTimeout function to make it possible.

return setTimeout(() => { resolve(this._auth(correctKey)) }, 2000)

I suspect that it could be possible because transmit command is an async process that might be not end when the second transmit order is being executed, make sense?

Thanks so much @pokusew

Franx0 avatar Jan 16 '19 16:01 Franx0