578 lines
20 KiB
C
578 lines
20 KiB
C
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <nanohub/aes.h>
|
|
#include <nanohub/rsa.h>
|
|
#include <nanohub/sha2.h>
|
|
|
|
#include <appSec.h>
|
|
#include <bl.h>
|
|
#include <heap.h>
|
|
#include <seos.h>
|
|
|
|
#define APP_HDR_SIZE (sizeof(struct ImageHeader))
|
|
#define APP_HDR_MAX_SIZE (sizeof(struct ImageHeader) + sizeof(struct AppSecSignHdr) + sizeof(struct AppSecEncrHdr))
|
|
#define APP_DATA_CHUNK_SIZE (AES_BLOCK_WORDS * sizeof(uint32_t)) //data blocks are this size
|
|
#define APP_SIG_SIZE RSA_BYTES
|
|
|
|
// verify block is SHA placed in integral number of encryption blocks (for SHA256 and AES256 happens to be exactly 2 AES blocks)
|
|
#define APP_VERIFY_BLOCK_SIZE ((SHA2_HASH_SIZE + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE
|
|
|
|
#define APP_SEC_SIG_ALIGN APP_DATA_CHUNK_SIZE
|
|
#define APP_SEC_ENCR_ALIGN APP_DATA_CHUNK_SIZE
|
|
|
|
#define STATE_INIT 0 // nothing gotten yet
|
|
#define STATE_RXING_HEADERS 1 // variable size headers (min APP_HDR_SIZE, max APP_HDR_MAX_SIZE)
|
|
#define STATE_RXING_DATA 2 // each data block is AES_BLOCK_WORDS 32-bit words (for AES reasons)
|
|
#define STATE_RXING_SIG_HASH 3 // each is RSA_BYTES bytes
|
|
#define STATE_RXING_SIG_PUBKEY 4 // each is RSA_BYTES bytes
|
|
#define STATE_VERIFY 5 // decryption of ciphertext done; now decrypting and verifying the encrypted plaintext SHA2
|
|
#define STATE_DONE 6 // all is finished and well
|
|
#define STATE_BAD 7 // unrecoverable badness has happened. this will *NOT* fix itself. It is now ok to give up, start over, cry, or pray to your favourite deity for help
|
|
#define STATE_MAX 8 // total number of states
|
|
|
|
//#define DEBUG_FSM
|
|
|
|
struct AppSecState {
|
|
union { //we save some memory by reusing this space.
|
|
struct {
|
|
struct AesCbcContext cbc;
|
|
struct Sha2state sha;
|
|
struct Sha2state cbcSha;
|
|
};
|
|
struct {
|
|
struct RsaState rsa;
|
|
uint32_t rsaState1, rsaState2, rsaStep;
|
|
};
|
|
};
|
|
uint32_t rsaTmp[RSA_WORDS];
|
|
uint32_t lastHash[SHA2_HASH_WORDS];
|
|
|
|
AppSecWriteCbk writeCbk;
|
|
AppSecPubKeyFindCbk pubKeyFindCbk;
|
|
AppSecGetAesKeyCbk aesKeyAccessCbk;
|
|
|
|
union {
|
|
union { //make the compiler work to make sure we have enough space
|
|
uint8_t placeholderAppHdr[APP_HDR_MAX_SIZE];
|
|
uint8_t placeholderDataChunk[APP_DATA_CHUNK_SIZE];
|
|
uint8_t placeholderSigChunk[APP_SIG_SIZE];
|
|
uint8_t placeholderAesKey[AES_KEY_WORDS * sizeof(uint32_t)];
|
|
};
|
|
uint8_t dataBytes[0]; //we actually use these two for access
|
|
uint32_t dataWords[0];
|
|
};
|
|
|
|
uint32_t signedBytesIn;
|
|
uint32_t encryptedBytesIn;
|
|
uint32_t signedBytesOut;
|
|
uint32_t encryptedBytesOut;
|
|
|
|
uint16_t haveBytes; //in dataBytes...
|
|
uint16_t chunkSize;
|
|
uint8_t curState;
|
|
uint8_t needSig :1;
|
|
uint8_t haveSig :1;
|
|
uint8_t haveEncr :1;
|
|
uint8_t haveTrustedKey :1;
|
|
uint8_t doingRsa :1;
|
|
};
|
|
|
|
static void limitChunkSize(struct AppSecState *state)
|
|
{
|
|
if (state->haveSig && state->chunkSize > state->signedBytesIn)
|
|
state->chunkSize = state->signedBytesIn;
|
|
if (state->haveEncr && state->chunkSize > state->encryptedBytesIn)
|
|
state->chunkSize = state->signedBytesIn;
|
|
}
|
|
|
|
static void appSecSetCurState(struct AppSecState *state, uint32_t curState)
|
|
{
|
|
const static uint16_t chunkSize[STATE_MAX] = {
|
|
[STATE_RXING_HEADERS] = APP_HDR_SIZE,
|
|
[STATE_RXING_DATA] = APP_DATA_CHUNK_SIZE,
|
|
[STATE_VERIFY] = APP_VERIFY_BLOCK_SIZE,
|
|
[STATE_RXING_SIG_HASH] = APP_SIG_SIZE,
|
|
[STATE_RXING_SIG_PUBKEY] = APP_SIG_SIZE,
|
|
};
|
|
if (curState >= STATE_MAX)
|
|
curState = STATE_BAD;
|
|
if (curState != state->curState || curState == STATE_INIT) {
|
|
#ifdef DEBUG_FSM
|
|
osLog(LOG_INFO, "%s: oldState=%" PRIu8
|
|
"; new state=%" PRIu32
|
|
"; old chunk size=%" PRIu16
|
|
"; new chunk size=%" PRIu16
|
|
"; have bytes=%" PRIu16
|
|
"\n",
|
|
__func__, state->curState, curState,
|
|
state->chunkSize, chunkSize[curState],
|
|
state->haveBytes);
|
|
#endif
|
|
state->curState = curState;
|
|
state->chunkSize = chunkSize[curState];
|
|
}
|
|
}
|
|
|
|
static inline uint32_t appSecGetCurState(const struct AppSecState *state)
|
|
{
|
|
return state->curState;
|
|
}
|
|
|
|
//init/deinit
|
|
struct AppSecState *appSecInit(AppSecWriteCbk writeCbk, AppSecPubKeyFindCbk pubKeyFindCbk, AppSecGetAesKeyCbk aesKeyAccessCbk, bool mandateSigning)
|
|
{
|
|
struct AppSecState *state = heapAlloc(sizeof(struct AppSecState));
|
|
|
|
if (!state)
|
|
return NULL;
|
|
|
|
memset(state, 0, sizeof(struct AppSecState));
|
|
|
|
state->writeCbk = writeCbk;
|
|
state->pubKeyFindCbk = pubKeyFindCbk;
|
|
state->aesKeyAccessCbk = aesKeyAccessCbk;
|
|
appSecSetCurState(state, STATE_INIT);
|
|
if (mandateSigning)
|
|
state->needSig = 1;
|
|
|
|
return state;
|
|
}
|
|
|
|
void appSecDeinit(struct AppSecState *state)
|
|
{
|
|
heapFree(state);
|
|
}
|
|
|
|
//if needed, decrypt and hash incoming data
|
|
static AppSecErr appSecBlockRx(struct AppSecState *state)
|
|
{
|
|
//if signatures are on, hash it
|
|
if (state->haveSig) {
|
|
|
|
//make sure we do not get too much data & account for the data we got
|
|
if (state->haveBytes > state->signedBytesIn)
|
|
return APP_SEC_TOO_MUCH_DATA;
|
|
state->signedBytesIn -= state->haveBytes;
|
|
|
|
//make sure we do not produce too much data (discard padding) & make sure we account for it
|
|
if (state->signedBytesOut < state->haveBytes)
|
|
state->haveBytes = state->signedBytesOut;
|
|
state->signedBytesOut -= state->haveBytes;
|
|
|
|
//hash the data
|
|
BL.blSha2processBytes(&state->sha, state->dataBytes, state->haveBytes);
|
|
}
|
|
|
|
// decrypt if encryption is on
|
|
if (state->haveEncr) {
|
|
|
|
uint32_t *dataP = state->dataWords;
|
|
uint32_t i, numBlocks = state->haveBytes / APP_DATA_CHUNK_SIZE;
|
|
|
|
//we should not be called with partial encr blocks
|
|
if (state->haveBytes % APP_DATA_CHUNK_SIZE)
|
|
return APP_SEC_TOO_LITTLE_DATA;
|
|
|
|
// make sure we do not get too much data & account for the data we got
|
|
if (state->haveBytes > state->encryptedBytesIn)
|
|
return APP_SEC_TOO_MUCH_DATA;
|
|
state->encryptedBytesIn -= state->haveBytes;
|
|
|
|
// decrypt
|
|
for (i = 0; i < numBlocks; i++, dataP += AES_BLOCK_WORDS)
|
|
BL.blAesCbcDecr(&state->cbc, dataP, dataP);
|
|
|
|
// make sure we do not produce too much data (discard padding) & make sure we account for it
|
|
if (state->encryptedBytesOut < state->haveBytes)
|
|
state->haveBytes = state->encryptedBytesOut;
|
|
state->encryptedBytesOut -= state->haveBytes;
|
|
|
|
if (state->haveBytes)
|
|
BL.blSha2processBytes(&state->cbcSha, state->dataBytes, state->haveBytes);
|
|
}
|
|
|
|
limitChunkSize(state);
|
|
|
|
return APP_SEC_NO_ERROR;
|
|
}
|
|
|
|
static AppSecErr appSecProcessIncomingHdr(struct AppSecState *state, uint32_t *needBytesOut)
|
|
{
|
|
struct ImageHeader *image;
|
|
struct nano_app_binary_t *aosp;
|
|
uint32_t flags;
|
|
uint32_t needBytes;
|
|
struct AppSecSignHdr *signHdr = NULL;
|
|
struct AppSecEncrHdr *encrHdr = NULL;
|
|
uint8_t *hdr = state->dataBytes;
|
|
AppSecErr ret;
|
|
|
|
image = (struct ImageHeader *)hdr; hdr += sizeof(*image);
|
|
aosp = &image->aosp;
|
|
flags = aosp->flags;
|
|
if (aosp->header_version != 1 ||
|
|
aosp->magic != NANOAPP_AOSP_MAGIC ||
|
|
image->layout.version != 1 ||
|
|
image->layout.magic != GOOGLE_LAYOUT_MAGIC)
|
|
return APP_SEC_HEADER_ERROR;
|
|
|
|
needBytes = sizeof(*image);
|
|
if ((flags & NANOAPP_SIGNED_FLAG) != 0)
|
|
needBytes += sizeof(*signHdr);
|
|
if ((flags & NANOAPP_ENCRYPTED_FLAG) != 0)
|
|
needBytes += sizeof(*encrHdr);
|
|
|
|
*needBytesOut = needBytes;
|
|
|
|
if (needBytes > state->haveBytes)
|
|
return APP_SEC_NO_ERROR;
|
|
|
|
*needBytesOut = 0;
|
|
|
|
if ((flags & NANOAPP_SIGNED_FLAG) != 0) {
|
|
signHdr = (struct AppSecSignHdr *)hdr; hdr += sizeof(*signHdr);
|
|
osLog(LOG_INFO, "%s: signed size=%" PRIu32 "\n",
|
|
__func__, signHdr->appDataLen);
|
|
if (!signHdr->appDataLen) {
|
|
//no data bytes
|
|
return APP_SEC_INVALID_DATA;
|
|
}
|
|
state->signedBytesIn = state->signedBytesOut = signHdr->appDataLen;
|
|
state->haveSig = 1;
|
|
BL.blSha2init(&state->sha);
|
|
BL.blSha2processBytes(&state->sha, state->dataBytes, needBytes);
|
|
}
|
|
|
|
if ((flags & NANOAPP_ENCRYPTED_FLAG) != 0) {
|
|
uint32_t k[AES_KEY_WORDS];
|
|
|
|
encrHdr = (struct AppSecEncrHdr *)hdr; hdr += sizeof(*encrHdr);
|
|
osLog(LOG_INFO, "%s: encrypted data size=%" PRIu32
|
|
"; key ID=%016" PRIX64 "\n",
|
|
__func__, encrHdr->dataLen, encrHdr->keyID);
|
|
|
|
if (!encrHdr->dataLen || !encrHdr->keyID)
|
|
return APP_SEC_INVALID_DATA;
|
|
ret = state->aesKeyAccessCbk(encrHdr->keyID, k);
|
|
if (ret != APP_SEC_NO_ERROR) {
|
|
osLog(LOG_ERROR, "%s: Secret key not found\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
BL.blAesCbcInitForDecr(&state->cbc, k, encrHdr->IV);
|
|
BL.blSha2init(&state->cbcSha);
|
|
state->encryptedBytesOut = encrHdr->dataLen;
|
|
state->encryptedBytesIn = ((state->encryptedBytesOut + APP_SEC_ENCR_ALIGN - 1) / APP_SEC_ENCR_ALIGN) * APP_SEC_ENCR_ALIGN;
|
|
state->haveEncr = 1;
|
|
osLog(LOG_INFO, "%s: encrypted aligned data size=%" PRIu32 "\n",
|
|
__func__, state->encryptedBytesIn);
|
|
|
|
if (state->haveSig) {
|
|
state->signedBytesIn = state->signedBytesOut = signHdr->appDataLen - sizeof(*encrHdr);
|
|
// at this point, signedBytesOut must equal encryptedBytesIn
|
|
if (state->signedBytesOut != (state->encryptedBytesIn + SHA2_HASH_SIZE)) {
|
|
osLog(LOG_ERROR, "%s: sig data size does not match encrypted data\n", __func__);
|
|
return APP_SEC_INVALID_DATA;
|
|
}
|
|
}
|
|
}
|
|
|
|
//if we are in must-sign mode and no signature was provided, fail
|
|
if (!state->haveSig && state->needSig) {
|
|
osLog(LOG_ERROR, "%s: only signed images can be uploaded\n", __func__);
|
|
return APP_SEC_SIG_VERIFY_FAIL;
|
|
}
|
|
|
|
// now, transform AOSP header to FW common header
|
|
struct FwCommonHdr common = {
|
|
.magic = APP_HDR_MAGIC,
|
|
.appId = aosp->app_id,
|
|
.fwVer = APP_HDR_VER_CUR,
|
|
.fwFlags = image->layout.flags,
|
|
.appVer = aosp->app_version,
|
|
.payInfoType = image->layout.payload,
|
|
.chreApiMajor = 0xFF,
|
|
.chreApiMinor = 0xFF,
|
|
};
|
|
|
|
if (image->layout.flags & FL_APP_HDR_CHRE) {
|
|
if (aosp->chre_api_major || aosp->chre_api_minor) {
|
|
common.chreApiMajor = aosp->chre_api_major;
|
|
common.chreApiMinor = aosp->chre_api_minor;
|
|
} else {
|
|
// fields not defined prior to CHRE 1.1
|
|
common.chreApiMajor = 0x01;
|
|
common.chreApiMinor = 0x00;
|
|
}
|
|
}
|
|
|
|
// check to see if this is special system types of payload
|
|
switch(image->layout.payload) {
|
|
case LAYOUT_APP:
|
|
common.fwFlags = (common.fwFlags | FL_APP_HDR_APPLICATION) & ~FL_APP_HDR_INTERNAL;
|
|
common.payInfoSize = sizeof(struct AppInfo);
|
|
osLog(LOG_INFO, "App container found\n");
|
|
break;
|
|
case LAYOUT_KEY:
|
|
common.fwFlags |= FL_APP_HDR_SECURE;
|
|
common.payInfoSize = sizeof(struct KeyInfo);
|
|
osLog(LOG_INFO, "Key container found\n");
|
|
break;
|
|
case LAYOUT_OS:
|
|
common.payInfoSize = sizeof(struct OsUpdateHdr);
|
|
osLog(LOG_INFO, "OS update container found\n");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
memcpy(state->dataBytes, &common, sizeof(common));
|
|
state->haveBytes = sizeof(common);
|
|
|
|
//we're now in data-accepting state
|
|
appSecSetCurState(state, STATE_RXING_DATA);
|
|
|
|
return APP_SEC_NO_ERROR;
|
|
}
|
|
|
|
static AppSecErr appSecProcessIncomingData(struct AppSecState *state)
|
|
{
|
|
//check for data-ending conditions
|
|
if (state->haveSig && !state->signedBytesIn) {
|
|
// we're all done with the signed portion of the data, now come the signatures
|
|
appSecSetCurState(state, STATE_RXING_SIG_HASH);
|
|
|
|
//collect the hash
|
|
memcpy(state->lastHash, BL.blSha2finish(&state->sha), SHA2_HASH_SIZE);
|
|
} else if (state->haveEncr && !state->encryptedBytesIn) {
|
|
if (appSecGetCurState(state) == STATE_RXING_DATA) {
|
|
//we're all done with encrypted plaintext
|
|
state->encryptedBytesIn = sizeof(state->cbcSha);
|
|
appSecSetCurState(state, STATE_VERIFY);
|
|
}
|
|
}
|
|
|
|
//pass to caller
|
|
return state->haveBytes ? state->writeCbk(state->dataBytes, state->haveBytes) : APP_SEC_NO_ERROR;
|
|
}
|
|
|
|
AppSecErr appSecDoSomeProcessing(struct AppSecState *state)
|
|
{
|
|
const uint32_t *result;
|
|
|
|
if (!state->doingRsa) {
|
|
//shouldn't be calling us then...
|
|
return APP_SEC_BAD;
|
|
}
|
|
|
|
result = BL.blRsaPubOpIterative(&state->rsa, state->rsaTmp, state->dataWords, &state->rsaState1, &state->rsaState2, &state->rsaStep);
|
|
if (state->rsaStep)
|
|
return APP_SEC_NEED_MORE_TIME;
|
|
|
|
//we just finished the RSA-ing
|
|
state->doingRsa = 0;
|
|
|
|
//verify signature padding (and thus likely: correct decryption)
|
|
result = BL.blSigPaddingVerify(result);
|
|
if (!result)
|
|
return APP_SEC_SIG_DECODE_FAIL;
|
|
|
|
//check if hashes match
|
|
if (memcmp(state->lastHash, result, SHA2_HASH_SIZE))
|
|
return APP_SEC_SIG_VERIFY_FAIL;
|
|
|
|
//hash the provided pubkey
|
|
BL.blSha2init(&state->sha);
|
|
BL.blSha2processBytes(&state->sha, state->dataBytes, APP_SIG_SIZE);
|
|
memcpy(state->lastHash, BL.blSha2finish(&state->sha), SHA2_HASH_SIZE);
|
|
appSecSetCurState(state, STATE_RXING_SIG_HASH);
|
|
|
|
return APP_SEC_NO_ERROR;
|
|
}
|
|
|
|
static AppSecErr appSecProcessIncomingSigData(struct AppSecState *state)
|
|
{
|
|
bool keyFound = false;
|
|
|
|
//if we're RXing the hash, just stash it away and move on
|
|
if (appSecGetCurState(state) == STATE_RXING_SIG_HASH) {
|
|
state->haveTrustedKey = 0;
|
|
memcpy(state->rsaTmp, state->dataWords, APP_SIG_SIZE);
|
|
appSecSetCurState(state, STATE_RXING_SIG_PUBKEY);
|
|
return APP_SEC_NO_ERROR;
|
|
}
|
|
|
|
// verify it is a known root
|
|
state->pubKeyFindCbk(state->dataWords, &keyFound);
|
|
state->haveTrustedKey = keyFound;
|
|
|
|
//we now have the pubKey. decrypt over time
|
|
state->doingRsa = 1;
|
|
state->rsaStep = 0;
|
|
return APP_SEC_NEED_MORE_TIME;
|
|
}
|
|
|
|
static AppSecErr appSecVerifyEncryptedData(struct AppSecState *state)
|
|
{
|
|
const uint32_t *hash = BL.blSha2finish(&state->cbcSha);
|
|
bool verified = memcmp(hash, state->dataBytes, SHA2_BLOCK_SIZE) == 0;
|
|
|
|
osLog(LOG_INFO, "%s: decryption verification: %s\n", __func__, verified ? "passed" : "failed");
|
|
|
|
// TODO: fix verify logic
|
|
// return verified ? APP_SEC_NO_ERROR : APP_SEC_VERIFY_FAILED;
|
|
return APP_SEC_NO_ERROR;
|
|
}
|
|
|
|
AppSecErr appSecRxData(struct AppSecState *state, const void *dataP, uint32_t len, uint32_t *lenUnusedP)
|
|
{
|
|
const uint8_t *data = (const uint8_t*)dataP;
|
|
AppSecErr ret = APP_SEC_NO_ERROR;
|
|
uint32_t needBytes;
|
|
|
|
if (appSecGetCurState(state) == STATE_INIT)
|
|
appSecSetCurState(state, STATE_RXING_HEADERS);
|
|
|
|
while (len) {
|
|
len--;
|
|
state->dataBytes[state->haveBytes++] = *data++;
|
|
if (state->haveBytes < state->chunkSize)
|
|
continue;
|
|
switch (appSecGetCurState(state)) {
|
|
case STATE_RXING_HEADERS:
|
|
// AOSP header is never encrypted; if it is signed, it will hash itself
|
|
needBytes = 0;
|
|
ret = appSecProcessIncomingHdr(state, &needBytes);
|
|
if (ret != APP_SEC_NO_ERROR)
|
|
goto out;
|
|
if (needBytes > state->chunkSize) {
|
|
state->chunkSize = needBytes;
|
|
// get more data and try again
|
|
continue;
|
|
}
|
|
// done with parsing header(s); we might have something to write to flash
|
|
if (state->haveBytes) {
|
|
osLog(LOG_INFO, "%s: save converted header [%" PRIu16 " bytes] to flash\n", __func__, state->haveBytes);
|
|
ret = appSecProcessIncomingData(state);
|
|
state->haveBytes = 0;
|
|
}
|
|
limitChunkSize(state);
|
|
goto out;
|
|
|
|
case STATE_RXING_DATA:
|
|
ret = appSecBlockRx(state);
|
|
if (ret != APP_SEC_NO_ERROR)
|
|
goto out;
|
|
|
|
ret = appSecProcessIncomingData(state);
|
|
state->haveBytes = 0;
|
|
if (ret != APP_SEC_NO_ERROR)
|
|
goto out;
|
|
break;
|
|
|
|
case STATE_VERIFY:
|
|
ret = appSecBlockRx(state);
|
|
if (ret == APP_SEC_NO_ERROR)
|
|
ret = appSecProcessIncomingData(state);
|
|
if (ret == APP_SEC_NO_ERROR)
|
|
ret = appSecVerifyEncryptedData(state);
|
|
goto out;
|
|
|
|
case STATE_RXING_SIG_HASH:
|
|
case STATE_RXING_SIG_PUBKEY:
|
|
//no need for calling appSecBlockRx() as sigs are not signed, and encryption cannot be done after signing
|
|
ret = appSecProcessIncomingSigData(state);
|
|
state->haveBytes = 0;
|
|
goto out;
|
|
|
|
default:
|
|
appSecSetCurState(state, STATE_BAD);
|
|
state->haveBytes = 0;
|
|
len = 0;
|
|
ret = APP_SEC_BAD;
|
|
break;
|
|
}
|
|
}
|
|
|
|
out:
|
|
*lenUnusedP = len;
|
|
|
|
if (ret != APP_SEC_NO_ERROR && ret != APP_SEC_NEED_MORE_TIME) {
|
|
osLog(LOG_ERROR, "%s: failed: state=%" PRIu32 "; err=%" PRIu32 "\n",
|
|
__func__, appSecGetCurState(state), ret);
|
|
appSecSetCurState(state, STATE_BAD);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
AppSecErr appSecRxDataOver(struct AppSecState *state)
|
|
{
|
|
AppSecErr ret;
|
|
|
|
// Feed remaining data to data processor, if any
|
|
if (state->haveBytes) {
|
|
// if we are using encryption and/or signing, we are supposed to consume all data at this point.
|
|
if (state->haveSig || state->haveEncr) {
|
|
appSecSetCurState(state, STATE_BAD);
|
|
return APP_SEC_TOO_LITTLE_DATA;
|
|
}
|
|
// Not in data rx stage when the incoming data ends? This is not good (if we had encr or sign we'd not be here)
|
|
if (appSecGetCurState(state) != STATE_RXING_DATA) {
|
|
appSecSetCurState(state, STATE_BAD);
|
|
return APP_SEC_TOO_LITTLE_DATA;
|
|
}
|
|
// Feed the remaining data to the data processor
|
|
ret = appSecProcessIncomingData(state);
|
|
if (ret != APP_SEC_NO_ERROR) {
|
|
appSecSetCurState(state, STATE_BAD);
|
|
return ret;
|
|
}
|
|
} else {
|
|
// we don't know in advance how many signature packs we shall receive,
|
|
// so we evaluate every signature pack as if it is the last, but do not
|
|
// return error if public key is not trusted; only here we make the final
|
|
// determination
|
|
if (state->haveSig) {
|
|
// check the most recent key status
|
|
if (!state->haveTrustedKey) {
|
|
appSecSetCurState(state, STATE_BAD);
|
|
return APP_SEC_SIG_ROOT_UNKNOWN;
|
|
} else {
|
|
appSecSetCurState(state, STATE_DONE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//for unsigned/unencrypted case we have no way to judge length, so we assume it is over when we're told it is
|
|
//this is potentially dangerous, but then again so is allowing unsigned uploads in general.
|
|
if (!state->haveSig && !state->haveEncr && appSecGetCurState(state) == STATE_RXING_DATA)
|
|
appSecSetCurState(state, STATE_DONE);
|
|
|
|
//Check the state and return our verdict
|
|
if(appSecGetCurState(state) == STATE_DONE)
|
|
return APP_SEC_NO_ERROR;
|
|
|
|
appSecSetCurState(state, STATE_BAD);
|
|
return APP_SEC_TOO_LITTLE_DATA;
|
|
}
|