KeyRingBasedDataProtectorTests.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. // Copyright (c) .NET Foundation. All rights reserved.
  2. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  3. using System;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Reflection;
  8. using System.Text;
  9. using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
  10. using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
  11. using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
  12. using Microsoft.AspNetCore.Testing;
  13. using Moq;
  14. using Xunit;
  15. namespace Microsoft.AspNetCore.DataProtection.KeyManagement
  16. {
  17. public class KeyRingBasedDataProtectorTests
  18. {
  19. [Fact]
  20. public void Protect_NullPlaintext_Throws()
  21. {
  22. // Arrange
  23. IDataProtector protector = new KeyRingBasedDataProtector(
  24. keyRingProvider: new Mock<IKeyRingProvider>().Object,
  25. logger: null,
  26. originalPurposes: null,
  27. newPurpose: "purpose");
  28. // Act & assert
  29. ExceptionAssert.ThrowsArgumentNull(() => protector.Protect(plaintext: null), "plaintext");
  30. }
  31. [Fact]
  32. public void Protect_EncryptsToDefaultProtector_MultiplePurposes()
  33. {
  34. // Arrange
  35. Guid defaultKey = new Guid("ba73c9ce-d322-4e45-af90-341307e11c38");
  36. byte[] expectedPlaintext = new byte[] { 0x03, 0x05, 0x07, 0x11, 0x13, 0x17, 0x19 };
  37. byte[] expectedAad = BuildAadFromPurposeStrings(defaultKey, "purpose1", "purpose2", "yet another purpose");
  38. byte[] expectedProtectedData = BuildProtectedDataFromCiphertext(defaultKey, new byte[] { 0x23, 0x29, 0x31, 0x37 });
  39. var mockEncryptor = new Mock<IAuthenticatedEncryptor>();
  40. mockEncryptor
  41. .Setup(o => o.Encrypt(It.IsAny<ArraySegment<byte>>(), It.IsAny<ArraySegment<byte>>()))
  42. .Returns<ArraySegment<byte>, ArraySegment<byte>>((actualPlaintext, actualAad) =>
  43. {
  44. Assert.Equal(expectedPlaintext, actualPlaintext);
  45. Assert.Equal(expectedAad, actualAad);
  46. return new byte[] { 0x23, 0x29, 0x31, 0x37 }; // ciphertext + tag
  47. });
  48. var mockKeyRing = new Mock<IKeyRing>(MockBehavior.Strict);
  49. mockKeyRing.Setup(o => o.DefaultKeyId).Returns(defaultKey);
  50. mockKeyRing.Setup(o => o.DefaultAuthenticatedEncryptor).Returns(mockEncryptor.Object);
  51. var mockKeyRingProvider = new Mock<IKeyRingProvider>();
  52. mockKeyRingProvider.Setup(o => o.GetCurrentKeyRing()).Returns(mockKeyRing.Object);
  53. IDataProtector protector = new KeyRingBasedDataProtector(
  54. keyRingProvider: mockKeyRingProvider.Object,
  55. logger: null,
  56. originalPurposes: new[] { "purpose1", "purpose2" },
  57. newPurpose: "yet another purpose");
  58. // Act
  59. byte[] retVal = protector.Protect(expectedPlaintext);
  60. // Assert
  61. Assert.Equal(expectedProtectedData, retVal);
  62. }
  63. [Fact]
  64. public void Protect_EncryptsToDefaultProtector_SinglePurpose()
  65. {
  66. // Arrange
  67. Guid defaultKey = new Guid("ba73c9ce-d322-4e45-af90-341307e11c38");
  68. byte[] expectedPlaintext = new byte[] { 0x03, 0x05, 0x07, 0x11, 0x13, 0x17, 0x19 };
  69. byte[] expectedAad = BuildAadFromPurposeStrings(defaultKey, "single purpose");
  70. byte[] expectedProtectedData = BuildProtectedDataFromCiphertext(defaultKey, new byte[] { 0x23, 0x29, 0x31, 0x37 });
  71. var mockEncryptor = new Mock<IAuthenticatedEncryptor>();
  72. mockEncryptor
  73. .Setup(o => o.Encrypt(It.IsAny<ArraySegment<byte>>(), It.IsAny<ArraySegment<byte>>()))
  74. .Returns<ArraySegment<byte>, ArraySegment<byte>>((actualPlaintext, actualAad) =>
  75. {
  76. Assert.Equal(expectedPlaintext, actualPlaintext);
  77. Assert.Equal(expectedAad, actualAad);
  78. return new byte[] { 0x23, 0x29, 0x31, 0x37 }; // ciphertext + tag
  79. });
  80. var mockKeyRing = new Mock<IKeyRing>(MockBehavior.Strict);
  81. mockKeyRing.Setup(o => o.DefaultKeyId).Returns(defaultKey);
  82. mockKeyRing.Setup(o => o.DefaultAuthenticatedEncryptor).Returns(mockEncryptor.Object);
  83. var mockKeyRingProvider = new Mock<IKeyRingProvider>();
  84. mockKeyRingProvider.Setup(o => o.GetCurrentKeyRing()).Returns(mockKeyRing.Object);
  85. IDataProtector protector = new KeyRingBasedDataProtector(
  86. keyRingProvider: mockKeyRingProvider.Object,
  87. logger: null,
  88. originalPurposes: new string[0],
  89. newPurpose: "single purpose");
  90. // Act
  91. byte[] retVal = protector.Protect(expectedPlaintext);
  92. // Assert
  93. Assert.Equal(expectedProtectedData, retVal);
  94. }
  95. [Fact]
  96. public void Protect_HomogenizesExceptionsToCryptographicException()
  97. {
  98. // Arrange
  99. IDataProtector protector = new KeyRingBasedDataProtector(
  100. keyRingProvider: new Mock<IKeyRingProvider>(MockBehavior.Strict).Object,
  101. logger: null,
  102. originalPurposes: null,
  103. newPurpose: "purpose");
  104. // Act & assert
  105. var ex = ExceptionAssert2.ThrowsCryptographicException(() => protector.Protect(new byte[0]));
  106. Assert.IsAssignableFrom(typeof(MockException), ex.InnerException);
  107. }
  108. [Fact]
  109. public void Unprotect_NullProtectedData_Throws()
  110. {
  111. // Arrange
  112. IDataProtector protector = new KeyRingBasedDataProtector(
  113. keyRingProvider: new Mock<IKeyRingProvider>().Object,
  114. logger: null,
  115. originalPurposes: null,
  116. newPurpose: "purpose");
  117. // Act & assert
  118. ExceptionAssert.ThrowsArgumentNull(() => protector.Unprotect(protectedData: null), "protectedData");
  119. }
  120. [Fact]
  121. public void Unprotect_PayloadTooShort_ThrowsBadMagicHeader()
  122. {
  123. // Arrange
  124. IDataProtector protector = new KeyRingBasedDataProtector(
  125. keyRingProvider: new Mock<IKeyRingProvider>().Object,
  126. logger: null,
  127. originalPurposes: null,
  128. newPurpose: "purpose");
  129. byte[] badProtectedPayload = BuildProtectedDataFromCiphertext(Guid.NewGuid(), new byte[0]);
  130. badProtectedPayload = badProtectedPayload.Take(badProtectedPayload.Length - 1).ToArray(); // chop off the last byte
  131. // Act & assert
  132. var ex = ExceptionAssert2.ThrowsCryptographicException(() => protector.Unprotect(badProtectedPayload));
  133. Assert.Equal(Resources.ProtectionProvider_BadMagicHeader, ex.Message);
  134. }
  135. [Fact]
  136. public void Unprotect_PayloadHasBadMagicHeader_ThrowsBadMagicHeader()
  137. {
  138. // Arrange
  139. IDataProtector protector = new KeyRingBasedDataProtector(
  140. keyRingProvider: new Mock<IKeyRingProvider>().Object,
  141. logger: null,
  142. originalPurposes: null,
  143. newPurpose: "purpose");
  144. byte[] badProtectedPayload = BuildProtectedDataFromCiphertext(Guid.NewGuid(), new byte[0]);
  145. badProtectedPayload[0]++; // corrupt the magic header
  146. // Act & assert
  147. var ex = ExceptionAssert2.ThrowsCryptographicException(() => protector.Unprotect(badProtectedPayload));
  148. Assert.Equal(Resources.ProtectionProvider_BadMagicHeader, ex.Message);
  149. }
  150. [Fact]
  151. public void Unprotect_PayloadHasIncorrectVersionMarker_ThrowsNewerVersion()
  152. {
  153. // Arrange
  154. IDataProtector protector = new KeyRingBasedDataProtector(
  155. keyRingProvider: new Mock<IKeyRingProvider>().Object,
  156. logger: null,
  157. originalPurposes: null,
  158. newPurpose: "purpose");
  159. byte[] badProtectedPayload = BuildProtectedDataFromCiphertext(Guid.NewGuid(), new byte[0]);
  160. badProtectedPayload[3]++; // bump the version payload
  161. // Act & assert
  162. var ex = ExceptionAssert2.ThrowsCryptographicException(() => protector.Unprotect(badProtectedPayload));
  163. Assert.Equal(Resources.ProtectionProvider_BadVersion, ex.Message);
  164. }
  165. [Fact]
  166. public void Unprotect_KeyNotFound_ThrowsKeyNotFound()
  167. {
  168. // Arrange
  169. Guid notFoundKeyId = new Guid("654057ab-2491-4471-a72a-b3b114afda38");
  170. byte[] protectedData = BuildProtectedDataFromCiphertext(
  171. keyId: notFoundKeyId,
  172. ciphertext: new byte[0]);
  173. var mockDescriptor = new Mock<IAuthenticatedEncryptorDescriptor>();
  174. mockDescriptor.Setup(o => o.CreateEncryptorInstance()).Returns(new Mock<IAuthenticatedEncryptor>().Object);
  175. // the keyring has only one key
  176. Key key = new Key(Guid.Empty, DateTimeOffset.Now, DateTimeOffset.Now, DateTimeOffset.Now, mockDescriptor.Object);
  177. var keyRing = new KeyRing(key, new[] { key });
  178. var mockKeyRingProvider = new Mock<IKeyRingProvider>();
  179. mockKeyRingProvider.Setup(o => o.GetCurrentKeyRing()).Returns(keyRing);
  180. IDataProtector protector = new KeyRingBasedDataProtector(
  181. keyRingProvider: mockKeyRingProvider.Object,
  182. logger: null,
  183. originalPurposes: null,
  184. newPurpose: "purpose");
  185. // Act & assert
  186. var ex = ExceptionAssert2.ThrowsCryptographicException(() => protector.Unprotect(protectedData));
  187. Assert.Equal(Error.Common_KeyNotFound(notFoundKeyId).Message, ex.Message);
  188. }
  189. [Fact]
  190. public void Unprotect_KeyRevoked_RevocationDisallowed_ThrowsKeyRevoked()
  191. {
  192. // Arrange
  193. Guid keyId = new Guid("654057ab-2491-4471-a72a-b3b114afda38");
  194. byte[] protectedData = BuildProtectedDataFromCiphertext(
  195. keyId: keyId,
  196. ciphertext: new byte[0]);
  197. var mockDescriptor = new Mock<IAuthenticatedEncryptorDescriptor>();
  198. mockDescriptor.Setup(o => o.CreateEncryptorInstance()).Returns(new Mock<IAuthenticatedEncryptor>().Object);
  199. // the keyring has only one key
  200. Key key = new Key(keyId, DateTimeOffset.Now, DateTimeOffset.Now, DateTimeOffset.Now, mockDescriptor.Object);
  201. key.SetRevoked();
  202. var keyRing = new KeyRing(key, new[] { key });
  203. var mockKeyRingProvider = new Mock<IKeyRingProvider>();
  204. mockKeyRingProvider.Setup(o => o.GetCurrentKeyRing()).Returns(keyRing);
  205. IDataProtector protector = new KeyRingBasedDataProtector(
  206. keyRingProvider: mockKeyRingProvider.Object,
  207. logger: null,
  208. originalPurposes: null,
  209. newPurpose: "purpose");
  210. // Act & assert
  211. var ex = ExceptionAssert2.ThrowsCryptographicException(() => protector.Unprotect(protectedData));
  212. Assert.Equal(Error.Common_KeyRevoked(keyId).Message, ex.Message);
  213. }
  214. [Fact]
  215. public void Unprotect_KeyRevoked_RevocationAllowed_ReturnsOriginalData_SetsRevokedAndMigrationFlags()
  216. {
  217. // Arrange
  218. Guid defaultKeyId = new Guid("ba73c9ce-d322-4e45-af90-341307e11c38");
  219. byte[] expectedCiphertext = new byte[] { 0x03, 0x05, 0x07, 0x11, 0x13, 0x17, 0x19 };
  220. byte[] protectedData = BuildProtectedDataFromCiphertext(defaultKeyId, expectedCiphertext);
  221. byte[] expectedAad = BuildAadFromPurposeStrings(defaultKeyId, "purpose");
  222. byte[] expectedPlaintext = new byte[] { 0x23, 0x29, 0x31, 0x37 };
  223. var mockEncryptor = new Mock<IAuthenticatedEncryptor>();
  224. mockEncryptor
  225. .Setup(o => o.Decrypt(It.IsAny<ArraySegment<byte>>(), It.IsAny<ArraySegment<byte>>()))
  226. .Returns<ArraySegment<byte>, ArraySegment<byte>>((actualCiphertext, actualAad) =>
  227. {
  228. Assert.Equal(expectedCiphertext, actualCiphertext);
  229. Assert.Equal(expectedAad, actualAad);
  230. return expectedPlaintext;
  231. });
  232. var mockDescriptor = new Mock<IAuthenticatedEncryptorDescriptor>();
  233. mockDescriptor.Setup(o => o.CreateEncryptorInstance()).Returns(mockEncryptor.Object);
  234. Key defaultKey = new Key(defaultKeyId, DateTimeOffset.Now, DateTimeOffset.Now, DateTimeOffset.Now, mockDescriptor.Object);
  235. defaultKey.SetRevoked();
  236. var keyRing = new KeyRing(defaultKey, new[] { defaultKey });
  237. var mockKeyRingProvider = new Mock<IKeyRingProvider>();
  238. mockKeyRingProvider.Setup(o => o.GetCurrentKeyRing()).Returns(keyRing);
  239. IDataProtector protector = new KeyRingBasedDataProtector(
  240. keyRingProvider: mockKeyRingProvider.Object,
  241. logger: null,
  242. originalPurposes: null,
  243. newPurpose: "purpose");
  244. // Act
  245. bool requiresMigration, wasRevoked;
  246. byte[] retVal = ((IPersistedDataProtector)protector).DangerousUnprotect(protectedData,
  247. ignoreRevocationErrors: true,
  248. requiresMigration: out requiresMigration,
  249. wasRevoked: out wasRevoked);
  250. // Assert
  251. Assert.Equal(expectedPlaintext, retVal);
  252. Assert.True(requiresMigration);
  253. Assert.True(wasRevoked);
  254. }
  255. [Fact]
  256. public void Unprotect_IsAlsoDefaultKey_Success_NoMigrationRequired()
  257. {
  258. // Arrange
  259. Guid defaultKeyId = new Guid("ba73c9ce-d322-4e45-af90-341307e11c38");
  260. byte[] expectedCiphertext = new byte[] { 0x03, 0x05, 0x07, 0x11, 0x13, 0x17, 0x19 };
  261. byte[] protectedData = BuildProtectedDataFromCiphertext(defaultKeyId, expectedCiphertext);
  262. byte[] expectedAad = BuildAadFromPurposeStrings(defaultKeyId, "purpose");
  263. byte[] expectedPlaintext = new byte[] { 0x23, 0x29, 0x31, 0x37 };
  264. var mockEncryptor = new Mock<IAuthenticatedEncryptor>();
  265. mockEncryptor
  266. .Setup(o => o.Decrypt(It.IsAny<ArraySegment<byte>>(), It.IsAny<ArraySegment<byte>>()))
  267. .Returns<ArraySegment<byte>, ArraySegment<byte>>((actualCiphertext, actualAad) =>
  268. {
  269. Assert.Equal(expectedCiphertext, actualCiphertext);
  270. Assert.Equal(expectedAad, actualAad);
  271. return expectedPlaintext;
  272. });
  273. var mockDescriptor = new Mock<IAuthenticatedEncryptorDescriptor>();
  274. mockDescriptor.Setup(o => o.CreateEncryptorInstance()).Returns(mockEncryptor.Object);
  275. Key defaultKey = new Key(defaultKeyId, DateTimeOffset.Now, DateTimeOffset.Now, DateTimeOffset.Now, mockDescriptor.Object);
  276. var keyRing = new KeyRing(defaultKey, new[] { defaultKey });
  277. var mockKeyRingProvider = new Mock<IKeyRingProvider>();
  278. mockKeyRingProvider.Setup(o => o.GetCurrentKeyRing()).Returns(keyRing);
  279. IDataProtector protector = new KeyRingBasedDataProtector(
  280. keyRingProvider: mockKeyRingProvider.Object,
  281. logger: null,
  282. originalPurposes: null,
  283. newPurpose: "purpose");
  284. // Act & assert - IDataProtector
  285. byte[] retVal = protector.Unprotect(protectedData);
  286. Assert.Equal(expectedPlaintext, retVal);
  287. // Act & assert - IPersistedDataProtector
  288. bool requiresMigration, wasRevoked;
  289. retVal = ((IPersistedDataProtector)protector).DangerousUnprotect(protectedData,
  290. ignoreRevocationErrors: false,
  291. requiresMigration: out requiresMigration,
  292. wasRevoked: out wasRevoked);
  293. Assert.Equal(expectedPlaintext, retVal);
  294. Assert.False(requiresMigration);
  295. Assert.False(wasRevoked);
  296. }
  297. [Fact]
  298. public void Unprotect_IsNotDefaultKey_Success_RequiresMigration()
  299. {
  300. // Arrange
  301. Guid defaultKeyId = new Guid("ba73c9ce-d322-4e45-af90-341307e11c38");
  302. Guid embeddedKeyId = new Guid("9b5d2db3-299f-4eac-89e9-e9067a5c1853");
  303. byte[] expectedCiphertext = new byte[] { 0x03, 0x05, 0x07, 0x11, 0x13, 0x17, 0x19 };
  304. byte[] protectedData = BuildProtectedDataFromCiphertext(embeddedKeyId, expectedCiphertext);
  305. byte[] expectedAad = BuildAadFromPurposeStrings(embeddedKeyId, "purpose");
  306. byte[] expectedPlaintext = new byte[] { 0x23, 0x29, 0x31, 0x37 };
  307. var mockEncryptor = new Mock<IAuthenticatedEncryptor>();
  308. mockEncryptor
  309. .Setup(o => o.Decrypt(It.IsAny<ArraySegment<byte>>(), It.IsAny<ArraySegment<byte>>()))
  310. .Returns<ArraySegment<byte>, ArraySegment<byte>>((actualCiphertext, actualAad) =>
  311. {
  312. Assert.Equal(expectedCiphertext, actualCiphertext);
  313. Assert.Equal(expectedAad, actualAad);
  314. return expectedPlaintext;
  315. });
  316. var mockDescriptor = new Mock<IAuthenticatedEncryptorDescriptor>();
  317. mockDescriptor.Setup(o => o.CreateEncryptorInstance()).Returns(mockEncryptor.Object);
  318. Key defaultKey = new Key(defaultKeyId, DateTimeOffset.Now, DateTimeOffset.Now, DateTimeOffset.Now, new Mock<IAuthenticatedEncryptorDescriptor>().Object);
  319. Key embeddedKey = new Key(embeddedKeyId, DateTimeOffset.Now, DateTimeOffset.Now, DateTimeOffset.Now, mockDescriptor.Object);
  320. var keyRing = new KeyRing(defaultKey, new[] { defaultKey, embeddedKey });
  321. var mockKeyRingProvider = new Mock<IKeyRingProvider>();
  322. mockKeyRingProvider.Setup(o => o.GetCurrentKeyRing()).Returns(keyRing);
  323. IDataProtector protector = new KeyRingBasedDataProtector(
  324. keyRingProvider: mockKeyRingProvider.Object,
  325. logger: null,
  326. originalPurposes: null,
  327. newPurpose: "purpose");
  328. // Act & assert - IDataProtector
  329. byte[] retVal = protector.Unprotect(protectedData);
  330. Assert.Equal(expectedPlaintext, retVal);
  331. // Act & assert - IPersistedDataProtector
  332. bool requiresMigration, wasRevoked;
  333. retVal = ((IPersistedDataProtector)protector).DangerousUnprotect(protectedData,
  334. ignoreRevocationErrors: false,
  335. requiresMigration: out requiresMigration,
  336. wasRevoked: out wasRevoked);
  337. Assert.Equal(expectedPlaintext, retVal);
  338. Assert.True(requiresMigration);
  339. Assert.False(wasRevoked);
  340. }
  341. [Fact]
  342. public void Protect_Unprotect_RoundTripsProperly()
  343. {
  344. // Arrange
  345. byte[] plaintext = new byte[] { 0x10, 0x20, 0x30, 0x40, 0x50 };
  346. Key key = new Key(Guid.NewGuid(), DateTimeOffset.Now, DateTimeOffset.Now, DateTimeOffset.Now, new AuthenticatedEncryptorConfiguration(new AuthenticatedEncryptionSettings()).CreateNewDescriptor());
  347. var keyRing = new KeyRing(key, new[] { key });
  348. var mockKeyRingProvider = new Mock<IKeyRingProvider>();
  349. mockKeyRingProvider.Setup(o => o.GetCurrentKeyRing()).Returns(keyRing);
  350. var protector = new KeyRingBasedDataProtector(
  351. keyRingProvider: mockKeyRingProvider.Object,
  352. logger: null,
  353. originalPurposes: null,
  354. newPurpose: "purpose");
  355. // Act - protect
  356. byte[] protectedData = protector.Protect(plaintext);
  357. Assert.NotNull(protectedData);
  358. Assert.NotEqual(plaintext, protectedData);
  359. // Act - unprotect
  360. byte[] roundTrippedPlaintext = protector.Unprotect(protectedData);
  361. Assert.Equal(plaintext, roundTrippedPlaintext);
  362. }
  363. [Fact]
  364. public void CreateProtector_ChainsPurposes()
  365. {
  366. // Arrange
  367. Guid defaultKey = new Guid("ba73c9ce-d322-4e45-af90-341307e11c38");
  368. byte[] expectedPlaintext = new byte[] { 0x03, 0x05, 0x07, 0x11, 0x13, 0x17, 0x19 };
  369. byte[] expectedAad = BuildAadFromPurposeStrings(defaultKey, "purpose1", "purpose2");
  370. byte[] expectedProtectedData = BuildProtectedDataFromCiphertext(defaultKey, new byte[] { 0x23, 0x29, 0x31, 0x37 });
  371. var mockEncryptor = new Mock<IAuthenticatedEncryptor>();
  372. mockEncryptor
  373. .Setup(o => o.Encrypt(It.IsAny<ArraySegment<byte>>(), It.IsAny<ArraySegment<byte>>()))
  374. .Returns<ArraySegment<byte>, ArraySegment<byte>>((actualPlaintext, actualAad) =>
  375. {
  376. Assert.Equal(expectedPlaintext, actualPlaintext);
  377. Assert.Equal(expectedAad, actualAad);
  378. return new byte[] { 0x23, 0x29, 0x31, 0x37 }; // ciphertext + tag
  379. });
  380. var mockKeyRing = new Mock<IKeyRing>(MockBehavior.Strict);
  381. mockKeyRing.Setup(o => o.DefaultKeyId).Returns(defaultKey);
  382. mockKeyRing.Setup(o => o.DefaultAuthenticatedEncryptor).Returns(mockEncryptor.Object);
  383. var mockKeyRingProvider = new Mock<IKeyRingProvider>();
  384. mockKeyRingProvider.Setup(o => o.GetCurrentKeyRing()).Returns(mockKeyRing.Object);
  385. IDataProtector protector = new KeyRingBasedDataProtector(
  386. keyRingProvider: mockKeyRingProvider.Object,
  387. logger: null,
  388. originalPurposes: null,
  389. newPurpose: "purpose1").CreateProtector("purpose2");
  390. // Act
  391. byte[] retVal = protector.Protect(expectedPlaintext);
  392. // Assert
  393. Assert.Equal(expectedProtectedData, retVal);
  394. }
  395. private static byte[] BuildAadFromPurposeStrings(Guid keyId, params string[] purposes)
  396. {
  397. var expectedAad = new byte[] { 0x09, 0xF0, 0xC9, 0xF0 } // magic header
  398. .Concat(keyId.ToByteArray()) // key id
  399. .Concat(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(purposes.Length))); // purposeCount
  400. foreach (string purpose in purposes)
  401. {
  402. var memStream = new MemoryStream();
  403. var writer = new BinaryWriter(memStream, encoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), leaveOpen: true);
  404. writer.Write(purpose); // also writes 7-bit encoded int length
  405. writer.Dispose();
  406. expectedAad = expectedAad.Concat(memStream.ToArray());
  407. }
  408. return expectedAad.ToArray();
  409. }
  410. private static byte[] BuildProtectedDataFromCiphertext(Guid keyId, byte[] ciphertext)
  411. {
  412. return new byte[] { 0x09, 0xF0, 0xC9, 0xF0 } // magic header
  413. .Concat(keyId.ToByteArray()) // key id
  414. .Concat(ciphertext).ToArray();
  415. }
  416. }
  417. }