BD Drive Reverse Engineering
Jump to navigation
Jump to search
Introduction
- The following information was reverse engineered from Storage Manager which runs in LPAR1 and from sv_iso_spu_module.self.
Information about EID4
- EID4 contains 2 128bit keys which are necessary to establish a secure communication channel to BD drive for sending vendor specific security commands.
- EID4 is encrypted with AES-CBC-256 algorithm.
- EID4 is of size 0x30 bytes: 0x0-0xf bytes = 1st key, 0x10-0x1f - 2nd key, 0x20-0x2f - CMAC-OMAC1 of EID4
- The first key is used for encrypting data sent from host to BD drive.
- The second key is used for decrypting data sent from BD drive to host.
Dumping EID4 IV and Key
- I modified sv_iso_spu_module.self to dump EID4 IV and key.
- I used my spuisofs Linux kernel module and the below SPU program to dump EID4 IV key on PS3 Linux.
- After dumping EID4 key use CMAC-OMAC1 algorithm to check the CMAC of EID4. If the EID4 key you got is correct then the CMAC should match.
SPU Program
My program to dump EID4 AES-CBC-256 IV and key to PPU memory:
/* * Dump EID4 IV and key to EA with MFC * * Copyright (C) 2012 glevand <[email protected]> * All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published * by the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ .text start: ila $2, 0x3dfa0 lr $sp, $2 ila $80, 0x3e000 lr $81, $3 stqd $7, 0($80) stqd $8, 0x10($80) # store EID4 IV stqd $9, 0x20($80) # store upper 16bytes of EID4 key stqd $10, 0x30($80) # store lower 16bytes of EID4 key stqd $11, 0x40($80) stqd $12, 0x50($80) lr $3, $80 lr $4, $81 il $5, 0x60 il $6, 0x7 il $7, 0x20 brsl $lr, 0x10 # mfc_dma_xfer il $3, 0x7 brsl $lr, 0x28 # mfc_dma_wait stop 0x666 # our evil stop code :) /* * r3 - LSA * r4 - EA * r5 - size * r6 - tag * r7 - cmd */ mfc_dma_xfer: wrch $ch16, $3 wrch $ch17, $4 shlqbyi $4, $4, 4 wrch $ch18, $4 wrch $ch19, $5 wrch $ch20, $6 wrch $ch21, $7 bi $lr /* * r3 - tag */ mfc_dma_wait: il $2, 0 nop $127 hbra 2f, 1f wrch $ch23, $2 1: rchcnt $2, $ch23 ceqi $2, $2, 1 nop $127 nop $127 nop $127 nop $127 nop $127 2: brz $2, 1b hbr 3f, $lr rdch $2, $ch24 il $2, 1 shl $2, $2, $3 wrch $ch22, $2 il $2, 2 wrch $ch23, $2 rdch $2, $ch24 nop $127 3: bi $lr
Result
- Test run with spuisofs.
[glevand@arch dump_eid4_key]$ ./dump_eid4_key ../dump_eid4_key.self ../eid4 spuisofs found at /mnt arg1 kernel virtual address d000000000722000 shadow: spe_execution_status 7 priv2: puint_mb_R 2 shadow: spe_execution_status b problem: spu_status_R 6660082 [glevand@arch dump_eid4_key]$ hexdump -C /mnt/arg1 ... Here should be you EID4 IV and key IV is at offset 0x10 (16 bytes) Key is at offset 0x20 (32 bytes) ...
Establish Secure Communication Channel
- With both keys from EID4 we are now able to establish a secure communication channel with BD drive and send vendor-specific ATAPI commands to it.
- Keys from EID4 are used only to derive a session key. After that they are not used anymore. A similar procedure is used e.g. to establish a secure communication channel with ENCDEC device.
- ATAPI commands SEND_KEY and REPORT_KEY are used to exchange random number between host and BD drive.
- Exchanged random numbers are used to derive the session key which is used later to send vendor-specific ATAPI commands (0xE0 and 0xE1) to BD drive.
- The same procedue is follwed e.g. by Storage Manager which runs in LPAR1.
- 3DES-CBC with 2 keys is used to encrypt commands sent to BD drive.
Session Key
TODO
Get Version
- Here is an example of a simple program which establishes a secure communication channel with BD drive and reads BD FW version by using vendor-specific ATAPI commands 0xE0 and 0xE1.
- It was tested on PS3 OtherOS++ 3.55 and Linux kernel 3.5.1.
- The program uses Linux SCSI Generic driver to send ATAPI commands to BD drive.
/*- * Copyright (C) 2012 glevand <[email protected]> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <getopt.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <scsi/sg.h> #include <scsi/scsi_ioctl.h> #include <openssl/aes.h> #include <openssl/des.h> static const unsigned char iv1[AES_BLOCK_SIZE] = { 0x22, 0x26, 0x92, 0x8d, 0x44, 0x03, 0x2f, 0x43, 0x6a, 0xfd, 0x26, 0x7e, 0x74, 0x8b, 0x23, 0x93, }; static const unsigned char iv2[DES_KEY_SZ] = { 0xe8, 0x0b, 0x3f, 0x0c, 0xd6, 0x56, 0x6d, 0xd0, }; static const unsigned char iv3[AES_BLOCK_SIZE] = { 0x3b, 0xd6, 0x24, 0x02, 0x0b, 0xd3, 0xf8, 0x65, 0xe8, 0x0b, 0x3f, 0x0c, 0xd6, 0x56, 0x6d, 0xd0, }; static const unsigned char key3[AES_BLOCK_SIZE] = { 0x12, 0x6c, 0x6b, 0x59, 0x45, 0x37, 0x0e, 0xee, 0xca, 0x68, 0x26, 0x2d, 0x02, 0xdd, 0x12, 0xd2, }; static const unsigned char key4[AES_BLOCK_SIZE] = { 0xd9, 0xa2, 0x0a, 0x79, 0x66, 0x6c, 0x27, 0xd1, 0x10, 0x32, 0xac, 0xcf, 0x0d, 0x7f, 0xb5, 0x01, }; static const unsigned char key5[AES_BLOCK_SIZE] = { 0x19, 0x76, 0x6f, 0xbc, 0x77, 0xe4, 0xe7, 0x5c, 0xf4, 0x41, 0xe4, 0x8b, 0x94, 0x2c, 0x5b, 0xd9, }; static const unsigned char key6[AES_BLOCK_SIZE] = { 0x50, 0xcb, 0xa7, 0xf0, 0xc2, 0xa7, 0xc0, 0xf6, 0xf3, 0x3a, 0x21, 0x43, 0x26, 0xac, 0x4e, 0xf3, }; static const unsigned char cmd_4_14[] = { 0x1e, 0x79, 0x18, 0x8e, 0x09, 0x3b, 0xc8, 0x77, 0x95, 0xb2, 0xcf, 0x2a, 0xe7, 0xaf, 0x9b, 0xb4, 0x86, 0x80, 0x18, 0x28, 0xc2, 0xca, 0x05, 0xba, 0xd1, 0xf2, 0x78, 0xf1, 0x80, 0x1f, 0xea, 0xcb, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8d, 0xb3, 0x46, 0x93, 0x42, 0x64, 0x81, 0x60, 0x16, 0x8f, 0x51, 0xd1, 0x93, 0x76, 0x23, 0x95, }; static struct option long_opts[] = { { "device", no_argument, NULL, 'd' }, { "verbose", no_argument, NULL, 'v' }, { "rnd1", no_argument, NULL, 'r' }, { "key1", no_argument, NULL, 'k' }, { "key2", no_argument, NULL, 'l' }, { NULL, 0, NULL, 0 } }; static const char *device = "/dev/sr0"; static int verbose; static unsigned char rnd1[AES_BLOCK_SIZE] = { 0x06, 0xd7, 0x33, 0xcb, 0x22, 0x4a, 0x83, 0x56, 0xa3, 0xe8, 0x39, 0x78, 0x66, 0xe4, 0x3e, 0xc2, }; static unsigned char key1[AES_BLOCK_SIZE]; static int key1_length; static unsigned char key2[AES_BLOCK_SIZE]; static int key2_length; static int parse_hex(const char *s, unsigned char *b, int maxlen) { int len; char c; int i; len = strlen(s); if (len % 2) return (-1); for (i = 0; (i < (len / 2)) && (i < maxlen); i++) { if (!isxdigit(s[2 * i + 0]) || !isxdigit(s[2 * i + 1])) return (-1); b[i] = 0; c = tolower(s[2 * i + 0]); if (isdigit(c)) b[i] += (c - '0') * 16; else b[i] += (c - 'a' + 10) * 16; c = tolower(s[2 * i + 1]); if (isdigit(c)) b[i] += c - '0'; else b[i] += c - 'a' + 10; } return (i); } static int parse_opts(int argc, char **argv) { int c; char *endptr; while ((c = getopt_long(argc, argv, "d:vtr:s:k:l:", long_opts, NULL)) != -1) { switch (c) { case 'd': device = optarg; break; case 'v': verbose++; break; case 'r': if (parse_hex(optarg, rnd1, AES_BLOCK_SIZE) != AES_BLOCK_SIZE) { fprintf(stderr, "invalid rnd1 specified: %s\n", optarg); return (-1); } break; case 'k': key1_length = parse_hex(optarg, key1, 2 * AES_BLOCK_SIZE); if (key1_length != AES_BLOCK_SIZE) { fprintf(stderr, "invalid key1 specified: %s\n", optarg); return (-1); } break; case 'l': key2_length = parse_hex(optarg, key2, 2 * AES_BLOCK_SIZE); if (key2_length != AES_BLOCK_SIZE) { fprintf(stderr, "invalid key2 specified: %s\n", optarg); return (-1); } break; default: fprintf(stderr, "invalid option specified: %c\n", c); return (-1); break; } } if (key1_length <= 0) { fprintf(stderr, "no key1 specified\n"); return (-1); } if (key2_length <= 0) { fprintf(stderr, "no key2 specified\n"); return (-1); } return (0); } static void aes_cbc_encrypt(const unsigned char *iv, const unsigned char *key, int key_length, const unsigned char *data, int data_length, unsigned char *out) { AES_KEY aes_key; unsigned char cbc[AES_BLOCK_SIZE]; int i; AES_set_encrypt_key(key, key_length, &aes_key); memcpy(cbc, iv, AES_BLOCK_SIZE); while (data_length >= AES_BLOCK_SIZE) { for (i = 0; i < AES_BLOCK_SIZE; i++) out[i] = cbc[i] ^ data[i]; AES_encrypt(out, out, &aes_key); memcpy(cbc, out, AES_BLOCK_SIZE); data += AES_BLOCK_SIZE; out += AES_BLOCK_SIZE; data_length -= AES_BLOCK_SIZE; } } static void aes_cbc_decrypt(const unsigned char *iv, const unsigned char *key, int key_length, const unsigned char *data, int data_length, unsigned char *out) { AES_KEY aes_key; unsigned char cbc[AES_BLOCK_SIZE]; unsigned char buf[AES_BLOCK_SIZE]; int i; AES_set_decrypt_key(key, key_length, &aes_key); memcpy(cbc, iv, AES_BLOCK_SIZE); while (data_length >= AES_BLOCK_SIZE) { memcpy(buf, data, AES_BLOCK_SIZE); AES_decrypt(data, out, &aes_key); for (i = 0; i < AES_BLOCK_SIZE; i++) out[i] ^= cbc[i]; memcpy(cbc, buf, AES_BLOCK_SIZE); data += AES_BLOCK_SIZE; out += AES_BLOCK_SIZE; data_length -= AES_BLOCK_SIZE; } } static void triple_des_cbc_encrypt(const unsigned char *iv, const unsigned char *key, const unsigned char *data, int data_length, unsigned char *out) { DES_cblock kcb1; DES_cblock kcb2; DES_cblock ivcb; DES_key_schedule ks1; DES_key_schedule ks2; memcpy(kcb1, key, DES_KEY_SZ); memcpy(kcb2, key + DES_KEY_SZ, DES_KEY_SZ); memcpy(ivcb, iv, DES_KEY_SZ); DES_set_key_unchecked(&kcb1, &ks1); DES_set_key_unchecked(&kcb2, &ks2); DES_ede2_cbc_encrypt(data, out, data_length, &ks1, &ks2, &ivcb, DES_ENCRYPT); } static void triple_des_cbc_decrypt(const unsigned char *iv, const unsigned char *key, const unsigned char *data, int data_length, unsigned char *out) { DES_cblock kcb1; DES_cblock kcb2; DES_cblock ivcb; DES_key_schedule ks1; DES_key_schedule ks2; memcpy(kcb1, key, DES_KEY_SZ); memcpy(kcb2, key + DES_KEY_SZ, DES_KEY_SZ); memcpy(ivcb, iv, DES_KEY_SZ); DES_set_key_unchecked(&kcb1, &ks1); DES_set_key_unchecked(&kcb2, &ks2); DES_ede2_cbc_encrypt(data, out, data_length, &ks1, &ks2, &ivcb, DES_DECRYPT); } static unsigned char cksum(const unsigned char *data, int len) { unsigned short sum = 0; while (len--) sum += *data++; return (~sum); } static void hex_fprintf(FILE *fp, const char *buf, size_t len) { int i; if (len <= 0) return; for (i = 0; i < len; i++) { if ((i > 0) && !(i % 16)) fprintf(fp, "\n"); fprintf(fp, "%02x ", buf[i]); } fprintf(fp, "\n"); } static int test_unit_ready(const char *device, unsigned int *req_sense) { int fd; struct sg_io_hdr io_hdr; char cmd[256], sense[32]; int ret; fd = open(device, O_RDWR | O_NONBLOCK); if (fd < 0) return (-1); memset(cmd, 0, 6); memset(&io_hdr, 0, sizeof(io_hdr)); io_hdr.interface_id = 'S'; io_hdr.dxfer_direction = SG_DXFER_TO_DEV; io_hdr.timeout = 20000; io_hdr.cmdp = cmd; io_hdr.cmd_len = 6; io_hdr.dxferp = NULL; io_hdr.dxfer_len = 0; io_hdr.sbp = sense; io_hdr.mx_sb_len = sizeof(sense); ret = ioctl(fd, SG_IO, &io_hdr); if (ret) { close(fd); return (-1); } close(fd); if (io_hdr.status) { fprintf(stderr, "TEST UNIT READY failed: status %d host status %d driver status %d\n", io_hdr.status, io_hdr.host_status, io_hdr.driver_status); fprintf(stderr, "sense buffer: "); hex_fprintf(stderr, sense, 0x10); *req_sense = (sense[2] << 16) | (sense[12] << 8) | sense[13]; return (-1); } *req_sense = 0; return (0); } static int send_key(const char *device, unsigned char key_class, unsigned short param_list_length, unsigned char agid, unsigned char key_fmt, unsigned char *param_list, unsigned int *req_sense) { int fd; struct sg_io_hdr io_hdr; char cmd[256], sense[32]; int ret; fd = open(device, O_RDWR | O_NONBLOCK); if (fd < 0) return (-1); cmd[0] = 0xa3; cmd[1] = 0x00; cmd[2] = 0x00; cmd[3] = 0x00; cmd[4] = 0x00; cmd[5] = 0x00; cmd[6] = 0x00; cmd[7] = key_class; *(uint16_t *) (cmd + 8) = param_list_length; cmd[10] = ((agid & 0x3) << 6) | (key_fmt & 0x3f); cmd[11] = 0x00; if (verbose) { fprintf(stdout, "cdb: "); hex_fprintf(stdout, cmd, 12); } memset(&io_hdr, 0, sizeof(io_hdr)); io_hdr.interface_id = 'S'; io_hdr.dxfer_direction = SG_DXFER_TO_DEV; io_hdr.timeout = 20000; io_hdr.cmdp = cmd; io_hdr.cmd_len = 12; io_hdr.dxferp = param_list; io_hdr.dxfer_len = param_list_length; io_hdr.sbp = sense; io_hdr.mx_sb_len = sizeof(sense); ret = ioctl(fd, SG_IO, &io_hdr); if (ret) { close(fd); return (-1); } close(fd); if (io_hdr.status) { fprintf(stderr, "SEND KEY failed: status %d host status %d driver status %d\n", io_hdr.status, io_hdr.host_status, io_hdr.driver_status); fprintf(stderr, "sense buffer: "); hex_fprintf(stderr, sense, 0x10); *req_sense = (sense[2] << 16) | (sense[12] << 8) | sense[13]; return (-1); } *req_sense = 0; return (0); } static int report_key(const char *device, unsigned char key_class, unsigned int alloc_length, unsigned char agid, unsigned char key_fmt, unsigned char *buf, unsigned int *req_sense) { int fd; struct sg_io_hdr io_hdr; char cmd[256], sense[32]; int ret; fd = open(device, O_RDWR | O_NONBLOCK); if (fd < 0) return (-1); cmd[0] = 0xa4; cmd[1] = 0x00; cmd[2] = 0x00; cmd[3] = 0x00; cmd[4] = 0x00; cmd[5] = 0x00; cmd[6] = 0x00; cmd[7] = key_class; *(uint16_t *) (cmd + 8) = alloc_length; cmd[10] = ((agid & 0x3) << 6) | (key_fmt & 0x3f); cmd[11] = 0x00; if (verbose) { fprintf(stdout, "cdb: "); hex_fprintf(stdout, cmd, 12); } memset(&io_hdr, 0, sizeof(io_hdr)); io_hdr.interface_id = 'S'; io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.timeout = 20000; io_hdr.cmdp = cmd; io_hdr.cmd_len = 12; io_hdr.dxferp = buf; io_hdr.dxfer_len = alloc_length; io_hdr.sbp = sense; io_hdr.mx_sb_len = sizeof(sense); ret = ioctl(fd, SG_IO, &io_hdr); if (ret) { close(fd); return (-1); } close(fd); if (io_hdr.status) { fprintf(stderr, "REPORT KEY failed: status %d host status %d driver status %d\n", io_hdr.status, io_hdr.host_status, io_hdr.driver_status); fprintf(stderr, "sense buffer: "); hex_fprintf(stderr, sense, 0x10); *req_sense = (sense[2] << 16) | (sense[12] << 8) | sense[13]; return (-1); } *req_sense = 0; return (0); } static int send_e1(const char *device, unsigned char param_list_length, const unsigned char cdb[8], unsigned char *param_list, unsigned int *req_sense) { int fd; struct sg_io_hdr io_hdr; char cmd[256], sense[32]; int ret; fd = open(device, O_RDWR | O_NONBLOCK); if (fd < 0) return (-1); memset(cmd, 0, 12); cmd[0] = 0xe1; cmd[1] = 0x00; cmd[2] = param_list_length; cmd[3] = 0x00; memcpy(cmd + 4, cdb, 8); if (verbose) { fprintf(stdout, "cdb: "); hex_fprintf(stdout, cmd, 12); } memset(&io_hdr, 0, sizeof(io_hdr)); io_hdr.interface_id = 'S'; io_hdr.dxfer_direction = SG_DXFER_TO_DEV; io_hdr.timeout = 20000; io_hdr.cmdp = cmd; io_hdr.cmd_len = 12; io_hdr.dxferp = param_list; io_hdr.dxfer_len = param_list_length; io_hdr.sbp = sense; io_hdr.mx_sb_len = sizeof(sense); ret = ioctl(fd, SG_IO, &io_hdr); if (ret) { close(fd); return (-1); } close(fd); if (io_hdr.status) { fprintf(stderr, "E1 failed: status %d host status %d driver status %d\n", io_hdr.status, io_hdr.host_status, io_hdr.driver_status); fprintf(stderr, "sense buffer: "); hex_fprintf(stderr, sense, 0x10); *req_sense = (sense[2] << 16) | (sense[12] << 8) | sense[13]; return (-1); } *req_sense = 0; return (0); } static int send_e0(const char *device, unsigned int alloc_length, const unsigned char cdb[8], unsigned char *buf, unsigned int *req_sense) { int fd; struct sg_io_hdr io_hdr; char cmd[256], sense[32]; int ret; fd = open(device, O_RDWR | O_NONBLOCK); if (fd < 0) return (-1); cmd[0] = 0xe0; cmd[1] = 0x00; cmd[2] = alloc_length; cmd[3] = 0x00; memcpy(cmd + 4, cdb, 8); if (verbose) { fprintf(stdout, "cdb: "); hex_fprintf(stdout, cmd, 12); } memset(&io_hdr, 0, sizeof(io_hdr)); io_hdr.interface_id = 'S'; io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.timeout = 20000; io_hdr.cmdp = cmd; io_hdr.cmd_len = 12; io_hdr.dxferp = buf; io_hdr.dxfer_len = alloc_length; io_hdr.sbp = sense; io_hdr.mx_sb_len = sizeof(sense); ret = ioctl(fd, SG_IO, &io_hdr); if (ret) { close(fd); return (-1); } close(fd); if (io_hdr.status) { fprintf(stderr, "E0 failed: status %d host status %d driver status %d\n", io_hdr.status, io_hdr.host_status, io_hdr.driver_status); fprintf(stderr, "sense buffer: "); hex_fprintf(stderr, sense, 0x10); *req_sense = (sense[2] << 16) | (sense[12] << 8) | sense[13]; return (-1); } *req_sense = 0; return (0); } int main(int argc, char **argv) { unsigned char cdb[256]; unsigned char buf[256]; unsigned char rnd2[AES_BLOCK_SIZE]; unsigned char key7[AES_BLOCK_SIZE]; unsigned char key8[AES_BLOCK_SIZE]; unsigned int req_sense; int ret; ret = parse_opts(argc, argv); if (ret) exit(255); /* test unit ready */ if (verbose) fprintf(stdout, "=== TEST UNIT READY (0x00) ===\n"); ret = test_unit_ready(device, &req_sense); if (ret && (req_sense != 0x23a00)) exit(1); /* security check */ if (verbose) fprintf(stdout, "=== SEND KEY (0xA3) ===\n"); memset(buf, 0, 0x14); ret = send_key(device, 0xe0, 0x14, 0x0, 0x0, buf, &req_sense); if (ret) exit(1); /* establish session */ memset(buf, 0, 0x14); *(uint16_t *) buf = 0x10; memcpy(buf + 4, rnd1, sizeof(rnd1)); fprintf(stdout, "rnd1: "); hex_fprintf(stdout, rnd1, sizeof(rnd1)); if (verbose) hex_fprintf(stdout, buf, 0x14); aes_cbc_encrypt(iv1, key1, 8 * sizeof(key1), buf + 4, 0x10, buf + 4); if (verbose) hex_fprintf(stdout, buf, 0x14); /* send encrypted host random */ if (verbose) fprintf(stdout, "=== SEND KEY (0xA3) ===\n"); ret = send_key(device, 0xe0, 0x14, 0x0, 0x0, buf, &req_sense); if (ret) exit(1); /* receive encrypted host and drive randoms */ if (verbose) fprintf(stdout, "=== REPORT KEY (0xA4) ===\n"); ret = report_key(device, 0xe0, 0x24, 0x0, 0x0, buf, &req_sense); if (ret) exit(1); if (verbose) hex_fprintf(stdout, buf, 0x24); /* NB: decrypt received host and drive randoms separately */ aes_cbc_decrypt(iv1, key2, 8 * sizeof(key2), buf + 4, 0x10, buf + 4); aes_cbc_decrypt(iv1, key2, 8 * sizeof(key2), buf + 0x14, 0x10, buf + 0x14); if (verbose) hex_fprintf(stdout, buf, 0x24); /* verify received host random */ if (memcmp(rnd1, buf + 4, sizeof(rnd1))) { fprintf(stderr, "rnd1 mismatch\n"); exit(1); } memcpy(rnd2, buf + 0x14, sizeof(rnd2)); fprintf(stdout, "rnd2: "); hex_fprintf(stdout, rnd2, sizeof(rnd2)); memset(buf, 0, 0x14); *(uint16_t *) buf = 0x10; memcpy(buf + 4, rnd2, sizeof(rnd2)); if (verbose) hex_fprintf(stdout, buf, 0x14); aes_cbc_encrypt(iv1, key1, 8 * sizeof(key1), buf + 4, 0x10, buf + 4); if (verbose) hex_fprintf(stdout, buf, 0x14); /* send encrypted drive random */ if (verbose) fprintf(stdout, "=== SEND KEY (0xA3) ===\n"); ret = send_key(device, 0xe0, 0x14, 0x0, 0x2, buf, &req_sense); if (ret) exit(1); /* derive session keys from host and drive randoms */ memcpy(key7, rnd1, 8); memcpy(key7 + 8, rnd2 + 8, 8); if (verbose) hex_fprintf(stdout, key7, sizeof(key7)); aes_cbc_encrypt(iv1, key3, 8 * sizeof(key3), key7, sizeof(key7), key7); fprintf(stdout, "key7: "); hex_fprintf(stdout, key7, sizeof(key7)); memcpy(key8, rnd1 + 8, 8); memcpy(key8 + 8, rnd2, 8); if (verbose) hex_fprintf(stdout, key8, sizeof(key8)); aes_cbc_encrypt(iv1, key4, 8 * sizeof(key4), key8, sizeof(key8), key8); fprintf(stdout, "key8: "); hex_fprintf(stdout, key8, sizeof(key8)); if (verbose) fprintf(stdout, "=== UNKNOWN (0xE1) ===\n"); memset(cdb, 0, 8); cdb[6] = 0xe6; /* random byte */ cdb[7] = cksum(cdb, 7); if (verbose) hex_fprintf(stdout, cdb, 8); triple_des_cbc_encrypt(iv2, key7, cdb, 8, cdb); if (verbose) hex_fprintf(stdout, cdb, 8); memset(buf, 0, 0x54); *(uint16_t *) buf = 0x50; buf[5] = 0xee; /* random byte */ memcpy(buf + 8, cmd_4_14, sizeof(cmd_4_14)); buf[4] = cksum(buf + 5, 0x4f); if (verbose) hex_fprintf(stdout, buf, 0x54); aes_cbc_encrypt(iv3, key7, 8 * sizeof(key7), buf + 4, 0x50, buf + 4); if (verbose) hex_fprintf(stdout, buf, 0x54); ret = send_e1(device, 0x54, cdb, buf, &req_sense); if (ret) exit(1); /* establish session again */ memset(buf, 0, 0x14); *(uint16_t *) buf = 0x10; memcpy(buf + 4, rnd1, sizeof(rnd1)); fprintf(stdout, "rnd1: "); hex_fprintf(stdout, rnd1, sizeof(rnd1)); if (verbose) hex_fprintf(stdout, buf, 0x14); aes_cbc_encrypt(iv1, key5, 8 * sizeof(key5), buf + 4, 0x10, buf + 4); if (verbose) hex_fprintf(stdout, buf, 0x14); /* send encrypted host random */ if (verbose) fprintf(stdout, "=== SEND KEY (0xA3) ===\n"); ret = send_key(device, 0xe0, 0x14, 0x0, 0x1, buf, &req_sense); if (ret) exit(1); /* receive encrypted host and drive randoms */ if (verbose) fprintf(stdout, "=== REPORT KEY (0xA4) ===\n"); ret = report_key(device, 0xe0, 0x24, 0x0, 0x1, buf, &req_sense); if (ret) exit(1); if (verbose) hex_fprintf(stdout, buf, 0x24); /* NB: decrypt received host and drive randoms separately */ aes_cbc_decrypt(iv1, key6, 8 * sizeof(key6), buf + 4, 0x10, buf + 4); aes_cbc_decrypt(iv1, key6, 8 * sizeof(key6), buf + 0x14, 0x10, buf + 0x14); if (verbose) hex_fprintf(stdout, buf, 0x24); /* verify received host random */ if (memcmp(rnd1, buf + 4, sizeof(rnd1))) { fprintf(stderr, "rnd1 mismatch\n"); exit(1); } memcpy(rnd2, buf + 0x14, sizeof(rnd2)); fprintf(stdout, "rnd2: "); hex_fprintf(stdout, rnd2, sizeof(rnd2)); memset(buf, 0, 0x14); *(uint16_t *) buf = 0x10; memcpy(buf + 4, rnd2, sizeof(rnd2)); if (verbose) hex_fprintf(stdout, buf, 0x14); aes_cbc_encrypt(iv1, key5, 8 * sizeof(key5), buf + 4, 0x10, buf + 4); if (verbose) hex_fprintf(stdout, buf, 0x14); /* send encrypted drive random */ if (verbose) fprintf(stdout, "=== SEND KEY (0xA3) ===\n"); ret = send_key(device, 0xe0, 0x14, 0x0, 0x3, buf, &req_sense); if (ret) exit(1); /* derive session keys from host and drive randoms */ memcpy(key7, rnd1, 8); memcpy(key7 + 8, rnd2 + 8, 8); if (verbose) hex_fprintf(stdout, key7, sizeof(key7)); aes_cbc_encrypt(iv1, key3, 8 * sizeof(key3), key7, sizeof(key7), key7); fprintf(stdout, "key7: "); hex_fprintf(stdout, key7, sizeof(key7)); memcpy(key8, rnd1 + 8, 8); memcpy(key8 + 8, rnd2, 8); if (verbose) hex_fprintf(stdout, key8, sizeof(key8)); aes_cbc_encrypt(iv1, key4, 8 * sizeof(key4), key8, sizeof(key8), key8); fprintf(stdout, "key8: "); hex_fprintf(stdout, key8, sizeof(key8)); /* retrieve response */ if (verbose) fprintf(stdout, "=== UNKNOWN (0xE0) ===\n"); memset(cdb, 0, 8); cdb[0] = 0x4; /* random byte */ cdb[6] = 0xe7; /* random byte */ cdb[7] = cksum(cdb, 7); if (verbose) hex_fprintf(stdout, cdb, 8); triple_des_cbc_encrypt(iv2, key7, cdb, 8, cdb); if (verbose) hex_fprintf(stdout, cdb, 8); ret = send_e0(device, 0x54, cdb, buf, &req_sense); if (ret) exit(1); if (verbose) hex_fprintf(stdout, buf, 0x54); aes_cbc_decrypt(iv3, key7, 8 * sizeof(key7), buf + 4, 0x50, buf + 4); if (verbose) hex_fprintf(stdout, buf, 0x54); if (buf[4] != cksum(buf + 5, 0x4f)) { fprintf(stderr, "checksum mismatch\n"); exit(1); } fprintf(stdout, "version: "); hex_fprintf(stdout, buf + 6, 8); exit(0); }