C++程序  |  267行  |  9.27 KB

/*
 * Copyright 2014 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 <assert.h>

#include <openssl/aes.h>
#include <openssl/sha.h>

#include <keymaster/google_keymaster_utils.h>
#include <keymaster/key_blob.h>

#include "ae.h"

namespace keymaster {

class KeyBlob::AeCtx {
  public:
    AeCtx() : ctx_(ae_allocate(NULL)) {}
    ~AeCtx() {
        ae_clear(ctx_);
        ae_free(ctx_);
    }

    ae_ctx* get() { return ctx_; }

  private:
    ae_ctx* ctx_;
};

const size_t KeyBlob::NONCE_LENGTH;
const size_t KeyBlob::TAG_LENGTH;

KeyBlob::KeyBlob(const AuthorizationSet& enforced, const AuthorizationSet& unenforced,
                 const AuthorizationSet& hidden, const keymaster_key_blob_t& key,
                 const keymaster_key_blob_t& master_key, const uint8_t nonce[NONCE_LENGTH])
    : error_(KM_ERROR_OK), nonce_(new uint8_t[NONCE_LENGTH]), tag_(new uint8_t[TAG_LENGTH]),
      enforced_(enforced), unenforced_(unenforced), hidden_(hidden) {
    if (!nonce_.get() || !tag_.get()) {
        error_ = KM_ERROR_MEMORY_ALLOCATION_FAILED;
        return;
    }
    error_ = KM_ERROR_OK;

    if (enforced_.is_valid() == AuthorizationSet::ALLOCATION_FAILURE ||
        unenforced_.is_valid() == AuthorizationSet::ALLOCATION_FAILURE ||
        hidden_.is_valid() == AuthorizationSet::ALLOCATION_FAILURE) {
        error_ = KM_ERROR_MEMORY_ALLOCATION_FAILED;
        return;
    }

    if (enforced_.is_valid() != AuthorizationSet::OK ||
        unenforced_.is_valid() != AuthorizationSet::OK ||
        hidden_.is_valid() != AuthorizationSet::OK) {
        error_ = KM_ERROR_UNKNOWN_ERROR;
        return;
    }

    if (!ExtractKeyCharacteristics())
        return;

    key_material_length_ = key.key_material_size;
    key_material_.reset(new uint8_t[key_material_length_]);
    encrypted_key_material_.reset(new uint8_t[key_material_length_]);

    if (!key_material_.get() || !encrypted_key_material_.get() || !nonce_.get() || !tag_.get()) {
        error_ = KM_ERROR_MEMORY_ALLOCATION_FAILED;
        return;
    }

    memcpy(nonce_.get(), nonce, NONCE_LENGTH);
    memcpy(key_material_.get(), key.key_material, key_material_length_);
    EncryptKey(master_key);
}

KeyBlob::KeyBlob(const keymaster_key_blob_t& key, const AuthorizationSet& hidden,
                 const keymaster_key_blob_t& master_key)
    : nonce_(new uint8_t[NONCE_LENGTH]), tag_(new uint8_t[TAG_LENGTH]), hidden_(hidden) {
    if (!nonce_.get() || !tag_.get()) {
        error_ = KM_ERROR_MEMORY_ALLOCATION_FAILED;
        return;
    }
    error_ = KM_ERROR_OK;

    const uint8_t* p = key.key_material;
    if (!Deserialize(&p, key.key_material + key.key_material_size))
        return;
    DecryptKey(master_key);
}

KeyBlob::KeyBlob(const uint8_t* key_blob, size_t blob_size)
    : nonce_(new uint8_t[NONCE_LENGTH]), tag_(new uint8_t[TAG_LENGTH]) {
    if (!nonce_.get() || !tag_.get()) {
        error_ = KM_ERROR_MEMORY_ALLOCATION_FAILED;
        return;
    }
    error_ = KM_ERROR_OK;

    if (!Deserialize(&key_blob, key_blob + blob_size))
        return;
}

size_t KeyBlob::SerializedSize() const {
    return NONCE_LENGTH + sizeof(uint32_t) + key_material_length() + TAG_LENGTH +
           enforced_.SerializedSize() + unenforced_.SerializedSize();
}

uint8_t* KeyBlob::Serialize(uint8_t* buf, const uint8_t* end) const {
    const uint8_t* start = buf;
    buf = append_to_buf(buf, end, nonce(), NONCE_LENGTH);
    buf = append_size_and_data_to_buf(buf, end, encrypted_key_material(), key_material_length());
    buf = append_to_buf(buf, end, tag(), TAG_LENGTH);
    buf = enforced_.Serialize(buf, end);
    buf = unenforced_.Serialize(buf, end);
    assert(buf - start == static_cast<ptrdiff_t>(SerializedSize()));
    return buf;
}

bool KeyBlob::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
    UniquePtr<uint8_t[]> tmp_key_ptr;
    if (!copy_from_buf(buf_ptr, end, nonce_.get(), NONCE_LENGTH) ||
        !copy_size_and_data_from_buf(buf_ptr, end, &key_material_length_, &tmp_key_ptr) ||
        !copy_from_buf(buf_ptr, end, tag_.get(), TAG_LENGTH) ||
        !enforced_.Deserialize(buf_ptr, end) || !unenforced_.Deserialize(buf_ptr, end)) {
        error_ = KM_ERROR_INVALID_KEY_BLOB;
        return false;
    }

    if (!ExtractKeyCharacteristics())
        return false;

    encrypted_key_material_.reset(tmp_key_ptr.release());
    key_material_.reset(new uint8_t[key_material_length_]);
    return true;
}

void KeyBlob::EncryptKey(const keymaster_key_blob_t& master_key) {
    UniquePtr<AeCtx> ctx(InitializeKeyWrappingContext(master_key, &error_));
    if (error_ != KM_ERROR_OK)
        return;

    int ae_err = ae_encrypt(ctx->get(), nonce_.get(), key_material(), key_material_length(),
                            NULL /* additional data */, 0 /* additional data length */,
                            encrypted_key_material_.get(), tag_.get(), 1 /* final */);
    if (ae_err < 0) {
        error_ = KM_ERROR_UNKNOWN_ERROR;
        return;
    }
    assert(ae_err == static_cast<int>(key_material_length_));
    error_ = KM_ERROR_OK;
}

void KeyBlob::DecryptKey(const keymaster_key_blob_t& master_key) {
    UniquePtr<AeCtx> ctx(InitializeKeyWrappingContext(master_key, &error_));
    if (error_ != KM_ERROR_OK)
        return;

    int ae_err =
        ae_decrypt(ctx->get(), nonce_.get(), encrypted_key_material(), key_material_length(),
                   NULL /* additional data */, 0 /* additional data length */, key_material_.get(),
                   tag_.get(), 1 /* final */);
    if (ae_err == AE_INVALID) {
        // Authentication failed!  Decryption probably succeeded(ish), but we don't want to return
        // any data when the authentication fails, so clear it.
        memset_s(key_material_.get(), 0, key_material_length());
        error_ = KM_ERROR_INVALID_KEY_BLOB;
        return;
    } else if (ae_err < 0) {
        error_ = KM_ERROR_UNKNOWN_ERROR;
        return;
    }
    assert(ae_err == static_cast<int>(key_material_length()));
    error_ = KM_ERROR_OK;
}

KeyBlob::AeCtx* KeyBlob::InitializeKeyWrappingContext(const keymaster_key_blob_t& master_key,
                                                      keymaster_error_t* error) const {
    size_t derivation_data_length;
    UniquePtr<const uint8_t[]> derivation_data(BuildDerivationData(&derivation_data_length));
    if (derivation_data.get() == NULL) {
        *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
        return NULL;
    }

    *error = KM_ERROR_OK;
    UniquePtr<AeCtx> ctx(new AeCtx);

    SHA256_CTX sha256_ctx;
    UniquePtr<uint8_t[]> hash_buf(new uint8_t[SHA256_DIGEST_LENGTH]);
    Eraser hash_eraser(hash_buf.get(), SHA256_DIGEST_LENGTH);
    UniquePtr<uint8_t[]> derived_key(new uint8_t[AES_BLOCK_SIZE]);
    Eraser derived_key_eraser(derived_key.get(), AES_BLOCK_SIZE);

    if (ctx.get() == NULL || hash_buf.get() == NULL || derived_key.get() == NULL) {
        *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
        return NULL;
    }

    Eraser sha256_ctx_eraser(sha256_ctx);

    // Hash derivation data.
    SHA256_Init(&sha256_ctx);
    SHA256_Update(&sha256_ctx, derivation_data.get(), derivation_data_length);
    SHA256_Final(hash_buf.get(), &sha256_ctx);

    // Encrypt hash with master key to build derived key.
    AES_KEY aes_key;
    Eraser aes_key_eraser(AES_KEY);
    if (AES_set_encrypt_key(master_key.key_material, master_key.key_material_size * 8, &aes_key) !=
        0) {
        *error = KM_ERROR_UNKNOWN_ERROR;
        return NULL;
    }
    AES_encrypt(hash_buf.get(), derived_key.get(), &aes_key);

    // Set up AES OCB context using derived key.
    if (ae_init(ctx->get(), derived_key.get(), AES_BLOCK_SIZE, NONCE_LENGTH, TAG_LENGTH) ==
        AE_SUCCESS)
        return ctx.release();
    else {
        memset_s(ctx.get(), 0, ae_ctx_sizeof());
        return NULL;
    }
}

const uint8_t* KeyBlob::BuildDerivationData(size_t* derivation_data_length) const {
    *derivation_data_length =
        hidden_.SerializedSize() + enforced_.SerializedSize() + unenforced_.SerializedSize();
    uint8_t* derivation_data = new uint8_t[*derivation_data_length];
    if (derivation_data != NULL) {
        uint8_t* buf = derivation_data;
        uint8_t* end = derivation_data + *derivation_data_length;
        buf = hidden_.Serialize(buf, end);
        buf = enforced_.Serialize(buf, end);
        buf = unenforced_.Serialize(buf, end);
    }
    return derivation_data;
}

bool KeyBlob::ExtractKeyCharacteristics() {
    if (!enforced_.GetTagValue(TAG_ALGORITHM, &algorithm_) &&
        !unenforced_.GetTagValue(TAG_ALGORITHM, &algorithm_)) {
        error_ = KM_ERROR_UNSUPPORTED_ALGORITHM;
        return false;
    }
    if (!enforced_.GetTagValue(TAG_KEY_SIZE, &key_size_bits_) &&
        !unenforced_.GetTagValue(TAG_KEY_SIZE, &key_size_bits_)) {
        error_ = KM_ERROR_UNSUPPORTED_KEY_SIZE;
        return false;
    }
    return true;
}

}  // namespace keymaster