blob: 229924911393fd3636665573970dbc49259355d1 [file] [log] [blame]
/*
* Copyright (C) 2021 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 <utils/Log.h>
#include <sys/stat.h>
#include <string>
#include "DeviceFiles.h"
#include "protos/DeviceFiles.pb.h"
#include <openssl/sha.h>
// Protobuf generated classes.
using clearkeydrm::HashedFile;
using clearkeydrm::License;
using clearkeydrm::License_LicenseState_ACTIVE;
using clearkeydrm::License_LicenseState_RELEASING;
using clearkeydrm::OfflineFile;
namespace {
const char kLicenseFileNameExt[] = ".lic";
bool Hash(const std::string& data, std::string* hash) {
if (!hash) return false;
hash->resize(SHA256_DIGEST_LENGTH);
const unsigned char* input = reinterpret_cast<const unsigned char*>(data.data());
unsigned char* output = reinterpret_cast<unsigned char*>(&(*hash)[0]);
SHA256(input, data.size(), output);
return true;
}
} // namespace
namespace clearkeydrm {
bool DeviceFiles::StoreLicense(const std::string& keySetId, LicenseState state,
const std::string& licenseResponse) {
OfflineFile file;
file.set_type(OfflineFile::LICENSE);
file.set_version(OfflineFile::VERSION_1);
License* license = file.mutable_license();
switch (state) {
case kLicenseStateActive:
license->set_state(License_LicenseState_ACTIVE);
license->set_license(licenseResponse);
break;
case kLicenseStateReleasing:
license->set_state(License_LicenseState_RELEASING);
license->set_license(licenseResponse);
break;
default:
ALOGW("StoreLicense: Unknown license state: %u", state);
return false;
}
std::string serializedFile;
file.SerializeToString(&serializedFile);
return StoreFileWithHash(keySetId + kLicenseFileNameExt, serializedFile);
}
bool DeviceFiles::StoreFileWithHash(const std::string& fileName,
const std::string& serializedFile) {
std::string hash;
if (!Hash(serializedFile, &hash)) {
ALOGE("StoreFileWithHash: Failed to compute hash");
return false;
}
HashedFile hashFile;
hashFile.set_file(serializedFile);
hashFile.set_hash(hash);
std::string serializedHashFile;
hashFile.SerializeToString(&serializedHashFile);
return StoreFileRaw(fileName, serializedHashFile);
}
bool DeviceFiles::StoreFileRaw(const std::string& fileName, const std::string& serializedHashFile) {
MemoryFileSystem::MemoryFile memFile;
memFile.setFileName(fileName);
memFile.setContent(serializedHashFile);
memFile.setFileSize(serializedHashFile.size());
size_t len = mFileHandle.Write(fileName, memFile);
if (len != static_cast<size_t>(serializedHashFile.size())) {
ALOGE("StoreFileRaw: Failed to write %s", fileName.c_str());
ALOGD("StoreFileRaw: expected=%zd, actual=%zu", serializedHashFile.size(), len);
return false;
}
ALOGD("StoreFileRaw: wrote %zu bytes to %s", serializedHashFile.size(), fileName.c_str());
return true;
}
bool DeviceFiles::RetrieveLicense(const std::string& keySetId, LicenseState* state,
std::string* offlineLicense) {
OfflineFile file;
if (!RetrieveHashedFile(keySetId + kLicenseFileNameExt, &file)) {
return false;
}
if (file.type() != OfflineFile::LICENSE) {
ALOGE("RetrieveLicense: Invalid file type");
return false;
}
if (file.version() != OfflineFile::VERSION_1) {
ALOGE("RetrieveLicense: Invalid file version");
return false;
}
if (!file.has_license()) {
ALOGE("RetrieveLicense: License not present");
return false;
}
License license = file.license();
switch (license.state()) {
case License_LicenseState_ACTIVE:
*state = kLicenseStateActive;
break;
case License_LicenseState_RELEASING:
*state = kLicenseStateReleasing;
break;
default:
ALOGW("RetrieveLicense: Unrecognized license state: %u", kLicenseStateUnknown);
*state = kLicenseStateUnknown;
break;
}
*offlineLicense = license.license();
return true;
}
bool DeviceFiles::DeleteLicense(const std::string& keySetId) {
return mFileHandle.RemoveFile(keySetId + kLicenseFileNameExt);
}
bool DeviceFiles::DeleteAllLicenses() {
return mFileHandle.RemoveAllFiles();
}
bool DeviceFiles::LicenseExists(const std::string& keySetId) {
return mFileHandle.FileExists(keySetId + kLicenseFileNameExt);
}
std::vector<std::string> DeviceFiles::ListLicenses() const {
std::vector<std::string> licenses = mFileHandle.ListFiles();
for (size_t i = 0; i < licenses.size(); i++) {
std::string& license = licenses[i];
license = license.substr(0, license.size() - strlen(kLicenseFileNameExt));
}
return licenses;
}
bool DeviceFiles::RetrieveHashedFile(const std::string& fileName, OfflineFile* deSerializedFile) {
if (!deSerializedFile) {
ALOGE("RetrieveHashedFile: invalid file parameter");
return false;
}
if (!FileExists(fileName)) {
ALOGE("RetrieveHashedFile: %s does not exist", fileName.c_str());
return false;
}
ssize_t bytes = GetFileSize(fileName);
if (bytes <= 0) {
ALOGE("RetrieveHashedFile: invalid file size: %s", fileName.c_str());
// Remove the corrupted file so the caller will not get the same error
// when trying to access the file repeatedly, causing the system to stall.
RemoveFile(fileName);
return false;
}
std::string serializedHashFile;
serializedHashFile.resize(bytes);
bytes = mFileHandle.Read(fileName, &serializedHashFile);
if (bytes != static_cast<ssize_t>(serializedHashFile.size())) {
ALOGE("RetrieveHashedFile: Failed to read from %s", fileName.c_str());
ALOGV("RetrieveHashedFile: expected: %zd, actual: %zd", serializedHashFile.size(), bytes);
// Remove the corrupted file so the caller will not get the same error
// when trying to access the file repeatedly, causing the system to stall.
RemoveFile(fileName);
return false;
}
ALOGV("RetrieveHashedFile: read %zd from %s", bytes, fileName.c_str());
HashedFile hashFile;
if (!hashFile.ParseFromString(serializedHashFile)) {
ALOGE("RetrieveHashedFile: Unable to parse hash file");
// Remove corrupt file.
RemoveFile(fileName);
return false;
}
std::string hash;
if (!Hash(hashFile.file(), &hash)) {
ALOGE("RetrieveHashedFile: Hash computation failed");
return false;
}
if (hash != hashFile.hash()) {
ALOGE("RetrieveHashedFile: Hash mismatch");
// Remove corrupt file.
RemoveFile(fileName);
return false;
}
if (!deSerializedFile->ParseFromString(hashFile.file())) {
ALOGE("RetrieveHashedFile: Unable to parse file");
// Remove corrupt file.
RemoveFile(fileName);
return false;
}
return true;
}
bool DeviceFiles::FileExists(const std::string& fileName) const {
return mFileHandle.FileExists(fileName);
}
bool DeviceFiles::RemoveFile(const std::string& fileName) {
return mFileHandle.RemoveFile(fileName);
}
ssize_t DeviceFiles::GetFileSize(const std::string& fileName) const {
return mFileHandle.GetFileSize(fileName);
}
} // namespace clearkeydrm