SignedXmlTest.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. //
  2. // SignedXmlTest.cs - NUnit Test Cases for SignedXml
  3. //
  4. // Author:
  5. // Sebastien Pouliot <[email protected]>
  6. //
  7. // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
  8. // (C) 2004 Novell (http://www.novell.com)
  9. //
  10. using System;
  11. using System.Security.Cryptography;
  12. using System.Security.Cryptography.Xml;
  13. using System.Text;
  14. using System.Xml;
  15. using NUnit.Framework;
  16. namespace MonoTests.System.Security.Cryptography.Xml {
  17. public class SignedXmlEx : SignedXml {
  18. public AsymmetricAlgorithm GetPublicKey ()
  19. {
  20. return base.GetPublicKey ();
  21. }
  22. }
  23. [TestFixture]
  24. public class SignedXmlTest : Assertion {
  25. private const string signature = "<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\" /><SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\" /><Reference URI=\"#MyObjectId\"><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\" /><DigestValue>CTnnhjxUQHJmD+t1MjVXrOW+MCA=</DigestValue></Reference></SignedInfo><SignatureValue>dbFt6Zw3vR+Xh7LbM/vuifyFA7gPh/NlDM2Glz/SJBsveISieuTBpZlk/zavAeuXR/Nu0Ztt4OP4tCOg09a2RNlrTP0dhkeEfL1jTzpnVaLHuQbCiwOWCgbRif7Xt7N12FuiHYb3BltP/YyXS4E12NxlGlqnDiFA1v/mkK5+C1o=</SignatureValue><KeyInfo><KeyValue xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><RSAKeyValue><Modulus>hEfTJNa2idz2u+fSYDDG4Lx/xuk4aBbvOPVNqgc1l9Y8t7Pt+ZyF+kkF3uUl8Y0700BFGAsprnhwrWENK+PGdtvM5796ZKxCCa0ooKkofiT4355HqK26hpV8dvj38vq/rkJe1jHZgkTKa+c/0vjcYZOI/RT/IZv9JfXxVWLuLxk=</Modulus><Exponent>EQ==</Exponent></RSAKeyValue></KeyValue></KeyInfo><Object Id=\"MyObjectId\" xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><ObjectListTag xmlns=\"\" /></Object></Signature>";
  26. [Test]
  27. public void StaticValues ()
  28. {
  29. AssertEquals ("XmlDsigCanonicalizationUrl", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315", SignedXml.XmlDsigCanonicalizationUrl);
  30. AssertEquals ("XmlDsigCanonicalizationWithCommentsUrl", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments", SignedXml.XmlDsigCanonicalizationWithCommentsUrl);
  31. AssertEquals ("XmlDsigDSAUrl", "http://www.w3.org/2000/09/xmldsig#dsa-sha1", SignedXml.XmlDsigDSAUrl);
  32. AssertEquals ("XmlDsigHMACSHA1Url", "http://www.w3.org/2000/09/xmldsig#hmac-sha1", SignedXml.XmlDsigHMACSHA1Url);
  33. AssertEquals ("XmlDsigMinimalCanonicalizationUrl", "http://www.w3.org/2000/09/xmldsig#minimal", SignedXml.XmlDsigMinimalCanonicalizationUrl);
  34. AssertEquals ("XmlDsigNamespaceUrl", "http://www.w3.org/2000/09/xmldsig#", SignedXml.XmlDsigNamespaceUrl);
  35. AssertEquals ("XmlDsigRSASHA1Url", "http://www.w3.org/2000/09/xmldsig#rsa-sha1", SignedXml.XmlDsigRSASHA1Url);
  36. AssertEquals ("XmlDsigSHA1Url", "http://www.w3.org/2000/09/xmldsig#sha1", SignedXml.XmlDsigSHA1Url);
  37. }
  38. [Test]
  39. public void Constructor_Empty ()
  40. {
  41. XmlDocument doc = new XmlDocument ();
  42. doc.LoadXml (signature);
  43. XmlNodeList xnl = doc.GetElementsByTagName ("Signature", SignedXml.XmlDsigNamespaceUrl);
  44. XmlElement xel = (XmlElement) xnl [0];
  45. SignedXml sx = new SignedXml (doc);
  46. sx.LoadXml (xel);
  47. Assert ("CheckSignature", sx.CheckSignature ());
  48. }
  49. [Test]
  50. public void Constructor_XmlDocument ()
  51. {
  52. XmlDocument doc = new XmlDocument ();
  53. doc.LoadXml (signature);
  54. XmlNodeList xnl = doc.GetElementsByTagName ("Signature", SignedXml.XmlDsigNamespaceUrl);
  55. XmlElement xel = (XmlElement) xnl [0];
  56. SignedXml sx = new SignedXml (doc);
  57. sx.LoadXml (doc.DocumentElement);
  58. Assert ("CheckSignature", sx.CheckSignature ());
  59. }
  60. [Test]
  61. [ExpectedException (typeof (ArgumentNullException))]
  62. public void Constructor_XmlDocument_Null ()
  63. {
  64. XmlDocument doc = null;
  65. SignedXml sx = new SignedXml (doc);
  66. }
  67. [Test]
  68. public void Constructor_XmlElement ()
  69. {
  70. XmlDocument doc = new XmlDocument ();
  71. doc.LoadXml (signature);
  72. XmlNodeList xnl = doc.GetElementsByTagName ("Signature", SignedXml.XmlDsigNamespaceUrl);
  73. XmlElement xel = (XmlElement) xnl [0];
  74. SignedXml sx = new SignedXml (doc.DocumentElement);
  75. sx.LoadXml (xel);
  76. Assert ("CheckSignature", sx.CheckSignature ());
  77. }
  78. [Test]
  79. [ExpectedException (typeof (CryptographicException))]
  80. public void Constructor_XmlElement_WithoutLoadXml ()
  81. {
  82. XmlDocument doc = new XmlDocument ();
  83. doc.LoadXml (signature);
  84. XmlNodeList xnl = doc.GetElementsByTagName ("Signature", SignedXml.XmlDsigNamespaceUrl);
  85. XmlElement xel = (XmlElement) xnl [0];
  86. SignedXml sx = new SignedXml (doc.DocumentElement);
  87. Assert ("!CheckSignature", sx.CheckSignature ());
  88. // SignedXml (XmlElement) != SignedXml () + LoadXml (XmlElement)
  89. }
  90. [Test]
  91. [ExpectedException (typeof (ArgumentNullException))]
  92. public void Constructor_XmlElement_Null ()
  93. {
  94. XmlElement xel = null;
  95. SignedXml sx = new SignedXml (xel);
  96. }
  97. // sample from MSDN (url)
  98. public SignedXml MSDNSample ()
  99. {
  100. // Create example data to sign.
  101. XmlDocument document = new XmlDocument ();
  102. XmlNode node = document.CreateNode (XmlNodeType.Element, "", "MyElement", "samples");
  103. node.InnerText = "This is some text";
  104. document.AppendChild (node);
  105. // Create the SignedXml message.
  106. SignedXml signedXml = new SignedXml ();
  107. // Create a data object to hold the data to sign.
  108. DataObject dataObject = new DataObject ();
  109. dataObject.Data = document.ChildNodes;
  110. dataObject.Id = "MyObjectId";
  111. // Add the data object to the signature.
  112. signedXml.AddObject (dataObject);
  113. // Create a reference to be able to package everything into the
  114. // message.
  115. Reference reference = new Reference ();
  116. reference.Uri = "#MyObjectId";
  117. // Add it to the message.
  118. signedXml.AddReference (reference);
  119. return signedXml;
  120. }
  121. [Test]
  122. public void AsymmetricRSASignature ()
  123. {
  124. SignedXml signedXml = MSDNSample ();
  125. RSA key = RSA.Create ();
  126. signedXml.SigningKey = key;
  127. // Add a KeyInfo.
  128. KeyInfo keyInfo = new KeyInfo ();
  129. keyInfo.AddClause (new RSAKeyValue (key));
  130. signedXml.KeyInfo = keyInfo;
  131. AssertEquals ("KeyInfo", 1, signedXml.KeyInfo.Count);
  132. AssertNull ("SignatureLength", signedXml.SignatureLength);
  133. AssertNull ("SignatureMethod", signedXml.SignatureMethod);
  134. AssertNull ("SignatureValue", signedXml.SignatureValue);
  135. AssertNull ("SigningKeyName", signedXml.SigningKeyName);
  136. // Compute the signature.
  137. signedXml.ComputeSignature ();
  138. AssertNull ("SigningKeyName", signedXml.SigningKeyName);
  139. AssertEquals ("SignatureMethod", SignedXml.XmlDsigRSASHA1Url, signedXml.SignatureMethod);
  140. AssertEquals ("SignatureValue", 128, signedXml.SignatureValue.Length);
  141. AssertNull ("SigningKeyName", signedXml.SigningKeyName);
  142. // Get the XML representation of the signature.
  143. XmlElement xmlSignature = signedXml.GetXml ();
  144. // LAMESPEC: we must reload the signature or it won't work
  145. // MS framework throw a "malformed element"
  146. SignedXml vrfy = new SignedXml ();
  147. vrfy.LoadXml (xmlSignature);
  148. // assert that we can verify our own signature
  149. Assert ("RSA-Compute/Verify", vrfy.CheckSignature ());
  150. }
  151. [Test]
  152. public void AsymmetricDSASignature ()
  153. {
  154. SignedXml signedXml = MSDNSample ();
  155. DSA key = DSA.Create ();
  156. signedXml.SigningKey = key;
  157. // Add a KeyInfo.
  158. KeyInfo keyInfo = new KeyInfo ();
  159. keyInfo.AddClause (new DSAKeyValue (key));
  160. signedXml.KeyInfo = keyInfo;
  161. AssertEquals ("KeyInfo", 1, signedXml.KeyInfo.Count);
  162. AssertNull ("SignatureLength", signedXml.SignatureLength);
  163. AssertNull ("SignatureMethod", signedXml.SignatureMethod);
  164. AssertNull ("SignatureValue", signedXml.SignatureValue);
  165. AssertNull ("SigningKeyName", signedXml.SigningKeyName);
  166. // Compute the signature.
  167. signedXml.ComputeSignature ();
  168. AssertNull ("SignatureLength", signedXml.SignatureLength);
  169. AssertEquals ("SignatureMethod", SignedXml.XmlDsigDSAUrl, signedXml.SignatureMethod);
  170. AssertEquals ("SignatureValue", 40, signedXml.SignatureValue.Length);
  171. AssertNull ("SigningKeyName", signedXml.SigningKeyName);
  172. // Get the XML representation of the signature.
  173. XmlElement xmlSignature = signedXml.GetXml ();
  174. // LAMESPEC: we must reload the signature or it won't work
  175. // MS framework throw a "malformed element"
  176. SignedXml vrfy = new SignedXml ();
  177. vrfy.LoadXml (xmlSignature);
  178. // assert that we can verify our own signature
  179. Assert ("DSA-Compute/Verify", vrfy.CheckSignature ());
  180. }
  181. [Test]
  182. public void SymmetricHMACSHA1Signature ()
  183. {
  184. SignedXml signedXml = MSDNSample ();
  185. // Compute the signature.
  186. byte[] secretkey = Encoding.Default.GetBytes ("password");
  187. HMACSHA1 hmac = new HMACSHA1 (secretkey);
  188. AssertNull ("KeyInfo", signedXml.KeyInfo);
  189. AssertNull ("SignatureLength", signedXml.SignatureLength);
  190. AssertNull ("SignatureMethod", signedXml.SignatureMethod);
  191. AssertNull ("SignatureValue", signedXml.SignatureValue);
  192. AssertNull ("SigningKeyName", signedXml.SigningKeyName);
  193. signedXml.ComputeSignature (hmac);
  194. AssertNull ("KeyInfo", signedXml.KeyInfo);
  195. AssertNull ("SignatureLength", signedXml.SignatureLength);
  196. AssertEquals ("SignatureMethod", SignedXml.XmlDsigHMACSHA1Url, signedXml.SignatureMethod);
  197. AssertEquals ("SignatureValue", 20, signedXml.SignatureValue.Length);
  198. AssertNull ("SigningKeyName", signedXml.SigningKeyName);
  199. // Get the XML representation of the signature.
  200. XmlElement xmlSignature = signedXml.GetXml ();
  201. // LAMESPEC: we must reload the signature or it won't work
  202. // MS framework throw a "malformed element"
  203. SignedXml vrfy = new SignedXml ();
  204. vrfy.LoadXml (xmlSignature);
  205. // assert that we can verify our own signature
  206. Assert ("HMACSHA1-Compute/Verify", vrfy.CheckSignature (hmac));
  207. }
  208. [Test]
  209. [ExpectedException (typeof (CryptographicException))]
  210. public void SymmetricMACTripleDESSignature ()
  211. {
  212. SignedXml signedXml = MSDNSample ();
  213. // Compute the signature.
  214. byte[] secretkey = Encoding.Default.GetBytes ("password");
  215. MACTripleDES hmac = new MACTripleDES (secretkey);
  216. signedXml.ComputeSignature (hmac);
  217. }
  218. // Using empty constructor
  219. // LAMESPEC: The two other constructors don't seems to apply in verifying signatures
  220. [Test]
  221. public void AsymmetricRSAVerify ()
  222. {
  223. string value = "<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\" /><SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\" /><Reference URI=\"#MyObjectId\"><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\" /><DigestValue>/Vvq6sXEVbtZC8GwNtLQnGOy/VI=</DigestValue></Reference></SignedInfo><SignatureValue>A6XuE8Cy9iOffRXaW9b0+dUcMUJQnlmwLsiqtQnADbCtZXnXAaeJ6nGnQ4Mm0IGi0AJc7/2CoJReXl7iW4hltmFguG1e3nl0VxCyCTHKGOCo1u8R3K+B1rTaenFbSxs42EM7/D9KETsPlzfYfis36yM3PqatiCUOsoMsAiMGzlc=</SignatureValue><KeyInfo><KeyValue xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><RSAKeyValue><Modulus>tI8QYIpbG/m6JLyvP+S3X8mzcaAIayxomyTimSh9UCpEucRnGvLw0P73uStNpiF7wltTZA1HEsv+Ha39dY/0j/Wiy3RAodGDRNuKQao1wu34aNybZ673brbsbHFUfw/o7nlKD2xO84fbajBZmKtBBDy63NHt+QL+grSrREPfCTM=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue></KeyInfo><Object Id=\"MyObjectId\"><MyElement xmlns=\"samples\">This is some text</MyElement></Object></Signature>";
  224. XmlDocument doc = new XmlDocument ();
  225. doc.LoadXml (value);
  226. SignedXml v1 = new SignedXml ();
  227. v1.LoadXml (doc.DocumentElement);
  228. Assert ("RSA-CheckSignature()", v1.CheckSignature ());
  229. SignedXml v2 = new SignedXml ();
  230. v2.LoadXml (doc.DocumentElement);
  231. AsymmetricAlgorithm key = null;
  232. bool vrfy = v2.CheckSignatureReturningKey (out key);
  233. Assert ("RSA-CheckSignatureReturningKey()", vrfy);
  234. SignedXml v3 = new SignedXml ();
  235. v3.LoadXml (doc.DocumentElement);
  236. Assert ("RSA-CheckSignature(key)", v3.CheckSignature (key));
  237. }
  238. // Using empty constructor
  239. // LAMESPEC: The two other constructors don't seems to apply in verifying signatures
  240. [Test]
  241. public void AsymmetricDSAVerify ()
  242. {
  243. string value = "<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\" /><SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#dsa-sha1\" /><Reference URI=\"#MyObjectId\"><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\" /><DigestValue>/Vvq6sXEVbtZC8GwNtLQnGOy/VI=</DigestValue></Reference></SignedInfo><SignatureValue>BYz/qRGjGsN1yMFPxWa3awUZm1y4I/IxOQroMxkOteRGgk1HIwhRYw==</SignatureValue><KeyInfo><KeyValue xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><DSAKeyValue><P>iglVaZ+LsSL8Y0aDXmFMBwva3xHqIypr3l/LtqBH9ziV2Sh1M4JVasAiKqytWIWt/s/Uk8Ckf2tO2Ww1vsNi1NL+Kg9T7FE52sn380/rF0miwGkZeidzm74OWhykb3J+wCTXaIwOzAWI1yN7FoeoN7wzF12jjlSXAXeqPMlViqk=</P><Q>u4sowiJMHilNRojtdmIuQY2YnB8=</Q><G>SdnN7d+wn1n+HH4Hr8MIryIRYgcXdbZ5TH7jAnuWc1koqRc1AZfcYAZ6RDf+orx6Lzn055FTFiN+1NHQfGUtXJCWW0zz0FVV1NJux7WRj8vGTldjJ5ef0oCenkpwDjcIxWsZgVobve4GPoyN1sAc1scnkJB59oupibklmF4y72A=</G><Y>XejzS8Z51yfl0zbYnxSYYbHqreSLjNCoGPB/KjM1TOyV5sMjz0StKtGrFWryTWc7EgvFY7kUth4e04VKf9HbK8z/FifHTXj8+Tszbjzw8GfInnBwLN+vJgbpnjtypmiI5Bm2nLiRbfkdAHP+OrKtr/EauM9GQfYuaxm3/Vj8B84=</Y><J>vGwGg9wqwwWP9xsoPoXu6kHArJtadiNKe9azBiUx5Ob883gd5wlKfEcGuKkBmBySGbgwxyOsIBovd9Kk48hF01ymfQzAAuHR0EdJECSsTsTTKVTLQNBU32O+PRbLYpv4E8kt6rNL83JLJCBY</J><Seed>sqzn8J6fd2gtEyq6YOqiUSHgPE8=</Seed><PgenCounter>sQ==</PgenCounter></DSAKeyValue></KeyValue></KeyInfo><Object Id=\"MyObjectId\"><MyElement xmlns=\"samples\">This is some text</MyElement></Object></Signature>";
  244. XmlDocument doc = new XmlDocument ();
  245. doc.LoadXml (value);
  246. SignedXml v1 = new SignedXml ();
  247. v1.LoadXml (doc.DocumentElement);
  248. Assert ("DSA-CheckSignature()", v1.CheckSignature ());
  249. SignedXml v2 = new SignedXml ();
  250. v2.LoadXml (doc.DocumentElement);
  251. AsymmetricAlgorithm key = null;
  252. bool vrfy = v2.CheckSignatureReturningKey (out key);
  253. Assert ("DSA-CheckSignatureReturningKey()", vrfy);
  254. SignedXml v3 = new SignedXml ();
  255. v3.LoadXml (doc.DocumentElement);
  256. Assert ("DSA-CheckSignature(key)", v3.CheckSignature (key));
  257. }
  258. [Test]
  259. public void SymmetricHMACSHA1Verify ()
  260. {
  261. string value = "<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\" /><SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#hmac-sha1\" /><Reference URI=\"#MyObjectId\"><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\" /><DigestValue>/Vvq6sXEVbtZC8GwNtLQnGOy/VI=</DigestValue></Reference></SignedInfo><SignatureValue>e2RxYr5yGbvTqZLCFcgA2RAC0yE=</SignatureValue><Object Id=\"MyObjectId\"><MyElement xmlns=\"samples\">This is some text</MyElement></Object></Signature>";
  262. XmlDocument doc = new XmlDocument ();
  263. doc.LoadXml (value);
  264. SignedXml v1 = new SignedXml ();
  265. v1.LoadXml (doc.DocumentElement);
  266. byte[] secretkey = Encoding.Default.GetBytes ("password");
  267. HMACSHA1 hmac = new HMACSHA1 (secretkey);
  268. Assert ("HMACSHA1-CheckSignature(key)", v1.CheckSignature (hmac));
  269. }
  270. [Test]
  271. // adapted from http://bugzilla.ximian.com/show_bug.cgi?id=52084
  272. public void GetIdElement ()
  273. {
  274. XmlDocument doc = new XmlDocument ();
  275. doc.LoadXml (signature);
  276. SignedXml v1 = new SignedXml ();
  277. v1.LoadXml (doc.DocumentElement);
  278. Assert ("CheckSignature", v1.CheckSignature ());
  279. XmlElement xel = v1.GetIdElement (doc, "MyObjectId");
  280. Assert ("GetIdElement", xel.InnerXml.StartsWith ("<ObjectListTag"));
  281. }
  282. [Test]
  283. public void GetPublicKey ()
  284. {
  285. XmlDocument doc = new XmlDocument ();
  286. doc.LoadXml (signature);
  287. SignedXmlEx sxe = new SignedXmlEx ();
  288. sxe.LoadXml (doc.DocumentElement);
  289. AsymmetricAlgorithm aa1 = sxe.GetPublicKey ();
  290. Assert ("First Public Key is RSA", (aa1 is RSA));
  291. AsymmetricAlgorithm aa2 = sxe.GetPublicKey ();
  292. AssertNull ("Second Public Key is null", aa2);
  293. }
  294. [Test]
  295. public void Add_Null ()
  296. {
  297. SignedXml sx = new SignedXml ();
  298. // no ArgumentNull exceptions for those
  299. sx.AddObject (null);
  300. sx.AddReference (null);
  301. }
  302. [Test]
  303. [ExpectedException (typeof (CryptographicException))]
  304. public void GetXml_WithoutInfo ()
  305. {
  306. SignedXml sx = new SignedXml ();
  307. XmlElement xel = sx.GetXml ();
  308. }
  309. [Test]
  310. [ExpectedException (typeof (ArgumentNullException))]
  311. public void LoadXml_Null ()
  312. {
  313. SignedXml sx = new SignedXml ();
  314. sx.LoadXml (null);
  315. }
  316. [Test]
  317. public void SigningKeyName ()
  318. {
  319. SignedXmlEx sxe = new SignedXmlEx ();
  320. AssertNull ("SigningKeyName", sxe.SigningKeyName);
  321. sxe.SigningKeyName = "mono";
  322. AssertEquals ("SigningKeyName", "mono", sxe.SigningKeyName);
  323. }
  324. }
  325. }