From e11497c3ae21936829d29b6dab067ad3ea36a24a Mon Sep 17 00:00:00 2001 From: Lizard Date: Wed, 5 Mar 2025 14:55:18 +0600 Subject: [PATCH] feat: v0.2.6 | Add `recoveryNested` function for @doegox method --- jsr.json | 2 +- package.json | 2 +- src/crapto1.ts | 47 +++++++++++++++++++++++++++++++++++++-------- src/utils.ts | 4 +--- tests/index.test.ts | 11 +++++++++++ 5 files changed, 53 insertions(+), 13 deletions(-) diff --git a/jsr.json b/jsr.json index bad6c14..e433be3 100644 --- a/jsr.json +++ b/jsr.json @@ -1,6 +1,6 @@ { "name": "@li0ard/crapto1-ts", - "version": "0.2.6", + "version": "0.2.7", "exports": "./src/index.ts", "publish": { "include": [ diff --git a/package.json b/package.json index 377b621..08b35c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@li0ard/crapto1_ts", - "version": "0.2.6", + "version": "0.2.7", "main": "dist/index.js", "types": "dist/index.d.ts", "type": "module", diff --git a/src/crapto1.ts b/src/crapto1.ts index 82f1f65..21d629d 100644 --- a/src/crapto1.ts +++ b/src/crapto1.ts @@ -251,11 +251,11 @@ export const lfsr_recovery64 = (ks2: number, ks3: number): Crypto1State[] => { * Recovery by two sets of 32 bit keystream authentication * @param uid UID * @param chal Tag challenge #1 (aka `nt`) - * @param rchal Reader challenge #1 (aka `nr_0`) - * @param rresp Reader response #1 (aka `ar_0`) + * @param rchal Reader challenge #1 (aka `{nr_0}`) + * @param rresp Reader response #1 (aka `{ar_0}`) * @param chal2 Tag challenge #2 (aka `nt1`) - * @param rchal2 Reader challenge #2 (aka `nr_1`) - * @param rresp2 Reader response #2 (aka `ar_1`) + * @param rchal2 Reader challenge #2 (aka `{nr_1}`) + * @param rresp2 Reader response #2 (aka `{ar_1}`) * @returns {bigint} */ export const recovery32 = (uid: number, chal: number, rchal: number, rresp: number, chal2: number, rchal2: number, rresp2: number): bigint => { @@ -277,10 +277,10 @@ export const recovery32 = (uid: number, chal: number, rchal: number, rresp: numb /** * Recovery by one set of full 64 bit keystream authentication * @param uid UID - * @param chal Tag challenge - * @param rchal Reader challenge - * @param rresp Reader response - * @param tresp Tag response + * @param chal Tag challenge (aka `nt`) + * @param rchal Reader challenge (aka `{nr}`) + * @param rresp Reader response (aka `{ar}`) + * @param tresp Tag response (aka `{at}`) * @returns {bigint} */ export const recovery64 = (uid: number, chal: number, rchal: number, rresp: number, tresp: number): bigint => { @@ -293,4 +293,35 @@ export const recovery64 = (uid: number, chal: number, rchal: number, rresp: numb lfsr_rollback_word(s, uid^chal) return s.lfsr +} + +/** + * Recovery by partial nested authentication + * + * @author doegox + * @param uid UID + * @param chal Tag challenge (aka `nt`) + * @param enc_chal Encrypted tag challenge (aka `{nt}`) + * @param rchal Reader challenge (aka `{nr}`) + * @param rresp Reader response (aka `{ar}`) + * @returns {bigint} + */ +export const recoveryNested = (uid: number, chal: number, enc_chal: number, rchal: number, rresp: number): bigint => { + let ar = prng_successor(chal, 64); + let ks0 = enc_chal ^ chal; + let ks2 = rresp ^ ar; + + let s = lfsr_recovery32(ks0, uid ^ chal); + + for (let t = 0; (s[t].odd !== 0) || (s[t].even !== 0); t++) { + crypto1_word(s[t], rchal, true); + if(ks2 == crypto1_word(s[t], 0)) { + lfsr_rollback_word(s[t], 0); + lfsr_rollback_word(s[t], rchal, true); + lfsr_rollback_word(s[t], uid ^ chal); + return s[t].lfsr + } + } + + return -1n; } \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index e8a832d..7b705d3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -19,9 +19,7 @@ export const bebit = (num: number, index: number): number => { } /** - * Filter function of Crypto1. - * - * Compute one bit of keystream from LFSR bits + * Filter function of Crypto1. Compute one bit of keystream from LFSR bits * @param x LFSR bits * @returns {number} */ diff --git a/tests/index.test.ts b/tests/index.test.ts index 87801d5..5dbe13c 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -1,6 +1,7 @@ import { expect, test } from "bun:test" import { Crypto1State, encrypt, lfsr_rollback_byte, recovery32, recovery64 } from "../src/" import { filter } from "../src/utils" +import { recoveryNested } from "../src/crapto1" test("Recovery by 2 auths", () => { // Real card @@ -46,6 +47,16 @@ test("Recovery by 1 auth", () => { )).toBe(0x62bea192fa37n) }) +test("Recovery by nested auth", () => { + expect(recoveryNested( + 0x5c467f63, + 0x4bbf8a12, + 0xabb30bd1, + 0x46033966, + 0xadc18162 + )).toBe(0x059e2905bfccn) +}) + test("State", () => { let testData = [ [0xd73A52b491AAn, 0x009E831F, 0x00F236A0],