diff --git a/Source/DECCipherModesGCM.pas b/Source/DECCipherModesGCM.pas
index 9e8f5da6..de69a561 100644
--- a/Source/DECCipherModesGCM.pas
+++ b/Source/DECCipherModesGCM.pas
@@ -232,10 +232,14 @@ TGCM = class(TObject)
///
/// Encrypted data used in the calculation
///
+ ///
+ /// Length of the ciphertext in bytes. Use when reading part of array.
+ ///
///
/// Calculated raw hash value which will later get returned as AuthenticatedTag
///
- function CalcGaloisHash(AuthenticatedData, Ciphertext: TBytes): T128;
+ function CalcGaloisHash(AuthenticatedData, Ciphertext : TBytes; CiphertextSize:
+ Integer): T128;
///
/// Encrypts a T128 value using the encryption method specified on init
@@ -518,23 +522,24 @@ procedure TGCM.Init(EncryptionMethod : TEncodeDecodeMethod;
b^ := 1;
end
else
- FY := CalcGaloisHash(nil, InitVector);
+ FY := CalcGaloisHash(nil, InitVector, length(InitVector));
FEncryptionMethod(@FY[0], @FE_K_Y0[0], 16);
end;
-function TGCM.CalcGaloisHash(AuthenticatedData, Ciphertext : TBytes): T128;
+function TGCM.CalcGaloisHash(AuthenticatedData, Ciphertext : TBytes;
+ CiphertextSize: Integer): T128;
var
AuthCipherLength : T128;
x : T128;
n : Uint64;
- procedure encode(data : TBytes);
+ procedure encode(data : TBytes; dataSize: Integer);
var
i, mod_d, div_d, len_d : UInt64;
hdata : T128;
begin
- len_d := length(data);
+ len_d := dataSize;
if (len_d > 0) then
begin
n := 0;
@@ -565,9 +570,10 @@ function TGCM.CalcGaloisHash(AuthenticatedData, Ciphertext : TBytes): T128;
begin
x := nullbytes;
- encode(AuthenticatedData);
- encode(Ciphertext);
- SetAuthenticationCipherLength(AuthCipherLength, length(AuthenticatedData) shl 3, length(ciphertext) shl 3);
+ encode(AuthenticatedData, length(AuthenticatedData));
+ Assert(length(Ciphertext) >= CiphertextSize);
+ encode(Ciphertext, CiphertextSize);
+ SetAuthenticationCipherLength(AuthCipherLength, length(AuthenticatedData) shl 3, CiphertextSize shl 3);
Result := poly_mult_H(XOR_T128(AuthCipherLength, x));
end;
@@ -598,7 +604,7 @@ procedure TGCM.DecodeGCM(Source, Dest: TBytes; Size: Integer);
XOR_ArrayWithT128(Source, i, UInt64(Size)-i, EncodeT128(FY), Dest);
end;
- a_tag := XOR_T128(CalcGaloisHash(DataToAuthenticate, Source), FE_K_Y0);
+ a_tag := XOR_T128(CalcGaloisHash(DataToAuthenticate, Source, Size), FE_K_Y0);
Setlength(FCalcAuthenticationTag, FCalcAuthenticationTagLength);
Move(a_tag[0], FCalcAuthenticationTag[0], FCalcAuthenticationTagLength);
@@ -642,7 +648,7 @@ procedure TGCM.EncodeGCM(Source, Dest: TBytes; Size: Integer);
XOR_ArrayWithT128(Source, i, UInt64(Size)-i, EncodeT128(FY), Dest);
end;
- AuthTag := XOR_T128(CalcGaloisHash(DataToAuthenticate, Dest), FE_K_Y0);
+ AuthTag := XOR_T128(CalcGaloisHash(DataToAuthenticate, Dest, Size), FE_K_Y0);
Setlength(FCalcAuthenticationTag, FCalcAuthenticationTagLength);
Move(AuthTag[0], FCalcAuthenticationTag[0], FCalcAuthenticationTagLength);
end;
diff --git a/Unit Tests/Data/gcmEncryptExtIV256_large.rsp b/Unit Tests/Data/gcmEncryptExtIV256_large.rsp
new file mode 100644
index 00000000..3d348062
--- /dev/null
+++ b/Unit Tests/Data/gcmEncryptExtIV256_large.rsp
@@ -0,0 +1,17 @@
+# GCM Encrypt with keysize 256 test information
+
+
+[Keylen = 256]
+[IVlen = 96]
+[PTlen = 12254]
+[AADlen = 0]
+[Taglen = 128]
+
+Count = 0
+Key = b52c505a37d78eda5dd34f20c22540ea1b58963cf8e5bf8ffa85f9f2492505b4
+IV = 516c33929df5a3284ff463d7
+PT = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+AAD =
+CT = 1962739460744fc0940336d9f22e3bef68003be42c3f92b24e3171cb1a21ba82c0df5dbbc9425b5e1a2f0f64a45a96667b72c2a3eaabbba0f521981981967fc79485a3b481582ddccefe29d7571b8f8836bec442eba11d31021ab410d269b6c2733cc635ae2e78ea1af6a16dbb2afa0f8ac27f77fd2361245f0920a339a96eacddf21650c5eecd095ca471d139aab4ac87d62aa92e5496a40bf318d47cf3acb26ec12512cd926a60b4fd7a006e9d292ff733cbd613f6a47ab94b3463b02d0174591ffd383199dd083a1102ea984763aab8cc173e7859d99bfd3a14aa6d78ac8896ff5ff864a7b7980511b794fdfa03a294bbdb5db009409d4e6bc9f01d2af0bf8e5a3028a6cd121b0eda67615dba5e455d7f3a22e0b553739cb0c4b5f6dae16c3d457de7079ee33a45475a5543f90dc1fb0b46ee8fd90abbfaa4e9e8bd8c27c7a5978b9004134eb400d373ae2c2c77710ab89946337fa22d5a9519d96fa292824c73cbc3d0653815ffa853ed11aa70a29b6a4b9773e5e8e21e13f60bdd4cb5fb6e9e9887c327f10545a57e04f1d7eed7c3befe3f6558008958fb153aaed1428cfea47185a39922027d6d989bb44cb0d96f60177aad57b679900a7e012dfdb0b215b6c44e7193fe06ad1cb6b6099da17866eecc85e6b081e9becd825b8c7e9e776b86ab3b098879a23639cc982dc10d0f4d79baa78082aef7f89e6735646afcf5d299920c42cf4965da583a7252d8f87bb5783994a7c90df6b072e1686ff99594a7c969f8b254eda7408200b75a1114216d9704ba9e23ab191e64608b5d456fff0cfe987e66b5c9be087b261c3f290c317ad6968bca6ae6476438eb3373d94c0143efc42bc74d065b6e8fd1a26028a24d9b2c193775b6346a0c561dbeda87642604a4395436a7f6333e74e97ce1b769b1b2f32e6952714ce0781da9667f50f491326fee90ff13989d157229436f1f7195449acd68e1f96244729d72a498c2a5a6cac22c979c3784b73a0e13fe5e97fb99915d02a6221417d0182553a4ed2013371402cd6ff46d2d3d5b6f42ae8a0132a7272322314ef83f7f322a18fbde5edfd9b58d9900010090e580660f623abfc5573194553185e0504d52ce86ace475cba7cec3f3fafb3feb69d57462d6dcaf1fb15c7870425832c6bbc1258a41acc1d4b70a4a1377092a5891629bf164175b4805ce73ebd4bab9edd5733e7f921137ea799f39cef1ed4ca884380f4101dbe2924b322c6ab4d4fec87bddf96ca7cdc1e6cd0d0b4ddb5ae1baca304f8347264999da1e8aac724d69634a547a51855b603b47ebff350bec81ef0bf69979e9be55222c7c8c1f3c56a7db5fc2e9c8504c49f60a44f6d7d94f7d43bb76c2d0691b28a840544664d95cc00527299f768fafc51f80d34e0df0cf075d97dc1d662703484e48eb792fde650c263aadd57e2a677cb7bfee08eee0e86ad53f733ab24900353b70232c6c7cf357869e257d780462fa8fc4e51cc1c9cf421e8ecd5ec55db601ebae9bd4db553206ac61e091db35e4e48bd525c9e279a432cc8bd40378f54be7e3ae5b494a26ef99a94879cdb57e556a7b3f194cee8273c9bb40544ce09293941bcd3f66453b347b6e0e7202a664ea11c8c47a5d8faa9b3e18aa963c5edad7c5856b804bbec815f7e8783bdb3f54009b776ac005f1fd04f7f8be3e84a45049f364453117e9dfb4356c28e958a60802589dcec3fc8453ab3215fd22c646a02801bf34a647122441ce0343c53a7e8fbd5842d5e9deb02aa90ed6792cccd05048316c008403f3ac8b2a94e5d5f03ad2499a8d9225dc09e880332965fefd203348cdd14a0d227ca7dbf706151f512ec3445c15c18acaebb72f9b2d5c062e28676b3a0e7610fafc79584f6e51a59f8f47919243bbf554e28bafbcc7bf98269bef90a62ed42d6ff2efd3562ded66333cbd5b4ae571c3282bc3805e770f56b2c777e8443820f60eb7d3b8f636677fcad5c88a104d7cb85716d52145d6d464f90654bfea7aec649cd374fa372170bc6d1c70f63d91b8a5a19aa7ef9a382c173a485160254fb8ce3f6b3fd62a85f79dfadd8beeec8d3747b946fda7d961e03a035f4187e1cf5b3e4674951c1e73e50e1b1cecb468f5b713364e541d23dcc9241e60968365153a5fefe9ffbe2912d032ef1fa05c2c939cec8491d145392127a3a60c4d876079a6894011ea024830a94b19a9fa64f2a6f1ceb1515d083aa2538c5b62624cc136c1b4a1f689231aface60eb39d6d9e499a431f6f29a036028db9c45f8af06856282c1763650ef36420a1e62a3ae4388be2c4282b35587325a247861700d226403916d38fbc322b9f96eb2ddb087392b0c4d040fadb4cb230c47dc743ddc7a915482c9a7c2d9e7e081147f45a0a451dcc87680368e0d437ccf6d74b437097b8d62d3326fdd305f086691f8cb0da5779a7278a7c4658f61fc46cb5f41a5367d669e7f017c36154e57c1cca23103293956acb224a86e1dd2b8a117d585a352b7e3eda6ddc14f54a78afbe6fdc77c6a434b01626a6a099355dc7da790de63d3d8b06094cca5f019d9d891e85961376ae414b416da25393fb4078290e92bd6d219176ea4f9f0bc55c09cf05ed0bdd91e3beacbcfb0ef7b0ca6995c8d7fc3eedfa9f2dcf940006e148c96177c7b8f88272149772e91216a9c989be6ea35cfc327c7ef3a62452e9cc2b3d7da37bed0e831ffc4128a54b10413916c436a52335ac04632581d505304ade624c8c5b3a2a7d31ca2195cc249eedc500e206494c8ef4bd07ab853ee847358199a457d72d853d00608a26971b9656c2a0e6a0fd7469c918fdab7d5160d05a46c38b1df1317bb76f8bef540508113b062a3e143b6fea35ae52e88eeb6a8bc5725732e44235d9990c3783bf9c0c9be7171eeb28476fd2a2d4bdf884a8a95c9e6163c0c97c5ba9f97c553e8594afc8afb2f2620b733d5887c21b2718a24a0be7c2f7923a418ac627ced96a81fecb01d68940e6cce8c28115d33117581d456179757e16de455baffd6feb443543723383584a4bb4bfa084fca4e20e573f67e5f09878974b641968dbc15e3fb20b380e14e8fe5a403e2caf71be464d93bfbe51f0ef31929973433478c42b12887d4eab7165f1ca1801f81e6269b11b4037ef74bab90e4da20ba106d4a616ecc3b8cb1287e70ae847fa03729d6d3b7e87ebf68b1b8c2b64eca1c5cee9ffa566ad5419087301c6aa21025b4de393b3896e6c9be4ea79ea913bf952e03b2d73617c4b8c9640eca26e32df31cfaedc96bd113cd9a2418122f3887b9ef3ae8b8c12f9c43bef17e27691fbeaa2f2f80641183c97d008a1f066c976166a9993f8f0cf02ab9d403c215b3f24d0278cc46606cc2f7b70e76140c5c4dd5bce2a2150bb58f8fd326d2221209c44440a8cb966c451a39c5af2265d66c343fba036a44663e0cc62e76e6271559c8137f08e2ad72bd42cd4e68f9f7b74a195f556f6d7aa243d38cf2f75b6428e737def85e3d95afbaecc1959f41a7f5b270f4fb5d37f6d20da559878c8b341b619c9240cb01cd2a02986c9431ce1dbb785d40ad9b701bdcaa796fe350a2700ebfe4369147e3b3612692e7fc290a98e04cf3ba36ce72092b762d2abd463031f7aa8c0e499d2408a627e854ddf905f3e15b822acf5b1b81a3b2637018e1ac5c3e86c9f1b0141e9be43ceaed4d2617881fac59943c89928bfb16c93f5956738959791e422dc17f4fadb111e34b36ff17ea2c5988c5a21bf6584f5a52fefc3754c425ef6d73c9b32618ad5a4e13de31101dd46b12a2b68da3958e1b419a1881ce8b694594c9f1f636311765ec8671ad447200f1bbdd77c267f7c85fa7b386d00c7721d80b1dfa6581ee75e3fd83161910122a0d3ae09e7a90795ce17504deadef82a9baf3010b6d27d9f98c6bdaff044140c7c8a81b5fc3706613e6c15c294e5861da668fb64f9c97184191f74c93b237f9e4905b2dd16c9d0c6c61a802fae9ef29323d6099cdd0e6137a098e93b4e969af13c93d2ddae2d853e95c634a1bd77f1c81516ce02b8a0a7e8f0de92f4832cd29ab4e6d2c2f7103f76c9da7c6a5e75d9106d25f3eb58b95b4bf7e8d425b1ee1570d5940a40f6161093e272d3fc4b6e1a7b5cd499a0c12166d8a62be072591496b61d437ae3c049e58526f03a6856bfe8c63224986b1009b2f1a4053ed8f47c1a09d0466ef0cafa0c1de966499f92c40206290c658581507a47dad25e3a25ea730b14197993c8e571aef8ea935e86497e758a3419e62778360f7a7f86ba94b9d8b13cd740106c2c418dd87a26388d096eb08cacb6eb2c743991185543bdb6628d2bb620ee45b84ed85eeac8112e5112a1247d76b08c7b4bfb44f25980ed4d54dc42ba5995b14cacbb979aeb9a65cca3adcbd397656c3e6162d610ae214acb39be5648ae16ada3dc5d6548e15cca75ad7ad9f098ef0612f77b583f0b4111d24cdca27cddff68ec9fbb75929d51d175490c6c8a674eb9261da87b51e5d05c8d4de2c2ac2cf434a0620b8cb5e0044e3e3006001894d4edbd645d99dc2299af1eba50d1e2e8ee88a6f665f5e58755a941266d9906a356e47c75269c1d3761ace61b83c3c9f27560b39de16c42d13aa2d46a95f6913f2b7e974b635295d7529493053ba31641ac88ac4b364272052608015c790c56d4d010acbbaca42165782cb8f938f77592661656e19fe30a4f077e2e1cc1356f5ee00e3dbaf433c5b60924c64daf2c2f6b7d5fc68514ba04c15af8aaa6cc9554cd3a339a1342b9ade7e512c350baf68c2a7a168277f525f1301bc4ce10d05ef8835a11dc93bc04b9315899e2c66ea3878f0be955bbd6616efb2b92420d0ddefce419d0f2179dc7f313adaf03898bcf81aa803511db73d8910737ecc61727b06a3ca51aa8fe2a6f201500d1b8cbc1ccf0794cb1a8d6508a10dca44532a16435186ba6c5906a61d71b83fba1ea8e35a9f69630cd854fc883123398616244f8d6615254d2c04072a7c1a702bff82dac4fc2b3a11ef923c062043a90906a12d23791fb9a275bd2cfe4cdd9ac1a78794e5fbbe834ed1af1c69de004450bfdedfbf7d9b255b2c03654b29becb57a4f5860e83d29577bc6edb1e51853d496dbe3ec60b7341f4edc32555cb9739071171b9a72268b07dd8bef66eaad217b00fb80bce4b7943fa497f0f31ccc9c840540efbe87921f6d3ff62afca8ae44a84e7d8f9995cfea72fed40d40bf27d192a5d383e730a4ffce342d6b4d1b58e1718dc8bd82293a8f7d3a1379ba8c2f1efe1f818afaeb56782831b83cd358655b99b7160d354e7c7bde14672e391c4ffe5293e5968440f201e1e9214a620e9fa6514228fa249536e4f06cc4f572211b8c50130aa16fd8fb476eeed9885c444c6b79cd3f408e02344459989138048b12d78124a6761b537f4bb9d26041adb35ebaa91bbf2e5ee51de34512556b150d05c6b86f36a27480903171fe7880cd5e7b2dc67c7a4045b62028f28b0366ae93e755ea75acf572c976c5d428db08228f6d35476d87006962798386eaa017426a18a2aa7335cc67154a867ecb93adca6e9cfe835c9492ee4c59cab815765fcd57dff2d985893a24f559748ea71a1dadb5ddb316c614db768c90f58cca0f9940326fa0fb57c6e7bb3d0b3e8f8d14a440992072bca0565c32d292cd31ea2267d65e89abf51c5dfca96192a05c155d87f18806257012beab17c768629c580bb0f591b3a53b1f34d464d28a4cab1b524507eb606f4203136f350490911a3543257d2e5073b55b13b055f6fcd293d477bf969df479cbb7d47f977233d53b5dc4052e9fab6c559e85505b279a3e8970d460903d9e93d64536c898dbf05b182ffe4ac8f9cc6a6ceb8768bd00c03ab26c83b816aad21a7eeaac5a34ef7bd0e07a6f97f0c084b9427426341dcc449b7a2fc82e7ed9bad72a5f36e8cd70c9c7a4a3e1798ebe5efc6ac1ea80f8bd9902eb7bff63211c0e5dc766e47c214b1824c951a4a81f34e65131b9dc0e7e6ffab53c5361ca938c372a78be114b6d9efc1fc7103aa1aca82d3c0b1e4eb5ec1382cffed520bf746d37cadb0fee7fce4599237c2bc07d2171fad1b0e5571cacf9c4ca6dacb6728b11139d6de0bf3d42f2a0c04cce637da1b0ccbb8d7f5578c1c2cf91865e9764bd916855b5f0b459466507c561ecc1f324343ada7876ba72f0679a2238c64caaba488438d16444a7eaecd3cdd2ff26bca474d66921500c5bafae41c352dfdeaa1b69f675ad5e536509a19c1d5068df9e068e286e6834db74500d38e9cd0852e22d6757fed155f5116b1dd9de55c1fc9e6ca105a9909b0ff67a2f0652283441ad9976a79e001fab7a5927fa6d1014426102d14170e50027073f44c9863085bfd2db896806e59c136dc6ad33c64f93c7290596349b29a612c275bf17796811637f38b27d04f188b4afe8880ce5992b54e7d38ba6bb56a62e5249ed612e30fbc4905c52981086bd9f97d81776d89deaf979327246565b9ec58b7162e140165b0e0cc240e68e6afbde8aa1aadec1beb641bd04853f6bbc903094e0195a23faa9960217506654433e0427655127d0d68c708a663de14364bd4147f03d3aa2ed0a9d47e34b22b613763c1fef4ba26801d27990fbab8992bcb951125a656393c7a45b191a1ba33d2f95ee2aa4e26da7e5f40c5b781417f3bada1869175c08951fc9ef2bdc7be922d6b9f946b108868050b0c616573f887f0e2fcc69d24e06675de6e75a30360c12b31fbef86a6deeed5b9b950ccbee02b86d683c0cfad5087054e5d1b9d6189ebbbf8bc46c80b467441eb87a72abe07f86bf0705adb9ff74981e01f06ffcc3c4d549fa0c72b9669f95b9eb31c1ea5b307ad70e5289a282e38fc1ae85418a3a396a756f63417027d54203c71f66de683ddb5f2e6eb8650f1eb17b12fa7594e4994ff9543d68d3e779889c15c4e66da0844cdd2e59c0e763ec067d44f96e57ab68ef4310f6e1eea00550976a18e21f14bda593fd816eb149ddfd48838518f3e8acc32b43ff7d9fc358ab80e4249d9761c0fcc8f8ee69f7760b8bae25c78e2875c025b1effc273d47a26745ae22be5d5d7a24c17c5b46b47fe07f596a99ec0eabda65f977034082807b7245f4b533b74758f009c7c734b1b700251c74f987c0c955a4ac8195dacf489b874ab936b78f7c0469e6465e46d091b6eb1fb2e84b6b85b2cdc3c519562b9ed2d081e58e21b5c28425a21205ce9823f07d9b4d4fd1563c07f7b56560b53b2124f8537274069bb41d87d9a307a2a6760b8c5964cfa644918a3e21fe29111acaba95791e0b09254902009d974c1e1ef9cd2c2973a60307c3739948f3a35f826544f94bdd454695e21a06c738e0ed9bfdda908d9dca474e835e1375635d4a2169d34c68704588fdbc0dd947eb0ef97aba3dc0314811b03b65b232ca656353b8c295aaefcf5173d85a4adfab963752e971ddbc5ba162d28ebf396e23c596e96717a31bef2ad7b34cd6ea6b08b5e6b623cb38bddcb8d2deba1cfd5657adfd9cd5ee42687d28ec3ccf014f1a410f82791c465e2cff9898ef916e74d295d4811e30edfba03e036e668ed66be635b3099b5972fce9d83c4270d244b48bffb7fd9d27e7820aa1b1bcfcc5bed5c478022a7ff8607a59a68ef1c759a3c51fc12a8f014308479137416281d065aa3b465cd20817cb8aee3006b1a5296421d2607513b7f2a22b99fb2795b27eb12ce76063672a426a52390efac80f64426c6f526f74bbac72f28710c952bc6b33d3ebaf58faab3a4858153bd0083d8c4e028ead2d4dc9a4660983f3f05bf6087153fba6088386c074840649591d0ffbb462b2d22fbee8d39a96ce86b0f8e4c44c057345e5a9763b66cf145a322c384196a46acc49d40cdad44d164293eeb2c432f9007942fddfe79acce1b96de01e41c4a6fa45993d6806248818d44594975e6b08c812fef3563c7a700a114103f77e6a65a27b25d235a01861aa9a2d3d79d5f24a61cdb9472899684d0677086b280fd6037418e126c527849654ce9b037a5e7ac384e4cce9357c9c1946e1329bab80593570f2bb2dd34a1f8e91752a77e6294ecc9cbcb1bfe672ff129d266e02d85e4d9624ff0bb47ab973dd4dec320e7c7abaeb839de47b97f964a5d8eb885adf2fb7a0333168e06a3ae27145f734df128a8660cd50799fd53038a9135aedbb71e710989da2ee4ba3ddedde03b260735d62ef47e6ff12f7ca6eb154eaf912fe8434f39755683eb985c062436a36765c5014741e9f89399b0d3935c745e16c52736cab827e0f88f5f0529e773e1bf01712563aaaef2020905fde6b405ae0556f126b2dd592a10f263a7766ad32168c189db902ce80ae321ced61fcf37ee1d9ff92c11ce2ca86e795eaee9f64338a7fcb98c51c89b9fa5b940470c4c7dc7c39e39b8790faf5f579607fc1e20a1175e6cc91e5fc644feb495f9a82b339921d68b76ed5a8f3d66d5604b9b3bd0fad13c8c0b667209e629b02ebbfb31311572c194f680e76b53f7f6ba1a0dca5a220e8bcc8f83e0e10ceee87e470d0a1b24134d0dc181a9987d6376991f3a5668a974aa08e815eaadb2a363c04830b623878cf7d471fbb172a1ffc15b3873b17a73aa9dc1e8a40dbe73459df0dcf40836b76ce7d34b6ba1f6f87489561b65d6c0f933aafe80b35821bc9dcb51cccc8a0f157af947eb17ca76a0a48b640c02cd305343a21a8622f1e44d49d1cc333700a2642aa714fcfaa00b048f1b11c3069dcfd9d82001b4d02c2cae54e069a3756f3271b139d06b118d2a26da503f56e0f8ec85bb540d337d398bbdb35827186a311af491d5a3c4340ca8cb73846c288ddd7636421562de17780de817f7826d82d6fac0447a62ab20d8d0ec9c2991145094e74d43c693065a37d6d392f8691411e5bab60bcac8f9dc45f618be9f844622d5fdde4a8eb75c428030a531b8770e26447578b3ff6454866642135b3253a89868d12e3c0f0936fdac4086dbce2a1791b6cb981fb0bbaae5cb7731b4c429d50e5826881047e53d739b39f7f9eee92c9d8937b39786ede0ca273f9bdb73082940d2eeac29875b12cb3ecb2a8ff1cf068de5db4ce0c19e52fdace96d1ef94d4093f87b74c3438480455ea8f4a1e73db6d66c39b83467cd923fe924a142a511afe39f2bf52db804b045c707995f1eb0490cddd2f31a1fc379e0e0f73a723f23933b85c16b21a0e85f0b7dcc4c3241d84c2cffd6e14ddd15181deefc4981452e6fdc1bdccaeb2d498ce657eb35df01c6f4987a2bfe69ce876a5fc269abc44d17275b0b5cf2cefd0215d3a4bfcd0767e794dd454c86ae68c009aba6c7799c4c259c5384b7fa8a1fb224fc88a6026d35760f379df149d352f197dd24ab7574ea61ff5a58b6ca03478ee783925a82f26363e0f9d91d633799f4586f3df2968e7e979ddb9a46163d4c782bf3abceb4bd1b0ef80740c322227d0a9cdc742c27e503236425e4427658cfca597592cb6ccae15d812dd5498eb19c96e55e52fc7bb1efe5389df12f5f13ef6565bc89b2339ed88d39c7b3dbd4d8600c360df3d811c46e0d298d4af6c54b8b2fa5c4d677419ffb3a8ae60ecf8f40314ddb38637834676b16bab358ee636cb5bbd24b0d5cab8ad4e5ecc4ed0cc9e603de66693163b362d2c17c3765c4414fdc91d4d55cade75873e594fe4788580fe86b555898f7fedd2d5d4abe54a0ea973b4c35359086ac588c2dbe0124c20843204d68e3ec9eab206ddd9a01b518ec38c1538086717459a776c2bd9b1c33326b9c810b3a533acfbfcd04e29f6e35196dd67597f53948b4304f59af91889112eaeab5a00e6847f00c0b29e6d33cc95fcc1ce5dd9fbe69da2bd6f0f9c703cb3bf611f748b1373d4f58e79e594cc126f23f388ebde22765c13f85edea0d5c433743397820246ec687cb03f461791fccf87907519146ac4803f881fdcb89111db1a01214ad9adf1638a8778886a5d25732632f86c84ed8ea8c89e08dcebbf0364f484ab4fe77a6a083d955a4fc044b50717d16d7f830977cd4a099b6e96bbf16b924472a75367291e8b18125f7160f340fb1104abe4decf1b15fa44e7b2a81f1c1f1e43e1795fd1bea5be903f347748f32ed0103499ebd52b5b111e02d17604a7531d5f77e0dc11b31f17284adb19f96c8244755051b67f3787538441d4f0124a477fc24dccc2876383e8ffab429cc7cbb392cd4529b8b1d2bda3a1034b6c076317ee03be06c1fa583f29c4910c0a6b1d928561641525ca68c487c006f8f1a05f00453341bb8c90e9a7cfee2852d5031276db9278746099a7d1914af7c6e0e1337c5f578c637ab8be002e1e89265e887b803d52a6123398a292d44daa37a6b7d1b55b4fb29b67efb63bf71cbe4ea5562ceb330fab4f871f19cf387e8a1ced194f1223596d05b13a57658b96262be77bdc62c96b6077514fecc345055b463c4ee358376cc68f2982f4d0b0c894ba0edb1c48055b35a82d00a3085401875d5d7ced71e1292179219558e1f6952b78a7fbed4148044e095bb1100dce58640352330f6b59dd7c8bd94aea2270257089ae1ca9b5fc904d705170c5480bd215586283c8bb3144886ce99e001f4cb66a643a8a962d830a940e29fccd126844421c4b343b40a12426352058f8e2dfb69e9d1d342cfe1d6f78ba114538b64c8198228a80a9f9e2998347dab5fdb9b04cd4025ea75b9ca70f83cef28cb2af74d5dfb887869acc8dd748e2f62dd3eb7fd60758ff46adb4c60bc2d5b023bdeb9dc1cdd20076d7a1a53d60ed345a29692746decac6975628d9a7840476df754e448ecffcd1544f0d37ef0932819c1cb00ae384144230b386675df5e82b4ea6dda50109543a1bd0fc1158f4221304700d390fc2ae525e45ab6e839e2c51511a60b9810e13870d89ec11428d3b9e67f94e12e85af79d16cea0ca12181b3a9db8e2aceacef9fb445f56ff1b751645a5f9f66205fc3f83bd52f6378ec4ca6cf1e37bbdcfc68f5281fadd051a15e911cc92d8c137748cd4ed8f7fd9cfd5e81339a9d16b8eeb3d286af478400a54c0900b6e9c1ccd2f579da1b0afe3f18047f851c5c2e6839d9daf93451bcb1a3552d7ecd7a3cbd6db11aff73e99ff4faab639a202530224ee220196d0c18249d7d02ca09dd8235fa0329ae029b7b89588210a442a6f6d4b9565541651a66f252a79857df062893a39d9bfe95b1292f2e318b86f861479051fbd1b93cf32007b361cda84960eae23175f67aa4176be10fe6db20a3f93d388e4fef5352de9f75a436bf4c865ff9b59dc1b10f01d0b79eab0e7bb2d735a088b77350a0c9820ce9f755f5fb0172baa9ef2e77811e70783f938734284cdd9479997e1f816c7833c398440e676e8498a684b9aeb1f3c4b98c7863e2faff4f9d90ac75303fd3a5132c328031dd4d896792affc92cba00e9bce1cb4c219d89bcac35bf8c28d9781661130cf084ae7a450ea19cdafaf7dbc4223a4cc6b36d7f59d2724a903792e330214b60a0ce92cee736ee5f42f19eabc6575922169e934cbec296e1434ca835773eb2059db075c2ba0fc36a989e3c3724da9c02e6d35bd285bd10830916e12211f9425488cf5d3db3ff8175beffe48502db96037439f6632a24b2b6bc392f2608a7691872df141dde41bbb9e54a8c28f11cb5869e911d1444fa9970f32b402e4dff925530b084f6a48f5da80cd0c68c3c40839bf6e6f45054ff20f2a18bf53f79266ad71bac06e20220a8b2ae55ebc104b5e96de200bc4e953e1b527dab7608f41a3890c51f9ab8a2908629119c7bb4a3703e6a2c515f6ff32e8d7b47e543746465d4a66bfc7d33bc5bbf237400bf89905d75eda073ae398b615cffdb29dd909f60bbb17a0ee2dde1b401228f9350f866bbeff302ef4159c9cf0c0c1234d849e47ab2066bd7d3f334a8ecd963d4e00538ddf3f0205f164c37af43b30993a7659a143d6ee01ce2d8f9fd0ca41b93e0e9e590ac02017b60486d5a72300be36d3145c69f8da26a18fb7a4cd3cbe03ae0c60b36847c49a9ea4a647620796de292638e7340c3c85dd216c5d785a274763a4cd7fb06a76a794b044cf4329ed3e6cdce0aaebdb5637d2637f3894d6a03063011ed46d64895451e08f820e4a06a0eaee1464100d20a4e788a98bd7dc81b65a9dabed3a7e9995201f78055a11f92a36486f25bb68e0e41a31575f319597265ea12cab364f75c85ddc1837aa6dc5a3730e066da68e5d2ad1882c6aa05b2aa18bdcec637b620a72390169aed98cbf27806ff17106cf86ce7f07300cad2bc5f5d49aad86dd843952b3e92ef2c6518172311a8ae05cd2423cc822d38dbcdb3a6f8d10fa308a151628e64928a129db5e0cb4df00f124b09fb0d8b0867191104e02b78306711f347e155f1c864c7a41168acb7234bb137d0731d3a02a05865dff1e1b8ffbd4e6d1b6fbc99d4190020dbbc68c8743c29bc831728f1e496a17113adb1fd3b046f66f2a50a5346105c913a475ab953d0d33828c85cb96ba398f670f80c1bfc57c7a043400cabc2f8b2e954bd3385f1d84a0acb32fa5b28155e3559c923b1f143700aebe06b8471e8d29e52c2226d991c349a50bc2d1dc6091288d19c95eafe1720f4edf9b4479019618f2f9753971791dbea20ec25d6cb731f46b0e98d783ef110a84a35d576c87c1280956757d81b5f6003147b7393e3ea05aaae6b4c1888417084af3f8c5c57ec70ed098b83864368210173bd9190420e8837856e065976e56838e63b526f2579e198a3dde90685f12d357e2b839a30c13d922a27453c67a870547f75654a667202d163217b58a600fd653aebe87c813f50693f4237d1c356c92fe3ce9f208e9f56c900ee0c35f9dd5140a42b78cd1b4d2df4ab3723e4a504dd3317938c6ea2c77cb0c3eb23a1d41319131235bb1128db8882e01fe896500efb09690481e03bcbe9ddeb2d6470015f43d462b7d7c520f54e83c6e3eeec37e505b0aa527f979f49b551e5b72db59b783ba77d59494b92c0d7c05cac66ca0b8558a98ab684826ed38f68ca41b223acbefbbd40d39de41b31a6ef206d8bd47f2c1c5bfba0fc0dc8fb3edc2934867b503e8d730b3d7d37511bb77ba458f465378d8b94e7e9045d7fc0b9d444cac58ca403dfd74ba9d471fbc9a2f5dd36b396b6dffab5a0d534d94a0cd6f60e5056f798e6a67a360727da5554fd3c74783d28131f1059363348d4c0c1888454e1b74c46c96e68ccddd34f9abd856fbc051d565b92372d4f8044910a8db2dab4f64f21388c1ce3c79fee9d4706024de1bbc157a5f382a4e6e105e1682a55e5edf2dbe43e5a60db0369ad6ec3553079e8f0f501eeca7a5eb108440bae20e75c7fb96432e611544c348282b9abe62be41b48e2fb68f06d22749f6e61a0fd05a7736a2864dbd35e37157e9345b2b304ff1f5b5cc9a69b7f64b62b7d2c4942b65dd7725782be92b2884ba7e1c4929042f19c5995a4e031cb07501686554e6a07670688d729ba51dd64719b526b4726c95101a159459c8d9e87c8546bf77cfc6f94b680c014e140fe412e9a5d3c53fa42cb7dea39da034b3869839fd70d783172eaaeb2bc4848000b7e36782e8bbbf7d16bb1a617fcbdc57a4b6f1f8579f31088302fe0bc3214e50de4a627b9c3f648bfe8db0c52b98631cb642a5b413e9f181546bd7c8fdd6edc4edfd15886cdbe846729caef143961bc5329d09b6413db4d4144bd19a6ed21016817724c602e909a2e714dda52495b22342d6108bc2b9353a9d1d3fb6248448e3aab8693101bb83bfc75cefeab2999f8429a442a4a85afb036af080be53eda8d7ad0549610d25f1be1db3a08063d2f33cd9a22d22606b40aea4e590b2ea13189323cc6ba26e2cadbf324f039621cec39b28318ec4b0eb69624a185895bf493c561967a036ddee3e7e4477419863df6e7b80d254bc21dec545fc6a1cf81360851a0bd729d883bc33bba6c863e77eaba4d2fdd4ebc63f467fa59dc1d7542f0b082d7769cce5fa37a46e7d17de28afed2fa48a5d524ca8d5ad9cab11bcc47f13c09e1af78f21ef41df23e6a3f82692990de26ba9d46c4d941224fe808bf3d64ef76ef57a0f44b60619ba96fa2d00ae6c5904c6a29e1ec9fe22ce369b42713c7b2ae798835f15b3b7e019b43025af9582a2e340bcd965a875923d82090174b1e56dd0ca3fc1bc9e47026d4ccf4cdd94ac0893ce8705109154f9495b488b3c58b818b7f18dd270e3145c8fd1897e59d5c1f5f66c6e5e22c3e9f22fa520c93001e24471b589bfe50e566f5c13c111af3a9960ae2a841a8f670db52ea4dedf8184e79e71012c5141910547da6ac43467442582e69ed3ab8e4aa24a546d3821b97a7f66f25e72e55b59628762a9fac88aad9973e8c697bde115455107e6efb093149cc394fdec50171391b1f92e8a358b9f2bb17a59c5cd473f7941ee0f2146cad85a8cb93698962b2a9221a6d2e9808de522e6dff064dad004f278c4ed679651b87bf246073fb41088750af7ac3253c5c6fe21c962800b329c659bbd2ee1628b08ab6e4cb4240d14867a2a4bfe23b55edf4ad34174ea118bdf35d3033428cf3bad8fa254151cd0f499e7ed807ffc5054021d2bfb9abfb256fb3bc77571e5d4469f3f5ab84414a1ca74ec329c489366f23961b2b3a53cff73afdfd4b8c891dd62ec0214536f6a760011bd7b5426e315a35006e82e0e4db9a5b432adfcb285462b83e453b6571c371a21be7fd7266809ba0527d366fc680b150c495785c9aad590c329b98c3f3597994c1cced78b7c00d79348b26eb52c4e952558236b4c6b3b7446d9cd5c7e7719063643d10977b3a78368f17bf5d0e0a7fb82ed3e88aff6c4ff1985521df45fa58afc41572c9f942c733cffe2ad111943eb6a483afb34d4faba4af57be2048019e7c8dfbf43963ec3379c484c86ff7ac67b5db931dd80d28c39733eb95ccca1b0c472d5ad042097c3db699976baf7b5c9b895bc91c102b16aeaa89de1c9a168413f8c2df93682cd201d559259e16d539cca0d92bb1ce66b60d5e40dddd14c12d37b1a61810c23c3a655ebdff89a45acdf3ca99cbb6f23a0d9a2bc31adc9f798381cd2ffd75bc7c22398418b21f6e38133963595d2ef88fb2e54911c15776d9ae336ca7760a969504b2d700095943c76aea975afda0b7a00e7e647264818980e995e0796c772ac0ab166dd937fc5a601f09dc5e408b35e1b16fd3421ae4b3dd349c8252b1ee4cd59794e0b63dba4238498aeb7d3baf5122563aa71b56919ad05301d44f68ad9ed3ae569e733d99f01ea8d91cfd5854ef03b3ba86b225c343ee0785f2521161344428ee7bd90a0f21567e59d0e559c9d046dbf75987b66915fd5c29827dd7a7447f738f06e215b0e1a465e7a3723428c6e0ff27710211f0b36cfef21f7946e8b98ad3c221abcb19f7b8589bc0ea38e3b845590ae3c08396d6597385ca93e9b9962352d0846f248d68360c201a6dc9a1208b1ce5cc6fced87383ebd94c902e99532761c95dd9f9f3953245a12d43a04e0e7ba7ee8199922abdc7a411c45712ae0839b2a1a70854f01ac68b57cefc478c3e9a197d1af9e51836bd01d70bb633c20ade6d77a0fc39b7f914d4d80380b2e062253fcf3091acdaa199e7317f73623bb8316cfcc18f61b397993c7d6b1f07b2fce6603a249b5858f0f071a874ecc5bb5f525ea6aa333fb4d26a810cc0c5576a4d5772dc77c05786cfecca8d9bd862fc1e952d461b8067e0a00cd757795f77340923dc140635870d26f0f34b4468875d026d111f56a464393ce744f709d4d4813bdc426e6fb3259d9f76146efec0f4999045d6ce12709342259ca13d4ca6430eaad18adfd632c25d771dbda3dec07389af255d7db81fe6fdb6da59b4f8adc876843e319c3f9fe6716e4e4819e529f126b1433f379322cb24d1c44d5c86b2c9f1998ff6dc7451ad11cd1c4a09232f1068298ac7346f6ffc2012c959927477ea2c92a23c6ccab449aed9fc7b7e6adbfdfa7c141797dc0f5ea18ea719ad3632dfcf83dc1019bc676c01dd54bff868a5c664bc94b8b6bb7f6cb26679a7710bbd609640d0983bb438806fbbbafffabc36be4437ede0a6e5f1d0058604dca937d09c6aba45c63438e352bedf93df57f46887664a252d5297a4f88aafd247907f194d115f75f02d0642f28018c49d829e6abe1f827515b9530de240698041b4e265f1387368081dcac217be5c66625ec8c6db96585c9f872942b0078d44881457f1032647972ea053fc79a9ab60abfae6392009981e7d0865ba87e1175e40a4ab47f00f45304dd49e69698a6ba078b36bf822ffb69ab6359ec3acdfa9e80d3a85faaf6480968af359cb1d70636fa3b6c488656e01784c0c01c152cf4f61aa3b69e99df84de84c92493465f1d0ecb7c2b3b7eb0179ea2d8260c107e1a612af1648b7b82727c599a6fe52dd59be808863c021bb13c7d888fce865519614696689a0adb7de154fd80993853407578253047219ca1b3cf2426676f42e45b7f7ee64ca2c300d3585bbed4639956ff909e22be9b3de02ea9dbcfdf8dcc4225471a7ad6603244bbd54daa44dbf361b2ba87ed77c607e476eefd1c7ae9944b122107e7e16e828aa9b5fe96ac792736900333d039b91d2cd762e4cc8718bd34c56538f85eda4b34666defd47e2dc96c9fe77e53c027d2302f7129db1474c9354cd07f4c0f59b88fcc38921a23e40a0fe4d78f88fca4121ed81454e9cd64bdce282ef1097e42ee72ca2cdf8491592702453b838a26bad7773de66c8b40f437fd7adcd2e72762e506735c5a3a7c48d91b89ae45006032023d7f7b4d8cc8bb6ffd353108287c5cbb2b376f0fd7b66240e82f40b184fa1eeea02c097ca72a9dd6e5a7653c7e0ce6117ba2eff78344d981ac4572035f311f99a9c6cd9baaed11a6153918ce330e603153b8f95baae1dd131ff5892b5e66d72890e8dd172b4de78c7b361ccb09b649cefe444c358cb5868325a15a73375fdf129f06bb90738b8aa70700bdeb4c7f0462c1fc0963d4c9a0722031da110265cecac78db92b450d2d3f31a5187c8bbe8f534a06385d951f87df053c780376cbba7aebec2118bd5003ab3ca03e6d633c6063aaf2c27054eb575d0be452eea09077b96a8bf8d3f136b37df569c6d70acc92069f2314cd5f80904a9990d8ba72b5c23770246a22040253d7fdd668f4f62d953005c7e0967adfcdd0ad7c375900ce8e136d11498342a68ff2e1d8a33c387720a3a4a70f5120abbca55c663777a42c623a0b6c6263acf0ab7874d0d8b8a5cc8799f2054328589a8954d04e8b1326e3538da504c7571b598e666d18055073de5e4978e54d023c96c53f0536e67d71d0810b
+Tag = 1355862d917943aaa7ea7d18a8ed5b2f
+
diff --git a/Unit Tests/Tests/TestDECCipherModesGCM.pas b/Unit Tests/Tests/TestDECCipherModesGCM.pas
index 5a7ba9e0..1172588d 100644
--- a/Unit Tests/Tests/TestDECCipherModesGCM.pas
+++ b/Unit Tests/Tests/TestDECCipherModesGCM.pas
@@ -29,7 +29,8 @@ interface
{$ELSE}
TestFramework,
{$ENDIF}
- System.SysUtils, Generics.Collections,
+ System.SysUtils, Generics.Collections, System.Math,
+ DECBaseClass,
DECCipherBase, DECCipherModes, DECCipherFormats, DECCiphers;
type
@@ -200,7 +201,11 @@ TGCMTestDataLoader = class(TObject)
/// List in which to store the test data loaded. The list must exist but
/// will not be cleared, so newly loaded data will be appended.
///
- procedure LoadFile(const FileName: string; TestData : TGCMTestDataList);
+ ///
+ /// Use when loading data set with incomplete entries.
+ ///
+ procedure LoadFile(const FileName: string; TestData : TGCMTestDataList;
+ AllowIncompleteEntries: Boolean = False);
end;
// Testmethods for class TDECCipher
@@ -217,6 +222,9 @@ TestTDECGCM = class(TTestCase)
private
function IsEqual(const a, b: TBytes): Boolean;
procedure DoTestDecodeFailure;
+ procedure DoTestEncodeStream_LoadAndTestCAVSData(const aMaxChunkSize: Int64);
+ procedure DoTestEncodeStream_TestSingleSet(const aSetIndex, aDataIndex:
+ Integer; const aMaxChunkSize: Int64 = -1);
public
procedure SetUp; override;
procedure TearDown; override;
@@ -226,6 +234,7 @@ TestTDECGCM = class(TTestCase)
procedure TestDecodeStream;
procedure TestDecodeAuthenticationFailure;
procedure TestEncodeStream;
+ procedure TestEncodeLargeStream;
procedure TestSetGetDataToAuthenticate;
procedure TestSetGetAuthenticationBitLength;
procedure TestGetStandardAuthenticationTagBitLengths;
@@ -302,7 +311,8 @@ function TGCMTestDataLoader.ExtractNumber(const Line: string): UInt16;
Result := StrToInt(s);
end;
-procedure TGCMTestDataLoader.LoadFile(const FileName: string; TestData : TGCMTestDataList);
+procedure TGCMTestDataLoader.LoadFile(const FileName: string;
+ TestData : TGCMTestDataList; AllowIncompleteEntries: Boolean = False);
var
Reader : TStreamReader;
Line : string;
@@ -350,6 +360,9 @@ procedure TGCMTestDataLoader.LoadFile(const FileName: string; TestData : TGCMTes
finally
Reader.Free;
end;
+
+ if AllowIncompleteEntries and (Index < Length(Entry.TestData) - 1) then
+ TestData.Add(Entry);
end;
procedure TGCMTestDataLoader.ReadBlockMetaDataLine(const Line : string;
@@ -713,73 +726,113 @@ procedure TestTDECGCM.TestDecodeStream;
end;
procedure TestTDECGCM.TestEncodeStream;
-var
- ctbStream: TBytesStream;
- ptBytes: TBytes;
- TestDataSet : TGCMTestSetEntry;
+begin
+ // -1 to disable chunking
+ DoTestEncodeStream_LoadAndTestCAVSData(-1);
+end;
+
+procedure TestTDECGCM.DoTestEncodeStream_LoadAndTestCAVSData(const
+ aMaxChunkSize: Int64);
+var
i : Integer;
- EncryptData : TBytes;
- ptbStream: TBytesStream;
+ TestDataSet : TGCMTestSetEntry;
+ curSetIndex: Integer;
begin
FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV128.rsp', FTestDataList);
FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV192.rsp', FTestDataList);
FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV256.rsp', FTestDataList);
- for TestDataSet in FTestDataList do
+ for curSetIndex := 0 to FTestDataList.Count - 1 do
begin
+ TestDataSet := FTestDataList[curSetIndex];
for i := Low(TestDataSet.TestData) to High(TestDataSet.TestData) do
begin
- ptBytes := TFormat_HexL.Decode(BytesOf(TestDataSet.TestData[i].PT));
+ DoTestEncodeStream_TestSingleSet(curSetIndex, i, aMaxChunkSize);
+ end;
+ end;
+end;
+
+procedure TestTDECGCM.TestEncodeLargeStream;
+begin
+ // There is only one record in test data set atm, so need to allow
+ // incomplete load
+ FTestDataLoader.LoadFile('..\..\Unit Tests\Data\gcmEncryptExtIV256_large.rsp',
+ FTestDataList, True);
+ Status('Encode large stream using chunking');
+ Assert(StreamBufferSize = 8192, 'Might need to update data set to have enough data!');
+ DoTestEncodeStream_TestSingleSet(0, 0, StreamBufferSize);
+ Status('Encode large stream without chunking');
+ DoTestEncodeStream_TestSingleSet(0, 0, -1);
+end;
+
+procedure TestTDECGCM.DoTestEncodeStream_TestSingleSet(const aSetIndex,
+ aDataIndex: Integer; const aMaxChunkSize: Int64 = -1);
+var
+ ctbStream: TBytesStream;
+ curChunkSize: Int64;
+ dataLeftToEncode: Int64;
+ ptBytes: TBytes;
+ TestDataSet : TGCMTestSetEntry;
+ EncryptData : TBytes;
+ ptbStream: TBytesStream;
+begin
+ TestDataSet := FTestDataList[aSetIndex];
+
+ ptBytes := TFormat_HexL.Decode(BytesOf(TestDataSet.TestData[aDataIndex].PT));
- FCipherAES.Init(BytesOf(TFormat_HexL.Decode(TestDataSet.TestData[i].CryptKey)),
- BytesOf(TFormat_HexL.Decode(TestDataSet.TestData[i].InitVector)),
- $FF);
+ FCipherAES.Init(BytesOf(TFormat_HexL.Decode(TestDataSet.TestData[aDataIndex].CryptKey)),
+ BytesOf(TFormat_HexL.Decode(TestDataSet.TestData[aDataIndex].InitVector)),
+ $FF);
- FCipherAES.AuthenticationResultBitLength := TestDataSet.Taglen;
- FCipherAES.DataToAuthenticate := TFormat_HexL.Decode(
- BytesOf(
- TestDataSet.TestData[i].AAD));
+ FCipherAES.AuthenticationResultBitLength := TestDataSet.Taglen;
+ FCipherAES.DataToAuthenticate := TFormat_HexL.Decode(
+ BytesOf(
+ TestDataSet.TestData[aDataIndex].AAD));
- ptbStream := TBytesStream.Create(ptBytes);
- ctbStream := TBytesStream.Create;
- try
- FCipherAES.EncodeStream(ptbStream, ctbStream, ptbStream.Size);
+ ptbStream := TBytesStream.Create(ptBytes);
+ ctbStream := TBytesStream.Create;
+ try
+ dataLeftToEncode := ptbStream.Size;
+ curChunkSize := dataLeftToEncode;
+ repeat
+ // Apply chunking if needed
+ if aMaxChunkSize > 0 then
+ curChunkSize := Min(dataLeftToEncode, aMaxChunkSize);
+ FCipherAES.EncodeStream(ptbStream, ctbStream, curChunkSize);
+ Dec(dataLeftToEncode, curChunkSize);
+ until (dataLeftToEncode = 0);
- FCipherAES.Done;
+ FCipherAES.Done;
- EncryptData := ctbStream.Bytes;
- SetLength(EncryptData, ctbStream.Size);
- except
- on E: Exception do
- Status('CryptKey ' + string(TestDataSet.TestData[i].CryptKey) +
- ' ' + E.ClassName + ': ' + E.Message);
- end;
+ EncryptData := ctbStream.Bytes;
+ SetLength(EncryptData, ctbStream.Size);
+ except
+ on E: Exception do
+ Status('CryptKey ' + string(TestDataSet.TestData[aDataIndex].CryptKey) +
+ ' ' + E.ClassName + ': ' + E.Message);
+ end;
- FreeAndNil(ptbStream);
- FreeAndNil(ctbStream);
+ FreeAndNil(ptbStream);
+ FreeAndNil(ctbStream);
- CheckEquals(string(TestDataSet.TestData[i].CT),
- StringOf(TFormat_HexL.Encode(EncryptData)),
- 'Cipher text wrong for Key ' +
- string(TestDataSet.TestData[i].CryptKey) + ' IV ' +
- string(TestDataSet.TestData[i].InitVector) + ' PT ' +
- string(TestDataSet.TestData[i].PT) + ' AAD ' +
- string(TestDataSet.TestData[i].AAD) + ' Exp.: ' +
- string(TestDataSet.TestData[i].CT) + ' Act.: ' +
- StringOf(TFormat_HexL.Encode(EncryptData)));
+ CheckEquals(string(TestDataSet.TestData[aDataIndex].CT),
+ StringOf(TFormat_HexL.Encode(EncryptData)),
+ 'Cipher text wrong for Key ' +
+ string(TestDataSet.TestData[aDataIndex].CryptKey) + ' IV ' +
+ string(TestDataSet.TestData[aDataIndex].InitVector) + ' PT ' +
+ string(TestDataSet.TestData[aDataIndex].PT) + ' AAD Exp.: ' +
+ string(TestDataSet.TestData[aDataIndex].AAD) + ' Act.: ' +
+ StringOf(TFormat_HexL.Encode(FCipherAES.DataToAuthenticate)));
- // Additional Authentication Data prüfen
- CheckEquals(string(TestDataSet.TestData[i].TagResult),
- StringOf(TFormat_HexL.Encode(FCipherAES.CalculatedAuthenticationResult)),
- 'Authentication tag wrong for Key ' +
- string(TestDataSet.TestData[i].CryptKey) + ' IV ' +
- string(TestDataSet.TestData[i].InitVector) + ' PT ' +
- string(TestDataSet.TestData[i].PT) + ' AAD ' +
- string(TestDataSet.TestData[i].AAD) + ' Exp.: ' +
- string(TestDataSet.TestData[i].TagResult) + ' Act.: ' +
- StringOf(TFormat_HexL.Encode(FCipherAES.DataToAuthenticate)));
- end;
- end;
+ // Additional Authentication Data prüfen
+ CheckEquals(string(TestDataSet.TestData[aDataIndex].TagResult),
+ StringOf(TFormat_HexL.Encode(FCipherAES.CalculatedAuthenticationResult)),
+ 'Authentication tag wrong for Key ' +
+ string(TestDataSet.TestData[aDataIndex].CryptKey) + ' IV ' +
+ string(TestDataSet.TestData[aDataIndex].InitVector) + ' PT ' +
+ string(TestDataSet.TestData[aDataIndex].PT) + ' AAD Exp.: ' +
+ string(TestDataSet.TestData[aDataIndex].AAD) + ' Act.: ' +
+ StringOf(TFormat_HexL.Encode(FCipherAES.DataToAuthenticate)));
end;
procedure TestTDECGCM.TestGetStandardAuthenticationTagBitLengths;