}// Encrypt()

		public static void Decrypt(byte[] masterKey, ArraySegment<byte> ciphertext, ref ArraySegment<byte>? outputSegment, ArraySegment<byte>? salt = null, uint counter = 1)
		{
			int cipherLength = ciphertext.Count - CONTEXT_BUFFER_LENGTH - MAC_LENGTH;
			if (cipherLength < 0) { outputSegment = null; return; }
			try
			{
				Kdf.SP800_108_Ctr.DeriveKey(hmacFactory: _hmacFactory, key: masterKey, label: salt, context: new ArraySegment<byte>(ciphertext.Array, ciphertext.Offset, CONTEXT_TWEAK_LENGTH), derivedOutput: new ArraySegment<byte>(_sessionKey.Value), counter: counter);
				Utils.BlockCopy(_sessionKey.Value, 0, _macKey.Value, 0, MAC_KEY_LENGTH);

				using (var hmac = _hmacFactory())
				{
					hmac.Key = _macKey.Value;
					hmac.TransformBlock(ciphertext.Array, ciphertext.Offset + CONTEXT_TWEAK_LENGTH, NONCE_LENGTH + cipherLength, null, 0);
					hmac.TransformFinalBlock(ciphertext.Array, 0, 0);
					var fullmacActual = hmac.HashInner;
					if (!Utils.ConstantTimeEqual(fullmacActual, 0, ciphertext.Array, ciphertext.Offset + ciphertext.Count - MAC_LENGTH, MAC_LENGTH)) { outputSegment = null; return; };
				}// using hmac

				if (outputSegment == null) outputSegment = (new byte[cipherLength]).AsNullableArraySegment();
				Utils.BlockCopy(ciphertext.Array, ciphertext.Offset + CONTEXT_TWEAK_LENGTH, _counterBuffer.Value, 0, NONCE_LENGTH);
				Utils.BlockCopy(_sessionKey.Value, MAC_KEY_LENGTH, _encKey.Value, 0, ENC_KEY_LENGTH);
				using (var ctrTransform = new Cipher.AesCtrCryptoTransform(key: _encKey.Value, counterBufferSegment: _counterBuffer.Value.AsArraySegment(), aesFactory: _aesFactory))
				{
					ctrTransform.TransformBlock(inputBuffer: ciphertext.Array, inputOffset: ciphertext.Offset + CONTEXT_BUFFER_LENGTH, inputCount: cipherLength, outputBuffer: outputSegment.GetValueOrDefault().Array, outputOffset: outputSegment.GetValueOrDefault().Offset);
				}// using aesDecryptor
			}
			finally { EtM_CTR.ClearKeyMaterial(); }
		}// Decrypt()
		public static void Encrypt(byte[] masterKey, ArraySegment<byte> plaintext, byte[] output, int outputOffset, ArraySegment<byte>? salt = null, uint counter = 1)
		{
			int ciphertextLength = CONTEXT_BUFFER_LENGTH + plaintext.Count + MAC_LENGTH;
			if (output.Length - outputOffset < ciphertextLength) throw new ArgumentOutOfRangeException(nameof(output), $"'{nameof(output)}' array segment is not big enough for the ciphertext");
			try
			{
				_cryptoRandom.NextBytes(_contextBuffer.Value);

				Kdf.SP800_108_Ctr.DeriveKey(hmacFactory: _hmacFactory, key: masterKey, label: salt, context: new ArraySegment<byte>(_contextBuffer.Value, 0, CONTEXT_TWEAK_LENGTH), derivedOutput: _sessionKey.Value.AsArraySegment(), counter: counter);

				Utils.BlockCopy(_sessionKey.Value, 0, _macKey.Value, 0, MAC_KEY_LENGTH);
				Utils.BlockCopy(_sessionKey.Value, MAC_KEY_LENGTH, _encKey.Value, 0, ENC_KEY_LENGTH);
				Utils.BlockCopy(_contextBuffer.Value, 0, output, outputOffset, CONTEXT_BUFFER_LENGTH);

				Utils.BlockCopy(_contextBuffer.Value, CONTEXT_TWEAK_LENGTH, _counterBuffer.Value, 0, NONCE_LENGTH);
				using (var ctrTransform = new Cipher.AesCtrCryptoTransform(key: _encKey.Value, counterBufferSegment: _counterBuffer.Value.AsArraySegment(), aesFactory: _aesFactory))
				{
					ctrTransform.TransformBlock(inputBuffer: plaintext.Array, inputOffset: plaintext.Offset, inputCount: plaintext.Count, outputBuffer: output, outputOffset: outputOffset + CONTEXT_BUFFER_LENGTH);
				}// using aesEncryptor

				using (var hmac = _hmacFactory())
				{
					hmac.Key = _macKey.Value;
					hmac.TransformBlock(output, outputOffset + CONTEXT_TWEAK_LENGTH, NONCE_LENGTH + plaintext.Count, null, 0);
					hmac.TransformFinalBlock(output, 0, 0);
					var fullmac = hmac.HashInner;
					Utils.BlockCopy(fullmac, 0, output, outputOffset + ciphertextLength - MAC_LENGTH, MAC_LENGTH);
				}// using hmac
			}
			finally { EtM_CTR.ClearKeyMaterial(); }
		}// Encrypt()
Exemple #3
0
		}// Decrypt()

		public static bool Authenticate(byte[] masterKey, ArraySegment<byte> ciphertext, ArraySegment<byte>? salt = null, uint counter = 1)
		{
			int cipherLength = ciphertext.Count - CONTEXT_BUFFER_LENGTH - MAC_LENGTH;
			if (cipherLength < 0) return false;

			var encKey = new byte[ENC_KEY_LENGTH];
			var macKey = new byte[MAC_KEY_LENGTH];
			var sessionKey = new byte[HMAC_LENGTH];

			try
			{
				var ciphertextArray = ciphertext.Array;
				var ciphertextOffset = ciphertext.Offset;

				Kdf.SP800_108_Ctr.DeriveKey(hmacFactory: _hmacFactory, key: masterKey, label: salt, context: new ArraySegment<byte>(ciphertextArray, ciphertextOffset, CONTEXT_TWEAK_LENGTH), derivedOutput: sessionKey.AsArraySegment(), counter: counter);

				//Utils.BlockCopy(sessionKey, 0, macKey, 0, MAC_KEY_LENGTH);
				for (int i = 0; i < macKey.Length; ++i) macKey[i] = sessionKey[i];

				using (var hmac = _hmacFactory())
				{
					hmac.Key = macKey;
					hmac.TransformBlock(ciphertextArray, ciphertextOffset + CONTEXT_TWEAK_LENGTH, NONCE_LENGTH + cipherLength, null, 0);
					hmac.TransformFinalBlock(ciphertextArray, 0, 0);
					var fullmacActual = hmac.HashInner;
					if (!Utils.ConstantTimeEqual(fullmacActual, 0, ciphertextArray, ciphertextOffset + ciphertext.Count - MAC_LENGTH, MAC_LENGTH)) return false;
				}// using hmac
				return true;
			}
			finally { EtM_CTR.ClearKeyMaterial(encKey, macKey, sessionKey); }
		}// Authenticate()
Exemple #4
0
		public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
		{
			int partialBlockSize = inputCount % EtM_Transform_Constants.INPUT_BLOCK_SIZE;
			int fullBlockSize = inputCount - partialBlockSize;

			if (partialBlockSize != 0)
				throw new Exception("inputCount must be a multiple of input block size (" + EtM_Transform_Constants.INPUT_BLOCK_SIZE.ToString() + ").");

			int i = 0, j = 0;
			if (fullBlockSize > 0)
			{
				for (; i < fullBlockSize; i += EtM_Transform_Constants.INPUT_BLOCK_SIZE, j += EtM_Transform_Constants.OUTPUT_BLOCK_SIZE)
				{
					EtM_CTR.Encrypt(
						masterKey: this.key,
						plaintext: new ArraySegment<byte>(inputBuffer, inputOffset + i, EtM_Transform_Constants.INPUT_BLOCK_SIZE),
						output: outputBuffer,
						outputOffset: outputOffset + j,
						salt: this.salt,
						counter: this.currentChunkNumber);

					if (this.currentChunkNumber == EtM_Transform_Constants.INITIAL_CHUNK_NUMBER)
					{
						EtM_EncryptTransform.PrependSaltWith1stBlockContext(ref this.salt, outputBuffer, outputOffset);
					}

					checked { ++this.currentChunkNumber; }
				}
			}
			return j;
		}// TransformBlock()
		}// Decrypt()

		public static byte[] Decrypt(byte[] masterKey, ArraySegment<byte> ciphertext, ArraySegment<byte>? salt = null, uint counter = 1)
		{
			int cipherLength = ciphertext.Count - CONTEXT_BUFFER_LENGTH - MAC_LENGTH;
			if (cipherLength < 0) return null;
			var bufferSegment = default(ArraySegment<byte>?);
			EtM_CTR.Decrypt(masterKey, ciphertext, ref bufferSegment, salt, counter);
			return (bufferSegment != null) ? bufferSegment.GetValueOrDefault().Array : null;
		}// Decrypt()
Exemple #6
0
		public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
		{
			int partialBlockSize = inputCount % EtM_Transform_Constants.OUTPUT_BLOCK_SIZE;
			int fullBlockSize = inputCount - partialBlockSize;

			if (partialBlockSize != 0)
				throw new Exception("inputCount must be a multiple of output block size (" + EtM_Transform_Constants.OUTPUT_BLOCK_SIZE.ToString() + ").");

			int i = 0, j = 0;
			if (fullBlockSize > 0)
			{
				var authenticateOnly = this.IsAuthenticateOnly;
				for (; i < fullBlockSize; i += EtM_Transform_Constants.OUTPUT_BLOCK_SIZE, j += EtM_Transform_Constants.INPUT_BLOCK_SIZE)
				{
					var outputSegment = new ArraySegment<byte>?(new ArraySegment<byte>(outputBuffer, outputOffset + j, EtM_Transform_Constants.INPUT_BLOCK_SIZE));
					var cipherText = new ArraySegment<byte>(inputBuffer, inputOffset + i, EtM_Transform_Constants.OUTPUT_BLOCK_SIZE);

					if (authenticateOnly)
					{
						if (!EtM_CTR.Authenticate(
							masterKey: this.key,
							ciphertext: cipherText,
							salt: this.salt,
							counter: this.currentChunkNumber))
							outputSegment = null;
					}
					else
					{
						EtM_CTR.Decrypt(
							masterKey: this.key,
							ciphertext: cipherText,
							outputSegment: ref outputSegment,
							salt: this.salt,
							counter: this.currentChunkNumber);
					}

					if (outputSegment == null)
					{
						this.key = null;
						throw new CryptographicException("Decryption failed for block " + this.currentChunkNumber.ToString() + ".");
					}

					if (this.currentChunkNumber == EtM_Transform_Constants.INITIAL_CHUNK_NUMBER)
					{
						EtM_EncryptTransform.PrependSaltWith1stBlockContext(ref this.salt, inputBuffer, inputOffset);
					}

					checked { ++this.currentChunkNumber; }
				}
			}
			return j;
		}// TransformBlock()
Exemple #7
0
		}// ClearKeyMaterial()

		public static void Encrypt(byte[] masterKey, ArraySegment<byte> plaintext, byte[] output, int outputOffset, ArraySegment<byte>? salt = null, uint counter = 1)
		{
			int ciphertextLength = CONTEXT_BUFFER_LENGTH + plaintext.Count + MAC_LENGTH;
			if (output.Length - outputOffset < ciphertextLength) throw new ArgumentOutOfRangeException(nameof(output), $"'{nameof(output)}' array segment is not big enough for the ciphertext");

			var counterBuffer = new byte[Cipher.AesConstants.AES_BLOCK_SIZE];
			var contextBuffer = new byte[CONTEXT_BUFFER_LENGTH];
			var encKey = new byte[ENC_KEY_LENGTH];
			var macKey = new byte[MAC_KEY_LENGTH];
			var sessionKey = new byte[HMAC_LENGTH];

			try
			{
				_cryptoRandom.NextBytes(contextBuffer, 0, CONTEXT_BUFFER_LENGTH);

				Kdf.SP800_108_Ctr.DeriveKey(hmacFactory: _hmacFactory, key: masterKey, label: salt, context: new ArraySegment<byte>(contextBuffer, 0, CONTEXT_TWEAK_LENGTH), derivedOutput: sessionKey.AsArraySegment(), counter: counter);

				//Utils.BlockCopy(sessionKey, 0, macKey, 0, MAC_KEY_LENGTH);
				for (int i = 0; i < macKey.Length; ++i) macKey[i] = sessionKey[i];

				//Utils.BlockCopy(sessionKey, MAC_KEY_LENGTH, encKey, 0, ENC_KEY_LENGTH);
				for (int i = 0; i < encKey.Length; ++i) encKey[i] = sessionKey[MAC_KEY_LENGTH + i];

				//Utils.BlockCopy(contextBuffer, 0, output, outputOffset, CONTEXT_BUFFER_LENGTH);
				for (int i = 0; i < contextBuffer.Length; ++i) output[outputOffset + i] = contextBuffer[i];

				//Utils.BlockCopy(contextBuffer, CONTEXT_TWEAK_LENGTH, counterBuffer, 0, NONCE_LENGTH);
				for (int i = 0; i < NONCE_LENGTH; ++i) counterBuffer[i] = contextBuffer[CONTEXT_TWEAK_LENGTH + i];

				using (var ctrTransform = new Cipher.AesCtrCryptoTransform(key: encKey, counterBufferSegment: counterBuffer.AsArraySegment(), aesFactory: _aesFactory))
				{
					ctrTransform.TransformBlock(inputBuffer: plaintext.Array, inputOffset: plaintext.Offset, inputCount: plaintext.Count, outputBuffer: output, outputOffset: outputOffset + CONTEXT_BUFFER_LENGTH);
				}// using aesEncryptor

				using (var hmac = _hmacFactory())
				{
					hmac.Key = macKey;
					hmac.TransformBlock(output, outputOffset + CONTEXT_TWEAK_LENGTH, NONCE_LENGTH + plaintext.Count, null, 0);
					hmac.TransformFinalBlock(output, 0, 0);
					var fullmac = hmac.HashInner;

					//Utils.BlockCopy(fullmac, 0, output, outputOffset + ciphertextLength - MAC_LENGTH, MAC_LENGTH);
					for (int i = 0; i < MAC_LENGTH; ++i) output[outputOffset + ciphertextLength - MAC_LENGTH + i] = fullmac[i];
				}// using hmac
			}
			finally { EtM_CTR.ClearKeyMaterial(encKey, macKey, sessionKey); }
		}// Encrypt()
Exemple #8
0
		}// Encrypt()

		public static void Decrypt(byte[] masterKey, ArraySegment<byte> ciphertext, ref ArraySegment<byte>? outputSegment, ArraySegment<byte>? salt = null, uint counter = 1)
		{
			int cipherLength = ciphertext.Count - CONTEXT_BUFFER_LENGTH - MAC_LENGTH;
			if (cipherLength < 0) { outputSegment = null; return; }

			var counterBuffer = new byte[Cipher.AesConstants.AES_BLOCK_SIZE];
			var encKey = new byte[ENC_KEY_LENGTH];
			var macKey = new byte[MAC_KEY_LENGTH];
			var sessionKey = new byte[HMAC_LENGTH];

			try
			{
				var ciphertextArray = ciphertext.Array;
				var ciphertextOffset = ciphertext.Offset;

				Kdf.SP800_108_Ctr.DeriveKey(hmacFactory: _hmacFactory, key: masterKey, label: salt, context: new ArraySegment<byte>(ciphertextArray, ciphertextOffset, CONTEXT_TWEAK_LENGTH), derivedOutput: sessionKey.AsArraySegment(), counter: counter);

				//Utils.BlockCopy(sessionKey, 0, macKey, 0, MAC_KEY_LENGTH);
				for (int i = 0; i < macKey.Length; ++i) macKey[i] = sessionKey[i];

				using (var hmac = _hmacFactory())
				{
					hmac.Key = macKey;
					hmac.TransformBlock(ciphertextArray, ciphertextOffset + CONTEXT_TWEAK_LENGTH, NONCE_LENGTH + cipherLength, null, 0);
					hmac.TransformFinalBlock(ciphertextArray, 0, 0);
					var fullmacActual = hmac.HashInner;
					if (!Utils.ConstantTimeEqual(fullmacActual, 0, ciphertextArray, ciphertextOffset + ciphertext.Count - MAC_LENGTH, MAC_LENGTH)) { outputSegment = null; return; };
				}// using hmac

				if (outputSegment == null) outputSegment = (new byte[cipherLength]).AsNullableArraySegment();

				//Utils.BlockCopy(ciphertext.Array, ciphertext.Offset + CONTEXT_TWEAK_LENGTH, counterBuffer, 0, NONCE_LENGTH);
				for (int i = 0; i < NONCE_LENGTH; ++i) counterBuffer[i] = ciphertextArray[ciphertextOffset + CONTEXT_TWEAK_LENGTH + i];

				//Utils.BlockCopy(sessionKey, MAC_KEY_LENGTH, encKey, 0, ENC_KEY_LENGTH);
				for (int i = 0; i < encKey.Length; ++i) encKey[i] = sessionKey[MAC_KEY_LENGTH + i];

				using (var ctrTransform = new Cipher.AesCtrCryptoTransform(key: encKey, counterBufferSegment: counterBuffer.AsArraySegment(), aesFactory: _aesFactory))
				{
					ctrTransform.TransformBlock(inputBuffer: ciphertextArray, inputOffset: ciphertextOffset + CONTEXT_BUFFER_LENGTH, inputCount: cipherLength, outputBuffer: outputSegment.GetValueOrDefault().Array, outputOffset: outputSegment.GetValueOrDefault().Offset);
				}// using aesDecryptor
			}
			finally { EtM_CTR.ClearKeyMaterial(encKey, macKey, sessionKey); }
		}// Decrypt()
Exemple #9
0
		}// TransformBlock()

		public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
		{
			if (this.key == null) return null; // key would be null if this instance has already been disposed
			if (inputCount >= EtM_Transform_Constants.INPUT_BLOCK_SIZE)
				throw new Exception("Final input block size must be smaller than " + EtM_Transform_Constants.INPUT_BLOCK_SIZE.ToString() + ".");

			byte[] outputBuffer = new byte[EtM_Transform_Constants.ETM_CTR_OVERHEAD + inputCount];

			EtM_CTR.Encrypt(
				masterKey: this.key,
				plaintext: new ArraySegment<byte>(inputBuffer, inputOffset, inputCount),
				output: outputBuffer,
				outputOffset: 0,
				salt: this.salt,
				counter: this.currentChunkNumber);

			this.Dispose();
			return outputBuffer;
		}// TransformFinalBlock()
		}// Decrypt()

		public static bool Authenticate(byte[] masterKey, ArraySegment<byte> ciphertext, ArraySegment<byte>? salt = null, uint counter = 1)
		{
			int cipherLength = ciphertext.Count - CONTEXT_BUFFER_LENGTH - MAC_LENGTH;
			if (cipherLength < 0) return false;
			try
			{
				Kdf.SP800_108_Ctr.DeriveKey(hmacFactory: _hmacFactory, key: masterKey, label: salt, context: new ArraySegment<byte>(ciphertext.Array, ciphertext.Offset, CONTEXT_TWEAK_LENGTH), derivedOutput: _sessionKey.Value.AsArraySegment(), counter: counter);
				Utils.BlockCopy(_sessionKey.Value, 0, _macKey.Value, 0, MAC_KEY_LENGTH);
				using (var hmac = _hmacFactory())
				{
					hmac.Key = _macKey.Value;
					hmac.TransformBlock(ciphertext.Array, ciphertext.Offset + CONTEXT_TWEAK_LENGTH, NONCE_LENGTH + cipherLength, null, 0);
					hmac.TransformFinalBlock(ciphertext.Array, 0, 0);
					var fullmacActual = hmac.HashInner;
					if (!Utils.ConstantTimeEqual(fullmacActual, 0, ciphertext.Array, ciphertext.Offset + ciphertext.Count - MAC_LENGTH, MAC_LENGTH)) return false;
				}// using hmac
				return true;
			}
			finally { EtM_CTR.ClearKeyMaterial(); }
		}// Authenticate()
Exemple #11
0
		}// TransformBlock()

		public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
		{
			if (this.key == null) return null; // key would be null if this instance has already been disposed, or previously-called TransformBlock() failed
			if (inputCount >= EtM_Transform_Constants.OUTPUT_BLOCK_SIZE)
				throw new Exception("Final output block size must be smaller than " + EtM_Transform_Constants.OUTPUT_BLOCK_SIZE.ToString() + ".");

			if (inputCount < EtM_Transform_Constants.ETM_CTR_OVERHEAD)
				throw new Exception("Final output block size must must be at least " + EtM_Transform_Constants.ETM_CTR_OVERHEAD.ToString() + ".");

			byte[] outputBuffer = null;
			var cipherText = new ArraySegment<byte>(inputBuffer, inputOffset, inputCount);

			if (this.IsAuthenticateOnly)
			{
				if (EtM_CTR.Authenticate(
					masterKey: this.key,
					ciphertext: cipherText,
					salt: this.salt,
					counter: this.currentChunkNumber))
					outputBuffer = Array.Empty<byte>();
			}
			else
			{
				outputBuffer = EtM_CTR.Decrypt(
					masterKey: this.key,
					ciphertext: cipherText,
					salt: this.salt,
					counter: this.currentChunkNumber);
			}
			this.Dispose();
			if (outputBuffer == null)
				throw new CryptographicException("Decryption failed for block " + this.currentChunkNumber.ToString() + ".");

			this.IsComplete = true;
			return outputBuffer;
		}// TransformFinalBlock()
Exemple #12
0
		public static bool Authenticate(byte[] masterKey, ArraySegment<byte> ciphertext, ArraySegment<byte>? salt = null)
		{
			return EtM_CTR.Authenticate(masterKey, ciphertext, salt);
		}
Exemple #13
0
		public static byte[] Decrypt(byte[] masterKey, ArraySegment<byte> ciphertext, ArraySegment<byte>? salt = null)
		{
			return EtM_CTR.Decrypt(masterKey, ciphertext, salt);
		}
Exemple #14
0
		public static byte[] Encrypt(byte[] masterKey, ArraySegment<byte> plaintext, ArraySegment<byte>? salt = null)
		{
			return EtM_CTR.Encrypt(masterKey, plaintext, salt);
		}
		}// Encrypt()

		public static byte[] Encrypt(byte[] masterKey, ArraySegment<byte> plaintext, ArraySegment<byte>? salt = null, uint counter = 1)
		{
			byte[] buffer = new byte[CONTEXT_BUFFER_LENGTH + plaintext.Count + MAC_LENGTH];
			EtM_CTR.Encrypt(masterKey: masterKey, plaintext: plaintext, output: buffer, outputOffset: 0, salt: salt, counter: counter);
			return buffer;
		}// Encrypt()