├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── renovate.json ├── rustfmt.toml └── src ├── cb1.rs ├── cb7.rs ├── lib.rs └── rc4.rs /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | workflow_dispatch: {} 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | RUST_BACKTRACE: full 13 | 14 | jobs: 15 | build: 16 | name: Build and test 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: dtolnay/rust-toolchain@stable 21 | - uses: taiki-e/install-action@v2 22 | with: 23 | tool: cargo-hack,cargo-nextest 24 | - run: cargo fmt --check 25 | - run: cargo hack build --each-feature --all-targets 26 | - run: cargo hack clippy --each-feature --all-targets -- -D warnings 27 | - run: cargo hack nextest run --each-feature 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "codebreaker" 3 | version = "0.3.2" 4 | description = "Decrypt & encrypt any cheat code for CodeBreaker PS2" 5 | authors = ["Mathias Lafeldt "] 6 | license = "MIT OR Apache-2.0" 7 | readme = "README.md" 8 | repository = "https://github.com/mlafeldt/codebreaker-rs" 9 | documentation = "https://docs.rs/codebreaker/" 10 | homepage = "https://crates.io/crates/codebreaker" 11 | keywords = ["codebreaker", "ps2", "gamehacking", "homebrew"] 12 | categories = ["algorithms", "cryptography", "no-std"] 13 | edition = "2024" 14 | 15 | [lib] 16 | name = "codebreaker" 17 | 18 | [dependencies] 19 | bytemuck = "1" 20 | num-bigint = "0.4" 21 | 22 | [dev-dependencies] 23 | doc-comment = "0.3" 24 | pretty_assertions = "1" 25 | 26 | [features] 27 | default = ["std"] 28 | std = ["num-bigint/std"] 29 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright (c) 2020-2024 Mathias Lafeldt 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2020-2024 Mathias Lafeldt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # codebreaker-rs 2 | 3 | [![Latest version](https://img.shields.io/crates/v/codebreaker.svg)](https://crates.io/crates/codebreaker) 4 | [![Documentation](https://docs.rs/codebreaker/badge.svg)](https://docs.rs/codebreaker) 5 | [![CI](https://github.com/mlafeldt/codebreaker-rs/workflows/CI/badge.svg)](https://github.com/mlafeldt/codebreaker-rs/actions) 6 | 7 | A Rust library to decrypt & encrypt any cheat code for CodeBreaker PS2. 8 | 9 | Originally reverse-engineered from MIPS R5900 assembly and [converted to C](https://github.com/mlafeldt/cb2util/blob/v1.9/cb2_crypto.c) in 2006. Now ported to Rust for [fun and profit](https://github.com/mlafeldt/cb2util/pull/13). 10 | 11 | For more information, check out my article on [7 Things I Learned From Porting a C Crypto Library to Rust](https://sharpend.io/7-things-i-learned-from-porting-a-c-crypto-library-to-rust/). 12 | 13 | ## Quickstart 14 | 15 | Add the crate as a dependency to your `Cargo.toml`: 16 | 17 | ```toml 18 | [dependencies] 19 | codebreaker = "0.3" 20 | ``` 21 | 22 | Now you can start decrypting some codes: 23 | 24 | ```rust 25 | use codebreaker::Codebreaker; 26 | 27 | let input: Vec<(u32, u32)> = vec![ 28 | (0x2043AFCC, 0x2411FFFF), 29 | (0x2A973DBD, 0x00000000), 30 | (0xB4336FA9, 0x4DFEFB79), 31 | (0x973E0B2A, 0xA7D4AF10), 32 | ]; 33 | let output: Vec<(u32, u32)> = vec![ 34 | (0x2043AFCC, 0x2411FFFF), 35 | (0x201F6024, 0x00000000), 36 | (0xBEEFC0DE, 0x00000000), 37 | (0x2096F5B8, 0x000000BE), 38 | ]; 39 | 40 | let mut cb = Codebreaker::new(); 41 | for (i, code) in input.iter().enumerate() { 42 | assert_eq!(cb.auto_decrypt_code(code.0, code.1), output[i]); 43 | } 44 | ``` 45 | 46 | Read the [full documentation](https://docs.rs/codebreaker) for more examples. 47 | 48 | ## `no_std` support 49 | 50 | The `codebreaker` crate has a Cargo feature named "std" that is enabled by default. In order to use the crate on embedded systems, this feature needs to be disabled: 51 | 52 | ```toml 53 | [dependencies] 54 | codebreaker = { version = "0.3", default-features = false } 55 | ``` 56 | 57 | ## License 58 | 59 | Copyright (c) 2020-2024 Mathias Lafeldt 60 | 61 | Licensed under the [Apache License, Version 2.0](LICENSE-APACHE) or the [MIT license](LICENSE-MIT), at your option. 62 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:recommended", 4 | "group:allNonMajor", 5 | "schedule:monthly" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 120 2 | edition = "2021" 3 | -------------------------------------------------------------------------------- /src/cb1.rs: -------------------------------------------------------------------------------- 1 | //! Encrypt and decrypt cheat codes for CodeBreaker PS2 v1 - v6. 2 | 3 | /// Encrypts a code and returns the result. 4 | /// 5 | /// # Example 6 | /// ``` 7 | /// use codebreaker::cb1; 8 | /// 9 | /// let code = cb1::encrypt_code(0x1023CED8, 0x000003E7); 10 | /// assert_eq!(code, (0x1A11330E, 0x000003E7)); 11 | /// ``` 12 | pub const fn encrypt_code(mut addr: u32, mut val: u32) -> (u32, u32) { 13 | let cmd = (addr >> 28) as usize; 14 | let tmp = addr & 0xff00_0000; 15 | addr = ((addr & 0xff) << 16) | ((addr >> 8) & 0xffff); 16 | addr = (tmp | (addr.wrapping_add(SEEDS[1][cmd]) & 0x00ff_ffff)) ^ SEEDS[0][cmd]; 17 | if cmd > 2 { 18 | val = addr ^ val.wrapping_add(SEEDS[2][cmd]); 19 | } 20 | (addr, val) 21 | } 22 | 23 | /// Encrypts a code directly. 24 | /// 25 | /// # Example 26 | /// ``` 27 | /// use codebreaker::cb1; 28 | /// 29 | /// let mut code = (0x1023CED8, 0x000003E7); 30 | /// cb1::encrypt_code_mut(&mut code.0, &mut code.1); 31 | /// assert_eq!(code, (0x1A11330E, 0x000003E7)); 32 | /// ``` 33 | pub const fn encrypt_code_mut(addr: &mut u32, val: &mut u32) { 34 | let code = encrypt_code(*addr, *val); 35 | *addr = code.0; 36 | *val = code.1; 37 | } 38 | 39 | /// Decrypts a code and returns the result. 40 | /// 41 | /// # Example 42 | /// ``` 43 | /// use codebreaker::cb1; 44 | /// 45 | /// let code = cb1::decrypt_code(0x1A11330E, 0x000003E7); 46 | /// assert_eq!(code, (0x1023CED8, 0x000003E7)); 47 | /// ``` 48 | pub const fn decrypt_code(mut addr: u32, mut val: u32) -> (u32, u32) { 49 | let cmd = (addr >> 28) as usize; 50 | if cmd > 2 { 51 | val = (addr ^ val).wrapping_sub(SEEDS[2][cmd]); 52 | } 53 | let tmp = addr ^ SEEDS[0][cmd]; 54 | addr = tmp.wrapping_sub(SEEDS[1][cmd]); 55 | addr = (tmp & 0xff00_0000) | ((addr & 0xffff) << 8) | ((addr >> 16) & 0xff); 56 | (addr, val) 57 | } 58 | 59 | /// Decrypts a code directly. 60 | /// 61 | /// # Example 62 | /// ``` 63 | /// use codebreaker::cb1; 64 | /// 65 | /// let mut code = (0x1A11330E, 0x000003E7); 66 | /// cb1::decrypt_code_mut(&mut code.0, &mut code.1); 67 | /// assert_eq!(code, (0x1023CED8, 0x000003E7)); 68 | /// ``` 69 | pub const fn decrypt_code_mut(addr: &mut u32, val: &mut u32) { 70 | let code = decrypt_code(*addr, *val); 71 | *addr = code.0; 72 | *val = code.1; 73 | } 74 | 75 | #[rustfmt::skip] 76 | const SEEDS: [[u32; 16]; 3] = [ 77 | [ 78 | 0x0a0b_8d9b, 0x0a01_33f8, 0x0af7_33ec, 0x0a15_c574, 79 | 0x0a50_ac20, 0x0a92_0fb9, 0x0a59_9f0b, 0x0a4a_a0e3, 80 | 0x0a21_c012, 0x0a90_6254, 0x0a31_fd54, 0x0a09_1c0e, 81 | 0x0a37_2b38, 0x0a6f_266c, 0x0a61_dd4a, 0x0a0d_bf92, 82 | ], 83 | [ 84 | 0x0028_8596, 0x0037_dd28, 0x003b_eef1, 0x000b_c822, 85 | 0x00bc_935d, 0x00a1_39f2, 0x00e9_bbf8, 0x00f5_7f7b, 86 | 0x0090_d704, 0x0018_14d4, 0x00c5_848e, 0x005b_83e7, 87 | 0x0010_8cf7, 0x0046_ce5a, 0x003a_5bf4, 0x006f_affc, 88 | ], 89 | [ 90 | 0x1dd9_a10a, 0xb95a_b9b0, 0x5cf5_d328, 0x95fe_7f10, 91 | 0x8e2d_6303, 0x16bb_6286, 0xe389_324c, 0x07ac_6ea8, 92 | 0xaa48_11d8, 0x76ce_4e18, 0xfe44_7516, 0xf9cd_94d0, 93 | 0x4c24_dedb, 0x6827_5c4e, 0x7249_4382, 0xc8aa_88e8, 94 | ], 95 | ]; 96 | 97 | #[cfg(test)] 98 | mod tests { 99 | use super::*; 100 | use crate::code::Code; 101 | use crate::std_alloc::{Vec, vec}; 102 | #[cfg(feature = "std")] 103 | use pretty_assertions::assert_eq; 104 | 105 | struct Test { 106 | decrypted: Code, 107 | encrypted: Code, 108 | } 109 | 110 | fn tests() -> Vec { 111 | vec![ 112 | Test { 113 | decrypted: "0031789A 00000063".into(), 114 | encrypted: "0AC93A95 00000063".into(), 115 | }, 116 | Test { 117 | decrypted: "1031A028 0000FFFF".into(), 118 | encrypted: "1A613D30 0000FFFF".into(), 119 | }, 120 | Test { 121 | decrypted: "201F6024 00000000".into(), 122 | encrypted: "2A973DBD 00000000".into(), 123 | }, 124 | Test { 125 | decrypted: "902DB32C 0C0BAFF1".into(), 126 | encrypted: "9AD420D3 180DDEDA".into(), 127 | }, 128 | Test { 129 | decrypted: "A008060C 08028007".into(), 130 | encrypted: "AAE071C0 ACA684DD".into(), 131 | }, 132 | Test { 133 | decrypted: "BEEFC0DE 00000000".into(), 134 | encrypted: "B4336FA9 4DFEFB79".into(), 135 | }, 136 | ] 137 | } 138 | 139 | #[test] 140 | fn test_encrypt_code() { 141 | for t in &tests() { 142 | let result: Code = encrypt_code(t.decrypted.0, t.decrypted.1).into(); 143 | assert_eq!(result, t.encrypted); 144 | } 145 | } 146 | 147 | #[test] 148 | fn test_encrypt_code_mut() { 149 | for t in &tests() { 150 | let mut code = t.decrypted; 151 | encrypt_code_mut(&mut code.0, &mut code.1); 152 | assert_eq!(code, t.encrypted); 153 | } 154 | } 155 | 156 | #[test] 157 | fn test_decrypt_code() { 158 | for t in &tests() { 159 | let result: Code = decrypt_code(t.encrypted.0, t.encrypted.1).into(); 160 | assert_eq!(result, t.decrypted); 161 | } 162 | } 163 | 164 | #[test] 165 | fn test_decrypt_code_mut() { 166 | for t in &tests() { 167 | let mut code = t.encrypted; 168 | decrypt_code_mut(&mut code.0, &mut code.1); 169 | assert_eq!(code, t.decrypted); 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/cb7.rs: -------------------------------------------------------------------------------- 1 | //! Encrypt and decrypt cheat codes for CodeBreaker PS2 v7+. 2 | 3 | use crate::rc4::Rc4; 4 | 5 | use core::fmt; 6 | 7 | use bytemuck::{bytes_of, bytes_of_mut, cast_slice}; 8 | 9 | /// A processor for CB v7+ codes. 10 | #[derive(Clone, Copy)] 11 | pub struct Cb7 { 12 | seeds: [[u8; 256]; 5], 13 | key: [u32; 5], 14 | beefcodf: bool, 15 | initialized: bool, 16 | } 17 | 18 | /// Implements the default CB v7 encryption used by former CMGSCCC.com. 19 | /// 20 | /// Lets you omit `B4336FA9 4DFEFB79` as the first code in the list. 21 | impl Default for Cb7 { 22 | fn default() -> Self { 23 | let mut cb7 = Self::new(); 24 | cb7.beefcode(BEEFCODE, 0); 25 | cb7 26 | } 27 | } 28 | 29 | impl fmt::Debug for Cb7 { 30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 31 | f.debug_struct("Cb7") 32 | .field("seeds[0][0..16]", &self.seeds[0][0..16].to_vec()) 33 | .field("key", &self.key) 34 | .field("beefcodf", &self.beefcodf) 35 | .field("initialized", &self.initialized) 36 | .finish() 37 | } 38 | } 39 | 40 | impl Cb7 { 41 | /// Returns a new processor for encrypting and decrypting a list of CB v7+ 42 | /// codes. 43 | pub const fn new() -> Self { 44 | Self { 45 | seeds: ZERO_SEEDS, 46 | key: [0; 5], 47 | beefcodf: false, 48 | initialized: false, 49 | } 50 | } 51 | 52 | /// Generates or changes the encryption key and seeds. 53 | /// 54 | /// Needs to be called for every "beefcode", which comes in two flavors: 55 | /// 56 | /// ```text 57 | /// BEEFC0DE vvvvvvvv 58 | /// 59 | /// or: 60 | /// 61 | /// BEEFC0DF vvvvvvvv 62 | /// wwwwwwww wwwwwwww 63 | /// 64 | /// v = seed value 65 | /// w = extra seed value 66 | /// ``` 67 | /// 68 | /// # Example 69 | /// ``` 70 | /// use codebreaker::cb7::Cb7; 71 | /// 72 | /// let mut cb7 = Cb7::new(); 73 | /// cb7.beefcode(0xBEEFC0DE, 0x00000000); 74 | /// ``` 75 | /// 76 | /// # Panics 77 | /// 78 | /// Panics if the passed code is not a "beefcode". 79 | pub fn beefcode(&mut self, addr: u32, val: u32) { 80 | assert!(is_beefcode(addr)); 81 | 82 | // Easily access all bytes of val as indices into seeds 83 | let mut idx = [0; 4]; 84 | val.to_le_bytes() 85 | .iter() 86 | .zip(&mut idx) 87 | .for_each(|(b, i)| *i = *b as usize); 88 | 89 | // Set up key and seeds 90 | if !self.initialized { 91 | self.key.copy_from_slice(&RC4_KEY); 92 | 93 | if val != 0 { 94 | self.seeds.copy_from_slice(&SEEDS); 95 | for i in 0..4 { 96 | self.key[i] = (u32::from(self.seeds[(i + 3) % 4][idx[3]]) << 24) 97 | | (u32::from(self.seeds[(i + 2) % 4][idx[2]]) << 16) 98 | | (u32::from(self.seeds[(i + 1) % 4][idx[1]]) << 8) 99 | | u32::from(self.seeds[i % 4][idx[0]]); 100 | } 101 | } else { 102 | self.seeds.copy_from_slice(&ZERO_SEEDS); 103 | } 104 | 105 | self.initialized = true; 106 | } else if val != 0 { 107 | for i in 0..4 { 108 | self.key[i] = (u32::from(self.seeds[(i + 3) % 4][idx[3]]) << 24) 109 | | (u32::from(self.seeds[(i + 2) % 4][idx[2]]) << 16) 110 | | (u32::from(self.seeds[(i + 1) % 4][idx[1]]) << 8) 111 | | u32::from(self.seeds[i % 4][idx[0]]); 112 | } 113 | } else { 114 | // Special case for 2x BEEFC0DE 00000000 in a row 115 | self.seeds.copy_from_slice(&ZERO_SEEDS); 116 | self.key[0] = 0; 117 | self.key[1] = 0; 118 | self.key[2] = 0; 119 | self.key[3] = 0; 120 | } 121 | 122 | // Use key to encrypt seeds with RC4 123 | let k = bytes_of_mut(&mut self.key); 124 | for i in 0..5 { 125 | let mut rc4 = Rc4::new(k); 126 | // Encrypt seeds 127 | rc4.crypt(&mut self.seeds[i]); 128 | // Encrypt original key for next round 129 | rc4.crypt(k); 130 | } 131 | 132 | // Since we don't know the extra seed value of BEEFC0DF yet, 133 | // all we can do is set a flag. 134 | self.beefcodf = addr & 1 != 0; 135 | } 136 | 137 | /// Encrypts a code and returns the result. 138 | /// 139 | /// # Example 140 | /// ``` 141 | /// use codebreaker::cb7::Cb7; 142 | /// 143 | /// let mut cb7 = Cb7::default(); 144 | /// let code = cb7.encrypt_code(0x2043AFCC, 0x2411FFFF); 145 | /// assert_eq!(code, (0x397951B0, 0x41569FE0)); 146 | /// ``` 147 | pub fn encrypt_code(&mut self, addr: u32, val: u32) -> (u32, u32) { 148 | let mut code = (addr, val); 149 | self.encrypt_code_mut(&mut code.0, &mut code.1); 150 | code 151 | } 152 | 153 | /// Encrypts a code directly. 154 | /// 155 | /// # Example 156 | /// ``` 157 | /// use codebreaker::cb7::Cb7; 158 | /// 159 | /// let mut cb7 = Cb7::default(); 160 | /// let mut code = (0x2043AFCC, 0x2411FFFF); 161 | /// cb7.encrypt_code_mut(&mut code.0, &mut code.1); 162 | /// assert_eq!(code, (0x397951B0, 0x41569FE0)); 163 | /// ``` 164 | pub fn encrypt_code_mut(&mut self, addr: &mut u32, val: &mut u32) { 165 | let oldaddr = *addr; 166 | let oldval = *val; 167 | 168 | // Step 1: Multiplication, modulo (2^32) 169 | *addr = mul_encrypt(*addr, self.key[0].wrapping_sub(self.key[1])); 170 | *val = mul_encrypt(*val, self.key[2].wrapping_add(self.key[3])); 171 | 172 | // Step 2: RC4 173 | let mut code = [*addr, *val]; 174 | let mut rc4 = Rc4::new(bytes_of(&self.key)); 175 | rc4.crypt(bytes_of_mut(&mut code)); 176 | *addr = code[0]; 177 | *val = code[1]; 178 | 179 | // Step 3: RSA 180 | rsa_crypt(addr, val, RSA_ENC_KEY, RSA_MODULUS); 181 | 182 | // Step 4: Encryption loop of 64 cycles, using the generated seeds 183 | let s: &[u32] = cast_slice(&self.seeds); 184 | for i in 0..64 { 185 | *addr = (addr.wrapping_add(s[2 * 64 + i]) ^ s[i]).wrapping_sub(*val ^ s[4 * 64 + i]); 186 | *val = (val.wrapping_sub(s[3 * 64 + i]) ^ s[64 + i]).wrapping_add(*addr ^ s[4 * 64 + i]); 187 | } 188 | 189 | // BEEFC0DE 190 | if is_beefcode(oldaddr) { 191 | self.beefcode(oldaddr, oldval); 192 | return; 193 | } 194 | 195 | // BEEFC0DF uses two codes. If the previous code was the first of the 196 | // two, use the current one to encrypt the seeds. 197 | if self.beefcodf { 198 | let mut rc4 = Rc4::new(bytes_of(&[oldaddr, oldval])); 199 | rc4.crypt(bytes_of_mut(&mut self.seeds)); 200 | self.beefcodf = false; 201 | } 202 | } 203 | 204 | /// Decrypts a code and returns the result. 205 | /// 206 | /// # Example 207 | /// ``` 208 | /// use codebreaker::cb7::Cb7; 209 | /// 210 | /// let mut cb7 = Cb7::default(); 211 | /// let code = cb7.decrypt_code(0x397951B0, 0x41569FE0); 212 | /// assert_eq!(code, (0x2043AFCC, 0x2411FFFF)); 213 | /// ``` 214 | pub fn decrypt_code(&mut self, addr: u32, val: u32) -> (u32, u32) { 215 | let mut code = (addr, val); 216 | self.decrypt_code_mut(&mut code.0, &mut code.1); 217 | code 218 | } 219 | 220 | /// Decrypts a code directly. 221 | /// 222 | /// # Example 223 | /// ``` 224 | /// use codebreaker::cb7::Cb7; 225 | /// 226 | /// let mut cb7 = Cb7::default(); 227 | /// let mut code = (0x397951B0, 0x41569FE0); 228 | /// cb7.decrypt_code_mut(&mut code.0, &mut code.1); 229 | /// assert_eq!(code, (0x2043AFCC, 0x2411FFFF)); 230 | /// ``` 231 | pub fn decrypt_code_mut(&mut self, addr: &mut u32, val: &mut u32) { 232 | // Step 1: Decryption loop of 64 cycles, using the generated seeds 233 | let s: &[u32] = cast_slice(&self.seeds); 234 | for i in (0..64).rev() { 235 | *val = (val.wrapping_sub(*addr ^ s[4 * 64 + i]) ^ s[64 + i]).wrapping_add(s[3 * 64 + i]); 236 | *addr = (addr.wrapping_add(*val ^ s[4 * 64 + i]) ^ s[i]).wrapping_sub(s[2 * 64 + i]); 237 | } 238 | 239 | // Step 2: RSA 240 | rsa_crypt(addr, val, RSA_DEC_KEY, RSA_MODULUS); 241 | 242 | // Step 3: RC4 243 | let mut code = [*addr, *val]; 244 | let mut rc4 = Rc4::new(bytes_of(&self.key)); 245 | rc4.crypt(bytes_of_mut(&mut code)); 246 | *addr = code[0]; 247 | *val = code[1]; 248 | 249 | // Step 4: Multiplication with multiplicative inverse, modulo (2^32) 250 | *addr = mul_decrypt(*addr, self.key[0].wrapping_sub(self.key[1])); 251 | *val = mul_decrypt(*val, self.key[2].wrapping_add(self.key[3])); 252 | 253 | // BEEFC0DF uses two codes. If the previous code was the first of the 254 | // two, use the current one to decrypt the seeds. 255 | if self.beefcodf { 256 | let mut rc4 = Rc4::new(bytes_of(&[*addr, *val])); 257 | rc4.crypt(bytes_of_mut(&mut self.seeds)); 258 | self.beefcodf = false; 259 | return; 260 | } 261 | 262 | // BEEFC0DE 263 | if is_beefcode(*addr) { 264 | self.beefcode(*addr, *val); 265 | } 266 | } 267 | } 268 | 269 | /// Returns true if the code address indicates a "beefcode". In that case, the 270 | /// [`beefcode`](struct.Cb7.html#method.beefcode) method should be invoked. 271 | /// 272 | /// # Example 273 | /// ``` 274 | /// use codebreaker::cb7::is_beefcode; 275 | /// 276 | /// assert_eq!(is_beefcode(0xBEEFC0DE), true); 277 | /// assert_eq!(is_beefcode(0xBEEFC0DF), true); 278 | /// assert_eq!(is_beefcode(0x12345678), false); 279 | /// ``` 280 | #[inline] 281 | pub const fn is_beefcode(addr: u32) -> bool { 282 | addr & 0xffff_fffe == BEEFCODE 283 | } 284 | 285 | // Multiplication, modulo (2^32) 286 | #[inline] 287 | const fn mul_encrypt(a: u32, b: u32) -> u32 { 288 | a.wrapping_mul(b | 1) 289 | } 290 | 291 | // Multiplication with multiplicative inverse, modulo (2^32) 292 | #[inline] 293 | const fn mul_decrypt(a: u32, b: u32) -> u32 { 294 | a.wrapping_mul(mod_inverse(b | 1)) 295 | } 296 | 297 | // Computes the multiplicative inverse of x modulo (2^32). x must be odd! 298 | // The code is based on Newton's method as explained in this blog post: 299 | // https://lemire.me/blog/2017/09/18/computing-the-inverse-of-odd-integers/ 300 | const fn mod_inverse(x: u32) -> u32 { 301 | let mut y = x; 302 | // Call this recurrence formula 4 times for 32-bit values: 303 | // f(y) = y * (2 - y * x) modulo 2^32 304 | y = y.wrapping_mul(2u32.wrapping_sub(y.wrapping_mul(x))); 305 | y = y.wrapping_mul(2u32.wrapping_sub(y.wrapping_mul(x))); 306 | y = y.wrapping_mul(2u32.wrapping_sub(y.wrapping_mul(x))); 307 | y = y.wrapping_mul(2u32.wrapping_sub(y.wrapping_mul(x))); 308 | y 309 | } 310 | 311 | // RSA encryption/decryption 312 | fn rsa_crypt(addr: &mut u32, val: &mut u32, rsakey: u64, modulus: u64) { 313 | use num_bigint::BigUint; 314 | 315 | let code = BigUint::from_slice(&[*val, *addr]); 316 | let m = BigUint::from(modulus); 317 | 318 | // Exponentiation is only invertible if code < modulus 319 | if code < m { 320 | let digits = code.modpow(&BigUint::from(rsakey), &m).to_u32_digits(); 321 | *addr = digits[1]; 322 | *val = digits[0]; 323 | } 324 | } 325 | 326 | const BEEFCODE: u32 = 0xbeef_c0de; 327 | 328 | const RC4_KEY: [u32; 5] = [0xd0db_a9d7, 0x13a0_a96c, 0x8041_0df0, 0x2ccd_be1f, 0xe570_a86b]; 329 | 330 | const RSA_DEC_KEY: u64 = 11; 331 | // This is how I calculated the encryption key e from d (some number theory): 332 | // 333 | // d = 11 334 | // n = 18446744073709551605 335 | // e = d^(-1) mod phi(n) 336 | // 337 | // n factored: 338 | // n = 5 * 2551 * 1446236305269271 339 | // = p*q*r, only single prime factors 340 | // 341 | // phi(n) = phi(p*q*r) 342 | // = phi(p) * phi(q) * phi(r), phi(p) = p - 1 343 | // = (p-1)*(q-1)*(r-1) 344 | // = (5-1) * (2551-1) * (1446236305269271-1) 345 | // = 4 * 2550 * 1446236305269270 346 | // = 14751610313746554000 347 | // 348 | // e = 11^(-1) mod 14751610313746554000 349 | // e = 2682110966135737091 350 | const RSA_ENC_KEY: u64 = 2_682_110_966_135_737_091; 351 | const RSA_MODULUS: u64 = 18_446_744_073_709_551_605; // 0xffff_ffff_ffff_fff5 352 | 353 | const ZERO_SEEDS: [[u8; 256]; 5] = [[0; 256]; 5]; 354 | 355 | #[rustfmt::skip] 356 | const SEEDS: [[u8; 256]; 5] = [ 357 | [ 358 | 0x84, 0x01, 0x21, 0xa4, 0xfa, 0x4d, 0x50, 0x8d, 0x75, 0x33, 0xc5, 0xf7, 0x4a, 0x6d, 0x7c, 0xa6, 359 | 0x1c, 0xf8, 0x40, 0x18, 0xa1, 0xb3, 0xa2, 0xf9, 0x6a, 0x19, 0x63, 0x66, 0x29, 0xae, 0x10, 0x75, 360 | 0x84, 0x7d, 0xec, 0x6a, 0xf9, 0x2d, 0x8e, 0x33, 0x44, 0x5c, 0x33, 0x6d, 0x78, 0x3e, 0x1b, 0x6c, 361 | 0x02, 0xe0, 0x7d, 0x77, 0x1d, 0xb1, 0x61, 0x2a, 0xcd, 0xc1, 0x38, 0x53, 0x1f, 0xa1, 0x6e, 0x3d, 362 | 0x03, 0x0d, 0x05, 0xdc, 0x50, 0x19, 0x85, 0x89, 0x9b, 0xf1, 0x8a, 0xc2, 0xd1, 0x5c, 0x22, 0xc4, 363 | 0x11, 0x29, 0xf6, 0x13, 0xec, 0x06, 0xe4, 0xbd, 0x08, 0x9e, 0xb7, 0x8d, 0x72, 0x92, 0x10, 0x3c, 364 | 0x41, 0x4e, 0x81, 0x55, 0x08, 0x9c, 0xa3, 0xbc, 0xa1, 0x79, 0xb0, 0x7a, 0x94, 0x3a, 0x39, 0x95, 365 | 0x7a, 0xc6, 0x96, 0x21, 0xb0, 0x07, 0x17, 0x5e, 0x53, 0x54, 0x08, 0xcf, 0x85, 0x6c, 0x4b, 0xbe, 366 | 0x30, 0x82, 0xdd, 0x1d, 0x3a, 0x24, 0x3c, 0xb2, 0x67, 0x0c, 0x36, 0x03, 0x51, 0x60, 0x3f, 0x67, 367 | 0xf1, 0xb2, 0x77, 0xdc, 0x12, 0x9d, 0x7b, 0xce, 0x65, 0xf8, 0x75, 0xea, 0x23, 0x63, 0x99, 0x54, 368 | 0x37, 0xc0, 0x3c, 0x42, 0x77, 0x12, 0xb7, 0xca, 0x54, 0xf1, 0x26, 0x1d, 0x1e, 0xd1, 0xab, 0x2c, 369 | 0xaf, 0xb6, 0x91, 0x2e, 0xbd, 0x84, 0x0b, 0xf2, 0x1a, 0x1e, 0x26, 0x1e, 0x00, 0x12, 0xb7, 0x77, 370 | 0xd6, 0x61, 0x1c, 0xce, 0xa9, 0x10, 0x19, 0xaa, 0x88, 0xe6, 0x35, 0x29, 0x32, 0x5f, 0x57, 0xa7, 371 | 0x94, 0x93, 0xa1, 0x2b, 0xeb, 0x9b, 0x17, 0x2a, 0xaa, 0x60, 0xd5, 0x19, 0xb2, 0x4e, 0x5a, 0xe2, 372 | 0xc9, 0x4a, 0x00, 0x68, 0x6e, 0x59, 0x36, 0xa6, 0xa0, 0xf9, 0x19, 0xa2, 0xc7, 0xc9, 0xd4, 0x29, 373 | 0x5c, 0x99, 0x3c, 0x5c, 0xe2, 0xcb, 0x94, 0x40, 0x8b, 0xf4, 0x3b, 0xd2, 0x38, 0x7d, 0xbf, 0xd0, 374 | ], 375 | [ 376 | 0xcc, 0x6d, 0x5d, 0x0b, 0x70, 0x25, 0x5d, 0x68, 0xfe, 0xbe, 0x6c, 0x3f, 0xa4, 0xd9, 0x95, 0x5f, 377 | 0x30, 0xae, 0x34, 0x39, 0x00, 0x89, 0xdc, 0x5a, 0xc8, 0x82, 0x24, 0x3a, 0xfc, 0xda, 0x3c, 0x1f, 378 | 0x73, 0x3f, 0x63, 0xaa, 0x53, 0xbd, 0x4e, 0xb5, 0x33, 0x48, 0x59, 0xc1, 0xb7, 0xe0, 0x0c, 0x99, 379 | 0xec, 0x3b, 0x32, 0x26, 0xb3, 0xb1, 0xe2, 0x8e, 0x54, 0x41, 0x55, 0xdb, 0x1d, 0x90, 0x0b, 0x48, 380 | 0xf3, 0x3f, 0xca, 0x1f, 0x19, 0xeb, 0x7f, 0x56, 0x52, 0xd7, 0x20, 0x67, 0x59, 0x4f, 0x4e, 0xdc, 381 | 0xbb, 0x6a, 0x8e, 0x45, 0x88, 0x0b, 0x93, 0xac, 0xcd, 0x0e, 0x29, 0x18, 0x7a, 0x16, 0x8d, 0x8d, 382 | 0xc2, 0x88, 0x6a, 0x9d, 0x39, 0xf4, 0x93, 0x14, 0xcd, 0xe0, 0x6b, 0xc7, 0x28, 0x21, 0x5c, 0x97, 383 | 0x70, 0x7c, 0xab, 0x53, 0x46, 0x33, 0x03, 0x18, 0xdf, 0x91, 0xfe, 0x06, 0xc0, 0xff, 0xa2, 0x58, 384 | 0xf3, 0xb0, 0x6b, 0x9b, 0x71, 0x91, 0x23, 0xda, 0x92, 0x67, 0x14, 0x34, 0x9f, 0xa5, 0xaf, 0x65, 385 | 0x62, 0xe8, 0x7f, 0x79, 0x35, 0x32, 0x29, 0x3e, 0x4f, 0xdc, 0xc7, 0x8e, 0xf1, 0x21, 0x9d, 0x3b, 386 | 0x61, 0xfc, 0x0b, 0x02, 0xec, 0xe4, 0xa7, 0xea, 0x77, 0xe7, 0x21, 0x63, 0x97, 0x7f, 0x23, 0x8a, 387 | 0x8b, 0xbe, 0x4e, 0x90, 0xc0, 0x89, 0x04, 0x44, 0x90, 0x57, 0x41, 0xb5, 0x74, 0xad, 0xb1, 0xe9, 388 | 0xf3, 0x91, 0xc7, 0x27, 0x3e, 0x00, 0x81, 0x99, 0xee, 0x38, 0xf5, 0x32, 0x4f, 0x27, 0x4f, 0x64, 389 | 0x39, 0x3d, 0xd3, 0x0b, 0x99, 0xd5, 0x99, 0xd6, 0x10, 0x4b, 0x43, 0x17, 0x38, 0x34, 0x54, 0x63, 390 | 0x19, 0x36, 0xbd, 0x15, 0xb1, 0x06, 0x1e, 0xde, 0x1b, 0xaf, 0xeb, 0xfa, 0x56, 0xb8, 0x8d, 0x9d, 391 | 0x14, 0x1a, 0xa6, 0x49, 0x56, 0x19, 0xca, 0xc1, 0x40, 0x6d, 0x71, 0xde, 0x68, 0xc1, 0xc3, 0x4a, 392 | ], 393 | [ 394 | 0x69, 0x31, 0x5c, 0xab, 0x7f, 0x5b, 0xe9, 0x81, 0x32, 0x58, 0x32, 0x0a, 0x97, 0xf3, 0xc7, 0xcf, 395 | 0xbb, 0x1d, 0xcf, 0x0e, 0x83, 0x35, 0x4c, 0x58, 0xce, 0xf7, 0x8a, 0xe4, 0xb0, 0xe4, 0x83, 0x48, 396 | 0x81, 0x77, 0x7c, 0x3f, 0xbc, 0x27, 0x3a, 0x1b, 0xa4, 0xe9, 0x06, 0xa4, 0x15, 0xab, 0x90, 0x10, 397 | 0x7d, 0x74, 0xda, 0xfc, 0x36, 0x09, 0xcc, 0xf7, 0x12, 0xb6, 0xf4, 0x94, 0xe9, 0x8b, 0x6a, 0x3b, 398 | 0x5e, 0x71, 0x46, 0x3e, 0x0b, 0x78, 0xad, 0x3b, 0x94, 0x5b, 0x89, 0x85, 0xa3, 0xe0, 0x01, 0xeb, 399 | 0x84, 0x41, 0xaa, 0xd7, 0xb3, 0x17, 0x16, 0xc3, 0x6c, 0xb1, 0x81, 0x73, 0xec, 0xe4, 0x6e, 0x09, 400 | 0x56, 0xee, 0x7a, 0xf6, 0x75, 0x6a, 0x73, 0x95, 0x8d, 0xda, 0x51, 0x63, 0x8b, 0xbb, 0xe0, 0x4d, 401 | 0xf8, 0xa0, 0x27, 0xf2, 0x9f, 0xc8, 0x15, 0x5a, 0x23, 0x85, 0x58, 0x04, 0x4a, 0x57, 0x28, 0x20, 402 | 0x6d, 0x9d, 0x85, 0x83, 0x3c, 0xbf, 0x02, 0xb0, 0x96, 0xe8, 0x73, 0x6f, 0x20, 0x6e, 0xb0, 0xe4, 403 | 0xc6, 0xfa, 0x71, 0xa6, 0x5d, 0xc5, 0xa0, 0xa3, 0xf8, 0x5c, 0x99, 0xcb, 0x9c, 0x04, 0x3a, 0xb2, 404 | 0x04, 0x8d, 0xa2, 0x9d, 0x32, 0xf0, 0xbd, 0xaa, 0xea, 0x81, 0x79, 0xe2, 0xa1, 0xba, 0x89, 0x12, 405 | 0xd5, 0x9f, 0x81, 0xeb, 0x63, 0xe7, 0xe5, 0xd4, 0xe9, 0x0e, 0x30, 0xbc, 0xcb, 0x70, 0xdd, 0x51, 406 | 0x77, 0xc0, 0x80, 0xb3, 0x49, 0x03, 0x9a, 0xb8, 0x8c, 0xa7, 0x63, 0x62, 0x8f, 0x72, 0x5c, 0xa6, 407 | 0xa0, 0xcf, 0x4f, 0xb4, 0x86, 0xfd, 0x49, 0xfa, 0x4a, 0x85, 0xdb, 0xfe, 0x61, 0xb7, 0x3a, 0xd7, 408 | 0x83, 0x70, 0x57, 0x49, 0x83, 0xa7, 0x10, 0x73, 0x74, 0x37, 0x87, 0xfd, 0x6b, 0x28, 0xb7, 0x31, 409 | 0x1e, 0x54, 0x1c, 0xe9, 0xd0, 0xb1, 0xca, 0x76, 0x3b, 0x21, 0xf7, 0x67, 0xbb, 0x48, 0x69, 0x39, 410 | ], 411 | [ 412 | 0x8d, 0xd1, 0x8c, 0x7b, 0x83, 0x8c, 0xa8, 0x18, 0xa7, 0x4a, 0x14, 0x03, 0x88, 0xb3, 0xce, 0x74, 413 | 0xbf, 0x5b, 0x87, 0x67, 0xa7, 0x85, 0x6b, 0x62, 0x96, 0x7c, 0xa9, 0xa6, 0xf6, 0x9e, 0xf4, 0x73, 414 | 0xc5, 0xc4, 0xb0, 0x2b, 0x73, 0x2e, 0x36, 0x77, 0xdf, 0xba, 0x57, 0xff, 0x7f, 0xe9, 0x84, 0xe1, 415 | 0x8d, 0x7b, 0xa2, 0xef, 0x4f, 0x10, 0xf3, 0xd3, 0xe8, 0xb4, 0xba, 0x20, 0x28, 0x79, 0x18, 0xd6, 416 | 0x0f, 0x1c, 0xaa, 0xbd, 0x0e, 0x45, 0xf7, 0x6c, 0x68, 0xb9, 0x29, 0x40, 0x1a, 0xcf, 0xb6, 0x0a, 417 | 0x13, 0xf8, 0xc0, 0x9c, 0x87, 0x10, 0x36, 0x14, 0x73, 0xa1, 0x75, 0x27, 0x14, 0x55, 0xaf, 0x78, 418 | 0x9a, 0x08, 0xc9, 0x05, 0xf2, 0xec, 0x24, 0x1b, 0x07, 0x4a, 0xdc, 0xf6, 0x48, 0xc6, 0x25, 0xcd, 419 | 0x12, 0x1d, 0xaf, 0x51, 0x8f, 0xe9, 0xca, 0x2c, 0x80, 0x57, 0x78, 0xb7, 0x96, 0x07, 0x19, 0x77, 420 | 0x6e, 0x16, 0x45, 0x47, 0x8e, 0x9c, 0x18, 0x55, 0xf1, 0x72, 0xb3, 0x8a, 0xea, 0x4e, 0x8d, 0x90, 421 | 0x2e, 0xbc, 0x08, 0xac, 0xf6, 0xa0, 0x5c, 0x16, 0xe3, 0x7a, 0xee, 0x67, 0xb8, 0x58, 0xdc, 0x16, 422 | 0x40, 0xed, 0xf9, 0x18, 0xb3, 0x0e, 0xd8, 0xee, 0xe1, 0xfa, 0xc3, 0x9f, 0x82, 0x99, 0x32, 0x41, 423 | 0x34, 0xbe, 0xc9, 0x50, 0x36, 0xe5, 0x66, 0xaa, 0x0d, 0x43, 0xf0, 0x3f, 0x26, 0x7c, 0xf3, 0x87, 424 | 0x26, 0xa4, 0xf5, 0xf8, 0xa0, 0x32, 0x46, 0x74, 0x2e, 0x5a, 0xe2, 0xe7, 0x6b, 0x02, 0xa8, 0xd0, 425 | 0xcf, 0xb8, 0x33, 0x15, 0x3b, 0x4f, 0xc7, 0x7a, 0xe8, 0x3d, 0x75, 0xd2, 0xfe, 0x42, 0x22, 0x22, 426 | 0xa8, 0x21, 0x33, 0xfb, 0xb0, 0x87, 0x92, 0x99, 0xca, 0xd7, 0xd7, 0x88, 0xac, 0xe4, 0x75, 0x83, 427 | 0x56, 0xbf, 0xce, 0xed, 0x4f, 0xf6, 0x22, 0x07, 0xca, 0xbc, 0xd2, 0xef, 0x1b, 0x75, 0xd6, 0x2d, 428 | ], 429 | [ 430 | 0xd2, 0x4f, 0x76, 0x51, 0xeb, 0xa1, 0xad, 0x84, 0xd6, 0x19, 0xe6, 0x97, 0xd9, 0xd3, 0x58, 0x6b, 431 | 0xfb, 0xb8, 0x20, 0xfd, 0x49, 0x56, 0x1b, 0x50, 0x61, 0x10, 0x57, 0xb8, 0x78, 0x07, 0xc1, 0x4a, 432 | 0xa2, 0xea, 0x47, 0x80, 0x00, 0x4a, 0xb3, 0x4e, 0x6f, 0x1a, 0xc1, 0xd5, 0x22, 0xf8, 0x54, 0x2f, 433 | 0x33, 0xe5, 0x7f, 0xb4, 0x13, 0x02, 0xa3, 0xa1, 0x8b, 0x1c, 0x6f, 0x19, 0xd6, 0x42, 0xb3, 0x24, 434 | 0x4b, 0x04, 0x30, 0x10, 0x02, 0x23, 0x6f, 0x10, 0x03, 0x4b, 0x0e, 0x33, 0x55, 0x22, 0xa4, 0x78, 435 | 0xec, 0xd2, 0x4a, 0x11, 0x8b, 0xfc, 0xff, 0x14, 0x7a, 0xed, 0x06, 0x47, 0x86, 0xfc, 0xf0, 0x03, 436 | 0x0f, 0x75, 0x07, 0xe4, 0x9a, 0xd3, 0xbb, 0x0d, 0x97, 0x1f, 0x6f, 0x80, 0x62, 0xa6, 0x9e, 0xc6, 437 | 0xb1, 0x10, 0x81, 0xa1, 0x6d, 0x55, 0x0f, 0x9e, 0x1b, 0xb7, 0xf5, 0xdc, 0x62, 0xa8, 0x63, 0x58, 438 | 0xcf, 0x2f, 0x6a, 0xad, 0x5e, 0xd3, 0x3f, 0xbd, 0x8d, 0x9b, 0x2a, 0x8b, 0xdf, 0x60, 0xb9, 0xaf, 439 | 0xaa, 0x70, 0xb4, 0xa8, 0x17, 0x99, 0x72, 0xb9, 0x88, 0x9d, 0x3d, 0x2a, 0x11, 0x87, 0x1e, 0xf3, 440 | 0x9d, 0x33, 0x8d, 0xed, 0x52, 0x60, 0x36, 0x71, 0xff, 0x7b, 0x37, 0x84, 0x3d, 0x27, 0x9e, 0xd9, 441 | 0xdf, 0x58, 0xf7, 0xc2, 0x58, 0x0c, 0x9d, 0x5e, 0xee, 0x23, 0x83, 0x70, 0x3f, 0x95, 0xbc, 0xf5, 442 | 0x42, 0x86, 0x91, 0x5b, 0x3f, 0x77, 0x31, 0xd2, 0xb7, 0x09, 0x59, 0x53, 0xf5, 0xf2, 0xe5, 0xf1, 443 | 0xdc, 0x92, 0x83, 0x14, 0xc1, 0xa2, 0x25, 0x62, 0x13, 0xfd, 0xd4, 0xc5, 0x54, 0x9d, 0x9c, 0x27, 444 | 0x6c, 0xc2, 0x75, 0x8b, 0xbc, 0xc7, 0x4e, 0x0a, 0xf6, 0x5c, 0x2f, 0x12, 0x8e, 0x25, 0xbb, 0xf2, 445 | 0x5f, 0x89, 0xaa, 0xea, 0xd9, 0xcd, 0x05, 0x74, 0x20, 0xd6, 0x17, 0xed, 0xf0, 0x66, 0x6c, 0x7b, 446 | ], 447 | ]; 448 | 449 | #[cfg(test)] 450 | mod tests { 451 | use super::*; 452 | use crate::code::Code; 453 | use crate::std_alloc::{Vec, vec}; 454 | #[cfg(feature = "std")] 455 | use pretty_assertions::assert_eq; 456 | 457 | const fn mul_tests() -> &'static [(u32, u32, u32)] { 458 | &[ 459 | (0x0000_0000, 0xa686_d3b6, 0x0000_0000), 460 | (0x000e_0000, 0xa686_d3b6, 0xac62_0000), 461 | (0x0067_bd20, 0x4fd9_31ff, 0x2008_02e0), 462 | (0x2ba0_a76e, 0xa686_d3b6, 0x2405_0002), 463 | (0x4adf_d954, 0x4fd9_31ff, 0x9029_beac), 464 | (0x7c01_6806, 0x2912_dedd, 0x0000_00be), 465 | (0xa942_2f21, 0xa686_d3b6, 0x03d2_03e7), 466 | (0xfff5_76e0, 0xa686_d3b6, 0x27bd_0020), 467 | ] 468 | } 469 | 470 | #[test] 471 | fn test_mul_encrypt() { 472 | for t in mul_tests() { 473 | assert_eq!(mul_encrypt(t.2, t.1), t.0); 474 | } 475 | } 476 | 477 | #[test] 478 | fn test_mul_decrypt() { 479 | for t in mul_tests() { 480 | assert_eq!(mul_decrypt(t.0, t.1), t.2); 481 | } 482 | } 483 | 484 | #[test] 485 | fn test_mod_inverse() { 486 | let tests = &[ 487 | (0x0d31_3243, 0x6c7b_2a6b), 488 | (0x0efd_8231, 0xd4c0_96d1), 489 | (0x2912_dedd, 0xe09d_e975), 490 | (0x4fd9_31ff, 0x9a62_cdff), 491 | (0x5a53_abb5, 0x58f4_2a9d), 492 | (0x9ab2_af6d, 0x1043_b265), 493 | (0xa686_d3b7, 0x57ed_7a07), 494 | (0xec35_a92f, 0xd274_3dcf), 495 | (0x0000_0000, 0x0000_0000), // Technically, 0 has no inverse 496 | (0x0000_0001, 0x0000_0001), 497 | (0xffff_ffff, 0xffff_ffff), 498 | ]; 499 | for t in tests { 500 | assert_eq!(mod_inverse(t.0), t.1); 501 | } 502 | } 503 | 504 | struct Test { 505 | beefcode: Code, 506 | decrypted: Vec, 507 | encrypted: Vec, 508 | } 509 | 510 | fn tests() -> Vec { 511 | vec![ 512 | Test { 513 | // default BEEFC0DE 514 | beefcode: "BEEFC0DE 00000000".into(), 515 | decrypted: vec![ 516 | "9029BEAC 0C0A9225".into(), 517 | "201F6024 00000000".into(), 518 | "2096F5B8 000000BE".into(), 519 | ], 520 | encrypted: vec![ 521 | "D08F3A49 00078A53".into(), 522 | "3818DDE5 E72B2B16".into(), 523 | "973E0B2A A7D4AF10".into(), 524 | ], 525 | }, 526 | Test { 527 | // non-default BEEFC0DE 528 | beefcode: "BEEFC0DE DEADFACE".into(), 529 | decrypted: vec![ 530 | "9029BEAC 0C0A9225".into(), 531 | "201F6024 00000000".into(), 532 | "2096F5B8 000000BE".into(), 533 | ], 534 | encrypted: vec![ 535 | "E65B5422 B12543CF".into(), 536 | "D14F5E52 FE26C9ED".into(), 537 | "DD9BB6F0 F5DF87F7".into(), 538 | ], 539 | }, 540 | Test { 541 | // BEEFC0DF 542 | beefcode: "BEEFC0DF B16B00B5".into(), 543 | decrypted: vec![ 544 | "01234567 89ABCDEF".into(), 545 | "9029BEAC 0C0A9225".into(), 546 | "201F6024 00000000".into(), 547 | "2096F5B8 000000BE".into(), 548 | ], 549 | encrypted: vec![ 550 | "862316AB C59C5FB1".into(), 551 | "06133B66 95444FF1".into(), 552 | "565FD08D 9154AFF4".into(), 553 | "4EF412FE D03E4E13".into(), 554 | ], 555 | }, 556 | Test { 557 | // BEEFC0DE & BEEFC0DF 558 | beefcode: "BEEFC0DE 00000000".into(), 559 | decrypted: vec![ 560 | "BEEFC0DF B16B00B5".into(), 561 | "01234567 89ABCDEF".into(), 562 | "9029BEAC 0C0A9225".into(), 563 | "201F6024 00000000".into(), 564 | "2096F5B8 000000BE".into(), 565 | ], 566 | encrypted: vec![ 567 | "FE8B8601 C7C6F6CE".into(), 568 | "2195D855 63FA11A7".into(), 569 | "0CA31760 A6F7E88A".into(), 570 | "679DC392 FA43E30B".into(), 571 | "1CD9CCC3 6AF74E36".into(), 572 | ], 573 | }, 574 | Test { 575 | // 2x default BEEFC0DE 576 | beefcode: "BEEFC0DE 00000000".into(), 577 | decrypted: vec![ 578 | "BEEFC0DE 00000000".into(), 579 | "9029BEAC 0C0A9225".into(), 580 | "201F6024 00000000".into(), 581 | "2096F5B8 000000BE".into(), 582 | ], 583 | encrypted: vec![ 584 | "8787C575 1AC4C1B4".into(), 585 | "02210430 184C16E8".into(), 586 | "32E2A916 7E6017BA".into(), 587 | "CBB720FD D61505E0".into(), 588 | ], 589 | }, 590 | ] 591 | } 592 | 593 | #[test] 594 | fn test_encrypt_code() { 595 | for t in &tests() { 596 | let mut cb7 = Cb7::new(); 597 | cb7.beefcode(t.beefcode.0, t.beefcode.1); 598 | 599 | for (i, &code) in t.decrypted.iter().enumerate() { 600 | let result: Code = cb7.encrypt_code(code.0, code.1).into(); 601 | assert_eq!(result, t.encrypted[i]); 602 | 603 | if is_beefcode(code.0) { 604 | cb7.beefcode(code.0, code.1); 605 | } 606 | } 607 | } 608 | } 609 | 610 | #[test] 611 | fn test_encrypt_code_mut() { 612 | for t in &mut tests() { 613 | let mut cb7 = Cb7::new(); 614 | cb7.beefcode(t.beefcode.0, t.beefcode.1); 615 | 616 | for (i, code) in t.decrypted.iter_mut().enumerate() { 617 | let raw = *code; 618 | cb7.encrypt_code_mut(&mut code.0, &mut code.1); 619 | assert_eq!(*code, t.encrypted[i]); 620 | 621 | if is_beefcode(raw.0) { 622 | cb7.beefcode(raw.0, raw.1); 623 | } 624 | } 625 | } 626 | } 627 | 628 | #[test] 629 | fn test_decrypt_code() { 630 | for t in &tests() { 631 | let mut cb7 = Cb7::new(); 632 | cb7.beefcode(t.beefcode.0, t.beefcode.1); 633 | 634 | for (i, &code) in t.encrypted.iter().enumerate() { 635 | let result: Code = cb7.decrypt_code(code.0, code.1).into(); 636 | assert_eq!(result, t.decrypted[i]); 637 | 638 | if is_beefcode(result.0) { 639 | cb7.beefcode(result.0, result.1); 640 | } 641 | } 642 | } 643 | } 644 | 645 | #[test] 646 | fn test_decrypt_code_mut() { 647 | for t in &mut tests() { 648 | let mut cb7 = Cb7::new(); 649 | cb7.beefcode(t.beefcode.0, t.beefcode.1); 650 | 651 | for (i, code) in t.encrypted.iter_mut().enumerate() { 652 | cb7.decrypt_code_mut(&mut code.0, &mut code.1); 653 | assert_eq!(*code, t.decrypted[i]); 654 | 655 | if is_beefcode(code.0) { 656 | cb7.beefcode(code.0, code.1); 657 | } 658 | } 659 | } 660 | } 661 | } 662 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Encrypt and decrypt cheat codes for all versions of CodeBreaker PS2. 2 | //! 3 | //! Uses [cb1](cb1/index.html) and [cb7](cb7/index.html) under the hood to 4 | //! support both CB v1 and v7 codes. 5 | //! 6 | //! # Quickstart 7 | //! 8 | //! ``` 9 | //! use codebreaker::Codebreaker; 10 | //! 11 | //! let input: Vec<(u32, u32)> = vec![ 12 | //! (0x2043AFCC, 0x2411FFFF), 13 | //! (0x2A973DBD, 0x00000000), 14 | //! (0xB4336FA9, 0x4DFEFB79), 15 | //! (0x973E0B2A, 0xA7D4AF10), 16 | //! ]; 17 | //! let output: Vec<(u32, u32)> = vec![ 18 | //! (0x2043AFCC, 0x2411FFFF), 19 | //! (0x201F6024, 0x00000000), 20 | //! (0xBEEFC0DE, 0x00000000), 21 | //! (0x2096F5B8, 0x000000BE), 22 | //! ]; 23 | //! 24 | //! let mut cb = Codebreaker::new(); 25 | //! for (i, code) in input.iter().enumerate() { 26 | //! assert_eq!(cb.auto_decrypt_code(code.0, code.1), output[i]); 27 | //! } 28 | //! ``` 29 | 30 | #![deny(clippy::all, clippy::nursery)] 31 | #![deny(nonstandard_style, rust_2018_idioms)] 32 | #![deny(missing_docs, missing_debug_implementations)] 33 | #![forbid(unsafe_code)] 34 | #![no_std] 35 | 36 | #[cfg(doctest)] 37 | doc_comment::doctest!("../README.md", readme); 38 | 39 | pub mod cb1; 40 | pub mod cb7; 41 | mod rc4; 42 | 43 | #[cfg(test)] 44 | mod std_alloc { 45 | #[cfg(feature = "std")] 46 | extern crate std as alloc; 47 | 48 | #[cfg(not(feature = "std"))] 49 | extern crate alloc; 50 | 51 | pub use alloc::{fmt, vec, vec::Vec}; 52 | } 53 | 54 | use cb7::{Cb7, is_beefcode}; 55 | 56 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 57 | enum Scheme { 58 | Raw, 59 | V1, 60 | V7, 61 | } 62 | 63 | /// A processor for CB v1 and v7 codes. 64 | #[derive(Debug, Clone, Copy)] 65 | pub struct Codebreaker { 66 | scheme: Scheme, 67 | cb7: Cb7, 68 | code_lines: usize, 69 | } 70 | 71 | /// Does the same as [`new`](#method.new). 72 | impl Default for Codebreaker { 73 | fn default() -> Self { 74 | Self::new() 75 | } 76 | } 77 | 78 | impl Codebreaker { 79 | /// Returns a new processor for encrypting and decrypting a list of CB v1 80 | /// and v7 codes. 81 | pub const fn new() -> Self { 82 | Self { 83 | scheme: Scheme::Raw, 84 | cb7: Cb7::new(), 85 | code_lines: 0, 86 | } 87 | } 88 | 89 | /// Returns a new processor for all CB v7 codes published on CMGSCCC.com. 90 | /// 91 | /// Lets you omit `B4336FA9 4DFEFB79` as the first code in the list. 92 | pub fn new_v7() -> Self { 93 | Self { 94 | scheme: Scheme::V7, 95 | cb7: Cb7::default(), 96 | code_lines: 0, 97 | } 98 | } 99 | 100 | /// Encrypts a code and returns the result. 101 | /// 102 | /// # Example 103 | /// ``` 104 | /// use codebreaker::Codebreaker; 105 | /// 106 | /// let mut cb = Codebreaker::new(); 107 | /// let code = cb.encrypt_code(0x2043AFCC, 0x2411FFFF); 108 | /// assert_eq!(code, (0x2AFF014C, 0x2411FFFF)); 109 | /// ``` 110 | pub fn encrypt_code(&mut self, addr: u32, val: u32) -> (u32, u32) { 111 | let mut code = (addr, val); 112 | self.encrypt_code_mut(&mut code.0, &mut code.1); 113 | code 114 | } 115 | 116 | /// Encrypts a code directly. 117 | /// 118 | /// # Example 119 | /// ``` 120 | /// use codebreaker::Codebreaker; 121 | /// 122 | /// let mut cb = Codebreaker::new(); 123 | /// let mut code = (0x2043AFCC, 0x2411FFFF); 124 | /// cb.encrypt_code_mut(&mut code.0, &mut code.1); 125 | /// assert_eq!(code, (0x2AFF014C, 0x2411FFFF)); 126 | /// ``` 127 | pub fn encrypt_code_mut(&mut self, addr: &mut u32, val: &mut u32) { 128 | let (oldaddr, oldval) = (*addr, *val); 129 | 130 | if self.scheme == Scheme::V7 { 131 | self.cb7.encrypt_code_mut(addr, val); 132 | } else { 133 | cb1::encrypt_code_mut(addr, val); 134 | } 135 | 136 | if is_beefcode(oldaddr) { 137 | self.cb7.beefcode(oldaddr, oldval); 138 | self.scheme = Scheme::V7; 139 | } 140 | } 141 | 142 | /// Decrypts a code and returns the result. 143 | /// 144 | /// # Example 145 | /// ``` 146 | /// use codebreaker::Codebreaker; 147 | /// 148 | /// let encrypted: Vec<(u32, u32)> = vec![ 149 | /// (0x2AFF014C, 0x2411FFFF), 150 | /// (0xB4336FA9, 0x4DFEFB79), 151 | /// (0x973E0B2A, 0xA7D4AF10), 152 | /// ]; 153 | /// let decrypted: Vec<(u32, u32)> = vec![ 154 | /// (0x2043AFCC, 0x2411FFFF), 155 | /// (0xBEEFC0DE, 0x00000000), 156 | /// (0x2096F5B8, 0x000000BE), 157 | /// ]; 158 | /// 159 | /// let mut cb = Codebreaker::new(); 160 | /// for (i, code) in encrypted.iter().enumerate() { 161 | /// let result = cb.decrypt_code(code.0, code.1); 162 | /// assert_eq!(result, decrypted[i]); 163 | /// } 164 | /// ``` 165 | pub fn decrypt_code(&mut self, addr: u32, val: u32) -> (u32, u32) { 166 | let mut code = (addr, val); 167 | self.decrypt_code_mut(&mut code.0, &mut code.1); 168 | code 169 | } 170 | 171 | /// Decrypts a code directly. 172 | /// 173 | /// # Example 174 | /// ``` 175 | /// use codebreaker::Codebreaker; 176 | /// 177 | /// let mut encrypted: Vec<(u32, u32)> = vec![ 178 | /// (0x2AFF014C, 0x2411FFFF), 179 | /// (0xB4336FA9, 0x4DFEFB79), 180 | /// (0x973E0B2A, 0xA7D4AF10), 181 | /// ]; 182 | /// let decrypted: Vec<(u32, u32)> = vec![ 183 | /// (0x2043AFCC, 0x2411FFFF), 184 | /// (0xBEEFC0DE, 0x00000000), 185 | /// (0x2096F5B8, 0x000000BE), 186 | /// ]; 187 | /// 188 | /// let mut cb = Codebreaker::new(); 189 | /// for code in encrypted.iter_mut() { 190 | /// cb.decrypt_code_mut(&mut code.0, &mut code.1); 191 | /// } 192 | /// assert_eq!(encrypted, decrypted); 193 | /// ``` 194 | pub fn decrypt_code_mut(&mut self, addr: &mut u32, val: &mut u32) { 195 | if self.scheme == Scheme::V7 { 196 | self.cb7.decrypt_code_mut(addr, val); 197 | } else { 198 | cb1::decrypt_code_mut(addr, val); 199 | } 200 | 201 | if is_beefcode(*addr) { 202 | self.cb7.beefcode(*addr, *val); 203 | self.scheme = Scheme::V7; 204 | } 205 | } 206 | 207 | /// Smart version of [`decrypt_code`](#method.decrypt_code) that detects if 208 | /// and how a code needs to be decrypted. 209 | /// 210 | /// # Example 211 | /// ``` 212 | /// use codebreaker::Codebreaker; 213 | /// 214 | /// let input: Vec<(u32, u32)> = vec![ 215 | /// (0x2043AFCC, 0x2411FFFF), 216 | /// (0x2A973DBD, 0x00000000), 217 | /// (0xB4336FA9, 0x4DFEFB79), 218 | /// (0x973E0B2A, 0xA7D4AF10), 219 | /// ]; 220 | /// let output: Vec<(u32, u32)> = vec![ 221 | /// (0x2043AFCC, 0x2411FFFF), 222 | /// (0x201F6024, 0x00000000), 223 | /// (0xBEEFC0DE, 0x00000000), 224 | /// (0x2096F5B8, 0x000000BE), 225 | /// ]; 226 | /// 227 | /// let mut cb = Codebreaker::new(); 228 | /// for (i, code) in input.iter().enumerate() { 229 | /// assert_eq!(cb.auto_decrypt_code(code.0, code.1), output[i]); 230 | /// } 231 | /// ``` 232 | pub fn auto_decrypt_code(&mut self, addr: u32, val: u32) -> (u32, u32) { 233 | let mut code = (addr, val); 234 | self.auto_decrypt_code_mut(&mut code.0, &mut code.1); 235 | code 236 | } 237 | 238 | /// Smart version of [`decrypt_code_mut`](#method.decrypt_code_mut) that 239 | /// detects if and how a code needs to be decrypted. 240 | pub fn auto_decrypt_code_mut(&mut self, addr: &mut u32, val: &mut u32) { 241 | if self.scheme != Scheme::V7 { 242 | if self.code_lines == 0 { 243 | self.code_lines = num_code_lines(*addr); 244 | if (*addr >> 24) & 0x0e != 0 { 245 | if is_beefcode(*addr) { 246 | // ignore raw beefcode 247 | self.code_lines -= 1; 248 | return; 249 | } 250 | self.scheme = Scheme::V1; 251 | self.code_lines -= 1; 252 | cb1::decrypt_code_mut(addr, val); 253 | } else { 254 | self.scheme = Scheme::Raw; 255 | self.code_lines -= 1; 256 | } 257 | } else { 258 | self.code_lines -= 1; 259 | if self.scheme == Scheme::Raw { 260 | return; 261 | } 262 | cb1::decrypt_code_mut(addr, val); 263 | } 264 | } else { 265 | self.cb7.decrypt_code_mut(addr, val); 266 | if self.code_lines == 0 { 267 | self.code_lines = num_code_lines(*addr); 268 | if self.code_lines == 1 && *addr == 0xffff_ffff { 269 | // XXX: changing encryption via "FFFFFFFF 000xnnnn" is not supported 270 | self.code_lines = 0; 271 | return; 272 | } 273 | } 274 | self.code_lines -= 1; 275 | } 276 | 277 | if is_beefcode(*addr) { 278 | self.cb7.beefcode(*addr, *val); 279 | self.scheme = Scheme::V7; 280 | self.code_lines = 1; 281 | } 282 | } 283 | } 284 | 285 | const fn num_code_lines(addr: u32) -> usize { 286 | let cmd = addr >> 28; 287 | 288 | if cmd < 3 || cmd > 6 { 289 | 1 290 | } else if cmd == 3 { 291 | if addr & 0x0040_0000 != 0 { 2 } else { 1 } 292 | } else { 293 | 2 294 | } 295 | } 296 | 297 | #[cfg(test)] 298 | mod tests { 299 | use super::*; 300 | use crate::code::Code; 301 | use crate::std_alloc::{Vec, vec}; 302 | #[cfg(feature = "std")] 303 | use pretty_assertions::assert_eq; 304 | 305 | struct Test { 306 | cb: Codebreaker, 307 | decrypted: Vec, 308 | encrypted: Vec, 309 | } 310 | 311 | fn tests() -> Vec { 312 | vec![ 313 | Test { 314 | cb: Codebreaker::new(), 315 | decrypted: vec![ 316 | "2043AFCC 2411FFFF".into(), 317 | "BEEFC0DE 00000000".into(), 318 | "2096F5B8 000000BE".into(), 319 | ], 320 | encrypted: vec![ 321 | "2AFF014C 2411FFFF".into(), 322 | "B4336FA9 4DFEFB79".into(), 323 | "973E0B2A A7D4AF10".into(), 324 | ], 325 | }, 326 | Test { 327 | cb: Codebreaker::new_v7(), 328 | decrypted: vec![ 329 | "9029BEAC 0C0A9225".into(), 330 | "201F6024 00000000".into(), 331 | "2096F5B8 000000BE".into(), 332 | ], 333 | encrypted: vec![ 334 | "D08F3A49 00078A53".into(), 335 | "3818DDE5 E72B2B16".into(), 336 | "973E0B2A A7D4AF10".into(), 337 | ], 338 | }, 339 | Test { 340 | cb: Codebreaker::default(), 341 | decrypted: vec![ 342 | "9029BEAC 0C0A9225".into(), 343 | "201F6024 00000000".into(), 344 | "2096F5B8 000000BE".into(), 345 | ], 346 | encrypted: vec![ 347 | "9A545CC6 188CBCFB".into(), 348 | "2A973DBD 00000000".into(), 349 | "2A03B60A 000000BE".into(), 350 | ], 351 | }, 352 | ] 353 | } 354 | 355 | #[test] 356 | fn test_encrypt_code() { 357 | for t in &mut tests() { 358 | for (i, &code) in t.decrypted.iter().enumerate() { 359 | let result: Code = t.cb.encrypt_code(code.0, code.1).into(); 360 | assert_eq!(result, t.encrypted[i]); 361 | } 362 | } 363 | } 364 | 365 | #[test] 366 | fn test_encrypt_code_mut() { 367 | for t in &mut tests() { 368 | for (i, code) in t.decrypted.iter_mut().enumerate() { 369 | t.cb.encrypt_code_mut(&mut code.0, &mut code.1); 370 | assert_eq!(*code, t.encrypted[i]); 371 | } 372 | } 373 | } 374 | 375 | #[test] 376 | fn test_decrypt_code() { 377 | for t in &mut tests() { 378 | for (i, &code) in t.encrypted.iter().enumerate() { 379 | let result: Code = t.cb.decrypt_code(code.0, code.1).into(); 380 | assert_eq!(result, t.decrypted[i]); 381 | } 382 | } 383 | } 384 | 385 | #[test] 386 | fn test_decrypt_code_mut() { 387 | for t in &mut tests() { 388 | for (i, code) in t.encrypted.iter_mut().enumerate() { 389 | t.cb.decrypt_code_mut(&mut code.0, &mut code.1); 390 | assert_eq!(*code, t.decrypted[i]); 391 | } 392 | } 393 | } 394 | 395 | struct AutoTest { 396 | input: Vec, 397 | output: Vec, 398 | } 399 | 400 | fn auto_tests() -> Vec { 401 | vec![ 402 | AutoTest { 403 | // raw 404 | input: vec![ 405 | "9029BEAC 0C0A9225".into(), 406 | "201F6024 00000000".into(), 407 | "2096F5B8 000000BE".into(), 408 | ], 409 | output: vec![ 410 | "9029BEAC 0C0A9225".into(), 411 | "201F6024 00000000".into(), 412 | "2096F5B8 000000BE".into(), 413 | ], 414 | }, 415 | AutoTest { 416 | // v1 encrypted 417 | input: vec![ 418 | "9A545CC6 188CBCFB".into(), 419 | "2A973DBD 00000000".into(), 420 | "2A03B60A 000000BE".into(), 421 | ], 422 | output: vec![ 423 | "9029BEAC 0C0A9225".into(), 424 | "201F6024 00000000".into(), 425 | "2096F5B8 000000BE".into(), 426 | ], 427 | }, 428 | AutoTest { 429 | // v7 encrypted 430 | input: vec![ 431 | "B4336FA9 4DFEFB79".into(), 432 | "D08F3A49 00078A53".into(), 433 | "3818DDE5 E72B2B16".into(), 434 | "973E0B2A A7D4AF10".into(), 435 | ], 436 | output: vec![ 437 | "BEEFC0DE 00000000".into(), 438 | "9029BEAC 0C0A9225".into(), 439 | "201F6024 00000000".into(), 440 | "2096F5B8 000000BE".into(), 441 | ], 442 | }, 443 | AutoTest { 444 | // v1 and v7 encrypted 445 | input: vec![ 446 | "9A545CC6 188CBCFB".into(), 447 | "2A973DBD 00000000".into(), 448 | "B4336FA9 4DFEFB79".into(), 449 | "973E0B2A A7D4AF10".into(), 450 | ], 451 | output: vec![ 452 | "9029BEAC 0C0A9225".into(), 453 | "201F6024 00000000".into(), 454 | "BEEFC0DE 00000000".into(), 455 | "2096F5B8 000000BE".into(), 456 | ], 457 | }, 458 | AutoTest { 459 | // raw, v1, and v7 encrypted 460 | input: vec![ 461 | "9029BEAC 0C0A9225".into(), 462 | "2A973DBD 00000000".into(), 463 | "B4336FA9 4DFEFB79".into(), 464 | "973E0B2A A7D4AF10".into(), 465 | ], 466 | output: vec![ 467 | "9029BEAC 0C0A9225".into(), 468 | "201F6024 00000000".into(), 469 | "BEEFC0DE 00000000".into(), 470 | "2096F5B8 000000BE".into(), 471 | ], 472 | }, 473 | ] 474 | } 475 | 476 | #[test] 477 | fn test_auto_decrypt_code() { 478 | for t in &mut auto_tests() { 479 | let mut cb = Codebreaker::new(); 480 | for (i, &code) in t.input.iter().enumerate() { 481 | let result: Code = cb.auto_decrypt_code(code.0, code.1).into(); 482 | assert_eq!(result, t.output[i]); 483 | } 484 | } 485 | } 486 | 487 | #[test] 488 | fn test_auto_decrypt_code_mut() { 489 | for t in &mut auto_tests() { 490 | let mut cb = Codebreaker::new(); 491 | for (i, code) in t.input.iter_mut().enumerate() { 492 | cb.auto_decrypt_code_mut(&mut code.0, &mut code.1); 493 | assert_eq!(*code, t.output[i]); 494 | } 495 | } 496 | } 497 | } 498 | 499 | #[cfg(test)] 500 | mod code { 501 | use crate::std_alloc::{Vec, fmt}; 502 | 503 | #[derive(Copy, Clone, PartialEq, Eq)] 504 | pub struct Code(pub u32, pub u32); 505 | 506 | impl From<(u32, u32)> for Code { 507 | fn from(t: (u32, u32)) -> Self { 508 | Self(t.0, t.1) 509 | } 510 | } 511 | 512 | impl From<&str> for Code { 513 | fn from(s: &str) -> Self { 514 | let t: Vec = s 515 | .splitn(2, ' ') 516 | .map(|v| u32::from_str_radix(v, 16).expect("invalid code format")) 517 | .collect(); 518 | 519 | Self(t[0], t[1]) 520 | } 521 | } 522 | 523 | // Implements ToString 524 | impl fmt::Display for Code { 525 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 526 | write!(f, "{:08X} {:08X}", self.0, self.1) 527 | } 528 | } 529 | 530 | // Used by assert_eq! 531 | impl fmt::Debug for Code { 532 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 533 | write!(f, "{self}") 534 | } 535 | } 536 | } 537 | -------------------------------------------------------------------------------- /src/rc4.rs: -------------------------------------------------------------------------------- 1 | // Implementation of the stream cipher RC4 2 | // Based on https://github.com/DaGenix/rust-crypto/blob/master/src/rc4.rs 3 | 4 | #[derive(Clone, Copy)] 5 | pub struct Rc4 { 6 | i: u8, 7 | j: u8, 8 | state: [u8; 256], 9 | } 10 | 11 | impl Rc4 { 12 | #[allow(clippy::needless_range_loop)] 13 | pub fn new(key: &[u8]) -> Self { 14 | assert!(!key.is_empty() && key.len() <= 256); 15 | let mut state = [0; 256]; 16 | for i in 0..256 { 17 | state[i] = i as u8; 18 | } 19 | let mut j: u8 = 0; 20 | for i in 0..256 { 21 | j = j.wrapping_add(state[i]).wrapping_add(key[i % key.len()]); 22 | state.swap(i, j as usize); 23 | } 24 | Self { i: 0, j: 0, state } 25 | } 26 | 27 | pub fn crypt(&mut self, buf: &mut [u8]) { 28 | for i in buf.iter_mut() { 29 | self.i = self.i.wrapping_add(1); 30 | self.j = self.j.wrapping_add(self.state[self.i as usize]); 31 | self.state.swap(self.i as usize, self.j as usize); 32 | let j = self.state[self.i as usize].wrapping_add(self.state[self.j as usize]); 33 | *i ^= self.state[j as usize]; 34 | } 35 | } 36 | } 37 | 38 | #[cfg(test)] 39 | mod tests { 40 | use super::*; 41 | #[cfg(feature = "std")] 42 | use pretty_assertions::assert_eq; 43 | 44 | struct Test { 45 | key: &'static str, 46 | input: &'static str, 47 | output: &'static [u8], 48 | } 49 | 50 | const fn wikipedia_tests() -> &'static [Test] { 51 | &[ 52 | Test { 53 | key: "Key", 54 | input: "Plaintext", 55 | output: &[0xbb, 0xf3, 0x16, 0xe8, 0xd9, 0x40, 0xaf, 0x0a, 0xd3], 56 | }, 57 | Test { 58 | key: "Wiki", 59 | input: "pedia", 60 | output: &[0x10, 0x21, 0xbf, 0x04, 0x20], 61 | }, 62 | Test { 63 | key: "Secret", 64 | input: "Attack at dawn", 65 | output: &[ 66 | 0x45, 0xa0, 0x1f, 0x64, 0x5f, 0xc3, 0x5b, 0x38, 0x35, 0x52, 0x54, 0x4b, 0x9b, 0xf5, 67 | ], 68 | }, 69 | ] 70 | } 71 | 72 | #[test] 73 | fn test_crypt() { 74 | for t in wikipedia_tests() { 75 | let mut rc4 = Rc4::new(t.key.as_bytes()); 76 | let mut buf = t.input.as_bytes().to_vec(); 77 | rc4.crypt(&mut buf); 78 | assert_eq!(buf, t.output); 79 | } 80 | } 81 | } 82 | --------------------------------------------------------------------------------