npm i @pretendonetwork/boss-crypto
- Decrypt (WiiU)
- Encrypt (WiiU)
- Decrypt (3DS) (RSA hash signatures are not verified due to lack of public key)
- Encrypt (3DS)
BOSS uses 2 keys:
- AES encryption key
- HMAC key
We cannot provide these keys directly as they are owned by Nintendo. You must dump them yourself from your console in order to use this library
To dump keys needed see this key dumping tool
Only one key is used to decrypt the contents, the AES encryption key. This is in keyslot 0x38 (Normalkey). See https://citra-emu.org/wiki/aes-keys/ and https://www.3dbrew.org/wiki/AES_Registers#Keyslots for more information. The SHA256 hashes are RSA signed, however we lack both the private and public key. So we cannot sign our own hashes legitimately and we cannot verify legitimate hashes. Luckily Luma patches these signature checks anyway
import fs from 'node:fs';
import { encryptWiiU } from '@pretendonetwork/boss-crypto';
const { BOSS_AES_KEY, BOSS_HMAC_KEY } = process.env;
const content = Buffer.from('Hello World');
const encrypted = encryptWiiU(content, BOSS_WIIU_AES_KEY, BOSS_WIIU_HMAC_KEY);
fs.writeFileSync(__dirname + '/Festival.boss', encrypted);
import fs from 'node:fs';
import { decryptWiiU } from '@pretendonetwork/boss-crypto';
const { BOSS_AES_KEY, BOSS_HMAC_KEY } = process.env;
const encryptedFilePath = __dirname + '/Festival.boss';
const { content } = decryptWiiU(encryptedFilePath, BOSS_AES_KEY, BOSS_HMAC_KEY);
fs.writeFileSync(__dirname + '/Festival.byml', content);
import fs from 'node:fs';
import { encrypt3DS } from '@pretendonetwork/boss-crypto';
const { BOSS_AES_KEY } = process.env;
const content = Buffer.from('Hello World');
const encrypted = encrypt3DS(BOSS_3DS_AES_KEY, 1692231927n, {
program_id: 0x0004001000022900, // can also be named "title_id"
content_datatype: 65537,
ns_data_id: 36,
version: 1,
content,
});
fs.writeFileSync(__dirname + '/hello-world.boss', encrypted);
import fs from 'node:fs';
import { decrypt3DS } from '@pretendonetwork/boss-crypto';
const { BOSS_AES_KEY } = process.env;
const encryptedFilePath = __dirname + '/EU_BGM1';
const { payload_contents } = decrypt3DS(encryptedFilePath, BOSS_AES_KEY);
fs.writeFileSync(__dirname + '/EU_BGM1.dec', payload_contents[0].content);
Returned when decrypting WiiU BOSS content. Contains some crypto information from the headers
THIS TYPE IS NOT PART OF THE REAL BOSS SPEC. IT IS MADE FOR THIS LIBRARY ONLY
type WUPBOSSInfo = {
hash_type: number;
iv: Buffer;
hmac: Buffer;
content: Buffer;
}
Holds the contents of one of the payloads of a 3DS BOSS container
type CTRPayloadContent = {
payload_content_header_hash: Buffer;
payload_content_header_hash_signature: Buffer;
program_id: bigint;
content_datatype: number;
ns_data_id: number;
version: number;
content: Buffer;
}
Returned when decrypting 3DS BOSS content. Contains all relevant data from the real BOSS container. See https://www.3dbrew.org/wiki/SpotPass#Content_Container for more details
type CTRBOSSContainer = {
hash_type: number;
release_date: bigint;
iv: Buffer;
content_header_hash: Buffer;
content_header_hash_signature: Buffer;
payload_contents: CTRPayloadContent[];
}
Passed in when encrypting 3DS contents. program_id
and title_id
are aliases, one must be set. release_date
is only needed when calling encrypt
. content
is only needed when calling encrypt3DS
.
type CTRCryptoOptions = {
program_id?: string | number | bigint;
title_id?: string | number | bigint;
release_date?: bigint;
content_datatype: number;
ns_data_id: number;
version: number;
content?: string | Buffer;
}
function decrypt(pathOrBuffer: string | Buffer, aesKey: string, hmacKey?: string): WUPBOSSInfo | CTRBOSSContainer
Takes in encrypted BOSS data and decrypts it. This function will check the BOSS header to see what version (WiiU or 3DS) the file is for and automatically call the corresponding decryption function
pathOrBuffer
: Either a string path to the file or a buffer containing the raw dataaesKey
: AES encryption keyhmacKey
: HMAC key (WiiU only)
WUPBOSSInfo | CTRBOSSContainer
function encrypt(pathOrBuffer: string | Buffer, version: number, aesKey: string, hmacKeyOrOptions: string | CTRCryptoOptions): Buffer
Takes in content and encrypts it. Will check version
to know what version (WiiU or 3DS) the file is for and automatically call the corresponding encryption function
pathOrBuffer
: Either a string path to the file or a buffer containing the raw dataversion
: BOSS version number (0x10001
= 3DS,0x20001
= WiiU)aesKey
: BOSS AES encryption keyhmacKeyOrOptions
: BOSS HMAC key (WiiU) orCTRCryptoOptions
(3DS)
Encrypted BOSS data buffer
function decryptWiiU(pathOrBuffer: string | Buffer, aesKey: string, hmacKey: string): WUPBOSSInfo
Takes in encrypted BOSS used for the WiiU data and decrypts it. This function is usually not needed and is called internally by decrypt
pathOrBuffer
: Either a string path to the file or a buffer containing the raw dataaesKey
: BOSS AES encryption keyhmacKey
: BOSS HMAC key
WUPBOSSInfo
function encryptWiiU(pathOrBuffer: string | Buffer, aesKey: string, hmacKey: string): Buffer
Takes in content and encrypts it for the WiiU
pathOrBuffer
: Either a string path to the file or a buffer containing the raw dataaesKey
: BOSS AES encryption keyhmacKey
: BOSS HMAC key
WiiU encrypted BOSS data
function decrypt3DS(pathOrBuffer: string | Buffer, aesKey: string | Buffer): CTRBOSSContainer
Takes in encrypted BOSS used for the 3DS data and decrypts it. This function is usually not needed and is called internally by decrypt
pathOrBuffer
: Either a string path to the file or a buffer containing the raw dataaesKey
: BOSS AES encryption key
CTRBOSSContainer
function encrypt3DS(aesKey: string | Buffer, serialNumber: bigint, options: CTRCryptoOptions[]): Buffer
Takes in multiple contents and encrypts them for the 3DS using the provided options and serial number
aesKey
: BOSS AES encryption keyserialNumber
: Serial number used in the BOSS containeroptions
: Array ofCTRCryptoOptions
3DS encrypted BOSS data