Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Key rotation policy. #194

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion src/endpoints/keyEndpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import { IAttestationReport } from "../attestation/ISnpAttestationReport";
import { IKeyItem } from "./IKeyItem";
import { KeyGeneration } from "./KeyGeneration";
import { validateAttestation } from "../attestation/AttestationValidation";
import { hpkeKeyIdMap, hpkeKeysMap } from "../repositories/Maps";
import { hpkeKeyIdMap, hpkeKeysMap, keyRotationPolicyMap } from "../repositories/Maps";
import { ServiceRequest } from "../utils/ServiceRequest";
import { LogContext, Logger } from "../utils/Logger";
import { TrustedTime } from "../utils/TrustedTime";
import { KeyRotationPolicy } from "../policies/KeyRotationPolicy";

// Enable the endpoint
enableEndpoint();
Expand Down Expand Up @@ -175,6 +177,40 @@ export const key = (
logContext
);
}

const keyRotation = KeyRotationPolicy.loadKeyRotationPolicyFromMap(
keyRotationPolicyMap,
logContext
).keyRotationPolicy;
const gracePeriodSeconds = keyRotation.gracePeriodSeconds;
const rotationIntervalSeconds = keyRotation.rotationIntervalSeconds;
// Get the current time using TrustedTime
const currentTime = TrustedTime.getCurrentTime();
const currentDate = new Date(currentTime);

// Get the creation date of the key
const creationDate = new Date(keyItem.timestamp!);

// Calculate the expiry date of the key by adding the rotation interval to the creation date
const expiryDateMillis =
creationDate.getTime() + rotationIntervalSeconds * 1000;
const expiryDate = new Date(expiryDateMillis);
// Calculate the grace period start date by subtracting the grace period from the expiry date
const gracePeriodMillis = gracePeriodSeconds * 1000;
const gracePeriodStartDate = new Date(
expiryDate.getTime() - gracePeriodMillis
);

if (currentDate > expiryDate) {
return ServiceResult.Failed<string>(
{ errorMessage: `${name}: Key has expired and is no longer valid` },
400,
logContext
);
} else if (currentDate > gracePeriodStartDate) {
Logger.warn(`${name}: Key is deprecated and will expire soon`);
}

const receipt = hpkeKeysMap.receipt(kid);

if (validateAttestationResult.statusCode === 202) {
Expand Down
4 changes: 4 additions & 0 deletions src/policies/IKeyRotationPolicy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface IKeyRotationPolicy {
rotationIntervalSeconds: number;
gracePeriodSeconds: number;
}
101 changes: 101 additions & 0 deletions src/policies/KeyRotationPolicy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import * as ccfapp from "@microsoft/ccf-app";
import { ccf } from "@microsoft/ccf-app/global";
import { IKeyRotationPolicy } from "./IKeyRotationPolicy";
import { Logger, LogContext } from "../utils/Logger";
import { KmsError } from "../utils/KmsError";

/**
* Class representing a Key Rotation Policy.
*/
export class KeyRotationPolicy {
/**
* Constructs a new KeyRotationPolicy instance.
* @param keyRotationPolicy - The key rotation policy settings.
*/
constructor(public keyRotationPolicy: IKeyRotationPolicy) {}

/**
* The log context for the KeyRotationPolicy class.
* @private
*/
private static readonly logContext = new LogContext().appendScope(
"KeyRotationPolicy"
);

/**
* Returns the default key rotation policy.
* @returns The default key rotation policy.
*/
public static defaultKeyRotationPolicy(): IKeyRotationPolicy {
return {
rotationIntervalSeconds: 300,
gracePeriodSeconds: 60,
};
}

/**
* Logs the key rotation policy settings.
* @param keyRotationPolicy - The key rotation policy to log.
*/
public static logKeyRotationPolicy(
keyRotationPolicy: IKeyRotationPolicy
): void {
Logger.debug(
`Rotation Interval Seconds: ${keyRotationPolicy.rotationIntervalSeconds}`,
KeyRotationPolicy.logContext
);
Logger.debug(
`Grace Period Seconds: ${keyRotationPolicy.gracePeriodSeconds}`,
KeyRotationPolicy.logContext
);
}

/**
* Loads the key rotation from the key rotation policy map.
* If a key rotation policy is found, it is parsed and returned as an instance of `KeyRotationPolicy`.
* If no key rotation policy is found, default key rotation policy are used.
* @param keyRotationPolicyMap - The map containing the key rotation policy.
* @param logContextIn - The log context to use.
* @returns A new KeyRotationPolicy instance.
* @throws {KmsError} If the key rotation policy cannot be parsed.
*/
public static loadKeyRotationPolicyFromMap(
keyRotationPolicyMap: ccfapp.KvMap,
logContextIn: LogContext
): KeyRotationPolicy {
const logContext = (logContextIn?.clone() || new LogContext()).appendScope(
"loadKeyRotationPolicyFromMap"
);

// Load the key rotation from the map
const key = "key_rotation_policy"; // Ensure the key matches the stored key in governance
const keyBuf = ccf.strToBuf(key);

const keyRotationPolicy = keyRotationPolicyMap.get(keyBuf);
const keyRotationPolicyStr = keyRotationPolicy
? ccf.bufToStr(keyRotationPolicy)
: undefined;
Logger.debug(
`Loading key rotation policy: ${keyRotationPolicyStr}`,
logContext
);

let keyRotation: IKeyRotationPolicy;
if (!keyRotationPolicyStr) {
Logger.warn(
`No key rotation policy found, using default key rotation policy`,
logContext
);
keyRotation = KeyRotationPolicy.defaultKeyRotationPolicy();
} else {
try {
keyRotation = JSON.parse(keyRotationPolicyStr) as IKeyRotationPolicy;
} catch {
const error = `Failed to parse key rotation policy: ${keyRotationPolicyStr}`;
Logger.error(error, logContext);
throw new KmsError(error, logContext);
}
}
return new KeyRotationPolicy(keyRotation);
}
}
2 changes: 2 additions & 0 deletions src/repositories/Maps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ export const keyReleaseMapName = "public:ccf.gov.policies.key_release";
export const keyReleasePolicyMap = ccf.kv[keyReleaseMapName];
export const settingsMapName = "public:ccf.gov.policies.settings";
export const settingsPolicyMap = ccf.kv[settingsMapName];
export const keyRotationMapName = "public:ccf.gov.policies.key_rotation";
export const keyRotationPolicyMap = ccf.kv[keyRotationMapName];
//#endregion
12 changes: 12 additions & 0 deletions src/utils/TrustedTime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export class TrustedTime {
private static lastTimestamp: number = 0;

public static getCurrentTime(): number {
const currentTime = Date.now();
if (currentTime <= this.lastTimestamp) {
throw new Error("System time moved backwards.");
}
this.lastTimestamp = currentTime;
return currentTime;
}
}
Loading