Microsoft Information Protection SDK - Cache storage

The MIP SDK implements a SQLite3 database for maintaining SDK cache storage. Prior to version 1.3 of the Microsoft Information Protection SDK, only two types of cache state storage were supported: On disk and in memory. Both of these types stored certain data, specifically licenses for protected content and policy information, in plaintext.

To improve the security posture of the SDK, we added support for a second type of on disk cache that uses platform-specific cryptographic APIs to protect the database and its contents.

The application defines the cache type when loading the profile as part of the FileProfileSettings, PolicyProfileSettings, or ProtectionProfileSettings objects. The cache type is static for the life of the profile. Changing to a different type of cache storage type requires destroying the existing profile and creating a new one.

Cache Storage Types

Starting in MIP SDK release 1.3, the following storage cache types are available.

Type Purpose
InMemory Maintains the storage cache in memory in the application.
OnDisk Stores the database on disk in the directory provided in the settings object. The database is stored in plaintext.
OnDiskEncrypted Stores the database on disk in the directory provided in the settings object. The database is encrypted using OS-specific APIs.

Each engine generated by the application generates a new encryption key.

Cache storage is set via one of the profile settings objects, through the mip::CacheStorageType enum.

FileProfile::Settings profileSettings(mMipContext,
    mip::CacheStorageType::OnDiskEncrypted, // Define the storage type to use.
    mAuthDelegate,
    std::make_shared<sample::consent::ConsentDelegateImpl>(),
    std::make_shared<FileProfileObserver>());

When to use each type

Cache storage is important for maintaining offline access to previously decrypted information, and ensuring performance for decryption operations when data has been previously consumed.

  • In Memory Storage: Use this storage type for long-lived processes where persisting the policy or license cache information across service restarts is not required.
  • On Disk: Use this storage type for applications where processes may frequently stop and start, but must maintain policy, license, and service discovery cache across restarts. This storage cache type is plaintext, so is better suited for server workloads where users won't have access to the state storage. Examples of this would be a Windows service or Linux daemon running on a server, or a SaaS application where only service admins would have access to the state data.
  • On Disk and Encrypted: Use this storage type for applications where processes may frequently stop and start, but must maintain policy, license, and service discovery cache across restarts. This storage cache is encrypted, so is better suited for workstation applications where a user may browse and discover the state database. The encryption helps to ensure that prying users won't have access to via the policy contents or protection license content in plain text. It's important to note that in all cases the data is encrypted with keys that the user may be able to access. A skilled adversary is able to decrypt the cache with minimal effort, but this does prevent tampering and browsing.

Supported Platforms for Encryption

Platform Version Notes
Microsoft Windows Windows 8 and newer Windows 7 supports only CacheStorageType::OnDisk
macOS High Sierra and later
Ubuntu Linux 16.04 and later Requires SecretService and LinuxEncryptedCache feature flag.
Android Android 7.0 or later
iOS All supported versions

While the MIP SDK does support other Linux distributions, we didn't test the cache encryption on RedHat Enterprise Linux, CentOS, or Debian.

Note

The feature flag to enable cache storage on Linux is set via mip::MipConfiguration::SetFeatureSettings()

Cache storage database tables

The MIP SDK maintains two databases for cache. One is for the Protection SDKs, and maintaining protection state details. The other is for the Policy SDKs and maintaining policy details and service information. Both are stored in the path defined in the settings object, under mip\mip.policies.sqlite3 and mip\mip.protection.sqlite3.

Protection Database

Table Purpose Encrypted
AuthInfoStore Stores authentication challenge details. No
ConsentStore Stores consent results for each engine. No
DnsInfoStore Stores DNS lookup results for Protection operations No
EngineStore Stores engine details, associated user, and custom client data No
KeyStore Stores symmetric encryption keys for each engine. Yes
LicenseStore Stores use license information for previously decrypted data. Yes
SdInfoStore Stores service discovery results. No

Note

The LicenseStore cache requires an identity to be set on the protection engine or file engine.

Policy Database

Table Purpose Encrypted
KeyStore Stores symmetric encryption keys for each engine. Yes
Policies Stores label policy information for each user. Yes
PoliciesUrl Stores backend policy service URL for specific user. No
Sensitivity Stores classification rules for a specific user policy. Yes
SensitivityUrls Stores backend sensitivity policy service URL for specific user. No

Database size considerations

The database size depends on two factors: The quantity of engines being added to the cache and the quantity of protection licenses that have been cached. As of MIP SDK 1.3, there is no mechanism to clean up the license cache as they expire. There will have to be an external process to remove the cache if it grows larger than is desired.

The most significant contributor to database growth will be the protection license cache. If licensing caching isn't required, either because the service round trips won't impact your application performance, or the cache may grow too large, the license cache can be disable. This is accomplished by setting CanCacheLicenses on the FileProfile::Settings object to false.

FileProfile::Settings profileSettings(mMipContext,
    mip::CacheStorageType::OnDiskEncrypted,
    mAuthDelegate,
    std::make_shared<sample::consent::ConsentDelegateImpl>(),
    std::make_shared<FileProfileObserver>());

profileSettings.SetCanCacheLicenses(false);

Caching Engines

In MIP SDK, an engine is created for each user performing any authenticated operation. Engines provides an interface for all operations that are performed on behalf of an authenticated identity. As discussed in Profiles and Engines concepts, FileEngine, PolicyEngine, or ProtectionEngine each has two states CREATED and LOADED. An engine needs to be created and loaded for it to be able to perform SDK operations. If an engine is not in use, the SDK caches the engine and retains it in CREATED state as long as possible depending on available resources. Each Respective SDK's profile class also provides a method UnloadEngineAsync to achieve this explicitly.

Each engine has a unique identifier id that is used in all engine management operations. The client application can provide an id explicitly, or the SDK can generated one, if it's not provided by the application. If a unique identifier is provided using engine settings objects at the time of engine creation, and caching is enabled in API profile as described above, same engines can be used every time the user performs an operation with the SDK. Follow the code snippets for creating a [mip::FileEngine](./concept-profile-engine-file-engine-cpp.md#create-file-engine-settings), [mip::PolicyEngine](./concept-profile-engine-policy-engine-cpp.md#implementation-create-policy-engine-settings).

Failing to provide an existing engineId will result in extra service round trips to fetch policy and will fetch licenses that may have already been cached for the existing engine. Caching the engine ID allows the SDK offline access to previously decrypted information and general performance improvements.

Next Steps

Next, learn more about Profile and Engine object concepts to understand how to properly set MIP engine IDs to properly utilize MIP SDK caching.