Home | History | Annotate | Download | only in nanoapp_sign
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <stdbool.h>
     18 #include <string.h>
     19 #include <stdint.h>
     20 #include <stdlib.h>
     21 #include <stdio.h>
     22 
     23 #include <nanohub/nanohub.h>
     24 #include <nanohub/nanoapp.h>
     25 #include <nanohub/sha2.h>
     26 #include <nanohub/rsa.h>
     27 
     28 static FILE* urandom = NULL;
     29 
     30 #if defined(__APPLE__) || defined(_WIN32)
     31 inline uint32_t bswap32 (uint32_t x) {
     32     uint32_t out = 0;
     33     for (int i=0; i < 4; ++i, x >>= 8)
     34         out = (out << 8) | (x & 0xFF);
     35     return out;
     36 }
     37 
     38 #define htobe32(x)  bswap32((x))
     39 #define htole32(x)  ((uint32_t)(x))
     40 #define be32toh(x)  bswap32((x))
     41 #define le32toh(x)  ((uint32_t)(x))
     42 #else
     43 #include <endian.h>
     44 #endif
     45 
     46 //read exactly one hex-encoded byte from a file, skipping all the fluff
     47 static int getHexEncodedByte(uint8_t *buf, uint32_t *ppos, uint32_t size)
     48 {
     49     int c, i;
     50     uint32_t pos = *ppos;
     51     uint8_t val = 0;
     52 
     53     //for first byte
     54     for (i = 0; i < 2; i++) {
     55         val <<= 4;
     56         while(1) {
     57             if (pos == size)
     58                 return -1;
     59             c = buf[pos++];
     60             *ppos = pos;
     61 
     62             if (c >= '0' && c <= '9')
     63                 val += c - '0';
     64             else if (c >= 'a' && c <= 'f')
     65                 val += c + 10 - 'a';
     66             else if (c >= 'A' && c <= 'F')
     67                 val += c + 10 - 'A';
     68             else if (i)                         //disallow everything between first and second nibble
     69                 return -1;
     70             else if (c > 'f' && c <= 'z')	//disallow nonalpha data
     71                 return -1;
     72             else if (c > 'F' && c <= 'Z')	//disallow nonalpha data
     73                 return -1;
     74             else
     75                 continue;
     76             break;
     77         }
     78     }
     79 
     80     return val;
     81 }
     82 
     83 //provide a random number for which the following property is true ((ret & 0xFF000000) && (ret & 0xFF0000) && (ret & 0xFF00) && (ret & 0xFF))
     84 static uint32_t rand32_no_zero_bytes(void)
     85 {
     86     uint32_t i, v;
     87     uint8_t byte;
     88 
     89     if (!urandom) {
     90         urandom = fopen("/dev/urandom", "rb");
     91         if (!urandom) {
     92             fprintf(stderr, "Failed to open /dev/urandom. Cannot procceed!\n");
     93             exit(-2);
     94         }
     95     }
     96 
     97     for (v = 0, i = 0; i < 4; i++) {
     98         do {
     99             if (!fread(&byte, 1, 1, urandom)) {
    100                 fprintf(stderr, "Failed to read /dev/urandom. Cannot procceed!\n");
    101                 exit(-3);
    102             }
    103         } while (!byte);
    104 
    105         v = (v << 8) | byte;
    106     }
    107 
    108     return v;
    109 }
    110 
    111 static void cleanup(void)
    112 {
    113     if (urandom)
    114         fclose(urandom);
    115 }
    116 
    117 struct RsaData {
    118     uint32_t num[RSA_LIMBS];
    119     uint32_t exponent[RSA_LIMBS];
    120     uint32_t modulus[RSA_LIMBS];
    121     struct RsaState state;
    122 };
    123 
    124 static bool validateSignature(uint8_t *sigPack, struct RsaData *rsa, bool verbose, uint32_t *refHash, bool preset)
    125 {
    126     int i;
    127     const uint32_t *rsaResult;
    128     const uint32_t *le32SigPack = (const uint32_t*)sigPack;
    129     //convert to native uint32_t; ignore possible alignment issues
    130     for (i = 0; i < RSA_LIMBS; i++)
    131         rsa->num[i] = le32toh(le32SigPack[i]);
    132     //update the user
    133     if (verbose)
    134         printHashRev(stderr, "RSA cyphertext", rsa->num, RSA_LIMBS);
    135     if (!preset)
    136         memcpy(rsa->modulus, sigPack + RSA_BYTES, RSA_BYTES);
    137 
    138     //do rsa op
    139     rsaResult = rsaPubOp(&rsa->state, rsa->num, rsa->modulus);
    140 
    141     //update the user
    142     if (verbose)
    143         printHashRev(stderr, "RSA plaintext", rsaResult, RSA_LIMBS);
    144 
    145     //verify padding is appropriate and valid
    146     if ((rsaResult[RSA_LIMBS - 1] & 0xffff0000) != 0x00020000) {
    147         fprintf(stderr, "Padding header is invalid\n");
    148         return false;
    149     }
    150 
    151     //verify first two bytes of padding
    152     if (!(rsaResult[RSA_LIMBS - 1] & 0xff00) || !(rsaResult[RSA_LIMBS - 1] & 0xff)) {
    153         fprintf(stderr, "Padding bytes 0..1 are invalid\n");
    154         return false;
    155     }
    156 
    157     //verify last 3 bytes of padding and the zero terminator
    158     if (!(rsaResult[8] & 0xff000000) || !(rsaResult[8] & 0xff0000) || !(rsaResult[8] & 0xff00) || (rsaResult[8] & 0xff)) {
    159         fprintf(stderr, "Padding last bytes & terminator invalid\n");
    160         return false;
    161     }
    162 
    163     //verify middle padding bytes
    164     for (i = 9; i < RSA_LIMBS - 1; i++) {
    165         if (!(rsaResult[i] & 0xff000000) || !(rsaResult[i] & 0xff0000) || !(rsaResult[i] & 0xff00) || !(rsaResult[i] & 0xff)) {
    166             fprintf(stderr, "Padding word %d invalid\n", i);
    167             return false;
    168         }
    169     }
    170     if (verbose) {
    171         printHash(stderr, "Recovered hash ", rsaResult, SHA2_HASH_WORDS);
    172         printHash(stderr, "Calculated hash", refHash, SHA2_HASH_WORDS);
    173     }
    174 
    175     if (!preset) {
    176         // we're doing full verification, with key extracted from signature pack
    177         if (memcmp(rsaResult, refHash, SHA2_HASH_SIZE)) {
    178             fprintf(stderr, "hash mismatch\n");
    179             return false;
    180         }
    181     } else {
    182         // we just decode the signature with key passed as an argument
    183         // in this case we return recovered hash
    184         memcpy(refHash, rsaResult, SHA2_HASH_SIZE);
    185     }
    186     return true;
    187 }
    188 
    189 #define SIGNATURE_BLOCK_SIZE    (2 * RSA_BYTES)
    190 
    191 static int handleConvertKey(uint8_t **pbuf, uint32_t bufUsed, FILE *out, struct RsaData *rsa)
    192 {
    193     bool  haveNonzero = false;
    194     uint8_t *buf = *pbuf;
    195     int i, c;
    196     uint32_t pos = 0;
    197     int ret;
    198 
    199     for (i = 0; i < (int)RSA_BYTES; i++) {
    200 
    201         //get a byte, skipping all zeroes (openssl likes to prepend one at times)
    202         do {
    203             c = getHexEncodedByte(buf, &pos, bufUsed);
    204         } while (c == 0 && !haveNonzero);
    205         haveNonzero = true;
    206         if (c < 0) {
    207             fprintf(stderr, "Invalid text RSA input data\n");
    208             return 2;
    209         }
    210 
    211         buf[i] = c;
    212     }
    213 
    214     // change form BE to native; ignore alignment
    215     uint32_t *be32Buf = (uint32_t*)buf;
    216     for (i = 0; i < RSA_LIMBS; i++)
    217         rsa->num[RSA_LIMBS - i - 1] = be32toh(be32Buf[i]);
    218 
    219     //output in our binary format (little-endian)
    220     ret = fwrite(rsa->num, 1, RSA_BYTES, out) == RSA_BYTES ? 0 : 2;
    221     fprintf(stderr, "Conversion status: %d\n", ret);
    222 
    223     return ret;
    224 }
    225 
    226 static int handleVerify(uint8_t **pbuf, uint32_t bufUsed, struct RsaData *rsa, bool verbose, bool bareData)
    227 {
    228     struct Sha2state shaState;
    229     uint8_t *buf = *pbuf;
    230     uint32_t masterPubKey[RSA_LIMBS];
    231 
    232     memcpy(masterPubKey, rsa->modulus, RSA_BYTES);
    233     if (!bareData) {
    234         struct ImageHeader *image = (struct ImageHeader *)buf;
    235         struct AppSecSignHdr *secHdr = (struct AppSecSignHdr *)&image[1];
    236         int block = 0;
    237         uint8_t *sigPack;
    238         bool trusted = false;
    239         bool lastTrusted = false;
    240         int sigData;
    241 
    242         if (bufUsed < (sizeof(*image) + sizeof(*secHdr))) {
    243             fprintf(stderr, "Invalid signature header: file is too short\n");
    244             return 2;
    245         }
    246 
    247         if (verbose)
    248             fprintf(stderr, "Original Data len=%" PRIu32 " b; file size=%" PRIu32 " b; diff=%" PRIu32 " b\n",
    249                     secHdr->appDataLen, bufUsed, bufUsed - secHdr->appDataLen);
    250 
    251         if (!(image->aosp.flags & NANOAPP_SIGNED_FLAG)) {
    252             fprintf(stderr, "image is not marked as signed, can not verify\n");
    253             return 2;
    254         }
    255         sigData = bufUsed - (secHdr->appDataLen + sizeof(*image) + sizeof(*secHdr));
    256         if (sigData <= 0 || (sigData % SIGNATURE_BLOCK_SIZE) != 0) {
    257             fprintf(stderr, "Invalid signature header: data size mismatch\n");
    258             return 2;
    259         }
    260 
    261         sha2init(&shaState);
    262         sha2processBytes(&shaState, buf, bufUsed - sigData);
    263         int nSig = sigData / SIGNATURE_BLOCK_SIZE;
    264         sigPack = buf + bufUsed - sigData;
    265         for (block = 0; block < nSig; ++block) {
    266             if (!validateSignature(sigPack, rsa, verbose, (uint32_t*)sha2finish(&shaState), false)) {
    267                 fprintf(stderr, "Signature verification failed: signature block #%d\n", block);
    268                 return 2;
    269             }
    270             if (memcmp(masterPubKey, rsa->modulus, RSA_BYTES) == 0) {
    271                 fprintf(stderr, "Key in block %d is trusted\n", block);
    272                 trusted = true;
    273                 lastTrusted = true;
    274             } else {
    275                 lastTrusted = false;
    276             }
    277             sha2init(&shaState);
    278             sha2processBytes(&shaState, sigPack+RSA_BYTES, RSA_BYTES);
    279             sigPack += SIGNATURE_BLOCK_SIZE;
    280         }
    281         if (trusted && !lastTrusted) {
    282             fprintf(stderr, "Trusted key is not the last in key sequence\n");
    283         }
    284         return trusted ? 0 : 2;
    285     } else {
    286         uint8_t *sigPack = buf + bufUsed - SIGNATURE_BLOCK_SIZE;
    287         uint32_t *hash;
    288         // can not do signature chains in bare mode
    289         if (bufUsed > SIGNATURE_BLOCK_SIZE) {
    290             sha2init(&shaState);
    291             sha2processBytes(&shaState, buf, bufUsed - SIGNATURE_BLOCK_SIZE);
    292             hash = (uint32_t*)sha2finish(&shaState);
    293             printHash(stderr, "File hash", hash, SHA2_HASH_WORDS);
    294             if (verbose)
    295                 printHashRev(stderr, "File PubKey", (uint32_t *)(sigPack + RSA_BYTES), RSA_LIMBS);
    296             if (!validateSignature(sigPack, rsa, verbose, hash, false)) {
    297                 fprintf(stderr, "Signature verification failed on raw data\n");
    298                 return 2;
    299             }
    300             if (memcmp(masterPubKey, sigPack + RSA_BYTES, RSA_BYTES) == 0) {
    301                 fprintf(stderr, "Signature verification passed and the key is trusted\n");
    302                 return 0;
    303             } else {
    304                 fprintf(stderr, "Signature verification passed but the key is not trusted\n");
    305                 return 2;
    306             }
    307         } else {
    308             fprintf(stderr, "Not enough raw data to extract signature from\n");
    309             return 2;
    310         }
    311     }
    312 
    313     return 0;
    314 }
    315 
    316 static int handleSign(uint8_t **pbuf, uint32_t bufUsed, FILE *out, struct RsaData *rsa, bool verbose, bool bareData)
    317 {
    318     struct Sha2state shaState;
    319     uint8_t *buf = *pbuf;
    320     uint32_t i;
    321     const uint32_t *hash;
    322     const uint32_t *rsaResult;
    323     int ret;
    324 
    325     if (!bareData) {
    326         struct ImageHeader *image = (struct ImageHeader *)buf;
    327         struct AppSecSignHdr *secHdr = (struct AppSecSignHdr *)&image[1];
    328         uint32_t grow = sizeof(*secHdr);
    329         if (!(image->aosp.flags & NANOAPP_SIGNED_FLAG)) {
    330             // this is the 1st signature in the chain; inject header, set flag
    331             buf = reallocOrDie(buf, bufUsed + grow);
    332             *pbuf = buf;
    333             image = (struct ImageHeader *)buf;
    334             secHdr = (struct AppSecSignHdr *)&image[1];
    335 
    336             fprintf(stderr, "Generating signature header\n");
    337             image->aosp.flags |= NANOAPP_SIGNED_FLAG;
    338             memmove((uint8_t*)&image[1] + grow, &image[1], bufUsed - sizeof(*image));
    339             secHdr->appDataLen = bufUsed - sizeof(*image);
    340             bufUsed += grow;
    341             fprintf(stderr, "Rehashing file\n");
    342             sha2init(&shaState);
    343             sha2processBytes(&shaState, buf, bufUsed);
    344         } else {
    345             int sigSz = bufUsed - sizeof(*image) - sizeof(*secHdr) - secHdr->appDataLen;
    346             int numSigs = sigSz / SIGNATURE_BLOCK_SIZE;
    347             if ((numSigs * (int)SIGNATURE_BLOCK_SIZE) != sigSz) {
    348                 fprintf(stderr, "Invalid signature block(s) detected\n");
    349                 return 2;
    350             } else {
    351                 fprintf(stderr, "Found %d appended signature(s)\n", numSigs);
    352                 // generating SHA256 of the last PubKey in chain
    353                 fprintf(stderr, "Hashing last signature's PubKey\n");
    354                 sha2init(&shaState);
    355                 sha2processBytes(&shaState, buf + bufUsed- RSA_BYTES, RSA_BYTES);
    356             }
    357         }
    358     } else {
    359         fprintf(stderr, "Signing raw data\n");
    360         sha2init(&shaState);
    361         sha2processBytes(&shaState, buf, bufUsed);
    362     }
    363 
    364     //update the user on the progress
    365     hash = sha2finish(&shaState);
    366     if (verbose)
    367         printHash(stderr, "SHA2 hash", hash, SHA2_HASH_WORDS);
    368 
    369     memcpy(rsa->num, hash, SHA2_HASH_SIZE);
    370 
    371     i = SHA2_HASH_WORDS;
    372     //write padding
    373     rsa->num[i++] = rand32_no_zero_bytes() << 8; //low byte here must be zero as per padding spec
    374     for (;i < RSA_LIMBS - 1; i++)
    375         rsa->num[i] = rand32_no_zero_bytes();
    376     rsa->num[i] = (rand32_no_zero_bytes() >> 16) | 0x00020000; //as per padding spec
    377 
    378     //update the user
    379     if (verbose)
    380         printHashRev(stderr, "RSA plaintext", rsa->num, RSA_LIMBS);
    381 
    382     //do the RSA thing
    383     fprintf(stderr, "Retriculating splines...");
    384     rsaResult = rsaPrivOp(&rsa->state, rsa->num, rsa->exponent, rsa->modulus);
    385     fprintf(stderr, "DONE\n");
    386 
    387     //update the user
    388     if (verbose)
    389         printHashRev(stderr, "RSA cyphertext", rsaResult, RSA_LIMBS);
    390 
    391     // output in a format that our microcontroller will be able to digest easily & directly
    392     // (an array of bytes representing little-endian 32-bit words)
    393     fwrite(buf, 1, bufUsed, out);
    394     fwrite(rsaResult, 1, sizeof(uint32_t[RSA_LIMBS]), out);
    395     ret = (fwrite(rsa->modulus, 1, RSA_BYTES, out) == RSA_BYTES) ? 0 : 2;
    396 
    397     fprintf(stderr, "Status: %s (%d)\n", ret == 0 ? "success" : "failed", ret);
    398     return ret;
    399 
    400 }
    401 
    402 static void fatalUsage(const char *name, const char *msg, const char *arg)
    403 {
    404     if (msg && arg)
    405         fprintf(stderr, "Error: %s: %s\n\n", msg, arg);
    406     else if (msg)
    407         fprintf(stderr, "Error: %s\n\n", msg);
    408 
    409     fprintf(stderr, "USAGE: %s [-v] [-e <pvt key>] [-m <pub key>] [-t] [-s] [-b] <input file> [<output file>]\n"
    410                     "       -v : be verbose\n"
    411                     "       -b : generate binary key from text file created by OpenSSL\n"
    412                     "       -s : sign post-processed file\n"
    413                     "       -t : verify signature of signed post-processed file\n"
    414                     "       -e : RSA binary private key\n"
    415                     "       -m : RSA binary public key\n"
    416                     "       -r : do not parse headers, do not generate headers (with -t, -s)\n"
    417                     , name);
    418     exit(1);
    419 }
    420 
    421 int main(int argc, char **argv)
    422 {
    423     uint32_t bufUsed = 0;
    424     uint8_t *buf = NULL;
    425     int ret = -1;
    426     const char **strArg = NULL;
    427     const char *appName = argv[0];
    428     const char *posArg[2] = { NULL };
    429     uint32_t posArgCnt = 0;
    430     FILE *out = NULL;
    431     const char *prev = NULL;
    432     bool verbose = false;
    433     bool sign = false;
    434     bool verify = false;
    435     bool txt2bin = false;
    436     bool bareData = false;
    437     const char *keyPvtFile = NULL;
    438     const char *keyPubFile = NULL;
    439     int multi = 0;
    440     struct RsaData rsa;
    441     struct ImageHeader *image;
    442 
    443     //it might not matter, but we still like to try to cleanup after ourselves
    444     (void)atexit(cleanup);
    445 
    446     for (int i = 1; i < argc; i++) {
    447         if (argv[i][0] == '-') {
    448             prev = argv[i];
    449             if (!strcmp(argv[i], "-v"))
    450                 verbose = true;
    451             else if (!strcmp(argv[i], "-s"))
    452                 sign = true;
    453             else if (!strcmp(argv[i], "-t"))
    454                 verify = true;
    455             else if (!strcmp(argv[i], "-b"))
    456                 txt2bin = true;
    457             else if (!strcmp(argv[i], "-e"))
    458                 strArg = &keyPvtFile;
    459             else if (!strcmp(argv[i], "-m"))
    460                 strArg = &keyPubFile;
    461             else if (!strcmp(argv[i], "-r"))
    462                 bareData = true;
    463             else
    464                 fatalUsage(appName, "unknown argument", argv[i]);
    465         } else {
    466             if (strArg) {
    467                     *strArg = argv[i];
    468                 strArg = NULL;
    469             } else {
    470                 if (posArgCnt < 2)
    471                     posArg[posArgCnt++] = argv[i];
    472                 else
    473                     fatalUsage(appName, "too many positional arguments", argv[i]);
    474             }
    475             prev = 0;
    476         }
    477     }
    478     if (prev)
    479         fatalUsage(appName, "missing argument after", prev);
    480 
    481     if (!posArgCnt)
    482         fatalUsage(appName, "missing input file name", NULL);
    483 
    484     if (sign)
    485         multi++;
    486     if (verify)
    487         multi++;
    488     if (txt2bin)
    489         multi++;
    490 
    491     if (multi != 1)
    492         fatalUsage(appName, "select either -s, -t, or -b", NULL);
    493 
    494     memset(&rsa, 0, sizeof(rsa));
    495 
    496     if (sign && !(keyPvtFile && keyPubFile))
    497         fatalUsage(appName, "We need both PUB (-m) and PVT (-e) keys for signing", NULL);
    498 
    499     if (verify && (!keyPubFile || keyPvtFile))
    500         fatalUsage(appName, "We only need PUB (-m)  key for signature checking", NULL);
    501 
    502     if (keyPvtFile) {
    503         if (!readFile(rsa.exponent, sizeof(rsa.exponent), keyPvtFile))
    504             fatalUsage(appName, "Can't read PVT key from", keyPvtFile);
    505 #ifdef DEBUG_KEYS
    506         else if (verbose)
    507             printHashRev(stderr, "RSA exponent", rsa.exponent, RSA_LIMBS);
    508 #endif
    509     }
    510 
    511     if (keyPubFile) {
    512         if (!readFile(rsa.modulus, sizeof(rsa.modulus), keyPubFile))
    513             fatalUsage(appName, "Can't read PUB key from", keyPubFile);
    514         else if (verbose)
    515             printHashRev(stderr, "RSA modulus", rsa.modulus, RSA_LIMBS);
    516     }
    517 
    518     buf = loadFile(posArg[0], &bufUsed);
    519     fprintf(stderr, "Read %" PRIu32 " bytes\n", bufUsed);
    520 
    521     image = (struct ImageHeader *)buf;
    522     if (!bareData && !txt2bin) {
    523         if (bufUsed >= sizeof(*image) &&
    524             image->aosp.header_version == 1 &&
    525             image->aosp.magic == NANOAPP_AOSP_MAGIC &&
    526             image->layout.magic == GOOGLE_LAYOUT_MAGIC) {
    527             fprintf(stderr, "Found AOSP header\n");
    528         } else {
    529             fprintf(stderr, "Unknown binary format\n");
    530             return 2;
    531         }
    532     }
    533 
    534     if (!posArg[1])
    535         out = stdout;
    536     else
    537         out = fopen(posArg[1], "w");
    538     if (!out)
    539         fatalUsage(appName, "failed to create/open output file", posArg[1]);
    540 
    541     if (sign)
    542         ret = handleSign(&buf, bufUsed, out, &rsa, verbose, bareData);
    543     else if (verify)
    544         ret = handleVerify(&buf, bufUsed, &rsa, verbose, bareData);
    545     else if (txt2bin)
    546         ret = handleConvertKey(&buf, bufUsed, out, &rsa);
    547 
    548     free(buf);
    549     fclose(out);
    550     return ret;
    551 }
    552