Skip to content

Decryption

ChouChiu edited this page Sep 23, 2026 · 3 revisions

歌词解密

库提供 QQ 音乐 QRC 与酷狗音乐 KRC 两种专有加密格式的解密实现。顶层便捷函数 decrypt_qrc() 与 decrypt_krc() 失败时返回 None;如需获取具体错误原因,可使用实现了 LyricsDecrypter 的 QrcDecrypter 与 KrcDecrypter。

QRC 解密

QRC 密文为十六进制编码字符串。解密流水线:

  1. 过滤空白字符并进行十六进制解码。
  2. Triple DES(3DES-EDE,固定 24 字节密钥)按 8 字节分组解密。
  3. zlib 解压缩。
  4. UTF-8 文本解码。
use lyrics_helper::{decrypt_qrc, parse, LyricsRawTypes};

fn main() {
    let encrypted = "9FD341D70ACF96D59FB0C7DBB4D6DC3597BDB2000DB276840C98E56C039C7200698E953B\
                     BA404B8F7B04CF1342633627F64E04CEF5B16748696B9A64B3987B174943C1CE7EFB04A0\
                     725C8833D4ED3DA19E2B2B2EAE76F5365BE4571E7EA46C44";

    if let Some(decrypted) = decrypt_qrc(encrypted) {
        println!("解密后的 QRC 文本:\n{decrypted}");

        let data = parse(&decrypted, LyricsRawTypes::Qrc).expect("解析失败");
        println!("解析得到 {} 行歌词", data.lines.as_ref().map_or(0, Vec::len));
    }
}

KRC 解密

KRC 密文为 Base64 编码的二进制流。解密流水线:

  1. Base64 解码。
  2. 跳过开头的 4 字节魔数头(krc1)。
  3. 使用固定 16 字节密钥逐字节循环异或(XOR)。
  4. zlib 解压缩。
  5. 去除正文开头的 UTF-8 BOM(0xEF, 0xBB, 0xBF,若存在)。
  6. UTF-8 文本解码。

Note

酷狗以 charset=utf8 下发 KRC,解压后的正文普遍带有 UTF-8 BOM。上游 C# 实现采用 Encoding.UTF8.GetString(...)[1..] 方式解码后再丢弃首个字符;本库仅在检测到 BOM 字节序列时才予以剥离,既避免了无 BOM 时吞掉正文首字符的问题,也防止按字节截断导致 UTF-8 序列破损。

use lyrics_helper::{decrypt_krc, parse, LyricsRawTypes};

fn main() {
    // 带有 krc1 魔数头与 UTF-8 BOM 的 KRC 文件 Base64
    let krc_content = "a3JjMTidGsglTQAO96N6R6CsQj70xu/m37M169y3v7RPU+Do3QAL+VO6caa00kJo1dGLHMwLQ\
                       P1Sv7SlsM90FOeOblfHU2Y=";

    if let Some(decrypted) = decrypt_krc(krc_content) {
        let data = parse(&decrypted, LyricsRawTypes::Krc).expect("解析失败");
        println!("解析得到 {} 行歌词", data.lines.as_ref().map_or(0, Vec::len));
    }
}

若输入为未编码的文件原始字节切片(&[u8]),可直接调用 lyrics_helper::decrypter::decrypter::krc::decrypter::decrypt_lyrics_from_file(&bytes)。

获取解密错误详情

QrcDecrypter 与 KrcDecrypter 实现 LyricsDecrypter trait,其 decrypt() 方法返回 Result<String, DecryptError>:

use lyrics_helper::traits::{DecryptError, LyricsDecrypter};
use lyrics_helper::{KrcDecrypter, QrcDecrypter};

fn main() {
    let krc_content = "a3JjMTidGsglTQAO96N6R6CsQj70xu/m37M169y3v7RPU+Do3QAL+VO6caa00kJo1dGLHMwLQ\
                       P1Sv7SlsM90FOeOblfHU2Y=";
    assert!(KrcDecrypter.decrypt(krc_content).is_ok());

    // 格式不符:非十六进制
    assert_eq!(QrcDecrypter.decrypt("not_hex"), Err(DecryptError::InvalidInput));

    // 解密数据无法通过 zlib 解压
    assert_eq!(QrcDecrypter.decrypt("00"), Err(DecryptError::DecompressionFailed));
}

DecryptError 实现了 Copy + Eq + Display + Error,包含三种失败原因:

  • InvalidInput:Base64/十六进制解码失败,或输入数据长度不足(如未满 4 字节或非 8 字节整倍数)。
  • DecompressionFailed:数据解密后无法通过 zlib 解压。
  • InvalidEncoding:解压后的字节流不是合法的 UTF-8 文本。

算法规格

格式 对称算法 压缩方式 输入特征
QRC Triple DES (3DES-EDE,24 字节密钥) zlib 十六进制文本或密文字节
KRC 逐字节 XOR (16 字节密钥) zlib Base64 文本或以 krc1 开头的二进制流

解密核心实现在 lyrics-crypto crate 内,Triple DES 与 XOR 均为自主实现,无需引入额外重量级加密库;zlib 解压由 flate2 支持。

Clone this wiki locally