public async Task <byte[]> BeginAsync() { if (_ubiqWebServices == null) { throw new ObjectDisposedException(GetType().Name); } else if (_aesGcmBlockCipher != null) { throw new InvalidOperationException("encryption in progress"); } if (_encryptionKey == null) { // JIT: request encryption key from server _encryptionKey = await _ubiqWebServices.GetEncryptionKeyAsync(_usesRequested).ConfigureAwait(false); } // check key 'usage count' against server-specified limit if (_useCount > _encryptionKey.MaxUses) { throw new InvalidOperationException("maximum key uses exceeded"); } _useCount++; var algorithmInfo = new AlgorithmInfo(_encryptionKey.SecurityModel.Algorithm); // generate random IV for encryption byte[] initVector = new byte[algorithmInfo.InitVectorLength]; var random = RandomNumberGenerator.Create(); random.GetBytes(initVector); var cipherHeader = new CipherHeader { Version = 0, Flags = CipherHeader.FLAGS_AAD_ENABLED, AlgorithmId = algorithmInfo.Id, InitVectorLength = (byte)initVector.Length, EncryptedDataKeyLength = (short)_encryptionKey.EncryptedDataKeyBytes.Length, InitVectorBytes = initVector, EncryptedDataKeyBytes = _encryptionKey.EncryptedDataKeyBytes }; // note: include cipher header bytes in AES result! var cipherHeaderBytes = cipherHeader.Serialize(); _aesGcmBlockCipher = new AesGcmBlockCipher( forEncryption: true, algorithmInfo: algorithmInfo, key: _encryptionKey.UnwrappedDataKey, initVector: initVector, additionalBytes: cipherHeaderBytes); return(cipherHeaderBytes); }
public byte[] Begin() { if (_ubiqWebServices == null) { throw new ObjectDisposedException(GetType().Name); } else if (_aesGcmBlockCipher != null) { throw new InvalidOperationException("decryption in progress"); } // prepare to receive initial header bytes _cipherHeader = null; _byteBuffer = null; // note: cached '_decryptionKey' may be present from a previous decryption run return(new byte[0]); }
// each encryption has a header on it that identifies the algorithm // used and an encryption of the data key that was used to encrypt // the original plain text. there is no guarantee how much of that // data will be passed to this function or how many times this // function will be called to process all of the data. to that end, // this function buffers data internally, when it is unable to // process it. // the function buffers data internally until the entire header is // received. once the header has been received, the encrypted data // key is sent to the server for decryption. after the header has // been successfully handled, this function always decrypts all of // the data in its internal buffer public async Task <byte[]> UpdateAsync(byte[] cipherBytes, int offset, int count) { if (_ubiqWebServices == null) { throw new ObjectDisposedException(GetType().Name); } byte[] plainBytes = new byte[0]; // returned if (_byteBuffer == null) { _byteBuffer = new ByteBuffer(); } // make sure new data is appended to end _byteBuffer.Enqueue(cipherBytes, offset, count); if (_cipherHeader == null) { // see if we've got enough data for the header record using (var byteStream = new MemoryStream(_byteBuffer.Peek())) { _cipherHeader = CipherHeader.Deserialize(byteStream); } if (_cipherHeader != null) { // success: prune cipher header bytes from the buffer _byteBuffer.Dequeue(_cipherHeader.Length()); if (_decryptionKey != null) { // See if we can reuse the key from a previous decryption, meaning // the new data was encrypted with the same key as the old data - i.e. // both cipher headers have the same key. // // If not, clear the previous decryption key. if (!_cipherHeader.EncryptedDataKeyBytes.SequenceEqual( _decryptionKey.LastCipherHeaderEncryptedDataKeyBytes)) { await ResetAsync().ConfigureAwait(false); } } // If needed, use the header info to fetch the decryption key. if (_decryptionKey == null) { // JIT: request encryption key from server _decryptionKey = await _ubiqWebServices.GetDecryptionKeyAsync(_cipherHeader.EncryptedDataKeyBytes).ConfigureAwait(false); } if (_decryptionKey != null) { var algorithmInfo = new AlgorithmInfo(_cipherHeader.AlgorithmId); // save key extracted from header to detect future key changes _decryptionKey.LastCipherHeaderEncryptedDataKeyBytes = _cipherHeader.EncryptedDataKeyBytes; // create decryptor from header-specified algorithm + server-supplied decryption key _aesGcmBlockCipher = new AesGcmBlockCipher(forEncryption: false, algorithmInfo: algorithmInfo, key: _decryptionKey.UnwrappedDataKey, initVector: _cipherHeader.InitVectorBytes, additionalBytes: ((_cipherHeader.Flags & CipherHeader.FLAGS_AAD_ENABLED) != 0) ? _cipherHeader.Serialize() : null); _decryptionKey.KeyUseCount++; } } else { // holding pattern... need more header bytes return(plainBytes); } } if ((_decryptionKey != null) && (_aesGcmBlockCipher != null)) { // pass all available buffered bytes to the decryptor if (_byteBuffer.Length > 0) { // tricky: the block cipher object will process all provided ciphertext // (including the trailing MAC signature), but may only return a subset of that // as plaintext var bufferedBytes = _byteBuffer.Dequeue(_byteBuffer.Length); plainBytes = _aesGcmBlockCipher.Update(bufferedBytes, 0, bufferedBytes.Length); } } return(plainBytes); }