blob: 06a8e7813439c10dbc9a31bc2952a395d0955647 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CRYPTOHOME_USER_SECRET_STASH_H_
#define CRYPTOHOME_USER_SECRET_STASH_H_
#include <brillo/secure_blob.h>
#include <stdint.h>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include "cryptohome/flatbuffer_schemas/user_secret_stash_container.h"
#include "cryptohome/storage/file_system_keyset.h"
namespace cryptohome {
// Returns the UserSecretStash experiment version. This will compared with the
// `last_invalid` field in the fetched experiment config to determine whether
// this version should enable the experiment. Will be incremented whenever a
// known issue that blocks the experiment is fixed so that it can be enabled
// again.
int UserSecretStashExperimentVersion();
// This is used by the UssExperimentConfigFetcher to set the experiment flag to
// enabled or disabled based on whether this USS version is valid and how much
// of the population should have the experiment enabled.
void SetUserSecretStashExperimentFlag(bool enabled);
// Returns whether the UserSecretStash experiment (using the USS instead of
// vault keysets) is enabled.
// The experiment is controlled by fetching a config file from gstatic. It
// matches the local USS version returned by
// `UserSecretStashExperimentVersion()` and the `last_invalid` version specified
// in the config file. If our version is greater, the experiment is enabled with
// `population` probability, and disabled otherwise. Whether the experiment is
// enabled can be overridden by creating the /var/lib/cryptohome/uss_enabled (to
// enable) or the /var/lib/cryptohome/uss_disabled (to disable) file. Unit tests
// can furthermore override this behavior using
// `SetUserSecretStashExperimentForTesting()`.
bool IsUserSecretStashExperimentEnabled();
// Allows to toggle the experiment state in tests. Passing nullopt reverts to
// the default behavior.
void SetUserSecretStashExperimentForTesting(std::optional<bool> enabled);
// This wraps the UserSecretStash flatbuffer message, and is the only way that
// the UserSecretStash is accessed. Don't pass the raw flatbuffer around.
class UserSecretStash {
public:
// Container for a wrapped (encrypted) USS main key.
struct WrappedKeyBlock {
// The algorithm used for wrapping the USS main key.
UserSecretStashEncryptionAlgorithm encryption_algorithm;
// This is the encrypted USS main key.
brillo::SecureBlob encrypted_key;
// The random IV used in the USS main key encryption.
brillo::SecureBlob iv;
// The GCM tag generated by the block cipher.
brillo::SecureBlob gcm_tag;
};
// Sets up a UserSecretStash with random contents (reset secret, etc.) and the
// values from the specified file system keyset.
static std::unique_ptr<UserSecretStash> CreateRandom(
const FileSystemKeyset& file_system_keyset);
// This deserializes the |flatbuffer| into a UserSecretStashContainer table.
// Besides unencrypted data, that table contains a ciphertext, which is
// decrypted with the |main_key| using AES-GCM-256. It doesn't return the
// plaintext, it populates the fields of the class with the encrypted message.
static std::unique_ptr<UserSecretStash> FromEncryptedContainer(
const brillo::SecureBlob& flatbuffer, const brillo::SecureBlob& main_key);
// Same as |FromEncryptedContainer()|, but the main key is unwrapped from the
// USS container using the given wrapping key. The |main_key| output argument
// is populated with the unwrapped main key on success.
static std::unique_ptr<UserSecretStash> FromEncryptedContainerWithWrappingKey(
const brillo::SecureBlob& flatbuffer,
const std::string& wrapping_id,
const brillo::SecureBlob& wrapping_key,
brillo::SecureBlob* main_key);
// Randomly generates a USS Main Key. This is intended to be used when
// creating a fresh USS via |CreateRandom()|.
static brillo::SecureBlob CreateRandomMainKey();
virtual ~UserSecretStash() = default;
// Because this class contains raw secrets, it should never be copy-able.
UserSecretStash(const UserSecretStash&) = delete;
UserSecretStash& operator=(const UserSecretStash&) = delete;
const FileSystemKeyset& GetFileSystemKeyset() const;
const brillo::SecureBlob& GetResetSecret() const;
void SetResetSecret(const brillo::SecureBlob& secret);
// The OS version on which this particular user secret stash was originally
// created. The format is the one of the CHROMEOS_RELEASE_VERSION field in
// /etc/lsb-release, e.g.: "11012.0.2018_08_28_1422". Empty if the version
// fetch failed at the creation time.
// !!!WARNING!!!: This value is not authenticated nor validated. It must not
// be used for security-critical features.
const std::string& GetCreatedOnOsVersion() const;
// Returns whether there's a wrapped key block with the given wrapping ID.
bool HasWrappedMainKey(const std::string& wrapping_id) const;
// Unwraps (decrypts) the USS main key from the wrapped key block with the
// given wrapping ID. Returns null if it doesn't exist or the unwrapping
// fails.
std::optional<brillo::SecureBlob> UnwrapMainKey(
const std::string& wrapping_id,
const brillo::SecureBlob& wrapping_key) const;
// Wraps (encrypts) the USS main key using the given wrapped key. The wrapped
// data is added into the USS as a wrapped key block with the given wrapping
// ID. |main_key| must be non-empty, and |wrapping_key| - of
// |kAesGcm256KeySize| length. Returns false if the wrapping ID is already
// used or the wrapping fails.
bool AddWrappedMainKey(const brillo::SecureBlob& main_key,
const std::string& wrapping_id,
const brillo::SecureBlob& wrapping_key);
// Removes the wrapped key with the given ID. If it doesn't exist, returns
// false.
bool RemoveWrappedMainKey(const std::string& wrapping_id);
// This uses the |main_key|, which should be 256-bit as of right now, to
// encrypt this UserSecretStash class. The object is converted to a
// UserSecretStashPayload table, serialized, encrypted with AES-GCM-256, and
// serialized as a UserSecretStashContainer table.
std::optional<brillo::SecureBlob> GetEncryptedContainer(
const brillo::SecureBlob& main_key);
private:
// Decrypts the USS payload flatbuffer using the passed main key and
// constructs the USS instance from it. Returns null on decryption or
// validation failure.
static std::unique_ptr<UserSecretStash> FromEncryptedPayload(
const brillo::SecureBlob& ciphertext,
const brillo::SecureBlob& iv,
const brillo::SecureBlob& gcm_tag,
const std::map<std::string, WrappedKeyBlock>& wrapped_key_blocks,
const std::string& created_on_os_version,
const brillo::SecureBlob& main_key);
UserSecretStash(const FileSystemKeyset& file_system_keyset,
const brillo::SecureBlob& reset_secret);
// Keys registered with the kernel to decrypt files and file names, together
// with corresponding salts and signatures.
const FileSystemKeyset file_system_keyset_;
// The reset secret used for any PinWeaver backed credentials.
brillo::SecureBlob reset_secret_;
// Stores multiple wrapped (encrypted) representations of the main key, each
// wrapped using a different intermediate key. The map's index is the wrapping
// ID, which is an opaque string (although upper programmatic layers can add
// semantics to it, in order to map it to the authentication method).
std::map<std::string, WrappedKeyBlock> wrapped_key_blocks_;
// The OS version on which this particular user secret stash was originally
// created.
std::string created_on_os_version_;
};
} // namespace cryptohome
#endif // CRYPTOHOME_USER_SECRET_STASH_H_