| /* |
| * Copyright (c) 2020, Mediatek Inc. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #pragma GCC diagnostic ignored "-Wall" |
| #pragma GCC diagnostic ignored "-Wextra" |
| #pragma GCC diagnostic ignored "-Wsign-compare" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/types.h> |
| #include <dirent.h> |
| #include <endian.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <stdint.h> |
| #include <errno.h> |
| #include <time.h> |
| #include <stdbool.h> |
| |
| #include "ufs.h" |
| #include "ufs_cmds.h" |
| #include "options.h" |
| #include "ufs_rpmb.h" |
| #include "ioctl.h" |
| #include "hmac_sha2.h" |
| #include "scsi_bsg_util.h" |
| |
| enum rpmb_op_type { |
| RPMB_WRITE_KEY = 0x01, |
| RPMB_READ_CNT = 0x02, |
| RPMB_WRITE = 0x03, |
| RPMB_READ = 0x04, |
| RPMB_READ_RESP = 0x05, |
| RPMB_SEC_CONF_WRITE = 0x06, |
| RPMB_SEC_CONF_READ = 0x07, |
| |
| }; |
| |
| /* description of the sense key values */ |
| static const char *const rpmb_res_txt[] = { |
| "Success", |
| "General failure", |
| "Authentication failure", |
| "Counter failure", |
| "Address failure", |
| "Write failure", |
| "Read failure", |
| "Authentication Key not yet programmed", |
| "Secure Write Protect Configuration Block access failure", |
| "Invalid Secure Write Protect Block Configuration parameter", |
| "Secure Write Protection not applicable" |
| }; |
| |
| #define RESP_KEY_PROG 0x100 |
| #define RESP_COUNTER_READ 0x200 |
| #define RESP_DATA_WRITE 0x300 |
| #define RESP_DATA_READ 0x400 |
| #define RESP_CONF_BLOCK_WRITE 0x600 |
| #define RESP_CONF_BLOCK_READ 0x700 |
| |
| #define RPMB_KEY_SIZE 32 |
| #define RPMB_MAC_SIZE 32 |
| #define RPMB_NONCE_SIZE 16 |
| #define RPMB_DATA_SIZE 256 |
| |
| #define UFS_BSG_PATH "/dev/ufs-bsg" |
| |
| #define DEFAULT_RPMB_NUM_BLOCKS 64 |
| |
| #define MAX_ADDRESS 0xFFFF |
| #define SECOND_BYTE_MASK 0xFF00 |
| |
| #define MAX_RETRY 3 |
| |
| static unsigned char key[RPMB_KEY_SIZE]; |
| |
| #define CUC(x) ((const unsigned char *)(x)) |
| |
| extern int do_read_desc(int fd, struct ufs_bsg_request *bsg_req, |
| struct ufs_bsg_reply *bsg_rsp, __u8 idn, __u8 index, |
| __u16 desc_buf_len, __u8 *data_buf); |
| |
| static void hmac_update_frm(hmac_sha256_ctx *ctx, struct rpmb_frame *frm) |
| { |
| hmac_sha256_update(ctx, CUC(frm->data), 256); |
| hmac_sha256_update(ctx, CUC(frm->nonce), 16); |
| hmac_sha256_update(ctx, CUC(&frm->write_counter), 4); |
| hmac_sha256_update(ctx, CUC(&frm->addr), 2); |
| hmac_sha256_update(ctx, CUC(&frm->block_count), 2); |
| hmac_sha256_update(ctx, CUC(&frm->result), 2); |
| hmac_sha256_update(ctx, CUC(&frm->req_resp), 2); |
| } |
| |
| static int rpmb_calc_hmac_sha256(struct rpmb_frame *frames, ssize_t blocks_cnt, |
| const unsigned char key[], __u32 key_size, |
| unsigned char mac[], __u32 mac_size) |
| { |
| hmac_sha256_ctx ctx; |
| __u32 i; |
| |
| hmac_sha256_init(&ctx, key, key_size); |
| |
| for (i = 0; i < blocks_cnt; i++) |
| hmac_update_frm(&ctx, (frames + i)); |
| |
| hmac_sha256_final(&ctx, mac, mac_size); |
| |
| return 0; |
| } |
| |
| static void print_operation_error(__u16 result) |
| { |
| if (result <= 0xA) |
| printf("\n %s\n", rpmb_res_txt[result]); |
| else |
| printf("\n Unsupported RPMB Operation Error %x\n", result); |
| } |
| |
| static int do_rpmb_op(int fd, struct rpmb_frame *frame_in, __u32 in_cnt, |
| struct rpmb_frame *frame_out, __u32 out_cnt, __u8 region, |
| __u8 sg_type) |
| { |
| int ret = -EINVAL; |
| int try_again; |
| __u16 req_resp = 0; |
| |
| if (!frame_in || !frame_out || !in_cnt || !out_cnt) { |
| print_error("Wrong rpmb parameters"); |
| goto out; |
| } |
| for (try_again = 0; try_again < MAX_RETRY; try_again++) { |
| ret = scsi_security_out(fd, frame_in, in_cnt, region, sg_type); |
| if (!ret) |
| break; |
| if (try_again < MAX_RETRY - 1) |
| WRITE_LOG("SO failed: %d\n", try_again); |
| else |
| print_error("SO 1st RPMB cmd failed"); |
| } |
| |
| req_resp = be16toh(frame_in->req_resp & SECOND_BYTE_MASK); |
| if ((req_resp == RPMB_WRITE) || |
| (req_resp == RPMB_WRITE_KEY) || |
| (req_resp == RPMB_SEC_CONF_WRITE)) { |
| memset(&frame_in[0], 0, sizeof(frame_in[0])); |
| req_resp = (req_resp & SECOND_BYTE_MASK) | RPMB_READ_RESP; |
| frame_in[0].req_resp = htobe16(req_resp); |
| for (try_again = 0; try_again < MAX_RETRY; try_again++) { |
| ret = scsi_security_out(fd, &frame_in[0], 1, |
| region, sg_type); |
| if (!ret) |
| break; |
| if (try_again < MAX_RETRY - 1) |
| WRITE_LOG("SO 2 failed: %d\n", try_again); |
| else |
| print_error("SO 2nd RPMB cmd failed"); |
| } |
| } |
| for (try_again = 0; try_again < MAX_RETRY; try_again++) { |
| ret = scsi_security_in(fd, frame_out, out_cnt, region, sg_type); |
| if (!ret) { |
| WRITE_LOG("Result Response addr %d , write count %d\n", |
| be16toh(frame_out->addr), |
| be32toh(frame_out->write_counter)); |
| break; |
| } |
| if (try_again < MAX_RETRY - 1) |
| WRITE_LOG("SI failed: %d\n", try_again); |
| else |
| print_error("SI RPMB cmd failed"); |
| } |
| out: |
| return ret; |
| } |
| |
| static int do_key(int fd, const unsigned char *key, __u8 region, __u8 sg_type) |
| { |
| int ret = INVALID; |
| struct rpmb_frame frame_in = { 0 }; |
| struct rpmb_frame frame_out = { 0 }; |
| |
| frame_in.req_resp = htobe16(RPMB_WRITE_KEY); |
| |
| if (key == NULL) { |
| WRITE_LOG0("key is NULL"); |
| goto out; |
| } |
| memcpy(frame_in.key_mac, key, sizeof(frame_in.key_mac)); |
| WRITE_LOG("Start : %s\n", __func__); |
| ret = do_rpmb_op(fd, &frame_in, 1, &frame_out, 1, region, sg_type); |
| |
| if (!ret) { |
| if (frame_out.result != 0) { |
| print_operation_error(be16toh(frame_out.result)); |
| goto out; |
| } else |
| printf("RPMB key is programmed\n"); |
| } |
| out: |
| return ret; |
| } |
| |
| static int do_read_counter(int fd, __u32 *cnt, __u8 region, __u8 sg_type, |
| bool prn_cnt) |
| { |
| int ret; |
| struct rpmb_frame frame_in = { 0 }; |
| struct rpmb_frame frame_out = { 0 }; |
| |
| WRITE_LOG("Start : %s %d\n", __func__, region); |
| frame_in.req_resp = htobe16(RPMB_READ_CNT); |
| ret = do_rpmb_op(fd, &frame_in, 1, &frame_out, 1, region, sg_type); |
| |
| if (!ret) { |
| if (frame_out.result != 0) { |
| print_operation_error(be16toh(frame_out.result)); |
| } else { |
| if (prn_cnt) |
| printf("RPMB write counter = %u\n", |
| be32toh(frame_out.write_counter)); |
| *cnt = be32toh(frame_out.write_counter); |
| } |
| } |
| return ret; |
| } |
| |
| static int do_read_rpmb(int fd, int out_fd, unsigned char *key, |
| int start_addr, int num_blocks, __u8 region, __u8 sg_type) |
| { |
| int ret = ERROR; |
| int i; |
| ssize_t write_size; |
| __u8 max_num_blocks; |
| __u8 num_read_blocks = 0; |
| struct rpmb_frame frame_in = { 0 }; |
| struct rpmb_frame *frames_out = NULL; |
| struct rpmb_frame *last_frame; |
| struct ufs_bsg_request bsg_req = { 0 }; |
| struct ufs_bsg_reply bsg_rsp = { 0 }; |
| int ufs_bsg_fd = INVALID; |
| __u8 data_buf[QUERY_DESC_GEOMETRY_MAX_SIZE] = { 0 }; |
| |
| WRITE_LOG("Start : %s , address %d , num_blocks %d\n", __func__, |
| start_addr, num_blocks); |
| |
| ufs_bsg_fd = open(UFS_BSG_PATH, O_RDWR); |
| if (ufs_bsg_fd != INVALID) { |
| ret = do_read_desc(ufs_bsg_fd, &bsg_req, &bsg_rsp, |
| QUERY_DESC_IDN_GEOMETRY, 0, |
| QUERY_DESC_GEOMETRY_MAX_SIZE, data_buf); |
| } |
| |
| if (ret) { |
| /* |
| * Could not read geometry descriptor, max block set |
| * DEFAULT_RPMB_NUM_BLOCKS); |
| */ |
| print_warn("Cannot get bRPMB_ReadWriteSize"); |
| max_num_blocks = DEFAULT_RPMB_NUM_BLOCKS; |
| } else { |
| max_num_blocks = data_buf[0x17]; |
| WRITE_LOG("max_num_blocks : %d\n", max_num_blocks); |
| } |
| |
| if (num_blocks > max_num_blocks) |
| num_read_blocks = max_num_blocks; |
| else |
| num_read_blocks = num_blocks; |
| while (num_blocks > 0) { |
| if (start_addr > MAX_ADDRESS) { |
| print_error("Max available address is reached"); |
| goto out; |
| } |
| |
| frames_out = (struct rpmb_frame *)calloc(num_read_blocks, |
| sizeof(struct rpmb_frame)); |
| if (!frames_out) { |
| print_error("Cannot allocate %d RPMB frames", |
| num_blocks); |
| goto out; |
| } |
| frame_in.req_resp = htobe16(RPMB_READ); |
| frame_in.addr = htobe16(start_addr); |
| frame_in.block_count = htobe16(num_read_blocks); |
| ret = do_rpmb_op(fd, &frame_in, 1, frames_out, |
| num_read_blocks, region, sg_type); |
| |
| if (ret != 0) { |
| print_error("RPMB operation is failed in addr %d ", |
| start_addr); |
| goto out; |
| } |
| if (frames_out[0].result != 0) { |
| print_operation_error(be16toh(frames_out[0].result)); |
| ret = -EINVAL; |
| goto out; |
| } |
| last_frame = &frames_out[num_read_blocks - 1]; |
| /* In case an user get the key ,verify the hash */ |
| if (key != NULL) { |
| __u8 mac[RPMB_MAC_SIZE]; |
| |
| rpmb_calc_hmac_sha256(frames_out, num_read_blocks, |
| key, RPMB_KEY_SIZE, |
| mac, RPMB_MAC_SIZE); |
| /* |
| * Compare calculated MAC and MAC from last frame |
| * Note the mac much only in case we read 1 block , |
| * otherwise the mac field is not much, in all frame , |
| * include the last one |
| */ |
| if (memcmp(mac, last_frame->key_mac, sizeof(mac))) |
| print_warn("RPMB MAC mismatch mac"); |
| } |
| for (i = 0; i < num_read_blocks; i++) { |
| write_size = write(out_fd, &(frames_out[i].data), |
| RPMB_DATA_SIZE); |
| if (write_size != RPMB_DATA_SIZE) { |
| WRITE_LOG("%s: failed in write sz=%d errno=%d", |
| __func__, (int)write_size, errno); |
| ret = INVALID; |
| goto out; |
| } |
| } |
| WRITE_LOG("num_blocks : %d start_addr %d num_read_blocks %d\n", |
| num_blocks, start_addr, num_read_blocks); |
| num_blocks = num_blocks - num_read_blocks; |
| start_addr = start_addr + num_read_blocks; |
| |
| if (num_blocks > max_num_blocks) |
| num_read_blocks = max_num_blocks; |
| else |
| num_read_blocks = num_blocks; |
| |
| if (frames_out) { |
| free(frames_out); |
| frames_out = NULL; |
| } |
| } |
| out: |
| if (frames_out) |
| free(frames_out); |
| if (ufs_bsg_fd != INVALID) |
| close(ufs_bsg_fd); |
| |
| return ret; |
| } |
| |
| static int do_write_rpmb(int fd, const unsigned char *key, int input_fd, |
| __u32 cnt, __u16 start_addr, __u16 num_blocks, |
| __u8 region, __u8 sg_type) |
| { |
| int ret = ERROR; |
| unsigned char mac[RPMB_MAC_SIZE]; |
| struct rpmb_frame *frames_in = NULL; |
| struct rpmb_frame frame_out = { 0 }; |
| ssize_t read_size = 0; |
| __u8 max_num_blocks; |
| __u8 num_write_blocks = 0; |
| int i = 0; |
| int j = 0; |
| struct ufs_bsg_request bsg_req = { 0 }; |
| struct ufs_bsg_reply bsg_rsp = { 0 }; |
| int ufs_bsg_fd; |
| __u8 data_buf[QUERY_DESC_GEOMETRY_MAX_SIZE] = { 0 }; |
| |
| WRITE_LOG("Start : %s\n", __func__); |
| |
| ufs_bsg_fd = open(UFS_BSG_PATH, O_RDWR); |
| if (ufs_bsg_fd != INVALID) { |
| ret = do_read_desc(ufs_bsg_fd, &bsg_req, &bsg_rsp, |
| QUERY_DESC_IDN_GEOMETRY, 0, |
| QUERY_DESC_GEOMETRY_MAX_SIZE, data_buf); |
| } |
| |
| if (ret) { |
| print_warn("Cannot get bRPMB_ReadWriteSize"); |
| max_num_blocks = DEFAULT_RPMB_NUM_BLOCKS; |
| } else { |
| /*bRPMB_ReadWriteSize e.g 0x40 * 256 = 16K*/ |
| max_num_blocks = data_buf[0x17]; |
| if (max_num_blocks <= 0) |
| max_num_blocks = 1; |
| ret = OK; |
| } |
| |
| if (num_blocks > max_num_blocks) |
| num_write_blocks = max_num_blocks; |
| else |
| num_write_blocks = num_blocks; |
| WRITE_LOG("max_num_blocks : %d num_block %d, cnt %d start_addr %d\n", |
| max_num_blocks, num_write_blocks, cnt, start_addr); |
| while (num_blocks > 0) { |
| if (start_addr > MAX_ADDRESS) { |
| print_error("Max available address is reached"); |
| goto out; |
| } |
| frames_in = (struct rpmb_frame *)calloc(num_write_blocks, |
| sizeof(struct rpmb_frame)); |
| if (!frames_in) { |
| print_error("Cannot allocate %d RPMB frames", |
| num_write_blocks); |
| ret = -ENOMEM; |
| goto out; |
| } |
| for (i = 0; i < num_write_blocks; i++) { |
| frames_in[i].req_resp = htobe16(RPMB_WRITE); |
| frames_in[i].addr = htobe16(start_addr); |
| frames_in[i].block_count = htobe16(num_write_blocks); |
| frames_in[i].write_counter = htobe32(cnt); |
| read_size = read(input_fd, frames_in[i].data, |
| RPMB_DATA_SIZE); |
| if (read_size != RPMB_DATA_SIZE) { |
| WRITE_LOG("%s: failed in read size=%d errno=%d", |
| __func__, (int)read_size, errno); |
| ret = EINVAL; |
| goto out; |
| } |
| } |
| |
| rpmb_calc_hmac_sha256(frames_in, num_write_blocks, |
| key, RPMB_KEY_SIZE, mac, RPMB_MAC_SIZE); |
| memcpy(frames_in[num_write_blocks - 1].key_mac, |
| mac, RPMB_MAC_SIZE); |
| |
| ret = do_rpmb_op(fd, frames_in, num_write_blocks, |
| &frame_out, 1, region, sg_type); |
| if (ret != 0) |
| goto out; |
| |
| /* Check RPMB response */ |
| if (frame_out.result != 0) { |
| print_operation_error(be16toh(frame_out.result)); |
| ret = -EINVAL; |
| goto out; |
| } |
| |
| WRITE_LOG("num_blocks : %d start_addr %d num_write_blocks %d ," |
| "iter %d,cnt %d\n", |
| num_blocks, start_addr, num_write_blocks, j, cnt); |
| num_blocks = num_blocks - num_write_blocks; |
| start_addr = start_addr + num_write_blocks; |
| if (num_blocks > max_num_blocks) |
| num_write_blocks = max_num_blocks; |
| else |
| num_write_blocks = num_blocks; |
| j++; |
| cnt++; |
| if (frames_in) { |
| free(frames_in); |
| frames_in = NULL; |
| } |
| |
| } |
| out: |
| if (ufs_bsg_fd != INVALID) |
| close(ufs_bsg_fd); |
| if (frames_in) |
| free(frames_in); |
| return ret; |
| } |
| |
| static int do_read_conf_block(int fd, const unsigned char *key, __u8 lun, |
| int output_fd, __u8 sg_type) |
| { |
| int ret = ERROR; |
| ssize_t write_size; |
| struct rpmb_frame frame_in = { 0 }; |
| struct rpmb_frame frame_out = { 0 }; |
| |
| WRITE_LOG("Start : %s\n", __func__); |
| |
| frame_in.req_resp = htobe16(RPMB_SEC_CONF_READ); |
| frame_in.data[0] = lun; |
| |
| ret = do_rpmb_op(fd, &frame_in, 1, &frame_out, 1, 0, sg_type); |
| if (ret != 0) { |
| print_error("Fail to read Secure Write Config Block"); |
| goto out; |
| } |
| if (frame_out.result != 0) { |
| print_operation_error(be16toh(frame_out.result)); |
| goto out; |
| } |
| |
| if (key != NULL) { |
| __u8 mac[RPMB_MAC_SIZE]; |
| |
| rpmb_calc_hmac_sha256(&frame_out, 1, key, |
| RPMB_KEY_SIZE, mac, RPMB_MAC_SIZE); |
| /* Compare calculated MAC and MAC from last frame |
| * Note the mac much only in case we read 1 block , otherwise the mac |
| * field is not much, in all frame ,include the last one */ |
| if (memcmp(mac, frame_out.key_mac, sizeof(mac))) |
| print_error("RPMB MAC mismatch mac"); |
| } |
| write_size = write(output_fd, frame_out.data, RPMB_DATA_SIZE); |
| if (write_size != RPMB_DATA_SIZE) { |
| WRITE_LOG("%s: failed in write size=%d errno=%d", |
| __func__, (int)write_size, errno); |
| ret = ERROR; |
| } else |
| printf("Secure Write Protect Config Block was read\n"); |
| out: |
| return ret; |
| } |
| |
| static int do_write_conf_block(int fd, const unsigned char *key, int input_fd, |
| __u32 cnt, __u8 sg_type) |
| { |
| int ret = INVALID; |
| __u8 mac[RPMB_MAC_SIZE]; |
| struct rpmb_frame frame_in = { 0 }; |
| struct rpmb_frame frame_out = { 0 }; |
| ssize_t read_size = 0; |
| |
| WRITE_LOG("Start : %s\n", __func__); |
| |
| frame_in.req_resp = htobe16(RPMB_SEC_CONF_WRITE); |
| frame_in.block_count = htobe16(1); |
| frame_in.write_counter = htobe32(cnt); |
| read_size = read(input_fd, frame_in.data, RPMB_DATA_SIZE); |
| if (read_size != RPMB_DATA_SIZE) { |
| WRITE_LOG("%s: failed in read size=%d errno=%d", |
| __func__, (int)read_size, errno); |
| ret = INVALID; |
| goto out; |
| } |
| |
| rpmb_calc_hmac_sha256(&frame_in, 1, |
| key, RPMB_KEY_SIZE, mac, RPMB_MAC_SIZE); |
| memcpy(frame_in.key_mac, mac, RPMB_MAC_SIZE); |
| |
| ret = do_rpmb_op(fd, &frame_in, 1, &frame_out, 1, 0, sg_type); |
| if (ret != 0) { |
| print_error("Fail to write Secure Write Config Block"); |
| goto out; |
| } |
| |
| /* Check RPMB response */ |
| if (frame_out.result != 0) { |
| print_operation_error(be16toh(frame_out.result)); |
| ret = -EINVAL; |
| } else |
| printf("Secure Write Protect Config Block was written\n"); |
| |
| out: |
| return ret; |
| } |
| |
| static unsigned char *get_auth_key(char *key_path) |
| { |
| unsigned char *pkey = NULL; |
| int key_fd = INVALID; |
| ssize_t read_size; |
| |
| if (key_path == NULL) |
| return NULL; |
| |
| key_fd = open(key_path, O_RDONLY); |
| if (key_fd < 0) { |
| perror("Key file open"); |
| } else { |
| read_size = read(key_fd, key, RPMB_KEY_SIZE); |
| if (read_size < RPMB_KEY_SIZE) { |
| print_error("Key must be %d bytes length,was read %d", |
| RPMB_KEY_SIZE, read_size); |
| } else |
| pkey = key; |
| } |
| |
| if (key_fd != INVALID) |
| close(key_fd); |
| return pkey; |
| } |
| |
| int do_rpmb(struct tool_options *opt) |
| { |
| int rc = INVALID; |
| int fd; |
| int output_fd = INVALID; |
| unsigned char *key_ptr = NULL; |
| __u32 cnt = 0; |
| __u8 lun; |
| |
| fd = open(opt->path, O_RDWR | O_SYNC); |
| if (fd < 0) { |
| perror("open"); |
| return ERROR; |
| } |
| |
| switch (opt->idn) { |
| case AUTHENTICATION_KEY: |
| key_ptr = get_auth_key(opt->keypath); |
| if (key_ptr == NULL) |
| goto out; |
| rc = do_key(fd, key_ptr, opt->region, opt->sg_type); |
| break; |
| case READ_WRITE_COUNTER: |
| rc = do_read_counter(fd, &cnt, opt->region, opt->sg_type, true); |
| break; |
| case READ_RPMB: |
| output_fd = open(opt->data, O_WRONLY | O_CREAT | O_SYNC, |
| S_IRUSR | S_IWUSR); |
| if (output_fd < 0) { |
| perror("Output file open"); |
| goto out; |
| } |
| if (opt->keypath[0] != 0) { |
| key_ptr = get_auth_key(opt->keypath); |
| if (key_ptr == NULL) |
| goto out; |
| } |
| |
| rc = do_read_rpmb(fd, output_fd, key_ptr, opt->start_block, |
| opt->num_block, opt->region, opt->sg_type); |
| if (!rc) |
| printf("Finish to read RPMB data\n"); |
| break; |
| case WRITE_RPMB: |
| key_ptr = get_auth_key(opt->keypath); |
| if (key_ptr == NULL) |
| goto out; |
| |
| output_fd = open(opt->data, O_RDONLY | O_SYNC); |
| if (output_fd < 0) { |
| perror("Input file open"); |
| goto out; |
| } |
| rc = do_read_counter(fd, &cnt, opt->region, opt->sg_type, |
| false); |
| if (rc) |
| goto out; |
| rc = do_write_rpmb(fd, key_ptr, output_fd, cnt, |
| opt->start_block, opt->num_block, |
| opt->region, opt->sg_type); |
| if (!rc) |
| printf("Finish to write RPMB data\n"); |
| break; |
| case READ_SEC_RPMB_CONF_BLOCK: |
| lun = opt->lun; |
| if (opt->keypath[0] != 0) { |
| key_ptr = get_auth_key(opt->keypath); |
| if (key_ptr == NULL) |
| goto out; |
| } |
| |
| output_fd = open(opt->data, O_WRONLY | O_CREAT | O_SYNC, |
| S_IRUSR | S_IWUSR); |
| if (output_fd < 0) { |
| perror("Output file open"); |
| goto out; |
| } |
| |
| rc = do_read_conf_block(fd, key_ptr, lun, output_fd, |
| opt->sg_type); |
| break; |
| case WRITE_SEC_RPMB_CONF_BLOCK: |
| key_ptr = get_auth_key(opt->keypath); |
| if (key_ptr == NULL) |
| goto out; |
| |
| output_fd = open(opt->data, O_RDONLY | O_SYNC); |
| if (output_fd < 0) { |
| perror("Input file open"); |
| goto out; |
| } |
| rc = do_read_counter(fd, &cnt, 0, opt->sg_type, false); |
| if (rc) |
| goto out; |
| |
| rc = do_write_conf_block(fd, key_ptr, output_fd, cnt, |
| opt->sg_type); |
| break; |
| default: |
| print_error("Unsupported RPMB cmd %d", opt->idn); |
| break; |
| } |
| out: |
| if (output_fd != INVALID) |
| close(output_fd); |
| close(fd); |
| return rc; |
| } |
| |
| void rpmb_help(char *tool_name) |
| { |
| printf("\n RPMB command usage:\n"); |
| printf("\n\t%s rpmb [-t] <rpmb cmd idn> [-p] <path to device>" |
| " -k <path to device> -l <lun> -d <output/input file.\n", |
| tool_name); |
| printf("\n\t-t\t RPMB cmd type idn\n" |
| "\t\t\t0:\tKey provision\n" |
| "\t\t\t1:\tRead Write counter\n" |
| "\t\t\t2:\tRead RPMB data\n" |
| "\t\t\t3:\tWrite RPMB data\n" |
| "\t\t\t4:\tSecure Write Protect Configuration Block Write\n" |
| "\t\t\t5:\tSecure Write Protect Configuration Block Read\n"); |
| |
| printf("\n\t-s\t RPMB start address (default value is 0)\n"); |
| printf("\n\t-n\t number of RPMB read/write blocks (default value is 1)\n"); |
| printf("\n\t-p\t device path (RPMB LUN)\n"); |
| printf("\n\t-k\t path to RPMB key, " |
| "the key path must pass to the tool in case of writing to RPMB,\n" |
| "\t\t in case of reading from RPMB ,the key may pass," |
| " in case we want to validate the MAC\n" |
| "\t\t in case we want to validate the hash value\n"); |
| printf("\n\t-l\t lun number(byte) using as parameter " |
| "for Secure Write Config Read\n"); |
| printf("\n\t-w\t path to data file\n"); |
| printf("\n\t-m\t RPMB region.\n"); |
| printf("\n\t-g\t sg struct ver - 0: SG_IO_VER4 (default), 1: SG_IO_VER3\n"); |
| printf("\n\tExample - Read 16MB of data from RPMB LUN started " |
| "from address 0 to output file\n" |
| "\t\t %s rpmb -t 2 -p /dev/0:0:0:49476 -s 0 -n 65536 -w output_file\n", |
| tool_name); |
| printf("\n\tExample - Write RPMB key\n" |
| "\t\t %s rpmb -t 0 -p /dev/0:0:0:49476 -k key_file\n", |
| tool_name); |
| printf("\n\tExample - Write RPMB key to region 2\n" |
| "\t\t %s rpmb -t 0 -m 2 -p /dev/0:0:0:49476 -k key_file\n", |
| tool_name); |
| printf("\n\tExample - Write Secure Write Config block\n" |
| "\t\t The input file is Secure Write Config block filled " |
| "according to the spec\n" |
| "\t\t %s rpmb -t 4 -p /dev/0:0:0:49476 -w input_file\n", |
| tool_name); |
| printf("\n\tExample - Read Secure Write Config block\n" |
| "\t\t After the command successfully finished , " |
| "the output file will contains\n" |
| "\t\t Secure Write Config block of lun 1\n" |
| "\t\t %s rpmb -t 5 -p /dev/0:0:0:49476 -l 1 -d output_file\n", |
| tool_name); |
| } |