/// <summary> /// Revokes an authorized key so it can no longer unlock the archive. /// </summary> /// <param name="authorizationId">The authorization ID of the key to revoke.</param> /// <exception cref="InvalidOperationException">The specified authorization record was not found.</exception> public void RevokeKey(Guid authorizationId) { var matchingAuthorizations = this.ArchiveMetadata.UserKeyAuthorizations .Where(k => k.AuthorizationId == authorizationId) .ToArray(); if (!matchingAuthorizations.Any()) { throw new EntityNotFoundException( $"Authorization with ID '{authorizationId}' was not found."); } if (matchingAuthorizations.Length > 1) { // There should never be two authorizations with the same ID. // If this happens, fail closed to avoid any further archive corruption. throw new ArchiveCorruptedException( $"Found 2 key authorizations with ID '{authorizationId}'. Cancelling revocation operation."); } if (this.ArchiveMetadata.UserKeyAuthorizations.Count == 1) { throw new InvalidOperationException($"Can't revoke the last authorized key."); } // We'll track the item being removed. If saving the changes fails, we'll // revert the removal to put the in-memory structure back into a consistent state. UserKeyAuthorization removedAuthorization = null; int removedIndex = -1; for (int i = 0; i < this.ArchiveMetadata.UserKeyAuthorizations.Count; i++) { if (this.ArchiveMetadata.UserKeyAuthorizations[i].AuthorizationId == authorizationId) { removedAuthorization = this.ArchiveMetadata.UserKeyAuthorizations[i]; removedIndex = i; this.ArchiveMetadata.UserKeyAuthorizations.RemoveAt(i); break; } } try { this.PersistMetadata(); } catch { // Revert the change. Keep in-memory structure consistent. this.ArchiveMetadata.UserKeyAuthorizations.Insert( removedIndex, removedAuthorization); throw; } }
public static bool TryDecryptArchiveKey( this UserKeyAuthorization authorization, UserKey userKey, SecuritySettings securitySettings, out ArchiveKey archiveKey) { ArgCheck.NotNull(authorization, nameof(authorization)); ArgCheck.NotNull(userKey, nameof(userKey)); ArgCheck.IsValid(securitySettings, nameof(securitySettings)); archiveKey = null; if (!CryptoHelpers.SecureEquals(userKey.KeyId, authorization.KeyId)) { return(false); } try { // The SecureArchive file format requires that the friendly name and keyId be // checked for tampering when using authenticated cyphers. var additionalData = Encoding.UTF8.GetBytes(authorization.FriendlyName + authorization.KeyId); var cryptoStrategy = CryptoHelpers.GetCryptoStrategy(securitySettings.EncryptionAlgo); var decryptedArchiveKey = userKey.Decrypt(cryptoStrategy, authorization.EncryptedArchiveKey, additionalData); if (!decryptedArchiveKey.IsEmpty) { archiveKey = new ArchiveKey(decryptedArchiveKey.ToArray()); } } catch { return(false); } return(archiveKey != null); }