PageRenderTime 67ms CodeModel.GetById 0ms RepoModel.GetById 1ms app.codeStats 0ms

/mordor/tests/crypto.cpp

http://github.com/mozy/mordor
C++ | 407 lines | 318 code | 51 blank | 38 comment | 19 complexity | dbf15bf54e191924fb97ca55549034a6 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2011 - Mozy, Inc.
  2. #include <openssl/evp.h>
  3. #include "mordor/streams/crypto.h"
  4. #include "mordor/streams/random.h"
  5. #include "mordor/streams/memory.h"
  6. #include "mordor/streams/limited.h"
  7. #include "mordor/streams/hash.h"
  8. #include "mordor/streams/null.h"
  9. #include "mordor/streams/transfer.h"
  10. #include "mordor/streams/ssl.h"
  11. #include "mordor/test/test.h"
  12. #include "mordor/streams/singleplex.h"
  13. #include "mordor/util.h"
  14. using namespace Mordor;
  15. using namespace Mordor::Test;
  16. // ciphertext generated by
  17. // openssl enc -e -aes-256-cbc -in test.txt -out test.enc
  18. // -K 4938f9c3774681b6d3fe17c9e99e4c62b0603262bbd8afafa8a14c74e2056526
  19. // -iv faeebfd03bf8e8515c5c5c19af842b75
  20. static const unsigned char plaintext[269] = {
  21. 0x4d, 0x61, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x64, 0x69, 0x73,
  22. 0x74, 0x69, 0x6e, 0x67, 0x75, 0x69, 0x73, 0x68, 0x65, 0x64,
  23. 0x2c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
  24. 0x20, 0x62, 0x79, 0x20, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65,
  25. 0x61, 0x73, 0x6f, 0x6e, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20,
  26. 0x62, 0x79, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x73, 0x69,
  27. 0x6e, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x20, 0x70, 0x61, 0x73,
  28. 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20,
  29. 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x6e, 0x69, 0x6d,
  30. 0x61, 0x6c, 0x73, 0x2c, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68,
  31. 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x6c, 0x75, 0x73, 0x74,
  32. 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x69,
  33. 0x6e, 0x64, 0x2c, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x62,
  34. 0x79, 0x20, 0x61, 0x20, 0x70, 0x65, 0x72, 0x73, 0x65, 0x76,
  35. 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20,
  36. 0x64, 0x65, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x20, 0x69, 0x6e,
  37. 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x69,
  38. 0x6e, 0x75, 0x65, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69,
  39. 0x6e, 0x64, 0x65, 0x66, 0x61, 0x74, 0x69, 0x67, 0x61, 0x62,
  40. 0x6c, 0x65, 0x20, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74,
  41. 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x6b, 0x6e, 0x6f,
  42. 0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x2c, 0x20, 0x65, 0x78,
  43. 0x63, 0x65, 0x65, 0x64, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20,
  44. 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x76, 0x65, 0x68, 0x65,
  45. 0x6d, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61,
  46. 0x6e, 0x79, 0x20, 0x63, 0x61, 0x72, 0x6e, 0x61, 0x6c, 0x20,
  47. 0x70, 0x6c, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x2e
  48. };
  49. static const unsigned char ciphertext[272] = {
  50. 0xe0, 0x1b, 0x5b, 0x95, 0xc8, 0x47, 0xa1, 0xa3, 0xb8, 0xd6,
  51. 0xf9, 0x8c, 0xb8, 0x16, 0x3a, 0xbb, 0x98, 0x9d, 0x71, 0xb4,
  52. 0x88, 0xd8, 0xa7, 0xe1, 0x67, 0x1a, 0xbc, 0xde, 0x10, 0xfb,
  53. 0x1b, 0x9b, 0x3c, 0xd4, 0x5c, 0xb0, 0xcd, 0x2e, 0x00, 0x46,
  54. 0x74, 0xd7, 0x24, 0x85, 0x17, 0xf1, 0x0e, 0x08, 0xc2, 0x69,
  55. 0x2d, 0x40, 0x77, 0xc9, 0x18, 0x63, 0x22, 0x0d, 0x1f, 0x24,
  56. 0x20, 0xfe, 0xb2, 0x23, 0xf2, 0xb9, 0x2a, 0x52, 0x68, 0x00,
  57. 0x59, 0x0a, 0x23, 0x6b, 0x4c, 0xed, 0x0e, 0x9b, 0x2f, 0x1b,
  58. 0x94, 0xfd, 0x28, 0x00, 0x28, 0xb7, 0x63, 0x72, 0x1b, 0x96,
  59. 0x25, 0x56, 0x33, 0x10, 0x59, 0x1f, 0xa7, 0x76, 0x32, 0x39,
  60. 0xac, 0x4d, 0xb7, 0xff, 0x7a, 0x1f, 0xae, 0xcd, 0xa8, 0x70,
  61. 0x14, 0x81, 0xb2, 0xce, 0xe2, 0x8c, 0xdb, 0x2f, 0xf3, 0x5e,
  62. 0x4b, 0xde, 0x54, 0xbd, 0xde, 0x9a, 0xe7, 0xa5, 0xcb, 0xbe,
  63. 0xf6, 0xef, 0x50, 0xcb, 0x1a, 0xb6, 0x00, 0xe5, 0x82, 0x77,
  64. 0xb9, 0x81, 0xbd, 0x35, 0x84, 0xe1, 0x9f, 0x31, 0xcc, 0xd7,
  65. 0x50, 0xa4, 0xc1, 0xce, 0x30, 0x9f, 0x78, 0x14, 0x92, 0x9f,
  66. 0x80, 0xb0, 0x21, 0xac, 0x9a, 0x2e, 0x71, 0x41, 0x61, 0xdd,
  67. 0xf0, 0xa6, 0xa0, 0x27, 0x12, 0x0b, 0x03, 0x90, 0x7d, 0xf6,
  68. 0x19, 0xce, 0x5e, 0x57, 0xe6, 0x2d, 0x31, 0xe1, 0xae, 0xba,
  69. 0x8c, 0x0c, 0x9e, 0x77, 0xfb, 0x0d, 0x4f, 0xe2, 0x68, 0x8d,
  70. 0x24, 0xc0, 0x47, 0x2d, 0x5e, 0xe2, 0x7c, 0x44, 0x71, 0x66,
  71. 0x35, 0x8f, 0x29, 0x38, 0xcd, 0x14, 0x53, 0xe2, 0x52, 0x9f,
  72. 0x4d, 0x33, 0x12, 0xfd, 0xf8, 0xbf, 0x3d, 0x3b, 0xf3, 0x9f,
  73. 0xed, 0x51, 0x6b, 0x34, 0x56, 0xcc, 0x1c, 0x00, 0x6b, 0x1b,
  74. 0xa5, 0xb3, 0xd0, 0x7b, 0x38, 0xe3, 0xca, 0x18, 0x26, 0xbf,
  75. 0x8b, 0x7b, 0x41, 0x22, 0x05, 0x2e, 0x08, 0xf8, 0xbf, 0x3e,
  76. 0x31, 0x46, 0x32, 0xa9, 0x1d, 0x90, 0xb0, 0x52, 0xf2, 0x25,
  77. 0xb9, 0xe7
  78. };
  79. static const unsigned char key[32] = {
  80. 0x49, 0x38, 0xf9, 0xc3, 0x77, 0x46, 0x81, 0xb6, 0xd3, 0xfe,
  81. 0x17, 0xc9, 0xe9, 0x9e, 0x4c, 0x62, 0xb0, 0x60, 0x32, 0x62,
  82. 0xbb, 0xd8, 0xaf, 0xaf, 0xa8, 0xa1, 0x4c, 0x74, 0xe2, 0x05,
  83. 0x65, 0x26
  84. };
  85. static std::string keyString()
  86. {
  87. std::string ret;
  88. ret.assign((const char *)key, sizeof(key));
  89. return ret;
  90. }
  91. static const unsigned char iv[16] = {
  92. 0xfa, 0xee, 0xbf, 0xd0, 0x3b, 0xf8, 0xe8, 0x51, 0x5c, 0x5c,
  93. 0x5c, 0x19, 0xaf, 0x84, 0x2b, 0x75
  94. };
  95. static std::string ivString()
  96. {
  97. std::string ret;
  98. ret.assign((const char *)iv, sizeof(iv));
  99. return ret;
  100. }
  101. MORDOR_UNITTEST(CryptoStream, encryptWrite)
  102. {
  103. Buffer plain;
  104. plain.copyIn(plaintext, sizeof(plaintext));
  105. Buffer cipher;
  106. cipher.copyIn(ciphertext, sizeof(ciphertext));
  107. MemoryStream src(plain);
  108. MemoryStream::ptr sink(new MemoryStream);
  109. CryptoStream cryptor(sink, EVP_aes_256_cbc(), keyString(), ivString(), CryptoStream::WRITE);
  110. transferStream(src, cryptor);
  111. cryptor.close();
  112. const Buffer &test = sink->buffer();
  113. MORDOR_TEST_ASSERT(test == cipher);
  114. }
  115. MORDOR_UNITTEST(CryptoStream, encryptRead)
  116. {
  117. Buffer plain;
  118. plain.copyIn(plaintext, sizeof(plaintext));
  119. Buffer cipher;
  120. cipher.copyIn(ciphertext, sizeof(ciphertext));
  121. MemoryStream::ptr src(new MemoryStream(plain));
  122. CryptoStream cryptor(src, EVP_aes_256_cbc(), keyString(), ivString(), CryptoStream::READ, CryptoStream::ENCRYPT);
  123. MemoryStream sink;
  124. transferStream(cryptor, sink);
  125. const Buffer &test = sink.buffer();
  126. MORDOR_TEST_ASSERT(test == cipher);
  127. }
  128. MORDOR_UNITTEST(CryptoStream, decryptWrite)
  129. {
  130. Buffer plain;
  131. plain.copyIn(plaintext, sizeof(plaintext));
  132. Buffer cipher;
  133. cipher.copyIn(ciphertext, sizeof(ciphertext));
  134. MemoryStream src(cipher);
  135. MemoryStream::ptr sink(new MemoryStream);
  136. CryptoStream decryptor(sink, EVP_aes_256_cbc(), keyString(), ivString(), CryptoStream::WRITE, CryptoStream::DECRYPT);
  137. transferStream(src, decryptor);
  138. decryptor.close();
  139. const Buffer &test = sink->buffer();
  140. MORDOR_TEST_ASSERT(test == plain);
  141. }
  142. MORDOR_UNITTEST(CryptoStream, decryptRead)
  143. {
  144. Buffer plain;
  145. plain.copyIn(plaintext, sizeof(plaintext));
  146. Buffer cipher;
  147. cipher.copyIn(ciphertext, sizeof(ciphertext));
  148. MemoryStream::ptr src(new MemoryStream(cipher));
  149. CryptoStream decryptor(src, EVP_aes_256_cbc(), keyString(), ivString(), CryptoStream::READ);
  150. MemoryStream sink;
  151. transferStream(decryptor, sink);
  152. const Buffer &test = sink.buffer();
  153. MORDOR_TEST_ASSERT(test == plain);
  154. }
  155. MORDOR_UNITTEST(CryptoStream, badKeyIvSize)
  156. {
  157. MemoryStream::ptr parent(new MemoryStream);
  158. CryptoStream::ptr crypto;
  159. MORDOR_TEST_ASSERT_EXCEPTION(crypto.reset(new CryptoStream(
  160. parent, EVP_aes_256_cbc(), keyString() + "bogus", ivString(),
  161. CryptoStream::WRITE)), OpenSSLException);
  162. MORDOR_TEST_ASSERT_EXCEPTION(crypto.reset(new CryptoStream(
  163. parent, EVP_aes_256_cbc(), keyString(), ivString() + "bogus",
  164. CryptoStream::WRITE)), OpenSSLException);
  165. MORDOR_TEST_ASSERT_EXCEPTION(crypto.reset(new CryptoStream(
  166. parent, EVP_aes_256_ecb(), keyString(), ivString() + "bogus",
  167. CryptoStream::WRITE)), OpenSSLException);
  168. }
  169. MORDOR_UNITTEST(CryptoStream, badPadding)
  170. {
  171. Buffer cipher;
  172. cipher.copyIn(ciphertext, sizeof(ciphertext) - 16);
  173. MemoryStream::ptr src(new MemoryStream(cipher));
  174. CryptoStream decryptor(src, EVP_aes_256_cbc(), keyString(), ivString(), CryptoStream::READ, CryptoStream::DECRYPT);
  175. MemoryStream sink;
  176. MORDOR_TEST_ASSERT_EXCEPTION(transferStream(decryptor, sink), OpenSSLException);
  177. }
  178. // test the stream in all four modes of operation
  179. // hashDecR <- decR <- hashEncR <- encR <- hashOrig <- random
  180. // encW -> hashEncW -> decW -> hashDecW -> null
  181. void TestStreaming(long long test_bytes, const std::string &iv, size_t transferBlock = 0)
  182. {
  183. // read side
  184. Stream::ptr random(new RandomStream);
  185. Stream::ptr source(new LimitedStream(random, test_bytes));
  186. HashStream::ptr hashOrig(new MD5Stream(source));
  187. Stream::ptr encR(
  188. new CryptoStream(hashOrig, EVP_aes_256_cbc(), keyString(), iv, CryptoStream::READ, CryptoStream::ENCRYPT));
  189. HashStream::ptr hashEncR(new MD5Stream(encR));
  190. Stream::ptr decR(
  191. new CryptoStream(hashEncR, EVP_aes_256_cbc(), keyString(), iv, CryptoStream::READ, CryptoStream::DECRYPT));
  192. HashStream::ptr hashDecR(new MD5Stream(decR));
  193. // write side
  194. HashStream::ptr hashDecW(new MD5Stream(NullStream::get_ptr()));
  195. Stream::ptr decW(
  196. new CryptoStream(hashDecW, EVP_aes_256_cbc(), keyString(), iv, CryptoStream::WRITE, CryptoStream::DECRYPT));
  197. HashStream::ptr hashEncW(new MD5Stream(decW));
  198. Stream::ptr encW(
  199. new CryptoStream(hashEncW, EVP_aes_256_cbc(), keyString(), iv, CryptoStream::WRITE, CryptoStream::ENCRYPT));
  200. // do it
  201. if (transferBlock == 0) {
  202. transferStream(hashDecR, encW);
  203. } else {
  204. std::vector<unsigned char> buf(transferBlock);
  205. size_t bytes;
  206. while(0 != (bytes = hashDecR->read(&buf[0], buf.size()))) {
  207. encW->write(&buf[0], bytes);
  208. }
  209. }
  210. encW->close();
  211. decW->close();
  212. // make sure the decrypted data matches the original, and does _not_ match the encrypted
  213. // (because otherwise any non-mutating filter stream would pass this test...)
  214. MORDOR_TEST_ASSERT(hashOrig->hash() != hashEncR->hash());
  215. MORDOR_TEST_ASSERT(hashOrig->hash() == hashDecR->hash());
  216. MORDOR_TEST_ASSERT(hashOrig->hash() != hashEncW->hash());
  217. MORDOR_TEST_ASSERT(hashOrig->hash() == hashDecW->hash());
  218. }
  219. MORDOR_UNITTEST(CryptoStream, streaming)
  220. {
  221. static const long long sizes[] = { 0, 1, 15, 16, 17, 131071, 131072, 131073 };
  222. size_t nsizes = sizeof(sizes) / sizeof(sizes[0]);
  223. for(size_t i = 0; i < nsizes; ++i) {
  224. TestStreaming(sizes[i], ivString());
  225. TestStreaming(sizes[i], CryptoStream::RANDOM_IV);
  226. }
  227. }
  228. MORDOR_UNITTEST(CryptoStream, oddBufferSizes)
  229. {
  230. static const long long file_sizes[] = { 0, 100, 4096 };
  231. size_t nfilesizes = sizeof(file_sizes) / sizeof(file_sizes[0]);
  232. static const size_t buffer_sizes[] = { 7, 16, 1000 };
  233. size_t nbuffersizes = sizeof(buffer_sizes) / sizeof(buffer_sizes[0]);
  234. for(size_t i = 0; i < nfilesizes; ++i) {
  235. for(size_t j = 0; j < nbuffersizes; ++j) {
  236. TestStreaming(file_sizes[i], ivString(), buffer_sizes[j]);
  237. TestStreaming(file_sizes[i], CryptoStream::RANDOM_IV, buffer_sizes[j]);
  238. }
  239. }
  240. }
  241. MORDOR_UNITTEST(CryptoStream, inferDirection)
  242. {
  243. MemoryStream::ptr parent(new MemoryStream);
  244. SingleplexStream::ptr sr(new SingleplexStream(parent, SingleplexStream::READ, false));
  245. SingleplexStream::ptr sw(new SingleplexStream(parent, SingleplexStream::WRITE, false));
  246. CryptoStream csr(sr, EVP_aes_256_cbc(), keyString(), ivString());
  247. MORDOR_TEST_ASSERT(csr.supportsRead());
  248. MORDOR_TEST_ASSERT(!csr.supportsWrite());
  249. CryptoStream csw(sw, EVP_aes_256_cbc(), keyString(), ivString());
  250. MORDOR_TEST_ASSERT(!csw.supportsRead());
  251. MORDOR_TEST_ASSERT(csw.supportsWrite());
  252. MORDOR_TEST_ASSERT_ASSERTED(CryptoStream(parent, EVP_aes_256_cbc(), keyString(), ivString()));
  253. }
  254. MORDOR_UNITTEST(CryptoStream, encryptRead_randomIV)
  255. {
  256. Buffer plain;
  257. plain.copyIn(plaintext, sizeof(plaintext));
  258. MemoryStream::ptr src(new MemoryStream(plain));
  259. // encrypt via read
  260. CryptoStream enc(src, EVP_aes_256_cbc(), keyString(),
  261. CryptoStream::RANDOM_IV, CryptoStream::READ, CryptoStream::ENCRYPT);
  262. MemoryStream::ptr intermediate(new MemoryStream);
  263. transferStream(enc, intermediate);
  264. const Buffer &cipher = intermediate->buffer();
  265. // the destination buffer should be the size of the ciphertext plus the size of the IV
  266. // (we can't compare its contents to ciphertext because we used a random IV)
  267. MORDOR_TEST_ASSERT_EQUAL(cipher.readAvailable(), sizeof(ciphertext) + 16);
  268. // decrypt via write
  269. intermediate->seek(0);
  270. MemoryStream::ptr dst(new MemoryStream);
  271. CryptoStream dec(dst, EVP_aes_256_cbc(), keyString(),
  272. CryptoStream::RANDOM_IV, CryptoStream::WRITE, CryptoStream::DECRYPT);
  273. transferStream(intermediate, dec);
  274. dec.close();
  275. const Buffer &decrypted = dst->buffer();
  276. // make sure we got the original plaintext out
  277. MORDOR_TEST_ASSERT(plain == decrypted);
  278. }
  279. MORDOR_UNITTEST(CryptoStream, encryptWrite_randomIV)
  280. {
  281. Buffer plain;
  282. plain.copyIn(plaintext, sizeof(plaintext));
  283. MemoryStream::ptr src(new MemoryStream(plain));
  284. // encrypt via write (just for fun, let CryptoStream infer defaults)
  285. MemoryStream::ptr intermediate(new MemoryStream);
  286. SingleplexStream::ptr spw(new SingleplexStream(intermediate, SingleplexStream::WRITE));
  287. CryptoStream enc(spw, EVP_aes_256_cbc(), keyString());
  288. transferStream(src, enc);
  289. enc.close();
  290. const Buffer &cipher = intermediate->buffer();
  291. // the destination buffer should be the size of the ciphertext plus the size of the IV
  292. // (we can't compare its contents to ciphertext because we used a random IV)
  293. MORDOR_TEST_ASSERT_EQUAL(cipher.readAvailable(), sizeof(ciphertext) + 16);
  294. // now decrypt via read (again, using a SingleplexStream so CryptoStream can infer read)
  295. MemoryStream::ptr dst(new MemoryStream);
  296. intermediate->seek(0);
  297. SingleplexStream::ptr spr(new SingleplexStream(intermediate, SingleplexStream::READ));
  298. CryptoStream dec(spr, EVP_aes_256_cbc(), keyString());
  299. transferStream(dec, dst);
  300. const Buffer &decrypted = dst->buffer();
  301. // make sure we got the original plaintext out
  302. MORDOR_TEST_ASSERT(plain == decrypted);
  303. }
  304. MORDOR_UNITTEST(CryptoStream, encryptRead_noIV)
  305. {
  306. Buffer plain;
  307. plain.copyIn(plaintext, sizeof(plaintext));
  308. MemoryStream::ptr src(new MemoryStream(plain));
  309. // encrypt via read
  310. CryptoStream enc(src, EVP_aes_256_ecb(), keyString(),
  311. std::string(), CryptoStream::READ, CryptoStream::ENCRYPT);
  312. MemoryStream::ptr intermediate(new MemoryStream);
  313. transferStream(enc, intermediate);
  314. const Buffer &cipher = intermediate->buffer();
  315. // the destination buffer should be the size as the reference ciphertext
  316. // (the content is, of course, different, because we're in ECB mode)
  317. MORDOR_TEST_ASSERT_EQUAL(cipher.readAvailable(), sizeof(ciphertext));
  318. // decrypt via write
  319. intermediate->seek(0);
  320. MemoryStream::ptr dst(new MemoryStream);
  321. CryptoStream dec(dst, EVP_aes_256_ecb(), keyString(),
  322. std::string(), CryptoStream::WRITE, CryptoStream::DECRYPT);
  323. transferStream(intermediate, dec);
  324. dec.close();
  325. const Buffer &decrypted = dst->buffer();
  326. // make sure we got the original plaintext out
  327. MORDOR_TEST_ASSERT(plain == decrypted);
  328. }
  329. MORDOR_UNITTEST(CryptoStream, encryptWrite_noIV)
  330. {
  331. Buffer plain;
  332. plain.copyIn(plaintext, sizeof(plaintext));
  333. MemoryStream::ptr src(new MemoryStream(plain));
  334. // encrypt via write (just for fun, let CryptoStream infer defaults)
  335. MemoryStream::ptr intermediate(new MemoryStream);
  336. SingleplexStream::ptr spw(new SingleplexStream(intermediate, SingleplexStream::WRITE));
  337. CryptoStream enc(spw, EVP_aes_256_ecb(), keyString());
  338. transferStream(src, enc);
  339. enc.close();
  340. //const Buffer &cipher = intermediate->buffer();
  341. // the destination buffer should be the size as the reference ciphertext
  342. // (the content is, of course, different, because we're in ECB mode)
  343. // EDIT: in this mode, we're generating an IV of the size OpenSSL tells us
  344. // and some versions of OpenSSL give a non-zero IV size in ECB mode
  345. // so don't actually test this.
  346. //MORDOR_TEST_ASSERT_EQUAL(cipher.readAvailable(), sizeof(ciphertext));
  347. // now decrypt via read (again, using a SingleplexStream so CryptoStream can infer read)
  348. MemoryStream::ptr dst(new MemoryStream);
  349. intermediate->seek(0);
  350. SingleplexStream::ptr spr(new SingleplexStream(intermediate, SingleplexStream::READ));
  351. CryptoStream dec(spr, EVP_aes_256_ecb(), keyString());
  352. transferStream(dec, dst);
  353. const Buffer &decrypted = dst->buffer();
  354. // make sure we got the original plaintext out
  355. MORDOR_TEST_ASSERT(plain == decrypted);
  356. }