public byte[] Update(byte[] plainBytes, int offset, int count) { if (_ubiqWebServices == null) { throw new ObjectDisposedException(GetType().Name); } else if ((_encryptionKey == null) || (_aesGcmBlockCipher == null)) { throw new InvalidOperationException("encryptor not initialized"); } var cipherBytes = _aesGcmBlockCipher.Update(plainBytes, offset, count); return(cipherBytes); }
// 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); }