When we forget a volume, forget per-volume key
Protect all per-volume-per-user keys with a per-volume key, which is
forgotten when the volume is forgotten. This means that the user's key
is securely lost even when their storage is encrypted at forgetting
time.
Bug: 25861755
Test: create a volume, forget it, check logs and filesystem.
Change-Id: I8df77bc91bbfa2258e082ddd54d6160dbf39b378
diff --git a/KeyStorage.cpp b/KeyStorage.cpp
index 143272d..8878a3c 100644
--- a/KeyStorage.cpp
+++ b/KeyStorage.cpp
@@ -88,7 +88,7 @@
return true;
}
-static std::string hashWithPrefix(char const* prefix, const std::string& tohash) {
+static void hashWithPrefix(char const* prefix, const std::string& tohash, std::string* res) {
SHA512_CTX c;
SHA512_Init(&c);
@@ -99,9 +99,8 @@
hashingPrefix.resize(SHA512_CBLOCK);
SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
SHA512_Update(&c, tohash.data(), tohash.size());
- std::string res(SHA512_DIGEST_LENGTH, '\0');
- SHA512_Final(reinterpret_cast<uint8_t*>(&res[0]), &c);
- return res;
+ res->assign(SHA512_DIGEST_LENGTH, '\0');
+ SHA512_Final(reinterpret_cast<uint8_t*>(&(*res)[0]), &c);
}
static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
@@ -160,6 +159,30 @@
return true;
}
+static bool readRandomBytesOrLog(size_t count, std::string* out) {
+ auto status = ReadRandomBytes(count, *out);
+ if (status != OK) {
+ LOG(ERROR) << "Random read failed with status: " << status;
+ return false;
+ }
+ return true;
+}
+
+bool createSecdiscardable(const std::string& filename, std::string* hash) {
+ std::string secdiscardable;
+ if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;
+ if (!writeStringToFile(secdiscardable, filename)) return false;
+ hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
+ return true;
+}
+
+bool readSecdiscardable(const std::string& filename, std::string* hash) {
+ std::string secdiscardable;
+ if (!readFileToString(filename, &secdiscardable)) return false;
+ hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
+ return true;
+}
+
static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
KeyPurpose purpose,
const AuthorizationSet &keyParams,
@@ -283,20 +306,11 @@
}
static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
- const std::string& salt, const std::string& secdiscardable,
+ const std::string& salt, const std::string& secdiscardable_hash,
std::string* appId) {
std::string stretched;
if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
- *appId = hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable) + stretched;
- return true;
-}
-
-static bool readRandomBytesOrLog(size_t count, std::string* out) {
- auto status = ReadRandomBytes(count, *out);
- if (status != OK) {
- LOG(ERROR) << "Random read failed with status: " << status;
- return false;
- }
+ *appId = secdiscardable_hash + stretched;
return true;
}
@@ -306,7 +320,8 @@
static bool encryptWithoutKeymaster(const std::string& preKey,
const KeyBuffer& plaintext, std::string* ciphertext) {
- auto key = hashWithPrefix(kHashPrefix_keygen, preKey);
+ std::string key;
+ hashWithPrefix(kHashPrefix_keygen, preKey, &key);
key.resize(AES_KEY_BYTES);
if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false;
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
@@ -356,7 +371,8 @@
LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size();
return false;
}
- auto key = hashWithPrefix(kHashPrefix_keygen, preKey);
+ std::string key;
+ hashWithPrefix(kHashPrefix_keygen, preKey, &key);
key.resize(AES_KEY_BYTES);
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
@@ -410,9 +426,8 @@
return false;
}
if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
- std::string secdiscardable;
- if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;
- if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false;
+ std::string secdiscardable_hash;
+ if (!createSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
std::string stretching = getStretching(auth);
if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
std::string salt;
@@ -424,7 +439,7 @@
if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
}
std::string appId;
- if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
+ if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
std::string encryptedKey;
if (auth.usesKeymaster()) {
Keymaster keymaster;
@@ -467,8 +482,8 @@
LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
return false;
}
- std::string secdiscardable;
- if (!readFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) return false;
+ std::string secdiscardable_hash;
+ if (!readSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
std::string stretching;
if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
std::string salt;
@@ -476,7 +491,7 @@
if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
}
std::string appId;
- if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
+ if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
std::string encryptedMessage;
if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
if (auth.usesKeymaster()) {