}// 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()
}// 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()
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()
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()
}// 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()
}// 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()
}// 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()
}// 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()
public static bool Authenticate(byte[] masterKey, ArraySegment<byte> ciphertext, ArraySegment<byte>? salt = null) { return EtM_CTR.Authenticate(masterKey, ciphertext, salt); }
public static byte[] Decrypt(byte[] masterKey, ArraySegment<byte> ciphertext, ArraySegment<byte>? salt = null) { return EtM_CTR.Decrypt(masterKey, ciphertext, salt); }
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()