miijs 2.5.0 → 2.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/amiiboHandler.js +29 -103
- package/asmCrypto.js +16 -8
- package/dist/miijs.browser.esm.js +7628 -0
- package/dist/miijs.browser.esm.js.map +7 -0
- package/dist/miijs.browser.js +7628 -0
- package/dist/miijs.browser.js.map +7 -0
- package/index.js +348 -68
- package/package.json +36 -2
- package/shims/amiibo-stub.js +11 -0
- package/shims/crypto-browser.js +4 -0
- package/shims/empty.js +2 -0
- package/shims/ffl-wrapper-stub.js +13 -0
- package/shims/path-browser.js +26 -0
- package/.github/workflows/npm-publish-github-packages.yml +0 -36
- package/crown.jpg +0 -0
- package/ideal.jsonc +0 -91
- package/miiFemaleBody.glb +0 -0
- package/miiMaleBody.glb +0 -0
- package/miijs.png +0 -0
package/amiiboHandler.js
CHANGED
|
@@ -1,56 +1,16 @@
|
|
|
1
|
-
const crypto = require('crypto');
|
|
2
1
|
|
|
3
|
-
|
|
4
|
-
If someone can find, or make, a guide for this, I will wipe all commits of this key from the repo and instead point to how to get this key for yourself.*/
|
|
5
|
-
const MASTER_KEY_BUFFER = Buffer.from('1D164B375B72A55728B91D64B6A3C205756E666978656420696E666F7300000EDB4B9E3F45278F397EFF9B4FB9930000044917DC76B49640D6F83939960FAED4EF392FAAB21428AA21FB54E5450547667F752D2873A20017FEF85C0575904B6D6C6F636B656420736563726574000010FDC8A07694B89E4C47D37DE8CE5C74C1044917DC76B49640D6F83939960FAED4EF392FAAB21428AA21FB54E545054766', 'hex');
|
|
2
|
+
// Takes Amiibo dumps that are either 532 bytes or 540 bytes, and manipulates a 96 byte (C/F)FSD Mii in/out of the dump from offset 0x4C.
|
|
6
3
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const TAG_TYPE_STRING = MASTER_KEY_BUFFER.slice(96, 110);
|
|
15
|
-
const TAG_MAGIC_BYTES_SIZE = MASTER_KEY_BUFFER[111];
|
|
16
|
-
const TAG_MAGIC_BYTES = MASTER_KEY_BUFFER.slice(112, 128);
|
|
17
|
-
const TAG_XOR_PAD = MASTER_KEY_BUFFER.slice(128, 160);
|
|
4
|
+
const { Buffer } = require('buffer');
|
|
5
|
+
const isBrowser = typeof window !== 'undefined' && typeof window.crypto !== 'undefined';
|
|
6
|
+
const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
|
7
|
+
const nodeCrypto = isNode ? require('crypto') : null;
|
|
8
|
+
const subtleCrypto = (typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.subtle)
|
|
9
|
+
? globalThis.crypto.subtle
|
|
10
|
+
: (nodeCrypto && nodeCrypto.webcrypto ? nodeCrypto.webcrypto.subtle : null);
|
|
18
11
|
|
|
19
12
|
const NFC3D_AMIIBO_SIZE = 520;
|
|
20
|
-
const NTAG215_SIZE = 540;
|
|
21
|
-
const NTAG215_SIZE_ALT = 532;
|
|
22
|
-
|
|
23
|
-
const MII_OFFSET_DECRYPTED = 0x4C;
|
|
24
|
-
const MII_SIZE = 96;
|
|
25
|
-
|
|
26
|
-
//Calculate CRC16 checksum for Mii data (for Amiibo format)
|
|
27
|
-
function calculateMiiChecksum(data) {
|
|
28
|
-
const checksumData = data.slice(0, 94);
|
|
29
|
-
let crc = 0;
|
|
30
|
-
for (let byteIndex = 0; byteIndex < checksumData.length; byteIndex++) {
|
|
31
|
-
for (let bitIndex = 7; bitIndex >= 0; bitIndex--) {
|
|
32
|
-
crc = (((crc << 1) | ((checksumData[byteIndex] >> bitIndex) & 0x1)) ^
|
|
33
|
-
(((crc & 0x8000) !== 0) ? 0x1021 : 0)) & 0xFFFF;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
for (let counter = 16; counter > 0; counter--) {
|
|
37
|
-
crc = ((crc << 1) ^ (((crc & 0x8000) !== 0) ? 0x1021 : 0)) & 0xFFFF;
|
|
38
|
-
}
|
|
39
|
-
return crc & 0xFFFF;
|
|
40
|
-
}
|
|
41
13
|
|
|
42
|
-
//Validate and fix Mii checksum - for 96-byte Amiibo format
|
|
43
|
-
function validateAndFixMiiChecksum(miiData) {
|
|
44
|
-
if (miiData.length !== 92 && miiData.length !== MII_SIZE) {
|
|
45
|
-
throw new Error(`Invalid Mii data size: expected 92 or ${MII_SIZE} bytes, got ${miiData.length}`);
|
|
46
|
-
}
|
|
47
|
-
const fullMii = Buffer.alloc(MII_SIZE);
|
|
48
|
-
miiData.slice(0, Math.min(94, miiData.length)).copy(fullMii, 0);
|
|
49
|
-
const newChecksum = calculateMiiChecksum(fullMii);
|
|
50
|
-
fullMii[94] = (newChecksum >> 8) & 0xFF;
|
|
51
|
-
fullMii[95] = newChecksum & 0xFF;
|
|
52
|
-
return fullMii;
|
|
53
|
-
}
|
|
54
14
|
function calcSeed(dump) {
|
|
55
15
|
const seed = Buffer.alloc(64);
|
|
56
16
|
dump.slice(0x029, 0x02B).copy(seed, 0x00);
|
|
@@ -89,7 +49,7 @@ function drbgGenerateBytes(hmacKey, seed, outputSize) {
|
|
|
89
49
|
iterBuffer[0] = (iteration >> 8) & 0xFF;
|
|
90
50
|
iterBuffer[1] = iteration & 0xFF;
|
|
91
51
|
seed.copy(iterBuffer, 2);
|
|
92
|
-
const hmac =
|
|
52
|
+
const hmac = nodeCrypto.createHmac('sha256', hmacKey);
|
|
93
53
|
hmac.update(iterBuffer);
|
|
94
54
|
const output = hmac.digest();
|
|
95
55
|
const toCopy = Math.min(32, outputSize - offset);
|
|
@@ -99,7 +59,6 @@ function drbgGenerateBytes(hmacKey, seed, outputSize) {
|
|
|
99
59
|
}
|
|
100
60
|
return result;
|
|
101
61
|
}
|
|
102
|
-
|
|
103
62
|
function deriveKeys(typeString, magicBytes, magicBytesSize, xorPad, hmacKey, baseSeed) {
|
|
104
63
|
const preparedSeed = prepareSeed(typeString, magicBytes, magicBytesSize, xorPad, baseSeed);
|
|
105
64
|
const derived = drbgGenerateBytes(hmacKey, preparedSeed, 48);
|
|
@@ -121,7 +80,6 @@ function tagToInternal(tag) {
|
|
|
121
80
|
tag.slice(0x054, 0x080).copy(internal, 0x1DC);
|
|
122
81
|
return internal;
|
|
123
82
|
}
|
|
124
|
-
|
|
125
83
|
function internalToTag(internal) {
|
|
126
84
|
const tag = Buffer.alloc(NFC3D_AMIIBO_SIZE);
|
|
127
85
|
internal.slice(0x000, 0x008).copy(tag, 0x008);
|
|
@@ -137,41 +95,40 @@ function internalToTag(internal) {
|
|
|
137
95
|
function decryptAmiibo(tag) {
|
|
138
96
|
const internal = tagToInternal(tag);
|
|
139
97
|
const seed = calcSeed(internal);
|
|
140
|
-
const dataKeys = deriveKeys(
|
|
141
|
-
const tagKeys = deriveKeys(
|
|
98
|
+
const dataKeys = deriveKeys(Buffer.from("756E666978656420696E666F7300",'hex'), Buffer.from("DB4B9E3F45278F397EFF9B4FB9930000",'hex'), 14, Buffer.from("044917DC76B49640D6F83939960FAED4EF392FAAB21428AA21FB54E545054766",'hex'), Buffer.from("1D164B375B72A55728B91D64B6A3C205",'hex'), seed);
|
|
99
|
+
const tagKeys = deriveKeys(Buffer.from("6C6F636B65642073656372657400",'hex'), Buffer.from("FDC8A07694B89E4C47D37DE8CE5C74C1",'hex'), 16, Buffer.from("044917DC76B49640D6F83939960FAED4EF392FAAB21428AA21FB54E545054766",'hex'), Buffer.from("7F752D2873A20017FEF85C0575904B6D",'hex'), seed);
|
|
142
100
|
const plain = Buffer.alloc(NFC3D_AMIIBO_SIZE);
|
|
143
|
-
const cipher =
|
|
101
|
+
const cipher = nodeCrypto.createDecipheriv('aes-128-ctr', dataKeys.aesKey, dataKeys.aesIV);
|
|
144
102
|
cipher.setAutoPadding(false);
|
|
145
103
|
const decrypted = cipher.update(internal.slice(0x02C, 0x1B4));
|
|
146
104
|
decrypted.copy(plain, 0x02C);
|
|
147
105
|
internal.slice(0x000, 0x008).copy(plain, 0x000);
|
|
148
106
|
internal.slice(0x028, 0x02C).copy(plain, 0x028);
|
|
149
107
|
internal.slice(0x1D4, 0x208).copy(plain, 0x1D4);
|
|
150
|
-
const tagHmac =
|
|
108
|
+
const tagHmac = nodeCrypto.createHmac('sha256', tagKeys.hmacKey);
|
|
151
109
|
tagHmac.update(plain.slice(0x1D4, 0x208));
|
|
152
110
|
const computedTagHmac = tagHmac.digest();
|
|
153
111
|
computedTagHmac.copy(plain, 0x1B4);
|
|
154
|
-
const dataHmac =
|
|
112
|
+
const dataHmac = nodeCrypto.createHmac('sha256', dataKeys.hmacKey);
|
|
155
113
|
dataHmac.update(plain.slice(0x029, 0x208));
|
|
156
114
|
const computedDataHmac = dataHmac.digest();
|
|
157
115
|
computedDataHmac.copy(plain, 0x008);
|
|
158
116
|
return plain;
|
|
159
117
|
}
|
|
160
|
-
|
|
161
118
|
function encryptAmiibo(plain) {
|
|
162
119
|
const seed = calcSeed(plain);
|
|
163
|
-
const dataKeys = deriveKeys(
|
|
164
|
-
const tagKeys = deriveKeys(
|
|
120
|
+
const dataKeys = deriveKeys(Buffer.from("756E666978656420696E666F7300",'hex'), Buffer.from("DB4B9E3F45278F397EFF9B4FB9930000",'hex'), 14, Buffer.from("044917DC76B49640D6F83939960FAED4EF392FAAB21428AA21FB54E545054766",'hex'), Buffer.from("1D164B375B72A55728B91D64B6A3C205",'hex'), seed);
|
|
121
|
+
const tagKeys = deriveKeys(Buffer.from("6C6F636B65642073656372657400",'hex'), Buffer.from("FDC8A07694B89E4C47D37DE8CE5C74C1",'hex'), 16, Buffer.from("044917DC76B49640D6F83939960FAED4EF392FAAB21428AA21FB54E545054766",'hex'), Buffer.from("7F752D2873A20017FEF85C0575904B6D",'hex'), seed);
|
|
165
122
|
const cipher_internal = Buffer.alloc(NFC3D_AMIIBO_SIZE);
|
|
166
|
-
const tagHmac =
|
|
123
|
+
const tagHmac = nodeCrypto.createHmac('sha256', tagKeys.hmacKey);
|
|
167
124
|
tagHmac.update(plain.slice(0x1D4, 0x208));
|
|
168
125
|
tagHmac.digest().copy(cipher_internal, 0x1B4);
|
|
169
|
-
const dataHmac =
|
|
126
|
+
const dataHmac = nodeCrypto.createHmac('sha256', dataKeys.hmacKey);
|
|
170
127
|
dataHmac.update(plain.slice(0x029, 0x1B4));
|
|
171
128
|
dataHmac.update(cipher_internal.slice(0x1B4, 0x1D4));
|
|
172
129
|
dataHmac.update(plain.slice(0x1D4, 0x208));
|
|
173
130
|
dataHmac.digest().copy(cipher_internal, 0x008);
|
|
174
|
-
const aesCipher =
|
|
131
|
+
const aesCipher = nodeCrypto.createCipheriv('aes-128-ctr', dataKeys.aesKey, dataKeys.aesIV);
|
|
175
132
|
aesCipher.setAutoPadding(false);
|
|
176
133
|
const encrypted = aesCipher.update(plain.slice(0x02C, 0x1B4));
|
|
177
134
|
encrypted.copy(cipher_internal, 0x02C);
|
|
@@ -182,54 +139,23 @@ function encryptAmiibo(plain) {
|
|
|
182
139
|
}
|
|
183
140
|
|
|
184
141
|
//Extract Mii data from an Amiibo dump
|
|
185
|
-
function extractMiiFromAmiibo(
|
|
186
|
-
|
|
187
|
-
throw new Error('Amiibo dump must be a Buffer');
|
|
188
|
-
}
|
|
189
|
-
const size = amiiboDump.length;
|
|
190
|
-
if (size !== NFC3D_AMIIBO_SIZE && size !== NTAG215_SIZE && size !== NTAG215_SIZE_ALT) {
|
|
191
|
-
throw new Error(`Invalid Amiibo dump size: ${size} (expected ${NFC3D_AMIIBO_SIZE}, ${NTAG215_SIZE_ALT}, or ${NTAG215_SIZE})`);
|
|
192
|
-
}
|
|
193
|
-
const tag = amiiboDump.slice(0, NFC3D_AMIIBO_SIZE);
|
|
142
|
+
function extractMiiFromAmiibo(dump) {
|
|
143
|
+
const tag = dump.slice(0, NFC3D_AMIIBO_SIZE);
|
|
194
144
|
const decrypted = decryptAmiibo(tag);
|
|
195
|
-
|
|
196
|
-
// Extract only the first 92 bytes (the actual Mii data, without checksum)
|
|
197
|
-
const miiData = decrypted.slice(MII_OFFSET_DECRYPTED, MII_OFFSET_DECRYPTED + 92);
|
|
198
|
-
|
|
145
|
+
const miiData = decrypted.slice(76, 172);// Extract the 96 Bytes (C/F)FSD Mii
|
|
199
146
|
return Buffer.from(miiData);
|
|
200
147
|
}
|
|
201
148
|
|
|
202
149
|
//Insert Mii data into an Amiibo dump
|
|
203
|
-
function insertMiiIntoAmiibo(
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
throw new Error('Mii data must be a Buffer');
|
|
209
|
-
}
|
|
210
|
-
const size = amiiboDump.length;
|
|
211
|
-
if (size !== NFC3D_AMIIBO_SIZE && size !== NTAG215_SIZE && size !== NTAG215_SIZE_ALT) {
|
|
212
|
-
throw new Error(`Invalid Amiibo dump size: ${size}`);
|
|
213
|
-
}
|
|
214
|
-
if (miiData.length !== 92 && miiData.length !== MII_SIZE) {
|
|
215
|
-
throw new Error(`Mii data must be 92 or ${MII_SIZE} bytes, got ${miiData.length}`);
|
|
216
|
-
}
|
|
217
|
-
const tag = amiiboDump.slice(0, NFC3D_AMIIBO_SIZE);
|
|
218
|
-
const decrypted = decryptAmiibo(tag);
|
|
219
|
-
|
|
220
|
-
// Validate and fix Mii checksum, ensuring it's 96 bytes with correct checksum
|
|
221
|
-
const miiWithChecksum = validateAndFixMiiChecksum(miiData);
|
|
222
|
-
|
|
223
|
-
// Insert Mii data (96 bytes)
|
|
224
|
-
miiWithChecksum.copy(decrypted, MII_OFFSET_DECRYPTED);
|
|
225
|
-
|
|
226
|
-
const encrypted = encryptAmiibo(decrypted);
|
|
227
|
-
const result = Buffer.alloc(size);
|
|
150
|
+
function insertMiiIntoAmiibo(dump, miiWithChecksum) {
|
|
151
|
+
const decrypted = decryptAmiibo(dump.slice(0, NFC3D_AMIIBO_SIZE));//Decrypt the Amiibo
|
|
152
|
+
miiWithChecksum.copy(decrypted, 76);//Insert the Mii into Amiibo
|
|
153
|
+
const encrypted = encryptAmiibo(decrypted);//Reencrypt the Amiibo
|
|
154
|
+
const result = Buffer.alloc(dump.length);
|
|
228
155
|
encrypted.copy(result, 0);
|
|
229
|
-
if (
|
|
230
|
-
|
|
156
|
+
if (dump.length > NFC3D_AMIIBO_SIZE) {
|
|
157
|
+
dump.slice(NFC3D_AMIIBO_SIZE).copy(result, NFC3D_AMIIBO_SIZE);
|
|
231
158
|
}
|
|
232
|
-
|
|
233
159
|
return result;
|
|
234
160
|
}
|
|
235
161
|
|
package/asmCrypto.js
CHANGED
|
@@ -3598,22 +3598,30 @@ function BigNumber_extGCD(a, b) {
|
|
|
3598
3598
|
}
|
|
3599
3599
|
|
|
3600
3600
|
function getRandomValues(buf) {
|
|
3601
|
-
if (typeof process !== 'undefined') {
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3601
|
+
if (typeof process !== 'undefined' && typeof require === 'function') {
|
|
3602
|
+
try {
|
|
3603
|
+
var nodeCrypto = eval('require')('crypto');
|
|
3604
|
+
var bytes = nodeCrypto.randomBytes(buf.length);
|
|
3605
|
+
buf.set(bytes);
|
|
3606
|
+
return;
|
|
3607
|
+
} catch (e) {
|
|
3608
|
+
// Fall through to browser methods
|
|
3609
|
+
}
|
|
3606
3610
|
}
|
|
3607
|
-
if (window.crypto && window.crypto.getRandomValues) {
|
|
3611
|
+
if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) {
|
|
3608
3612
|
window.crypto.getRandomValues(buf);
|
|
3609
3613
|
return;
|
|
3610
3614
|
}
|
|
3611
|
-
if (self.crypto && self.crypto.getRandomValues) {
|
|
3615
|
+
if (typeof self !== 'undefined' && self.crypto && self.crypto.getRandomValues) {
|
|
3612
3616
|
self.crypto.getRandomValues(buf);
|
|
3613
3617
|
return;
|
|
3614
3618
|
}
|
|
3619
|
+
if (typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.getRandomValues) {
|
|
3620
|
+
globalThis.crypto.getRandomValues(buf);
|
|
3621
|
+
return;
|
|
3622
|
+
}
|
|
3615
3623
|
// @ts-ignore
|
|
3616
|
-
if (window.msCrypto && window.msCrypto.getRandomValues) {
|
|
3624
|
+
if (typeof window !== 'undefined' && window.msCrypto && window.msCrypto.getRandomValues) {
|
|
3617
3625
|
// @ts-ignore
|
|
3618
3626
|
window.msCrypto.getRandomValues(buf);
|
|
3619
3627
|
return;
|