Compare commits
2 Commits
1e1d642149
...
c8af0b1211
Author | SHA1 | Date | |
---|---|---|---|
c8af0b1211 | |||
2222e7bc50 |
@ -10,6 +10,7 @@
|
|||||||
<sourceFolder url="file://$MODULE_DIR$/um_crypto/utils/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/um_crypto/utils/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/um_crypto/ncm/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/um_crypto/ncm/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/um_audio/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/um_audio/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/um_crypto/kgm/src" isTestSource="false" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/um_wasm_loader/dist" />
|
<excludeFolder url="file://$MODULE_DIR$/um_wasm_loader/dist" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/um_wasm_loader/pkg" />
|
<excludeFolder url="file://$MODULE_DIR$/um_wasm_loader/pkg" />
|
||||||
|
42
Cargo.lock
generated
42
Cargo.lock
generated
@ -74,6 +74,15 @@ version = "0.22.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-padding"
|
name = "block-padding"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@ -216,6 +225,16 @@ version = "0.1.13"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.13.0"
|
version = "1.13.0"
|
||||||
@ -303,6 +322,16 @@ version = "0.4.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "md-5"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minicov"
|
name = "minicov"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
@ -441,6 +470,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
"umc_kgm",
|
||||||
"umc_kuwo",
|
"umc_kuwo",
|
||||||
"umc_ncm",
|
"umc_ncm",
|
||||||
"umc_qmc",
|
"umc_qmc",
|
||||||
@ -462,6 +492,16 @@ dependencies = [
|
|||||||
"wasm-bindgen-test",
|
"wasm-bindgen-test",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "umc_kgm"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"itertools",
|
||||||
|
"thiserror",
|
||||||
|
"umc_utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "umc_kuwo"
|
name = "umc_kuwo"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -507,6 +547,8 @@ name = "umc_utils"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
|
"itertools",
|
||||||
|
"md-5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -7,6 +7,7 @@ edition = "2021"
|
|||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
clap = { version = "4.5.17", features = ["derive"] }
|
clap = { version = "4.5.17", features = ["derive"] }
|
||||||
umc_kuwo = { path = "../um_crypto/kuwo" }
|
umc_kuwo = { path = "../um_crypto/kuwo" }
|
||||||
umc_qmc = { path = "../um_crypto/qmc" }
|
umc_kgm = { path = "../um_crypto/kgm" }
|
||||||
umc_ncm = { path = "../um_crypto/ncm" }
|
umc_ncm = { path = "../um_crypto/ncm" }
|
||||||
|
umc_qmc = { path = "../um_crypto/qmc" }
|
||||||
umc_utils = { path = "../um_crypto/utils" }
|
umc_utils = { path = "../um_crypto/utils" }
|
||||||
|
44
um_cli/src/cmd/kgm.rs
Normal file
44
um_cli/src/cmd/kgm.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
use crate::Cli;
|
||||||
|
use clap::Args;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use umc_kgm::header::Header;
|
||||||
|
|
||||||
|
/// Decrypt a KGM/VPR file (Kugou Music)
|
||||||
|
#[derive(Args)]
|
||||||
|
pub struct ArgsKGM {
|
||||||
|
/// Path to output file, e.g. /export/Music/song.flac
|
||||||
|
#[arg(short, long)]
|
||||||
|
output: PathBuf,
|
||||||
|
|
||||||
|
/// Path to input file, e.g. /export/Music/song.kgm
|
||||||
|
#[arg(name = "input")]
|
||||||
|
input: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArgsKGM {
|
||||||
|
pub fn run(&self, cli: &Cli) -> anyhow::Result<i32> {
|
||||||
|
let mut file_input = File::open(&self.input)?;
|
||||||
|
let mut header = [0u8; 0x40];
|
||||||
|
file_input.read_exact(&mut header)?;
|
||||||
|
let kgm_header = Header::from_buffer(&mut header)?;
|
||||||
|
let decipher = kgm_header.make_decipher()?;
|
||||||
|
file_input.seek(SeekFrom::Start(kgm_header.offset_to_data as u64))?;
|
||||||
|
|
||||||
|
let mut offset = 0usize;
|
||||||
|
let mut buffer = vec![0u8; cli.buffer_size].into_boxed_slice();
|
||||||
|
let mut file_output = File::create(&self.output)?;
|
||||||
|
while let Ok(n) = file_input.read(&mut buffer) {
|
||||||
|
if n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
decipher.decrypt(&mut buffer[..n], offset);
|
||||||
|
file_output.write_all(&buffer[..n])?;
|
||||||
|
offset += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
|
|
||||||
|
pub mod kgm;
|
||||||
pub mod ncm;
|
pub mod ncm;
|
||||||
pub mod qmc1;
|
pub mod qmc1;
|
||||||
pub mod qmc2;
|
pub mod qmc2;
|
||||||
@ -12,4 +13,6 @@ pub enum Commands {
|
|||||||
QMCv2(qmc2::ArgsQMCv2),
|
QMCv2(qmc2::ArgsQMCv2),
|
||||||
#[command(name = "ncm")]
|
#[command(name = "ncm")]
|
||||||
NCM(ncm::ArgsNCM),
|
NCM(ncm::ArgsNCM),
|
||||||
|
#[command(name = "kgm")]
|
||||||
|
KGM(kgm::ArgsKGM),
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ pub struct ArgsQMCv2 {
|
|||||||
info_only: bool,
|
info_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_ekey(ekey: &str) -> Result<Box<[u8]>> {
|
fn read_ekey(ekey: &str) -> Result<Vec<u8>> {
|
||||||
let mut external_file = false;
|
let mut external_file = false;
|
||||||
let mut decrypt_ekey = true;
|
let mut decrypt_ekey = true;
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ fn read_ekey(ekey: &str) -> Result<Box<[u8]>> {
|
|||||||
let ekey = ekey.trim();
|
let ekey = ekey.trim();
|
||||||
let ekey = match decrypt_ekey {
|
let ekey = match decrypt_ekey {
|
||||||
true => umc_qmc::ekey::decrypt(ekey)?,
|
true => umc_qmc::ekey::decrypt(ekey)?,
|
||||||
false => base64::decode(ekey)?.into_boxed_slice(),
|
false => base64::decode(ekey)?,
|
||||||
};
|
};
|
||||||
Ok(ekey)
|
Ok(ekey)
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ fn run_command(cli: &Cli) -> Result<i32> {
|
|||||||
Some(Commands::QMCv1(cmd)) => cmd.run(&cli),
|
Some(Commands::QMCv1(cmd)) => cmd.run(&cli),
|
||||||
Some(Commands::QMCv2(cmd)) => cmd.run(&cli),
|
Some(Commands::QMCv2(cmd)) => cmd.run(&cli),
|
||||||
Some(Commands::NCM(cmd)) => cmd.run(&cli),
|
Some(Commands::NCM(cmd)) => cmd.run(&cli),
|
||||||
|
Some(Commands::KGM(cmd)) => cmd.run(&cli),
|
||||||
None => {
|
None => {
|
||||||
// https://github.com/clap-rs/clap/issues/3857#issuecomment-1161796261
|
// https://github.com/clap-rs/clap/issues/3857#issuecomment-1161796261
|
||||||
todo!("implement a sensible default command, similar to um/cli");
|
todo!("implement a sensible default command, similar to um/cli");
|
||||||
|
10
um_crypto/kgm/Cargo.toml
Normal file
10
um_crypto/kgm/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "umc_kgm"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
byteorder = "1.5.0"
|
||||||
|
itertools = "0.13.0"
|
||||||
|
thiserror = "1.0.63"
|
||||||
|
umc_utils = { path = "../utils" }
|
BIN
um_crypto/kgm/src/__fixtures__/kgm_v2_hdr.bin
Normal file
BIN
um_crypto/kgm/src/__fixtures__/kgm_v2_hdr.bin
Normal file
Binary file not shown.
59
um_crypto/kgm/src/header.rs
Normal file
59
um_crypto/kgm/src/header.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
use crate::v2::DecipherV2;
|
||||||
|
use crate::v3::DecipherV3;
|
||||||
|
use crate::{Decipher, KugouError};
|
||||||
|
use byteorder::{ByteOrder, LE};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct Header {
|
||||||
|
pub magic: [u8; 0x10],
|
||||||
|
pub offset_to_data: usize,
|
||||||
|
pub crypto_version: u32,
|
||||||
|
pub key_slot: u32,
|
||||||
|
pub decrypt_test_data: [u8; 0x10],
|
||||||
|
pub file_key: [u8; 0x10],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Header {
|
||||||
|
pub fn from_buffer<T>(buffer: T) -> Result<Self, KugouError>
|
||||||
|
where
|
||||||
|
T: AsRef<[u8]>,
|
||||||
|
{
|
||||||
|
let buffer = buffer.as_ref();
|
||||||
|
if buffer.len() < 0x3c {
|
||||||
|
Err(KugouError::HeaderTooSmall(0x3c))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut magic = [0u8; 0x10];
|
||||||
|
magic.copy_from_slice(&buffer[..0x10]);
|
||||||
|
let offset_to_data = LE::read_u32(&buffer[0x10..0x14]) as usize;
|
||||||
|
let crypto_version = LE::read_u32(&buffer[0x14..0x18]);
|
||||||
|
let key_slot = LE::read_u32(&buffer[0x18..0x1C]);
|
||||||
|
let mut decrypt_test_data = [0u8; 0x10];
|
||||||
|
decrypt_test_data.copy_from_slice(&buffer[0x1c..0x2c]);
|
||||||
|
let mut file_key = [0u8; 0x10];
|
||||||
|
file_key.copy_from_slice(&buffer[0x2c..0x3c]);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
magic,
|
||||||
|
offset_to_data,
|
||||||
|
crypto_version,
|
||||||
|
key_slot,
|
||||||
|
decrypt_test_data,
|
||||||
|
file_key,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_decipher(&self) -> Result<Box<dyn Decipher>, KugouError> {
|
||||||
|
let slot_key: &[u8] = match self.key_slot {
|
||||||
|
1 => b"l,/'",
|
||||||
|
slot => Err(KugouError::UnsupportedKeySlot(slot))?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let decipher: Box<dyn Decipher> = match self.crypto_version {
|
||||||
|
2 => Box::from(DecipherV2::new(self, slot_key)?),
|
||||||
|
3 => Box::from(DecipherV3::new(self, slot_key)?),
|
||||||
|
version => Err(KugouError::UnsupportedCipherVersion(version))?,
|
||||||
|
};
|
||||||
|
Ok(decipher)
|
||||||
|
}
|
||||||
|
}
|
21
um_crypto/kgm/src/lib.rs
Normal file
21
um_crypto/kgm/src/lib.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
pub mod header;
|
||||||
|
pub mod v2;
|
||||||
|
mod v3;
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum KugouError {
|
||||||
|
#[error("Header too small, need at least {0} bytes.")]
|
||||||
|
HeaderTooSmall(usize),
|
||||||
|
|
||||||
|
#[error("Unsupported key slot: {0}")]
|
||||||
|
UnsupportedKeySlot(u32),
|
||||||
|
|
||||||
|
#[error("Unsupported cipher version: {0}")]
|
||||||
|
UnsupportedCipherVersion(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Decipher {
|
||||||
|
fn decrypt(&self, buffer: &mut [u8], offset: usize);
|
||||||
|
}
|
31
um_crypto/kgm/src/v2.rs
Normal file
31
um_crypto/kgm/src/v2.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use crate::header::Header;
|
||||||
|
use crate::{Decipher, KugouError};
|
||||||
|
|
||||||
|
pub struct DecipherV2 {
|
||||||
|
key: [u8; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DecipherV2 {
|
||||||
|
pub fn new(_header: &Header, slot_key: &[u8]) -> Result<Self, KugouError> {
|
||||||
|
let mut key = [0u8; 4];
|
||||||
|
key.copy_from_slice(slot_key);
|
||||||
|
Ok(Self { key })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decipher for DecipherV2 {
|
||||||
|
fn decrypt(&self, buffer: &mut [u8], offset: usize) {
|
||||||
|
let key_stream = self.key.iter().cycle().skip(offset % self.key.len());
|
||||||
|
for (datum, &k) in buffer.iter_mut().zip(key_stream) {
|
||||||
|
*datum ^= k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_v2_init() -> Result<(), KugouError> {
|
||||||
|
let hdr_v2 = Header::from_buffer(include_bytes!("__fixtures__/kgm_v2_hdr.bin"))?;
|
||||||
|
DecipherV2::new(&hdr_v2)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
48
um_crypto/kgm/src/v3.rs
Normal file
48
um_crypto/kgm/src/v3.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use crate::header::Header;
|
||||||
|
use crate::{Decipher, KugouError};
|
||||||
|
|
||||||
|
pub struct DecipherV3 {
|
||||||
|
slot_key: [u8; 16],
|
||||||
|
file_key: [u8; 17],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DecipherV3 {
|
||||||
|
fn hash_key<T: AsRef<[u8]>>(data: T) -> [u8; 16] {
|
||||||
|
let digest = umc_utils::md5(data);
|
||||||
|
let mut result = [0u8; 16];
|
||||||
|
for (result, digest) in result.rchunks_exact_mut(2).zip(digest.chunks_exact(2)) {
|
||||||
|
result[0] = digest[0];
|
||||||
|
result[1] = digest[1];
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(header: &Header, slot_key: &[u8]) -> Result<Self, KugouError> {
|
||||||
|
let slot_key = Self::hash_key(slot_key);
|
||||||
|
|
||||||
|
let mut file_key = [0x6b; 17];
|
||||||
|
file_key[..16].copy_from_slice(&Self::hash_key(header.file_key));
|
||||||
|
|
||||||
|
Ok(Self { slot_key, file_key })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decipher for DecipherV3 {
|
||||||
|
fn decrypt(&self, buffer: &mut [u8], offset: usize) {
|
||||||
|
let slot_key_stream = self.slot_key.iter().cycle().skip(offset);
|
||||||
|
let file_key_stream = self.file_key.iter().cycle().skip(offset);
|
||||||
|
|
||||||
|
let mut offset = offset as u32;
|
||||||
|
let key_stream = slot_key_stream.zip(file_key_stream);
|
||||||
|
for (datum, (&slot_key, &file_key)) in buffer.iter_mut().zip(key_stream) {
|
||||||
|
let mut temp = *datum;
|
||||||
|
temp ^= file_key;
|
||||||
|
temp ^= temp.wrapping_shl(4);
|
||||||
|
temp ^= slot_key;
|
||||||
|
temp ^= offset.to_ne_bytes().iter().fold(0, |acc, &x| acc ^ x);
|
||||||
|
*datum = temp;
|
||||||
|
|
||||||
|
offset = offset.wrapping_add(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -105,7 +105,7 @@ impl Header {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cipher<T>(&self, ekey: Option<T>) -> Result<Cipher>
|
pub fn get_decipher<T>(&self, ekey: Option<T>) -> Result<Cipher>
|
||||||
where
|
where
|
||||||
T: AsRef<[u8]>,
|
T: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
|
@ -5,3 +5,5 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
|
itertools = "0.13.0"
|
||||||
|
md-5 = "0.10.6"
|
||||||
|
@ -1 +1,3 @@
|
|||||||
pub mod base64;
|
pub mod base64;
|
||||||
|
mod md5;
|
||||||
|
pub use md5::md5;
|
||||||
|
5
um_crypto/utils/src/md5.rs
Normal file
5
um_crypto/utils/src/md5.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
use md5::{Digest, Md5};
|
||||||
|
|
||||||
|
pub fn md5<T: AsRef<[u8]>>(buffer: T) -> [u8; 16] {
|
||||||
|
Md5::digest(buffer).into()
|
||||||
|
}
|
@ -25,9 +25,9 @@ impl JsKuwoHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create an instance of cipher (decipher) for decryption
|
/// Create an instance of cipher (decipher) for decryption
|
||||||
#[wasm_bindgen(js_name=makeCipher)]
|
#[wasm_bindgen(js_name=makeDecipher)]
|
||||||
pub fn make_cipher(&self, ekey: Option<String>) -> Result<JsCipher, JsError> {
|
pub fn make_decipher(&self, ekey: Option<String>) -> Result<JsCipher, JsError> {
|
||||||
let cipher = self.0.get_cipher(ekey).map_err(map_js_error)?;
|
let cipher = self.0.get_decipher(ekey).map_err(map_js_error)?;
|
||||||
Ok(JsCipher(cipher))
|
Ok(JsCipher(cipher))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user