/// <summary>
        /// Creates a new <see cref="UserKeyAuthorization"/> entry for a user key and a particular archive.
        /// </summary>
        /// <param name="friendlyName">A friendly name to help the user identify the key.</param>
        /// <param name="userKey">The <see cref="UserKey"/> to authorize.</param>
        /// <param name="archiveKey">The key used to encrypt the archive that the user key is being authorized for.</param>
        /// <param name="securitySettings">The archive's <see cref="SecuritySettings"/>.</param>
        /// <returns>The new <see cref="UserKeyAuthorization"/> entry.</returns>
        public static UserKeyAuthorization CreateNewAuthorization(
            UserKeyAuthorizationParameters newKeyParams,
            ReadOnlySpan <byte> keyDerivationSalt,
            ArchiveKey archiveKey,
            SecuritySettings securitySettings)
        {
            ArgCheck.IsValid(newKeyParams, nameof(newKeyParams));
            ArgCheck.NotEmpty(keyDerivationSalt, nameof(keyDerivationSalt));
            ArgCheck.NotNull(archiveKey, nameof(archiveKey));
            ArgCheck.IsValid(securitySettings, nameof(securitySettings));

            using var userKey = UserKey.DeriveFrom(
                      newKeyParams.UserSecret,
                      keyDerivationSalt,
                      securitySettings);

            // The SecureArchive file format requires that the friendly name and keyId be
            // checked for tampering when using authenticated cyphers.
            var additionalData = Encoding.UTF8.GetBytes(newKeyParams.FriendlyName + userKey.KeyId);

            var cryptoStrategy      = CryptoHelpers.GetCryptoStrategy(securitySettings.EncryptionAlgo);
            var encryptedArchiveKey = userKey.EncryptSecret(cryptoStrategy, archiveKey, additionalData);

            return(new UserKeyAuthorization
            {
                AuthorizationId = Guid.NewGuid(),
                FriendlyName = newKeyParams.FriendlyName,
                KeyId = userKey.KeyId,
                TimeAdded = DateTime.UtcNow,
                EncryptedArchiveKey = encryptedArchiveKey,
                SecretMetadata = newKeyParams.SecretMetadata,
            });
        }
Esempio n. 2
0
        /// <summary>
        /// Authorizes a new user key to access the archive.
        /// </summary>
        /// <param name="authorizationParameters">The new user key authorization parameters.</param>
        public void AuthorizeNewKey(UserKeyAuthorizationParameters authorizationParameters)
        {
            ArgCheck.IsValid(authorizationParameters, nameof(authorizationParameters));

            this.ThrowIfLocked();

            var newAuthorization = UserKeyAuthorizationExtensions.CreateNewAuthorization(
                authorizationParameters,
                this.ArchiveMetadata.KeyDerivationSalt.ToArray(),
                this.ArchiveKey,
                this.ArchiveMetadata.SecuritySettings);

            this.ArchiveMetadata.UserKeyAuthorizations.Add(newAuthorization);

            try
            {
                this.PersistMetadata();
            }
            catch
            {
                // Revert the change. Keep in-memory structure consistent.
                this.ArchiveMetadata.UserKeyAuthorizations.RemoveAt(
                    this.ArchiveMetadata.UserKeyAuthorizations.Count - 1);
                throw;
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Loads a <see cref="AegisBondArchive"/> from disk. This operation does not unlock the archive.
        /// </summary>
        /// <param name="fileSettings">Settings for where the archive and related files are stored.</param>
        /// <returns>The loaded <see cref="AegisBondArchive"/>.</returns>
        public static AegisArchive Load(SecureArchiveFileSettings fileSettings)
        {
            ArgCheck.IsValid(fileSettings, nameof(fileSettings));

            ZipArchive   tempSecureArchive = null;
            AegisArchive archive           = null;

            try
            {
                // Open the secure archive and read the metadata entry.
                tempSecureArchive = OpenSecureArchiveFile(fileSettings);

                var metadataEntry = tempSecureArchive.GetEntry(AegisConstants.SecureArchiveMetadataEntryName);

                if (metadataEntry is null)
                {
                    throw new ArchiveCorruptedException("The secure archive does not contain any internal metadata!");
                }

                using var metadataReader = new StreamReader(metadataEntry.Open());

                var metadataJson = metadataReader.ReadToEnd();
                var metadata     = JsonSerializer.Deserialize <SecureArchiveMetadata>(metadataJson);

                if (metadata is null)
                {
                    throw new ArchiveCorruptedException("Unable to parse the archive internal metadata!");
                }

                archive = new AegisArchive
                {
                    ArchiveMetadata = metadata,
                    FileSettings    = fileSettings,
                    SecureArchive   = tempSecureArchive,
                };

                // We've transfered dispose ownership of tempSecureArchive to the archive.
                tempSecureArchive = null;
            }
            finally
            {
                tempSecureArchive?.Dispose();
            }

            return(archive);
        }
        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);
        }
Esempio n. 5
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SecureArchiveCreationParameters"/> class.
        /// </summary>
        /// <param name="firstKeyAuthorizationParam">The parameters to authorize the first user key.</param>
        public SecureArchiveCreationParameters(UserKeyAuthorizationParameters firstKeyAuthorizationParam)
        {
            ArgCheck.IsValid(firstKeyAuthorizationParam, nameof(firstKeyAuthorizationParam));

            this.FirstKeyAuthorizationParams = firstKeyAuthorizationParam;
        }
Esempio n. 6
0
        /// <summary>
        /// Creates a new <see cref="AegisArchive"/> that contains no files.
        /// </summary>
        /// <param name="fileSettings">Settings for where the archive and related files are stored.</param>
        /// <param name="creationParameters">The <see cref="SecureArchiveCreationParameters"/> to use when creating the archive.</param>
        /// <returns>A new <see cref="AegisArchive"/> that is opened but not yet persisted to disk.</returns>
        public static AegisArchive CreateNew(
            SecureArchiveFileSettings fileSettings,
            SecureArchiveCreationParameters creationParameters)
        {
            ArgCheck.IsValid(fileSettings, nameof(fileSettings));
            ArgCheck.IsValid(creationParameters, nameof(creationParameters));

            var currentTime = DateTimeOffset.UtcNow;
            var archiveId   = Guid.NewGuid();

            ArchiveKey tempArchiveKey = null;

            AegisArchive tempArchive = null;
            AegisArchive archive     = null;

            try
            {
                tempArchiveKey = ArchiveKey.CreateNew(creationParameters.SecuritySettings);

                // Derive and authorize the first user key.
                var keyDerivationSalt = CryptoHelpers.GetRandomBytes(creationParameters.KeyDerivationSaltSizeInBytes);

                var firstUserKeyAuthorization = UserKeyAuthorizationExtensions.CreateNewAuthorization(
                    creationParameters.FirstKeyAuthorizationParams,
                    keyDerivationSalt,
                    tempArchiveKey,
                    creationParameters.SecuritySettings);

                var authCanary = tempArchiveKey.Encrypt(
                    CryptoHelpers.GetCryptoStrategy(creationParameters.SecuritySettings.EncryptionAlgo),
                    archiveId.ToByteArray());

                var archiveMetadata = new SecureArchiveMetadata
                {
                    Id = archiveId,
                    SecuritySettings      = creationParameters.SecuritySettings,
                    CreateTime            = currentTime,
                    LastModifiedTime      = currentTime,
                    KeyDerivationSalt     = new List <byte>(keyDerivationSalt),
                    AuthCanary            = authCanary,
                    UserKeyAuthorizations = new List <UserKeyAuthorization> {
                        firstUserKeyAuthorization
                    },
                };

                tempArchive = new AegisArchive
                {
                    ArchiveMetadata = archiveMetadata,
                    ArchiveKey      = tempArchiveKey,
                    FileSettings    = fileSettings,
                    FileIndex       = new FileIndex(),
                    SecureArchive   = OpenSecureArchiveFile(fileSettings, createNewArchive: true),
                };

                tempArchive.PersistMetadata();

                // Transfer the archive reference to the return variable.
                archive     = tempArchive;
                tempArchive = null;

                // Dispose ownership of the archive key now belongs to the archive.
                tempArchiveKey = null;
            }
            finally
            {
                tempArchive?.Dispose();
                tempArchiveKey?.Dispose();
            }

            return(archive);
        }