mirror of
https://github.com/gchq/CyberChef.git
synced 2024-11-16 08:58:30 +01:00
302 lines
11 KiB
JavaScript
302 lines
11 KiB
JavaScript
|
/**
|
||
|
* @author n1073645 [n1073645@gmail.com]
|
||
|
* @copyright Crown Copyright 2020
|
||
|
* @license Apache-2.0
|
||
|
*/
|
||
|
|
||
|
import Operation from "../Operation.mjs";
|
||
|
import OperationError from "../errors/OperationError.mjs";
|
||
|
|
||
|
/**
|
||
|
* Extract ID3 operation
|
||
|
*/
|
||
|
class ExtractID3 extends Operation {
|
||
|
|
||
|
/**
|
||
|
* ExtractID3 constructor
|
||
|
*/
|
||
|
constructor() {
|
||
|
super();
|
||
|
|
||
|
this.name = "Extract ID3";
|
||
|
this.module = "Default";
|
||
|
this.description = "";
|
||
|
this.infoURL = "";
|
||
|
this.inputType = "ArrayBuffer";
|
||
|
this.outputType = "string";
|
||
|
this.args = [];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param {ArrayBuffer} input
|
||
|
* @param {Object[]} args
|
||
|
* @returns {string}
|
||
|
*/
|
||
|
run(input, args) {
|
||
|
|
||
|
// Borrowed from https://github.com/aadsm/jsmediatags
|
||
|
const FRAME_DESCRIPTIONS = {
|
||
|
// v2.2
|
||
|
"BUF": "Recommended buffer size",
|
||
|
"CNT": "Play counter",
|
||
|
"COM": "Comments",
|
||
|
"CRA": "Audio encryption",
|
||
|
"CRM": "Encrypted meta frame",
|
||
|
"ETC": "Event timing codes",
|
||
|
"EQU": "Equalization",
|
||
|
"GEO": "General encapsulated object",
|
||
|
"IPL": "Involved people list",
|
||
|
"LNK": "Linked information",
|
||
|
"MCI": "Music CD Identifier",
|
||
|
"MLL": "MPEG location lookup table",
|
||
|
"PIC": "Attached picture",
|
||
|
"POP": "Popularimeter",
|
||
|
"REV": "Reverb",
|
||
|
"RVA": "Relative volume adjustment",
|
||
|
"SLT": "Synchronized lyric/text",
|
||
|
"STC": "Synced tempo codes",
|
||
|
"TAL": "Album/Movie/Show title",
|
||
|
"TBP": "BPM (Beats Per Minute)",
|
||
|
"TCM": "Composer",
|
||
|
"TCO": "Content type",
|
||
|
"TCR": "Copyright message",
|
||
|
"TDA": "Date",
|
||
|
"TDY": "Playlist delay",
|
||
|
"TEN": "Encoded by",
|
||
|
"TFT": "File type",
|
||
|
"TIM": "Time",
|
||
|
"TKE": "Initial key",
|
||
|
"TLA": "Language(s)",
|
||
|
"TLE": "Length",
|
||
|
"TMT": "Media type",
|
||
|
"TOA": "Original artist(s)/performer(s)",
|
||
|
"TOF": "Original filename",
|
||
|
"TOL": "Original Lyricist(s)/text writer(s)",
|
||
|
"TOR": "Original release year",
|
||
|
"TOT": "Original album/Movie/Show title",
|
||
|
"TP1": "Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group",
|
||
|
"TP2": "Band/Orchestra/Accompaniment",
|
||
|
"TP3": "Conductor/Performer refinement",
|
||
|
"TP4": "Interpreted, remixed, or otherwise modified by",
|
||
|
"TPA": "Part of a set",
|
||
|
"TPB": "Publisher",
|
||
|
"TRC": "ISRC (International Standard Recording Code)",
|
||
|
"TRD": "Recording dates",
|
||
|
"TRK": "Track number/Position in set",
|
||
|
"TSI": "Size",
|
||
|
"TSS": "Software/hardware and settings used for encoding",
|
||
|
"TT1": "Content group description",
|
||
|
"TT2": "Title/Songname/Content description",
|
||
|
"TT3": "Subtitle/Description refinement",
|
||
|
"TXT": "Lyricist/text writer",
|
||
|
"TXX": "User defined text information frame",
|
||
|
"TYE": "Year",
|
||
|
"UFI": "Unique file identifier",
|
||
|
"ULT": "Unsychronized lyric/text transcription",
|
||
|
"WAF": "Official audio file webpage",
|
||
|
"WAR": "Official artist/performer webpage",
|
||
|
"WAS": "Official audio source webpage",
|
||
|
"WCM": "Commercial information",
|
||
|
"WCP": "Copyright/Legal information",
|
||
|
"WPB": "Publishers official webpage",
|
||
|
"WXX": "User defined URL link frame",
|
||
|
// v2.3
|
||
|
"AENC": "Audio encryption",
|
||
|
"APIC": "Attached picture",
|
||
|
"ASPI": "Audio seek point index",
|
||
|
"CHAP": "Chapter",
|
||
|
"CTOC": "Table of contents",
|
||
|
"COMM": "Comments",
|
||
|
"COMR": "Commercial frame",
|
||
|
"ENCR": "Encryption method registration",
|
||
|
"EQU2": "Equalisation (2)",
|
||
|
"EQUA": "Equalization",
|
||
|
"ETCO": "Event timing codes",
|
||
|
"GEOB": "General encapsulated object",
|
||
|
"GRID": "Group identification registration",
|
||
|
"IPLS": "Involved people list",
|
||
|
"LINK": "Linked information",
|
||
|
"MCDI": "Music CD identifier",
|
||
|
"MLLT": "MPEG location lookup table",
|
||
|
"OWNE": "Ownership frame",
|
||
|
"PRIV": "Private frame",
|
||
|
"PCNT": "Play counter",
|
||
|
"POPM": "Popularimeter",
|
||
|
"POSS": "Position synchronisation frame",
|
||
|
"RBUF": "Recommended buffer size",
|
||
|
"RVA2": "Relative volume adjustment (2)",
|
||
|
"RVAD": "Relative volume adjustment",
|
||
|
"RVRB": "Reverb",
|
||
|
"SEEK": "Seek frame",
|
||
|
"SYLT": "Synchronized lyric/text",
|
||
|
"SYTC": "Synchronized tempo codes",
|
||
|
"TALB": "Album/Movie/Show title",
|
||
|
"TBPM": "BPM (beats per minute)",
|
||
|
"TCOM": "Composer",
|
||
|
"TCON": "Content type",
|
||
|
"TCOP": "Copyright message",
|
||
|
"TDAT": "Date",
|
||
|
"TDLY": "Playlist delay",
|
||
|
"TDRC": "Recording time",
|
||
|
"TDRL": "Release time",
|
||
|
"TDTG": "Tagging time",
|
||
|
"TENC": "Encoded by",
|
||
|
"TEXT": "Lyricist/Text writer",
|
||
|
"TFLT": "File type",
|
||
|
"TIME": "Time",
|
||
|
"TIPL": "Involved people list",
|
||
|
"TIT1": "Content group description",
|
||
|
"TIT2": "Title/songname/content description",
|
||
|
"TIT3": "Subtitle/Description refinement",
|
||
|
"TKEY": "Initial key",
|
||
|
"TLAN": "Language(s)",
|
||
|
"TLEN": "Length",
|
||
|
"TMCL": "Musician credits list",
|
||
|
"TMED": "Media type",
|
||
|
"TMOO": "Mood",
|
||
|
"TOAL": "Original album/movie/show title",
|
||
|
"TOFN": "Original filename",
|
||
|
"TOLY": "Original lyricist(s)/text writer(s)",
|
||
|
"TOPE": "Original artist(s)/performer(s)",
|
||
|
"TORY": "Original release year",
|
||
|
"TOWN": "File owner/licensee",
|
||
|
"TPE1": "Lead performer(s)/Soloist(s)",
|
||
|
"TPE2": "Band/orchestra/accompaniment",
|
||
|
"TPE3": "Conductor/performer refinement",
|
||
|
"TPE4": "Interpreted, remixed, or otherwise modified by",
|
||
|
"TPOS": "Part of a set",
|
||
|
"TPRO": "Produced notice",
|
||
|
"TPUB": "Publisher",
|
||
|
"TRCK": "Track number/Position in set",
|
||
|
"TRDA": "Recording dates",
|
||
|
"TRSN": "Internet radio station name",
|
||
|
"TRSO": "Internet radio station owner",
|
||
|
"TSOA": "Album sort order",
|
||
|
"TSOP": "Performer sort order",
|
||
|
"TSOT": "Title sort order",
|
||
|
"TSIZ": "Size",
|
||
|
"TSRC": "ISRC (international standard recording code)",
|
||
|
"TSSE": "Software/Hardware and settings used for encoding",
|
||
|
"TSST": "Set subtitle",
|
||
|
"TYER": "Year",
|
||
|
"TXXX": "User defined text information frame",
|
||
|
"UFID": "Unique file identifier",
|
||
|
"USER": "Terms of use",
|
||
|
"USLT": "Unsychronized lyric/text transcription",
|
||
|
"WCOM": "Commercial information",
|
||
|
"WCOP": "Copyright/Legal information",
|
||
|
"WOAF": "Official audio file webpage",
|
||
|
"WOAR": "Official artist/performer webpage",
|
||
|
"WOAS": "Official audio source webpage",
|
||
|
"WORS": "Official internet radio station homepage",
|
||
|
"WPAY": "Payment",
|
||
|
"WPUB": "Publishers official webpage",
|
||
|
"WXXX": "User defined URL link frame"
|
||
|
};
|
||
|
|
||
|
input = new Uint8Array(input);
|
||
|
let result = "";
|
||
|
|
||
|
/**
|
||
|
* Extracts the ID3 header fields.
|
||
|
*/
|
||
|
function extractHeader() {
|
||
|
if (input.slice(0, 3).toString() !== [0x49, 0x44, 0x33].toString())
|
||
|
throw new OperationError("No valid ID3 header.");
|
||
|
result = "{\n";
|
||
|
result += " Type: \"ID3\",\n";
|
||
|
|
||
|
// Tag version.
|
||
|
result += " Version: \"" + input[3].toString() + "." + input[4].toString() + "\",\n";
|
||
|
|
||
|
// Header flags.
|
||
|
result += " Flags: " + input[5].toString() + ",\n";
|
||
|
input = input.slice(6);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Converts the size fields to a single integer.
|
||
|
*
|
||
|
* @param {number} num
|
||
|
* @returns {string}
|
||
|
*/
|
||
|
function readSize(num) {
|
||
|
let result = 0;
|
||
|
|
||
|
// The sizes are 7 bit numbers stored in 8 bit locations.
|
||
|
for (let i = (num) * 7; i; i -= 7) {
|
||
|
result = (result << i) | input[0];
|
||
|
input = input.slice(1);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads frame header based on ID.
|
||
|
*
|
||
|
* @param {string} id
|
||
|
* @returns {number}
|
||
|
*/
|
||
|
function readFrame(id) {
|
||
|
result += " " + id + ": {\n";
|
||
|
result += " ID: \"" + id + "\",\n";
|
||
|
|
||
|
// Size of frame.
|
||
|
const size = readSize(4);
|
||
|
result += " Size: " + size.toString() + ",\n";
|
||
|
result += " Description: \"" + FRAME_DESCRIPTIONS[id] + "\",\n";
|
||
|
input = input.slice(2);
|
||
|
let data = "";
|
||
|
|
||
|
// Read data from frame.
|
||
|
for (let i = 1; i < size; i++)
|
||
|
data += String.fromCharCode(input[i]);
|
||
|
|
||
|
// Move to next Frame
|
||
|
input = input.slice(size);
|
||
|
result += " Data: \"" + data + "\",\n";
|
||
|
result += " },\n";
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
extractHeader();
|
||
|
|
||
|
const headerTagSize = readSize(4);
|
||
|
result += " Tags: {\n";
|
||
|
|
||
|
let pos = 10;
|
||
|
|
||
|
// While the current element is in the header.
|
||
|
while (pos < headerTagSize) {
|
||
|
|
||
|
// Frame Identifier of frame.
|
||
|
let id = String.fromCharCode(input[0]) + String.fromCharCode(input[1]) + String.fromCharCode(input[2]);
|
||
|
input = input.slice(3);
|
||
|
|
||
|
// If the next character is non-zero it is an identifier.
|
||
|
if (input[0] !== 0) {
|
||
|
id += String.fromCharCode(input[0]);
|
||
|
}
|
||
|
input = input.slice(1);
|
||
|
if (id in FRAME_DESCRIPTIONS) {
|
||
|
pos += 10 + readFrame(id);
|
||
|
// If end of header.
|
||
|
} else if (id === "\x00\x00\x00") {
|
||
|
break;
|
||
|
} else {
|
||
|
throw new OperationError("Unknown Frame Identifier: " + id);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Tidy up the output.
|
||
|
result += " },\n";
|
||
|
result += " Size: "+headerTagSize.toString() + ",\n";
|
||
|
result += "}";
|
||
|
return result;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
export default ExtractID3;
|