407 lines
11 KiB
C
407 lines
11 KiB
C
/*
|
|
* Copyright (C) 2021 Amlogic, Inc. All rights reserved.
|
|
*
|
|
* All information contained herein is Amlogic confidential.
|
|
*
|
|
* This software is provided to you pursuant to Software License
|
|
* Agreement (SLA) with Amlogic Inc ("Amlogic"). This software may be
|
|
* used only in accordance with the terms of this agreement.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification is strictly prohibited without prior written permission
|
|
* from Amlogic.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "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 COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS 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 <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <openssl/evp.h>
|
|
#include <string.h>
|
|
#include <getopt.h>
|
|
|
|
#define DEBUG (0)
|
|
|
|
#define BLOCK_SIZE (16)
|
|
#define LBN_SIZE (512)
|
|
#define LBN_SHIFT (9)
|
|
#define IV_FACTOR_BASE_ADDR (0x800)
|
|
|
|
uint8_t key_bin[16] = { 0x00 };
|
|
|
|
uint32_t key_bin_len = sizeof(key_bin);
|
|
|
|
#if DEBUG
|
|
static void dump_bufs(const char *name, const uint8_t *buf, int32_t len)
|
|
{
|
|
char line_buf[128] = {0x0};
|
|
char *line_buf_ptr = line_buf;
|
|
uint32_t i = 0;
|
|
const uint8_t *ptr = buf;
|
|
const int32_t sz = len;
|
|
|
|
printf("\n========= Start dump %s(%d)\n", name, sz);
|
|
while (len - 16 >= 0) {
|
|
printf("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
|
ptr[0], ptr[i+1], ptr[i+2], ptr[i+3], ptr[4], ptr[i+5], ptr[i+6], ptr[i+7],
|
|
ptr[i+8], ptr[i+9], ptr[i+10], ptr[i+11], ptr[i+12], ptr[i+13], ptr[i+14], ptr[i+15]);
|
|
ptr += 16;
|
|
len -= 16;
|
|
}
|
|
|
|
if (len * 3 <= sizeof(line_buf)) {
|
|
while (len) {
|
|
snprintf(line_buf_ptr, sizeof(line_buf), "%02x ", *ptr);
|
|
line_buf_ptr += 3; /*"xx "*/
|
|
len--;
|
|
}
|
|
if (strlen(line_buf))
|
|
printf("%s\n", line_buf);
|
|
printf("========= End dump %s(%d)\n", name, sz);
|
|
} else {
|
|
printf("remaining length exceeds linebuf length\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Get logical block number for encryption.
|
|
* Size of each lbn is 512 bytes. */
|
|
static inline uint64_t get_lbn(uint64_t offset)
|
|
{
|
|
return (offset >> LBN_SHIFT) + IV_FACTOR_BASE_ADDR;
|
|
}
|
|
|
|
uint32_t calc_md5(uint8_t *in, uint32_t in_sz, uint8_t *md5_hash, uint32_t *md5_hash_len)
|
|
{
|
|
const EVP_MD *md = EVP_md5();
|
|
|
|
if (!md || !in || !in_sz || !md5_hash || !md5_hash_len) {
|
|
printf("%s:%d: Invalid input\n", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
if (!(*md5_hash_len)) {
|
|
printf("%s:%d: No output space\n", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
if (EVP_Digest(in, in_sz, md5_hash, md5_hash_len, md, NULL) != 1) {
|
|
printf("%s:%d: EVP_Digest failed\n", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t calc_aes_128_cbc(uint8_t *in, uint32_t in_sz,
|
|
uint8_t *key, uint32_t key_sz,
|
|
uint8_t *iv, uint32_t iv_sz,
|
|
uint8_t *out, int32_t *out_sz)
|
|
{
|
|
EVP_CIPHER_CTX *ctx = NULL;
|
|
int32_t len;
|
|
int32_t ret = 0;
|
|
|
|
if (!in || !key || !iv || !out || !out_sz) {
|
|
printf("%s:%d: Invalid input\n", __func__, __LINE__);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (!(*out_sz)) {
|
|
printf("%s:%d: No output space\n", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
if (!ctx) {
|
|
printf("%s:%d: failed to allocate ctx\n", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
if (EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) {
|
|
printf("%s:%d: EVP_EncryptInit_ex failed\n", __func__, __LINE__);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
/* set no-padding */
|
|
EVP_CIPHER_CTX_set_padding(ctx, 0);
|
|
|
|
if (EVP_EncryptUpdate(ctx, out, out_sz, in, in_sz) != 1) {
|
|
printf("%s:%d: EVP_EncryptUpdate failed\n", __func__, __LINE__);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (EVP_EncryptFinal_ex(ctx, out + *out_sz, &len) != 1) {
|
|
printf("%s:%d: EVP_EncryptFinal_ex failed\n", __func__, __LINE__);
|
|
ret = -1;
|
|
}
|
|
out:
|
|
if (ctx)
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
return ret;
|
|
}
|
|
|
|
static int32_t get_enc_iv(uint64_t offset, uint8_t *md5, uint32_t md5_sz, uint8_t *out, int32_t *out_sz)
|
|
{
|
|
uint64_t lbn = get_lbn(offset);
|
|
uint8_t iv[BLOCK_SIZE] = {};
|
|
uint8_t iv_iv[BLOCK_SIZE] = {0xff};
|
|
int32_t ret = 0;
|
|
|
|
if (!out || !out_sz || !*out_sz) {
|
|
printf("%s:%d: Invalid input.\n", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
iv[0] = lbn & 0xFF;
|
|
iv[1] = (lbn >> 8) & 0xFF;
|
|
iv[2] = (lbn >> 16) & 0xFF;
|
|
iv[3] = (lbn >> 24) & 0xFF;
|
|
|
|
ret = calc_aes_128_cbc(iv, sizeof(iv),
|
|
md5, md5_sz, /* key */
|
|
iv_iv, sizeof(iv_iv), /* iv */
|
|
out, out_sz);
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t get_file_sz(FILE *fp) {
|
|
uint32_t sz = 0;
|
|
uint32_t cur = ftell(fp);;
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
sz = ftell(fp);
|
|
fseek(fp, cur, SEEK_SET);
|
|
return sz;
|
|
}
|
|
|
|
void print_usage(void)
|
|
{
|
|
printf("Usage: ./enc_part [options] (%s)\n", VERSION);
|
|
printf("Options:\n");
|
|
printf(" -i, --in\t\t\tInput file to be encrypted.\n");
|
|
printf(" -o, --out\t\t\tEncrypted output file.\n");
|
|
printf(" -k, --key\t\t\tEncryption key in hexdump format, e.g. aabb1122\n");
|
|
}
|
|
|
|
static int hex2bin(const char *hex, void *bin, size_t binlen)
|
|
{
|
|
int i, c, n1, n2, hexlen, k;
|
|
|
|
if (!hex || !bin || !binlen) {
|
|
printf("%s:%d: invalid input.\n", __func__, __LINE__);
|
|
return 0;
|
|
}
|
|
|
|
hexlen = strnlen(hex, 64);
|
|
if (hexlen > binlen * 2) {
|
|
printf("%s:%d: input overflow(%d, %zu)\n", __func__, __LINE__, hexlen, binlen);
|
|
return 0;
|
|
}
|
|
|
|
k = 0;
|
|
n1 = -1;
|
|
n2 = -1;
|
|
for (i = 0; i < hexlen; i++) {
|
|
n2 = n1;
|
|
c = hex[i];
|
|
if (c >= '0' && c <= '9') {
|
|
n1 = c - '0';
|
|
} else if (c >= 'a' && c <= 'f') {
|
|
n1 = c - 'a' + 10;
|
|
} else if (c >= 'A' && c <= 'F') {
|
|
n1 = c - 'A' + 10;
|
|
} else if (c == ' ') {
|
|
n1 = -1;
|
|
continue;
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
if (n1 >= 0 && n2 >= 0) {
|
|
((uint8_t *)bin)[k] = (n2 << 4) | n1;
|
|
n1 = -1;
|
|
k++;
|
|
}
|
|
}
|
|
return k;
|
|
}
|
|
|
|
static void dump_params(const char *in, const char *out,
|
|
const char *key) {
|
|
printf("****************enc_part(%s)*********************\n", VERSION);
|
|
printf("in file :\t%s\n", in);
|
|
printf("out file:\t%s\n", out);
|
|
printf("key:\t\t%s\n", key);
|
|
printf("******************************************\n");
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
int32_t opt, long_index;
|
|
FILE *in = NULL, *out = NULL;
|
|
const char* in_file = NULL;
|
|
uint32_t in_sz = 0;
|
|
const char* out_file = NULL;
|
|
const char* key = NULL;
|
|
|
|
int32_t remaining, read_sz, ret, retVal = 0;
|
|
uint8_t *buf = NULL, *o_buf = NULL;
|
|
uint8_t page_iv[BLOCK_SIZE] = {};
|
|
int32_t page_iv_sz = BLOCK_SIZE;
|
|
uint64_t offset = 0;
|
|
int32_t wr_sz;
|
|
uint32_t padding = 0;
|
|
uint32_t temp_sz = 0;
|
|
uint32_t remain_of_blk = 0;
|
|
|
|
uint8_t md5[16];
|
|
uint32_t md5_sz = sizeof(md5);
|
|
|
|
if (argc < 2) {
|
|
print_usage();
|
|
return -1;
|
|
}
|
|
|
|
struct option long_options[] = {
|
|
{ "in", required_argument, 0, 'i'},
|
|
{ "out", required_argument, 0, 'o'},
|
|
{ "key", required_argument, 0, 'k'},
|
|
};
|
|
|
|
while ((opt = getopt_long(argc, argv,"i:o:k:",
|
|
long_options, &long_index )) != -1) {
|
|
switch (opt) {
|
|
case 'i' :
|
|
in_file = optarg;
|
|
break;
|
|
case 'o' :
|
|
out_file = optarg;
|
|
break;
|
|
case 'k' :
|
|
key = optarg;
|
|
break;
|
|
default:
|
|
print_usage();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (!in_file || !out_file) {
|
|
print_usage();
|
|
return -1;
|
|
}
|
|
|
|
in = fopen(in_file, "rb");
|
|
if (!in) {
|
|
printf("failed to open file: %s\n", in_file);
|
|
goto out;
|
|
}
|
|
in_sz = get_file_sz(in);
|
|
|
|
/* Try to open file in place (in_file == out_file) */
|
|
out = fopen(out_file, "r+b");
|
|
if (!out) {
|
|
/* Try to open file as a new one */
|
|
out = fopen(out_file, "w+b");
|
|
if (!out) {
|
|
printf("failed to open file: %s\n", out_file);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
buf = (uint8_t*)malloc(LBN_SIZE);
|
|
if (!buf) {
|
|
printf("failed to malloc buffer(%u)\n", LBN_SIZE);
|
|
goto out;
|
|
}
|
|
o_buf = (uint8_t*)malloc(LBN_SIZE);
|
|
if (!o_buf) {
|
|
printf("failed to malloc buffer(%u)\n", LBN_SIZE);
|
|
goto out;
|
|
}
|
|
|
|
remaining = in_sz;
|
|
key_bin_len = hex2bin(key, key_bin, key_bin_len);
|
|
|
|
/* partition key hash */
|
|
calc_md5(key_bin, key_bin_len, md5, &md5_sz);
|
|
|
|
dump_params(in_file, out_file, key);
|
|
|
|
while (remaining > 0) {
|
|
/* Read in a lbn if it is possible */
|
|
read_sz = (remaining >= LBN_SIZE)? LBN_SIZE: remaining;
|
|
ret = fread(buf, sizeof(uint8_t), read_sz, in);
|
|
if (ret != read_sz) {
|
|
printf("%s:%d: Error to read in file\n", __func__, __LINE__);
|
|
retVal = -1;
|
|
goto out;
|
|
}
|
|
remain_of_blk = read_sz & (BLOCK_SIZE - 1);
|
|
if (remain_of_blk) {
|
|
/* Since LBN_SIZE is multiple of BLOCK_SIZE, entering
|
|
* this case means it is the last read */
|
|
padding = BLOCK_SIZE - remain_of_blk;
|
|
temp_sz = read_sz + padding;
|
|
}
|
|
/* update iv */
|
|
page_iv_sz = BLOCK_SIZE;
|
|
ret = get_enc_iv(offset, md5, md5_sz, page_iv, &page_iv_sz);
|
|
if (ret < 0) {
|
|
printf("%s:%d: Error to get enc iv\n", __func__, __LINE__);
|
|
retVal = -1;
|
|
goto out;
|
|
}
|
|
if (remain_of_blk) {
|
|
wr_sz = temp_sz;
|
|
ret = calc_aes_128_cbc(buf, temp_sz, /* input */
|
|
key_bin, key_bin_len, /* key */
|
|
page_iv, page_iv_sz, /* iv */
|
|
o_buf, &wr_sz); /* output */
|
|
} else {
|
|
wr_sz = read_sz;
|
|
ret = calc_aes_128_cbc(buf, read_sz, /* input*/
|
|
key_bin, key_bin_len, /* key */
|
|
page_iv, page_iv_sz, /* iv */
|
|
o_buf, &wr_sz); /* output */
|
|
}
|
|
if (ret < 0) {
|
|
printf("%s:%d: Error to calc aes 128 cbc\n", __func__, __LINE__);
|
|
retVal = -1;
|
|
goto out;
|
|
}
|
|
|
|
ret = fwrite(o_buf, sizeof(uint8_t), read_sz, out);
|
|
if (ret != read_sz) {
|
|
printf("Error to write output file\n");
|
|
retVal = -1;
|
|
goto out;
|
|
}
|
|
|
|
offset += read_sz;
|
|
remaining -= read_sz;
|
|
}
|
|
out:
|
|
if (in) fclose(in);
|
|
if (out) fclose(out);
|
|
if (buf) free(buf);
|
|
if (o_buf) free(o_buf);
|
|
|
|
return retVal;
|
|
}
|