feat: qrc file support
This commit is contained in:
parent
fdc867bbc3
commit
051805a019
@ -16,6 +16,7 @@
|
||||
<sourceFolder url="file://$MODULE_DIR$/um_crypto/xiami/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/um_crypto/qtfm/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/um_crypto/mg3d/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/um_crypto/qrc/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/um_wasm_loader/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/um_wasm_loader/pkg" />
|
||||
|
36
Cargo.lock
generated
36
Cargo.lock
generated
@ -2,6 +2,12 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.4"
|
||||
@ -376,6 +382,15 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
@ -493,18 +508,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.63"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.63"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -558,6 +573,7 @@ dependencies = [
|
||||
"umc_mg3d",
|
||||
"umc_ncm",
|
||||
"umc_qmc",
|
||||
"umc_qrc",
|
||||
"umc_qtfm",
|
||||
"umc_xiami",
|
||||
"umc_xmly",
|
||||
@ -639,6 +655,18 @@ dependencies = [
|
||||
"umc_utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "umc_qrc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"hex",
|
||||
"itertools",
|
||||
"miniz_oxide",
|
||||
"thiserror",
|
||||
"umc_qmc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "umc_qtfm"
|
||||
version = "0.1.0"
|
||||
|
@ -5,4 +5,4 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.5.0"
|
||||
thiserror = "1.0.63"
|
||||
thiserror = "1.0.64"
|
||||
|
@ -10,6 +10,6 @@ cipher = "0.4.4"
|
||||
hmac = "0.12.1"
|
||||
pbkdf2 = "0.12.2"
|
||||
sha1 = "0.10.5"
|
||||
thiserror = "1.0.63"
|
||||
thiserror = "1.0.64"
|
||||
umc_qmc = { path = "../qmc" }
|
||||
umc_utils = { path = "../utils" }
|
||||
|
@ -6,5 +6,5 @@ edition = "2021"
|
||||
[dependencies]
|
||||
byteorder = "1.5.0"
|
||||
itertools = "0.13.0"
|
||||
thiserror = "1.0.63"
|
||||
thiserror = "1.0.64"
|
||||
umc_utils = { path = "../utils" }
|
||||
|
@ -7,6 +7,6 @@ edition = "2021"
|
||||
anyhow = "1.0.86"
|
||||
byteorder = "1.5.0"
|
||||
itertools = "0.13.0"
|
||||
thiserror = "1.0.63"
|
||||
thiserror = "1.0.64"
|
||||
umc_qmc = { path = "../qmc" }
|
||||
umc_utils = { path = "../utils" }
|
||||
|
@ -5,5 +5,5 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
hex = "0.4.3"
|
||||
thiserror = "1.0.63"
|
||||
thiserror = "1.0.64"
|
||||
umc_utils = { path = "../utils" }
|
||||
|
@ -9,7 +9,7 @@ byteorder = "1.5.0"
|
||||
cipher = { version = "0.4.4", features = ["block-padding"] }
|
||||
crc = "3.2.1"
|
||||
itertools = "0.13.0"
|
||||
thiserror = "1.0.63"
|
||||
thiserror = "1.0.64"
|
||||
umc_utils = { path = "../utils" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -9,5 +9,5 @@ byteorder = "1.5.0"
|
||||
itertools = "0.13.0"
|
||||
lazy_static = "1.5.0"
|
||||
tc_tea = { version = "0.2.0", default-features = false }
|
||||
thiserror = "1.0.63"
|
||||
thiserror = "1.0.64"
|
||||
umc_utils = { path = "../utils" }
|
||||
|
12
um_crypto/qrc/Cargo.toml
Normal file
12
um_crypto/qrc/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "umc_qrc"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.5.0"
|
||||
itertools = "0.13.0"
|
||||
miniz_oxide = "0.8.0"
|
||||
thiserror = "1.0.64"
|
||||
umc_qmc = { path = "../qmc" }
|
||||
hex = "0.4.3"
|
1
um_crypto/qrc/src/__fixture__/qrc_network_1_jp.txt
Normal file
1
um_crypto/qrc/src/__fixture__/qrc_network_1_jp.txt
Normal file
File diff suppressed because one or more lines are too long
1
um_crypto/qrc/src/__fixture__/qrc_network_1_roma.txt
Normal file
1
um_crypto/qrc/src/__fixture__/qrc_network_1_roma.txt
Normal file
File diff suppressed because one or more lines are too long
1
um_crypto/qrc/src/__fixture__/qrc_network_1_trans.txt
Normal file
1
um_crypto/qrc/src/__fixture__/qrc_network_1_trans.txt
Normal file
@ -0,0 +1 @@
|
||||
B2C78CE32CF1913A99A770B577281231519C5CEA3E75D8B9E80B77B6B6DC5857E948004C2C3D1BC62F22DF38BB8AE5BE33950BAB349485EEFB1CB2BC92657302BF90B85913E2349699AF483235ACF654FC89E8F1D32963D3463CD98D1CF4359AD509CE87B157537CE60F56598AD498D2AA4F46FF0E3948D5F88081E519026C3D952F58ED5E5BCA16827482D37BE303D39091BA5CFF7A1816AA3D25F3E1668BF4E4EDCA0F5FA3833536A6ED3A4D9C213B911BE32B33553C7D4730753E4023ACCEB54AFD6FDE8316F07AB12AC4C53147D26F2927F1C32090711B4C0F5A6B25442CA687C9BF0B5210D51F2E9735E2BEFD6C0C8BBA24FA622AA260851AB2FA48155193B6FE3BEB28BB5604E3ACBAD074398C3FD92E0EB439C87CF730044C550AC3580D228AE18178FB7800F809D244EDB18BE65DD4EA1DCA7B9E8F0C8ECCE028D3C26BDA11D5FE466665C124BD21187D0E000B127D59A6CADECE061F3E327D21CB2811E8D9B82B7FA8BAC20C3839C28A34FBB1AEDDD2524D393363D892F37032C2D1FCD45C92BF1C19643CBB2D58841C8D56DD8C43F03CE44432565BF91916DFC94BAB104A9EBE0B40731CA5F2D8B1EBE7A09CC7DB66E384F9CFEAE38ED674B8BCB62D095C838F84781624598BA7AD028A1D7A924E14B5352D059B5A1AFBEA82C46CDBBCC96FE0265D775ACB34D25D7181FDA6C06952C5AFF7DAC6164C0ABBFF048ED0EB1997171D944AA6F6FC250DFCB65719A6E5F27A55D0024BD46472403B3A833669CB5B50E44D1F4ACE79C55209744FFF8C3EFD9B26B805C9C97CAA13A4F9CB2F1A4841817648BEA9527C72492FF33458DE512E2BA4A578718D983DD264AE5FF0A8BF2E4BE121C4D862017A8CBDA32088B3A922C2B7CE3A7BB01DAB6AAC48AC42CF3CB1A644A5DF083D4313DD5B42439E0DA4A65A35FF914B33ECC7D4B639E5031AC3F4CCA13360F0EBAB4F3C40BC47B7011C4589A2D493C752B10DF637B21216E7213913ED438D500E1BF3FC60FCC95623924B833FEB06B52B7D3000914EE749272087A34330F801BE8C7432A7CE2A234F9D31354A9AD2449F1097B3AC1F25DCDB0E5EC8246B5065A434B9620D245A132363BB0B4CA4A773526233EA7B429E2EC84C266B235DC01E13FC342A2FA2D26382ABCF0FD93F9A05B3FA0FCDF6FAA3209D5193871187890E2115AEB65F543E6AA7AF8E22C3552AE3C81BBEA9FFC85AFAF404573D60A6523192246FCA2015DE5D8E4B22436F70AE84C458A6AD3A71932CD035D05AA6CC98A6D77B5DD7531DE9BBA04960C3C3ACD610E3209A0D09995B0608DC03D8602AD1CFC2BACA632AB07CD10D3D489F33F30753FFF930F07C58182CDDEFE417A8644F71C1F637B99E4BC85FCCB3D8966DA11849D56605FAAD7D89649753447DC258328D02D3972BAF5B4C8AD0581FE1AC3F5E1975F815A4D66F61A6F0AC8FA3D2F806063AB0054D43018592059B79F74CE032884F584BAD3A9FDAD5EDBB35FA7A6756A85E9B102F9ACCEAC467358F3E34734BBADD50645E6870F84B375226A3D68C29943856A32B3AA138C6ED1F4F17DFE9944D512694E974784344DFA32D4B68118779363CBB4D9AB6D9DB545358E39A5B2E4A2F98F0A7506A9945F6C3549C1DCF563A125D7CF3E42D25F60D44D8C6C00774E1A792FB62337595A31742A7D5CB9C84E45487995DF6ED239A0DD58310D8F9398BE23EEA9608373D3490830CD4B5659E1DF896A0E4B934770483482251B2D3C549630D4E1E1EA54DE95414627AB6A76020A27E0D96D5685DCD244071C3D2FA22431B53313FADE7B3D715C84547FCC9E329FDB9CCD19EFD704E8288B8558C7DC626DA319C18829C65777BC6A53843F3FCF78309CBAA748C75E124F4D839325455C5034F47323124F4A498A090562583C2F7409C8EBCC0C539A4815A1F740297D5C6E0FCBA4081A6B0C76D276E774407A4B7A1A4413EC948D87F53B2BA87D23828F899836332DF86AEA9A6D93208AA4317B4DEDCEAF7D8413B29476DDE79296786E8AC8EDD7306C67851AD7E7EEF222F7F7A4D3B9005CC9A06FA2E9F0C36753C125F4FA8CD42F9607C813384F2AD8C2C79952E5A3CAC41EC159890864C20A1F43F10406F021EF9227C187939E6E2FC7E9B91A16063D616A7B988A2FA65EAA5CB93EF434F2D0DDD763FF1EB4C2E04B5B9D0B4D0F3A5AB8834B220E98B89C3F4EE61C2FF52451213DA4EB7ECE204947CD5A79DE6ECDE7FD53B80F7F9DF4797EBBD9A6FE9FE1D4898276D
|
94
um_crypto/qrc/src/des/constants.rs
Normal file
94
um_crypto/qrc/src/des/constants.rs
Normal file
@ -0,0 +1,94 @@
|
||||
pub const KEY_RND_SHIFTS: [u8; 16] = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1];
|
||||
pub const SBOXES: [[u8; 64]; 8] = [
|
||||
[
|
||||
14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1, 3, 10, 10, 6, 6, 12, 12, 11, 5, 9,
|
||||
9, 5, 0, 3, 7, 8, 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7, 15, 5, 12, 11, 9, 3,
|
||||
7, 14, 3, 10, 10, 0, 5, 6, 0, 13,
|
||||
],
|
||||
[
|
||||
15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 15, 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0,
|
||||
9, 5, 11, 10, 5, 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2, 5, 11, 8, 6, 12, 7,
|
||||
6, 12, 9, 0, 3, 5, 2, 14, 15, 9,
|
||||
],
|
||||
[
|
||||
10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10, 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4,
|
||||
11, 2, 15, 8, 1, 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7, 11, 4, 1, 15, 2, 14,
|
||||
12, 3, 5, 11, 10, 5, 14, 2, 7, 12,
|
||||
],
|
||||
[
|
||||
7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3, 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12,
|
||||
10, 4, 14, 15, 9, 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 10, 7, 13, 13, 8, 15, 9, 1, 4, 3,
|
||||
5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14,
|
||||
],
|
||||
[
|
||||
2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1, 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0,
|
||||
9, 14, 8, 9, 6, 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13, 15, 6, 9, 15, 12, 0,
|
||||
5, 9, 6, 10, 3, 4, 0, 5, 14, 3,
|
||||
],
|
||||
[
|
||||
12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5, 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7,
|
||||
11, 5, 3, 11, 8, 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10, 7, 11, 0, 14, 4, 1,
|
||||
10, 7, 1, 6, 13, 0, 11, 8, 6, 13,
|
||||
],
|
||||
[
|
||||
4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10, 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10,
|
||||
15, 6, 8, 1, 6, 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7, 10, 9, 15, 5, 6, 0,
|
||||
8, 15, 0, 14, 5, 2, 9, 3, 2, 12,
|
||||
],
|
||||
[
|
||||
13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4, 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0,
|
||||
14, 12, 9, 7, 2, 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13, 0, 15, 6, 12, 10, 9,
|
||||
13, 0, 15, 3, 3, 5, 5, 6, 8, 11,
|
||||
],
|
||||
];
|
||||
|
||||
pub const PBOX: [u8; 32] = [
|
||||
15, 6, 19, 20, 28, 11, 27, 16, 0, 14, 22, 25, 4, 17, 30, 9, 1, 7, 23, 13, 31, 26, 2, 8, 18, 12,
|
||||
29, 5, 21, 10, 3, 24,
|
||||
];
|
||||
|
||||
pub const IP: [u8; 64] = [
|
||||
57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63,
|
||||
55, 47, 39, 31, 23, 15, 7, 56, 48, 40, 32, 24, 16, 8, 0, 58, 50, 42, 34, 26, 18, 10, 2, 60, 52,
|
||||
44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6,
|
||||
];
|
||||
|
||||
pub const IP_INV: [u8; 64] = [
|
||||
39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
|
||||
36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26,
|
||||
33, 1, 41, 9, 49, 17, 57, 25, 32, 0, 40, 8, 48, 16, 56, 24,
|
||||
];
|
||||
|
||||
pub const KEY_PERMUTATION_TABLE: [u8; 56] = [
|
||||
// key_param_c
|
||||
56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59,
|
||||
51, 43, 35, //
|
||||
// key_param_d
|
||||
62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27,
|
||||
19, 11, 3,
|
||||
];
|
||||
|
||||
pub const KEY_COMPRESSION: [u8; 48] = [
|
||||
// part 1
|
||||
13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
|
||||
// part 2
|
||||
45, 56, 35, 41, 51, 59, 34, 44, 55, 49, 37, 52, 48, 53, 43, 60, 38, 57, 50, 46, 54, 40, 33, 36,
|
||||
];
|
||||
|
||||
pub const KEY_EXPANSION: [u8; 48] = [
|
||||
31, 0, 1, 2, 3, 4, 3, 4, 5, 6, 7, 8, 7, 8, 9, 10, 11, 12, 11, 12, 13, 14, 15, 16, 15, 16, 17,
|
||||
18, 19, 20, 19, 20, 21, 22, 23, 24, 23, 24, 25, 26, 27, 28, 27, 28, 29, 30, 31, 0,
|
||||
];
|
||||
|
||||
pub const U64_SHIFT_TABLE_CACHE: [u64; 64] = {
|
||||
let mut data = [0u64; 64];
|
||||
|
||||
let mut i = 0;
|
||||
while i < 32 {
|
||||
data[i] = 1u64 << (31 - i);
|
||||
data[i + 32] = 1u64 << (31 - i + 32);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
data
|
||||
};
|
148
um_crypto/qrc/src/des/des.rs
Normal file
148
um_crypto/qrc/src/des/des.rs
Normal file
@ -0,0 +1,148 @@
|
||||
use super::constants;
|
||||
use super::utils::{hi32, lo32, make_u64, map_u32_bits, map_u64, swap_u64};
|
||||
use crate::QrcError;
|
||||
use byteorder::{ByteOrder, LE};
|
||||
use itertools::Either;
|
||||
|
||||
type DesSubKeys = [u64; 16];
|
||||
|
||||
pub enum DESMode {
|
||||
Encrypt,
|
||||
Decrypt,
|
||||
}
|
||||
|
||||
/// QRC's modified DES implementation
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct QrcDes {
|
||||
keys: DesSubKeys,
|
||||
}
|
||||
|
||||
impl QrcDes {
|
||||
fn ip(data: u64) -> u64 {
|
||||
map_u64(data, &constants::IP)
|
||||
}
|
||||
|
||||
fn ip_inv(data: u64) -> u64 {
|
||||
map_u64(data, &constants::IP_INV)
|
||||
}
|
||||
|
||||
const SBOX_SHIFTS: [u8; 8] = [26, 20, 14, 8, 58, 52, 46, 40];
|
||||
fn sbox_transform(state: u64) -> u32 {
|
||||
let stream = constants::SBOXES.iter().zip(Self::SBOX_SHIFTS);
|
||||
|
||||
stream.fold(0u32, |result, (sbox, large_state_shift)| {
|
||||
let sbox_idx = (state >> large_state_shift) & 0b111111;
|
||||
(result << 4) | (sbox[sbox_idx as usize] as u32)
|
||||
})
|
||||
}
|
||||
|
||||
fn des_crypt_proc(state: u64, key: u64) -> u64 {
|
||||
let mut state = state;
|
||||
let state_hi32 = hi32(state);
|
||||
let state_lo32 = lo32(state);
|
||||
|
||||
state = map_u64(make_u64(state_hi32, state_hi32), &constants::KEY_EXPANSION);
|
||||
state ^= key;
|
||||
|
||||
let mut next_lo32 = Self::sbox_transform(state);
|
||||
next_lo32 = map_u32_bits(next_lo32, &constants::PBOX);
|
||||
next_lo32 ^= state_lo32;
|
||||
make_u64(next_lo32, state_hi32)
|
||||
}
|
||||
|
||||
/// Create a new QrcDes Instance
|
||||
pub fn new(key: &[u8; 8], mode: DESMode) -> Self {
|
||||
Self {
|
||||
keys: Self::derive_subkeys(key, mode),
|
||||
}
|
||||
}
|
||||
|
||||
fn derive_subkeys(key: &[u8; 8], mode: DESMode) -> DesSubKeys {
|
||||
let key = u64::from_le_bytes(*key);
|
||||
|
||||
let param = map_u64(key, &constants::KEY_PERMUTATION_TABLE);
|
||||
let mut param_c = lo32(param);
|
||||
let mut param_d = hi32(param);
|
||||
|
||||
let update_param = |param: &mut u32, shift_left: u8| {
|
||||
let shift_right = 28 - shift_left;
|
||||
*param = (*param << shift_left) | ((*param >> shift_right) & 0xFFFFFFF0);
|
||||
};
|
||||
|
||||
let mut subkeys = DesSubKeys::default();
|
||||
|
||||
let key_iter = match mode {
|
||||
DESMode::Decrypt => Either::Left(subkeys.iter_mut().rev()),
|
||||
DESMode::Encrypt => Either::Right(subkeys.iter_mut()),
|
||||
};
|
||||
|
||||
for (subkey, shift_left) in key_iter.zip(constants::KEY_RND_SHIFTS) {
|
||||
update_param(&mut param_c, shift_left);
|
||||
update_param(&mut param_d, shift_left);
|
||||
|
||||
let key = make_u64(param_d, param_c);
|
||||
*subkey = map_u64(key, &constants::KEY_COMPRESSION);
|
||||
}
|
||||
|
||||
subkeys
|
||||
}
|
||||
|
||||
pub fn transform_block(&self, data: u64) -> u64 {
|
||||
let mut state = Self::ip(data);
|
||||
|
||||
let keys = self.keys.iter();
|
||||
state = keys.fold(state, |state, &key| Self::des_crypt_proc(state, key));
|
||||
|
||||
// Swap data hi32/lo32
|
||||
state = swap_u64(state);
|
||||
|
||||
// Final permutation
|
||||
state = Self::ip_inv(state);
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
pub fn transform_bytes(&self, data: &mut [u8]) -> Result<(), QrcError> {
|
||||
if data.len() % 8 != 0 {
|
||||
Err(QrcError::QRCDesInputSizeError)?;
|
||||
}
|
||||
|
||||
for block in data.chunks_exact_mut(8) {
|
||||
let value = LE::read_u64(block);
|
||||
let transformed = self.transform_block(value);
|
||||
LE::write_u64(block, transformed);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{DESMode, QrcDes};
|
||||
|
||||
#[test]
|
||||
fn test_des_decrypt() {
|
||||
let mut input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6];
|
||||
let expected_data = [
|
||||
0xFD, 0x0E, 0x64, 0x06, 0x65, 0xBE, 0x74, 0x13, //
|
||||
0x77, 0x63, 0x3B, 0x02, 0x45, 0x4E, 0x70, 0x7A, //
|
||||
];
|
||||
|
||||
let des = QrcDes::new(b"TEST!KEY", DESMode::Decrypt);
|
||||
des.transform_bytes(&mut input).unwrap();
|
||||
assert_eq!(input, expected_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_des_encrypt() {
|
||||
let mut input = [
|
||||
0xFD, 0x0E, 0x64, 0x06, 0x65, 0xBE, 0x74, 0x13, //
|
||||
0x77, 0x63, 0x3B, 0x02, 0x45, 0x4E, 0x70, 0x7A, //
|
||||
];
|
||||
let expected_data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6];
|
||||
|
||||
let des = QrcDes::new(b"TEST!KEY", DESMode::Encrypt);
|
||||
des.transform_bytes(&mut input).unwrap();
|
||||
assert_eq!(input, expected_data);
|
||||
}
|
||||
}
|
5
um_crypto/qrc/src/des/mod.rs
Normal file
5
um_crypto/qrc/src/des/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
mod constants;
|
||||
mod des;
|
||||
mod utils;
|
||||
|
||||
pub use des::{DESMode, QrcDes};
|
55
um_crypto/qrc/src/des/utils.rs
Normal file
55
um_crypto/qrc/src/des/utils.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use crate::des::constants::U64_SHIFT_TABLE_CACHE;
|
||||
|
||||
pub const fn make_u64(hi32: u32, lo32: u32) -> u64 {
|
||||
((hi32 as u64) << 32) | (lo32 as u64)
|
||||
}
|
||||
|
||||
pub const fn swap_u64(value: u64) -> u64 {
|
||||
(value.wrapping_shr(32)) | (value.wrapping_shl(32))
|
||||
}
|
||||
|
||||
pub const fn lo32(value: u64) -> u32 {
|
||||
value as u32
|
||||
}
|
||||
|
||||
pub const fn hi32(value: u64) -> u32 {
|
||||
value.wrapping_shr(32) as u32
|
||||
}
|
||||
|
||||
pub const fn get_u64_by_shift_idx(value: u8) -> u64 {
|
||||
// 1u64.wrapping_shl(31u32.wrapping_sub(value as u32))
|
||||
// This is not portable, so let's use a pre-computed table...
|
||||
|
||||
U64_SHIFT_TABLE_CACHE[value as usize]
|
||||
}
|
||||
|
||||
pub fn map_bit(result: u64, src: u64, check: u8, set: u8) -> u64 {
|
||||
match get_u64_by_shift_idx(check) & src {
|
||||
0 => result,
|
||||
_ => result | get_u64_by_shift_idx(set),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_u32_bits(src_value: u32, table: &[u8]) -> u32 {
|
||||
let stream = table.iter().enumerate();
|
||||
|
||||
stream.fold(0u64, |result, (i, &check_idx)| {
|
||||
map_bit(result, src_value as u64, check_idx, i as u8)
|
||||
}) as u32
|
||||
}
|
||||
|
||||
pub fn map_u64(src_value: u64, table: &[u8]) -> u64 {
|
||||
assert_eq!(table.len() % 2, 0, "table.len() should be even");
|
||||
|
||||
let (table_lo32, table_hi32) = table.split_at(table.len() / 2);
|
||||
|
||||
let mut lo32 = 0u64;
|
||||
let mut hi32 = 0u64;
|
||||
|
||||
for (i, (&idx_lo32, &idx_hi32)) in table_lo32.iter().zip(table_hi32).enumerate() {
|
||||
lo32 = map_bit(lo32, src_value, idx_lo32, i as u8);
|
||||
hi32 = map_bit(hi32, src_value, idx_hi32, i as u8);
|
||||
}
|
||||
|
||||
make_u64(hi32 as u32, lo32 as u32)
|
||||
}
|
118
um_crypto/qrc/src/lib.rs
Normal file
118
um_crypto/qrc/src/lib.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use hex::FromHexError;
|
||||
use miniz_oxide::inflate::{decompress_to_vec_zlib_with_limit as inflate, DecompressError};
|
||||
use thiserror::Error;
|
||||
|
||||
mod des;
|
||||
use crate::des::DESMode;
|
||||
use des::QrcDes;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum QrcError {
|
||||
#[error("QRCDes: input is not block of 8 bytes")]
|
||||
QRCDesInputSizeError,
|
||||
|
||||
#[error("QRC: Failed to inflate: {0}")]
|
||||
QRCInflateError(DecompressError),
|
||||
|
||||
#[error("QRC: Failed to decode hex: {0}")]
|
||||
QRCHexDecodeError(FromHexError),
|
||||
|
||||
#[error("QRC: Invalid file magic header")]
|
||||
QRCInvalidMagicHeader,
|
||||
}
|
||||
|
||||
// Max 4MiB for QRC
|
||||
const MAX_QRC_SIZE: usize = 4 * 1024 * 1024;
|
||||
|
||||
const DES_KEY_1: &[u8; 8] = b"!@#)(NHL";
|
||||
const DES_KEY_2: &[u8; 8] = b"123ZXC!@";
|
||||
const DES_KEY_3: &[u8; 8] = b"!@#)(*$%";
|
||||
|
||||
pub fn decrypt_qrc(data: &[u8]) -> Result<Vec<u8>, QrcError> {
|
||||
let mut temp = data.to_vec();
|
||||
QrcDes::new(DES_KEY_1, DESMode::Decrypt).transform_bytes(&mut temp)?;
|
||||
QrcDes::new(DES_KEY_2, DESMode::Encrypt).transform_bytes(&mut temp)?;
|
||||
QrcDes::new(DES_KEY_3, DESMode::Decrypt).transform_bytes(&mut temp)?;
|
||||
let result = inflate(&temp[..], MAX_QRC_SIZE).map_err(QrcError::QRCInflateError)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Decrypt QRC data from API response
|
||||
pub fn decrypt_qrc_network(data: &str) -> Result<Vec<u8>, QrcError> {
|
||||
let data = hex::decode(data).map_err(QrcError::QRCHexDecodeError)?;
|
||||
decrypt_qrc(&data[..])
|
||||
}
|
||||
|
||||
const QRC_MAGIC: [u8; 11] = [
|
||||
0x98, 0x25, 0xB0, 0xAC, 0xE3, 0x02, 0x83, 0x68, 0xE8, 0xFC, 0x6C,
|
||||
];
|
||||
|
||||
/// Decrypt QRC data from cached local file
|
||||
pub fn decrypt_qrc_file(data: &[u8]) -> Result<Vec<u8>, QrcError> {
|
||||
let data = match data.strip_prefix(&QRC_MAGIC) {
|
||||
None => Err(QrcError::QRCInvalidMagicHeader)?,
|
||||
Some(data) => data,
|
||||
};
|
||||
let mut temp = data.to_vec();
|
||||
umc_qmc::v1::decrypt(&mut temp, QRC_MAGIC.len());
|
||||
decrypt_qrc(&temp[..])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{decrypt_qrc_file, decrypt_qrc_network};
|
||||
|
||||
#[test]
|
||||
fn test_qrc_file() {
|
||||
let data = [
|
||||
0x98, 0x25, 0xB0, 0xAC, 0xE3, 0x02, 0x83, 0x68, 0xE8, 0xFC, 0x6C, 0xAB, 0x9A, 0x34,
|
||||
0xE2, 0x31, 0x26, 0xAF, 0x6E, 0x2A, 0x23, 0xB3, 0x56, 0xC3, 0xBF, 0x8A, 0xA6,
|
||||
];
|
||||
|
||||
let result = decrypt_qrc_file(&data).expect("Decryption failed.");
|
||||
assert_eq!(result, b"nothing");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_qrc_file_2() {
|
||||
let data = [
|
||||
0x98, 0x25, 0xB0, 0xAC, 0xE3, 0x02, 0x83, 0x68, 0xE8, 0xFC, 0x6C, 0x07, 0xBC, 0x8C,
|
||||
0x46, 0x97, 0x36, 0xDF, 0x06, 0x13, 0xE0, 0x31, 0xD1, 0xF8, 0x98, 0xEF, 0xD0, 0x1B,
|
||||
0xEA, 0x6B, 0x04, 0x1D, 0xDB, 0xE0, 0x0F, 0x33, 0x2B, 0xBE, 0x95, 0x27, 0xB9, 0xF6,
|
||||
0xEE, 0x0C, 0x75, 0x0C, 0x46, 0x4C, 0xA8, 0xE8, 0x37, 0x93, 0x03, 0xC0, 0xA6, 0x98,
|
||||
0xD0, 0x4B, 0x6E, 0xBB, 0x2A, 0x8C, 0x3E, 0xE8, 0x7F, 0xC2, 0x0F, 0x6E, 0x2E, 0x3E,
|
||||
0xAD, 0x38, 0xCF, 0x74, 0x01, 0x17, 0xDA, 0xE0, 0x62, 0x45, 0x4F, 0xF8, 0x35,
|
||||
];
|
||||
|
||||
let result = decrypt_qrc_file(&data).expect("Decryption failed.");
|
||||
assert_eq!(
|
||||
String::from_utf8_lossy(&result),
|
||||
"[00:00:00]此歌曲为没有填词的纯音乐,请您欣赏"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_qrc_from_network() {
|
||||
// Original QRC
|
||||
let input_data = include_str!("__fixture__/qrc_network_1_jp.txt");
|
||||
let qrc_original_b = decrypt_qrc_network(input_data).expect("decrypt failed");
|
||||
let qrc_original = String::from_utf8(qrc_original_b).expect("decode failed");
|
||||
assert!(qrc_original.contains("太(1399,388)陽(1787,486)系(2273,1433)を(3706,404)"));
|
||||
|
||||
// QRC 罗马字
|
||||
let input_data = include_str!("__fixture__/qrc_network_1_roma.txt");
|
||||
let qrc_romaji_b = decrypt_qrc_network(input_data).expect("decrypt failed");
|
||||
let qrc_romaji = String::from_utf8(qrc_romaji_b).expect("decode failed");
|
||||
assert!(qrc_romaji.contains("ta (1399,194)i (1593,194)yo (1787,243)u (2029,243)ke"));
|
||||
|
||||
// QRC 翻译 (LRC)
|
||||
let input_data = include_str!("__fixture__/qrc_network_1_trans.txt");
|
||||
let qrc_translation_b = decrypt_qrc_network(input_data).expect("decrypt failed");
|
||||
let qrc_translation = String::from_utf8(qrc_translation_b).expect("decode failed");
|
||||
assert!(qrc_translation.contains("[00:01.39]摆脱太阳系"));
|
||||
|
||||
println!("qrc_original: {}", qrc_original);
|
||||
println!("qrc_romaji: {}", qrc_romaji);
|
||||
println!("qrc_translation: {}", qrc_translation);
|
||||
}
|
||||
}
|
@ -8,5 +8,5 @@ aes = "0.8.4"
|
||||
byteorder = "1.5.0"
|
||||
cbc = "0.1.2"
|
||||
ctr = "0.9.2"
|
||||
thiserror = "1.0.63"
|
||||
thiserror = "1.0.64"
|
||||
umc_utils = { path = "../utils" }
|
||||
|
@ -4,4 +4,4 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
thiserror = "1.0.63"
|
||||
thiserror = "1.0.64"
|
||||
|
@ -10,5 +10,5 @@ cbc = "0.1.2"
|
||||
cipher = "0.4.4"
|
||||
hex = "0.4.3"
|
||||
lazy_static = "1.5.0"
|
||||
thiserror = "1.0.63"
|
||||
thiserror = "1.0.64"
|
||||
umc_utils = { path = "../utils" }
|
||||
|
@ -29,6 +29,7 @@ umc_kuwo = { path = "../um_crypto/kuwo" }
|
||||
umc_mg3d = { path = "../um_crypto/mg3d" }
|
||||
umc_ncm = { path = "../um_crypto/ncm" }
|
||||
umc_qmc = { path = "../um_crypto/qmc" }
|
||||
umc_qrc = { path = "../um_crypto/qrc" }
|
||||
umc_qtfm = { path = "../um_crypto/qtfm" }
|
||||
umc_xiami = { path = "../um_crypto/xiami" }
|
||||
umc_xmly = { path = "../um_crypto/xmly" }
|
||||
|
@ -5,6 +5,7 @@ pub mod kuwo;
|
||||
pub mod mg3d;
|
||||
pub mod ncm;
|
||||
pub mod qmc;
|
||||
mod qrc;
|
||||
pub mod qtfm;
|
||||
pub mod xiami;
|
||||
pub mod xmly;
|
||||
|
14
um_wasm/src/exports/qrc.rs
Normal file
14
um_wasm/src/exports/qrc.rs
Normal file
@ -0,0 +1,14 @@
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen::JsError;
|
||||
|
||||
/// QRC Decrypt ("*.qrc" cache file)
|
||||
#[wasm_bindgen(js_name=decryptQRCFile)]
|
||||
pub fn js_decrypt_qrc(buffer: &mut [u8]) -> Result<Vec<u8>, JsError> {
|
||||
umc_qrc::decrypt_qrc_file(buffer).map_err(JsError::from)
|
||||
}
|
||||
|
||||
/// QRC Decrypt (network response)
|
||||
#[wasm_bindgen(js_name=decryptQRCNetwork)]
|
||||
pub fn js_decrypt_qrc_network(buffer: &str) -> Result<Vec<u8>, JsError> {
|
||||
umc_qrc::decrypt_qrc_network(buffer).map_err(JsError::from)
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@unlock-music/crypto",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"description": "Project Unlock Music: 加解密支持库",
|
||||
"scripts": {
|
||||
"build": "node build.js",
|
||||
|
Loading…
Reference in New Issue
Block a user