internal virtual CryptoKey GetLatestOrCreateIntermediateKey() { Option <EnvelopeKeyRecord> newestIntermediateKeyRecord = LoadLatestKeyRecord(partition.IntermediateKeyId); if (newestIntermediateKeyRecord.IsSome) { EnvelopeKeyRecord keyRecord = (EnvelopeKeyRecord)newestIntermediateKeyRecord; // If the key we just got back isn't expired, then just use it if (!IsKeyExpiredOrRevoked(keyRecord)) { try { return(WithExistingSystemKey( keyRecord.ParentKeyMeta.IfNone(() => throw new MetadataMissingException( "Could not find parentKeyMeta (SK) for intermediateKey ")), true, key => DecryptKey(keyRecord, key))); } catch (MetadataMissingException e) { Logger.LogDebug( e, "The SK for the IK ({keyId}, {created}) is missing or in an invalid state. Will create new IK instead.", partition.IntermediateKeyId, keyRecord.Created); } } // If we're here we know we have an expired key. // If we're doing queued rotation, flag it and continue to use the expired key if (cryptoPolicy.IsQueuedKeyRotation()) { // TODO : Queued rotation Logger.LogDebug("Queuing up IK {keyId} for rotation", partition.IntermediateKeyId); try { return(WithExistingSystemKey( keyRecord.ParentKeyMeta.IfNone(() => throw new MetadataMissingException( "Could not find parentKeyMeta (SK) for intermediateKey")), true, key => DecryptKey(keyRecord, key))); } catch (MetadataMissingException e) { Logger.LogDebug( e, "The SK for the IK ({keyId}, {created}) is missing or in an invalid state. Will create new IK instead.", partition.IntermediateKeyId, keyRecord.Created); } } // If we're here then we're doing inline rotation and have an expired key. // Fall through as if we didn't have the key } DateTimeOffset intermediateKeyCreated = cryptoPolicy.TruncateToIntermediateKeyPrecision(DateTimeOffset.UtcNow); CryptoKey intermediateKey = crypto.GenerateKey(intermediateKeyCreated); try { EnvelopeKeyRecord newIntermediateKeyRecord = WithSystemKeyForWrite(systemCryptoKey => new EnvelopeKeyRecord( intermediateKey.GetCreated(), new KeyMeta(partition.SystemKeyId, systemCryptoKey.GetCreated()), crypto.EncryptKey(intermediateKey, systemCryptoKey), false)); Logger.LogDebug( "Attempting to store new IK {keyId}, for created {created}", partition.IntermediateKeyId, newIntermediateKeyRecord.Created); if (metastore.Store( partition.IntermediateKeyId, newIntermediateKeyRecord.Created, newIntermediateKeyRecord.ToJson())) { return(intermediateKey); } else { Logger.LogDebug( "Attempted to store new IK {keyId} but detected duplicate for created {created}, disposing newly created IK", partition.IntermediateKeyId, intermediateKey.GetCreated()); DisposeKey(intermediateKey, null); } } catch (Exception e) { DisposeKey(intermediateKey, e); throw new AppEncryptionException("Unable to store new Intermediate Key", e); } // If we're here, storing of the newly generated key failed above which means we attempted to // save a duplicate key to the metastore. If that's the case, then we know a valid key exists // in the metastore, so let's grab it and return it. // Using a new variable instead of the one above because the WithSystemKeyForWrite use above wants finality Option <EnvelopeKeyRecord> actualLatestIntermediateKeyRecord = LoadLatestKeyRecord(partition.IntermediateKeyId); if (actualLatestIntermediateKeyRecord.IsSome) { EnvelopeKeyRecord keyRecord = (EnvelopeKeyRecord)actualLatestIntermediateKeyRecord; // NOTE: Not wrapping this in try/catch to allow errors to bubble up. If we're missing meta in this flow, something's wrong. return(WithExistingSystemKey( keyRecord.ParentKeyMeta.IfNone(() => throw new MetadataMissingException("Could not find parentKeyMeta (SK) for intermediateKey")), true, key => DecryptKey(keyRecord, key))); } else { throw new AppEncryptionException("IntermediateKey not present after LoadLatestKeyRecord retry"); } }
internal static Mock <IMetastore <JObject> > CreateMetastoreMock( Partition partition, KeyManagementService kms, KeyState metaIK, KeyState metaSK, CryptoKeyHolder cryptoKeyHolder, IMetastore <JObject> metastore) { CryptoKey systemKey = cryptoKeyHolder.SystemKey; Mock <IMetastore <JObject> > metastoreSpy = new Mock <IMetastore <JObject> >(); metastoreSpy .Setup(x => x.Load(It.IsAny <string>(), It.IsAny <DateTimeOffset>())) .Returns <string, DateTimeOffset>(metastore.Load); metastoreSpy .Setup(x => x.LoadLatest(It.IsAny <string>())) .Returns <string>(metastore.LoadLatest); metastoreSpy .Setup(x => x.Store(It.IsAny <string>(), It.IsAny <DateTimeOffset>(), It.IsAny <JObject>())) .Returns <string, DateTimeOffset, JObject>(metastore.Store); if (metaSK != KeyState.Empty) { if (metaSK == KeyState.Retired) { // We create a revoked copy of the same key DateTimeOffset created = systemKey.GetCreated(); systemKey = systemKey .WithKey(bytes => Crypto.GenerateKeyFromBytes(bytes, created, true)); } EnvelopeKeyRecord systemKeyRecord = new EnvelopeKeyRecord( systemKey.GetCreated(), null, kms.EncryptKey(systemKey), systemKey.IsRevoked()); metastore.Store( partition.SystemKeyId, systemKeyRecord.Created, systemKeyRecord.ToJson()); } if (metaIK != KeyState.Empty) { CryptoKey intermediateKey = cryptoKeyHolder.IntermediateKey; if (metaIK == KeyState.Retired) { // We create a revoked copy of the same key DateTimeOffset created = intermediateKey.GetCreated(); intermediateKey = intermediateKey .WithKey(bytes => Crypto.GenerateKeyFromBytes(bytes, created, true)); } EnvelopeKeyRecord intermediateKeyRecord = new EnvelopeKeyRecord( intermediateKey.GetCreated(), new KeyMeta(partition.SystemKeyId, systemKey.GetCreated()), Crypto.EncryptKey(intermediateKey, systemKey), intermediateKey.IsRevoked()); metastore.Store( partition.IntermediateKeyId, intermediateKeyRecord.Created, intermediateKeyRecord.ToJson()); } return(metastoreSpy); }