/*
* Copyright 2019–present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
namespace MongoDB.Libmongocrypt
{
///
/// A encryption or decryption session. It may not be reused.
///
///
///
public class CryptContext : IDisposable, IStatus
{
///
/// States of the CryptContext state machine
///
public enum StateCode
{
///
/// LibMongoCrypt hit an error
///
MONGOCRYPT_CTX_ERROR = 0,
///
/// LibMongoCrypt wants the collection information by running a OP_MSG command against the users' mongod.
///
MONGOCRYPT_CTX_NEED_MONGO_COLLINFO = 1,
///
/// LibMongoCrypt wants a command to be run against mongocryptd.
///
MONGOCRYPT_CTX_NEED_MONGO_MARKINGS = 2,
///
/// LibMongoCrypt wants a command to be run against mongod key vault.
///
MONGOCRYPT_CTX_NEED_MONGO_KEYS = 3,
///
/// LibMongoCrypt wants a request sent to KMS.
///
MONGOCRYPT_CTX_NEED_KMS = 4,
///
/// LibMongoCrypt is ready to do encryption, call Finish().
///
MONGOCRYPT_CTX_READY = 5,
///
/// LibMongoCrypt is complete.
///
MONGOCRYPT_CTX_DONE = 6,
///
/// LibMongoCrypt requires new credentials.
///
MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS = 7
}
private ContextSafeHandle _handle;
private Status _status;
internal CryptContext(ContextSafeHandle handle)
{
_handle = handle;
_status = new Status();
}
///
/// Gets the state.
///
///
/// The state.
///
public StateCode State
{
get
{
return Library.mongocrypt_ctx_state(_handle);
}
}
///
/// Gets a value indicating whether this instance is done.
///
///
/// true if this instance is done; otherwise, false.
///
public bool IsDone
{
get { return State == StateCode.MONGOCRYPT_CTX_DONE; }
}
///
/// Gets the operation.
///
/// Binary payload to send to either KMS, mongod, or mongocryptd
public Binary GetOperation()
{
Binary binary = new Binary();
Check(Library.mongocrypt_ctx_mongo_op(_handle, binary.Handle));
return binary;
}
///
/// Feeds the result from running a remote operation back to the libmongocrypt.
///
/// The buffer.
public void Feed(byte[] buffer)
{
unsafe
{
fixed (byte* p = buffer)
{
IntPtr ptr = (IntPtr)p;
using (PinnedBinary pinned = new PinnedBinary(ptr, (uint)buffer.Length))
{
Check(Library.mongocrypt_ctx_mongo_feed(_handle, pinned.Handle));
}
}
}
}
///
/// Signal the feeding is done.
///
public void MarkDone()
{
Check(Library.mongocrypt_ctx_mongo_done(_handle));
}
///
/// Finalizes for encryption.
///
/// The encrypted or decrypted result.
public Binary FinalizeForEncryption()
{
Binary binary = new Binary();
Check(Library.mongocrypt_ctx_finalize(_handle, binary.Handle));
return binary;
}
///
/// Gets a collection of KMS message requests to make
///
/// Collection of KMS Messages
public KmsRequestCollection GetKmsMessageRequests()
{
var requests = new List();
for (IntPtr request = Library.mongocrypt_ctx_next_kms_ctx(_handle); request != IntPtr.Zero; request = Library.mongocrypt_ctx_next_kms_ctx(_handle))
{
requests.Add(new KmsRequest(request));
}
return new KmsRequestCollection(requests, this);
}
public void SetCredentials(byte[] credentials)
{
PinnedBinary.RunAsPinnedBinary(_handle, credentials, _status, (h, b) => Library.mongocrypt_ctx_provide_kms_providers(h, b));
}
void IStatus.Check(Status status)
{
Library.mongocrypt_ctx_status(_handle, status.Handle);
}
#region IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// Adapted from: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.safehandle?view=netcore-3.0
if (_handle != null && !_handle.IsInvalid)
{
// Free the handle
_handle.Dispose();
}
}
#endregion
internal void MarkKmsDone()
{
Check(Library.mongocrypt_ctx_kms_done(_handle));
}
private void Check(bool success)
{
if (!success)
{
_status.Check(this);
}
}
}
}