}//NextBytes()

		void NextBytesInternal(ArraySegment<byte> bufferSegment)
		{
			BCrypt.NTSTATUS status;
			var buffer = bufferSegment.Array;
			var offset = bufferSegment.Offset;
			var count = bufferSegment.Count;

			if (count > CACHE_THRESHOLD)
			{
				status = (offset == 0) ? BCrypt.BCryptGenRandom(buffer, count) : BCrypt.BCryptGenRandom_PinnedBuffer(buffer, offset, count);
				if (status == BCrypt.NTSTATUS.STATUS_SUCCESS) return;
				throw new CryptographicException((int)status);
			}

			lock (_byteCache)
			{
				if (_byteCachePosition + count <= BYTE_CACHE_SIZE)
				{
					Utils.BlockCopy(_byteCache, _byteCachePosition, buffer, offset, count);
					_byteCachePosition += count;
					return;
				}

				status = BCrypt.BCryptGenRandom(_byteCache, BYTE_CACHE_SIZE);
				if (status == BCrypt.NTSTATUS.STATUS_SUCCESS)
				{
					_byteCachePosition = count;
					Utils.BlockCopy(_byteCache, 0, buffer, offset, count);
					return;
				}
				throw new CryptographicException((int)status);
			}// lock
		}//NextBytesInternal()
예제 #2
0
		}// 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 < Cipher.AesConstants.AES_BLOCK_SIZE) return null;
			try
			{
				Kdf.SP800_108_Ctr.DeriveKey(hmacFactory: _hmacFactory, key: masterKey, label: salt, context: new ArraySegment<byte>(ciphertext.Array, ciphertext.Offset, CONTEXT_BUFFER_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, AES_IV_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 null;
				}// using hmac

				Utils.BlockCopy(ciphertext.Array, ciphertext.Offset + CONTEXT_TWEAK_LENGTH, _iv.Value, 0, AES_IV_LENGTH);
				Utils.BlockCopy(_sessionKey.Value, MAC_KEY_LENGTH, _encKey.Value, 0, ENC_KEY_LENGTH);

				using (var aes = _aesFactory())
				{
					EtM_CBC.ValidateAes(aes);
					using (var aesDecryptor = aes.CreateDecryptor(_encKey.Value, _iv.Value))
					{
						return aesDecryptor.TransformFinalBlock(ciphertext.Array, ciphertext.Offset + CONTEXT_BUFFER_LENGTH, cipherLength);
					}// using aesDecryptor
				}// using aes
			}
			finally { EtM_CBC.ClearKeyMaterial(); }
		}// Decrypt()
예제 #3
0
		}//NextBytes()

		void NextBytesInternal(byte[] buffer, int offset, int count)
		{
			BCrypt.NTSTATUS status;

			if (count > CACHE_THRESHOLD)
			{
				status = (offset == 0) ? BCrypt.BCryptGenRandom(buffer, count) : BCrypt.BCryptGenRandom_WithOffset(buffer, offset, count);
				if (status == BCrypt.NTSTATUS.STATUS_SUCCESS) return;
				ThrowNewCryptographicException((int)status);
			}

			lock (_byteCache)
			{
				if (_byteCachePosition + count <= BYTE_CACHE_SIZE)
				{
					Utils.BlockCopy(_byteCache, _byteCachePosition, buffer, offset, count);
					_byteCachePosition += count;
					return;
				}

				status = BCrypt.BCryptGenRandom(_byteCache, BYTE_CACHE_SIZE);
				if (status == BCrypt.NTSTATUS.STATUS_SUCCESS)
				{
					_byteCachePosition = count;
					Utils.BlockCopy(_byteCache, 0, buffer, offset, count);
					return;
				}
				ThrowNewCryptographicException((int)status);
			}// lock
		}//NextBytesInternal()
예제 #4
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; }
			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()
예제 #5
0
		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()
예제 #6
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 < Cipher.AesConstants.AES_BLOCK_SIZE) { outputSegment = null; return; }
			int fullBlockLength = cipherLength - AES_IV_LENGTH;
			byte[] finalBlock = null;
			try
			{
				var iv = _iv.Value;
				var encKey = _encKey.Value;
				var macKey = _macKey.Value;
				var sessionKey = _sessionKey.Value;

				Kdf.SP800_108_Ctr.DeriveKey(hmacFactory: _hmacFactory, key: masterKey, label: salt, context: new ArraySegment<byte>(ciphertext.Array, ciphertext.Offset, CONTEXT_BUFFER_LENGTH), derivedOutput: sessionKey.AsArraySegment(), counter: counter);
				Utils.BlockCopy(sessionKey, 0, macKey, 0, MAC_KEY_LENGTH);

				using (var hmac = _hmacFactory())
				{
					hmac.Key = macKey;
					hmac.TransformBlock(ciphertext.Array, ciphertext.Offset + CONTEXT_TWEAK_LENGTH, AES_IV_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

				Utils.BlockCopy(ciphertext.Array, ciphertext.Offset + CONTEXT_TWEAK_LENGTH, iv, 0, AES_IV_LENGTH);
				Utils.BlockCopy(sessionKey, MAC_KEY_LENGTH, encKey, 0, ENC_KEY_LENGTH);

				using (var aes = _aesFactory())
				{
					EtM_CBC.ValidateAes(aes);
					using (var aesDecryptor = aes.CreateDecryptor(encKey, iv))
					{
						int fullBlockTransformed = 0;
						if (fullBlockLength > 0)
							fullBlockTransformed = aesDecryptor.TransformBlock(inputBuffer: ciphertext.Array, inputOffset: ciphertext.Offset + CONTEXT_BUFFER_LENGTH, inputCount: fullBlockLength, outputBuffer: outputSegment.Value.Array, outputOffset: outputSegment.Value.Offset);

						finalBlock = aesDecryptor.TransformFinalBlock(ciphertext.Array, ciphertext.Offset + CONTEXT_BUFFER_LENGTH + fullBlockLength, cipherLength - fullBlockLength);
						Utils.BlockCopy(finalBlock, 0, outputSegment.Value.Array, outputSegment.Value.Offset + fullBlockTransformed, finalBlock.Length);
						outputSegment = new ArraySegment<byte>?(new ArraySegment<byte>(outputSegment.Value.Array, outputSegment.Value.Offset, fullBlockTransformed + finalBlock.Length));
					}// using aesDecryptor
				}// using aes
			}
			finally
			{
				EtM_CBC.ClearKeyMaterial();
				if (finalBlock != null) Array.Clear(finalBlock, 0, finalBlock.Length);
			}
		}// Decrypt()
예제 #7
0
		public static void Encrypt(byte[] masterKey, ArraySegment<byte> plaintext, byte[] output, int outputOffset, ArraySegment<byte>? salt = null, uint counter = 1)
		{
			int fullBlockLength = plaintext.Count & (-Cipher.AesConstants.AES_BLOCK_SIZE);
			int finalBlockLength = plaintext.Count % Cipher.AesConstants.AES_BLOCK_SIZE;
			int paddingLength = Cipher.AesConstants.AES_BLOCK_SIZE - finalBlockLength;
			int ciphertextLength = CONTEXT_BUFFER_LENGTH + plaintext.Count + paddingLength + MAC_LENGTH;
			if (output.Length - outputOffset < ciphertextLength) throw new ArgumentOutOfRangeException(nameof(output), $"'{nameof(output)}' array segment is not big enough for the ciphertext");

			try
			{
				var iv = _iv.Value;
				var contextBuffer = _contextBuffer.Value;
				var encKey = _encKey.Value;
				var macKey = _macKey.Value;
				var sessionKey = _sessionKey.Value;

				using (var aes = _aesFactory())
				{
					EtM_CBC.ValidateAes(aes);
					_cryptoRandom.NextBytes(contextBuffer, 0, CONTEXT_BUFFER_LENGTH);

					Utils.BlockCopy(contextBuffer, CONTEXT_TWEAK_LENGTH, iv, 0, AES_IV_LENGTH);
					Kdf.SP800_108_Ctr.DeriveKey(hmacFactory: _hmacFactory, key: masterKey, label: salt, context: contextBuffer.AsArraySegment(), derivedOutput: sessionKey.AsArraySegment(), counter: counter);

					Utils.BlockCopy(sessionKey, 0, macKey, 0, MAC_KEY_LENGTH);
					Utils.BlockCopy(sessionKey, MAC_KEY_LENGTH, encKey, 0, ENC_KEY_LENGTH);
					Utils.BlockCopy(contextBuffer, 0, output, outputOffset, CONTEXT_BUFFER_LENGTH);
					using (var aesEncryptor = aes.CreateEncryptor(encKey, iv))
					{
						if (fullBlockLength > 0)
							aesEncryptor.TransformBlock(inputBuffer: plaintext.Array, inputOffset: plaintext.Offset, inputCount: fullBlockLength, outputBuffer: output, outputOffset: outputOffset + CONTEXT_BUFFER_LENGTH);

						var finalBlockBuffer = aesEncryptor.TransformFinalBlock(inputBuffer: plaintext.Array, inputOffset: plaintext.Offset + fullBlockLength, inputCount: finalBlockLength);
						Utils.BlockCopy(finalBlockBuffer, 0, output, outputOffset + CONTEXT_BUFFER_LENGTH + fullBlockLength, finalBlockBuffer.Length);
					}// using aesEncryptor
				}// using aes

				using (var hmac = _hmacFactory())
				{
					hmac.Key = macKey;
					hmac.TransformBlock(output, outputOffset + CONTEXT_TWEAK_LENGTH, AES_IV_LENGTH + plaintext.Count + paddingLength, 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_CBC.ClearKeyMaterial(); }
		}// Encrypt()
예제 #8
0
		internal static void PrependSaltWith1stBlockContext(ref ArraySegment<byte>? salt, byte[] contextBuffer, int contextBufferOffset)
		{
			int saltLength = salt?.Count ?? 0;
			var streamContextCombinedWithSalt = new byte[EtM_CTR.CONTEXT_TWEAK_LENGTH + saltLength];

			for (int i = EtM_CTR.CONTEXT_TWEAK_LENGTH - 1; i >= 0; --i)
			{
				streamContextCombinedWithSalt[i] = contextBuffer[contextBufferOffset + i];
			}

			if (saltLength > 0)
			{
				var saltValue = salt.GetValueOrDefault();
				Utils.BlockCopy(saltValue.Array, saltValue.Offset, streamContextCombinedWithSalt, EtM_CTR.CONTEXT_TWEAK_LENGTH, saltLength);
			}
			salt = new ArraySegment<byte>(streamContextCombinedWithSalt);
		}// PrependSaltWith1stBlockContext()
		void NextBytesInternal(ArraySegment<byte> bufferSegment)
		{
			BCrypt.NTSTATUS status;
			var buffer = bufferSegment.Array;
			var offset = bufferSegment.Offset;
			var count = bufferSegment.Count;

			if (count > CACHE_THRESHOLD)
			{
				status = (offset == 0) ? BCrypt.BCryptGenRandom(buffer, count) : BCrypt.BCryptGenRandom_PinnedBuffer(buffer, offset, count);
				if (status == BCrypt.NTSTATUS.STATUS_SUCCESS) return;
				throw new CryptographicException((int)status);
			}

			while (true)
			{
				int currentByteCachePosition = Interlocked.Add(ref _byteCachePosition, count);
				if (currentByteCachePosition <= BYTE_CACHE_SIZE && currentByteCachePosition > 0)
				{
					Utils.BlockCopy(_byteCache, currentByteCachePosition - count, buffer, 0, count);
					return;
				}

				lock (_byteCache)
				{
					currentByteCachePosition = _byteCachePosition; // atomic read
					if (currentByteCachePosition > (BYTE_CACHE_SIZE - count) || currentByteCachePosition <= 0)
					{
						status = BCrypt.BCryptGenRandom(_byteCache, BYTE_CACHE_SIZE);
						if (status == BCrypt.NTSTATUS.STATUS_SUCCESS)
						{
							_byteCachePosition = count; // atomic write
							Utils.BlockCopy(_byteCache, 0, buffer, 0, count);
							return;
						}

						// defensive logic to prevent _byteCachePosition from wrapping into valid range due to BCryptGenRandom failures
						if (currentByteCachePosition > BYTE_CACHE_SIZE || currentByteCachePosition < 0) _byteCachePosition = BYTE_CACHE_SIZE;

						throw new CryptographicException((int)status);
					}// if outside the valid range
				}// lock
			}// while(true)
		}//NextBytes()
예제 #10
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;
			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()