diff --git a/src/lib/pubkey/pqcrystals/pqcrystals_encoding.h b/src/lib/pubkey/pqcrystals/pqcrystals_encoding.h index 9b66ced137..7802e67ab5 100644 --- a/src/lib/pubkey/pqcrystals/pqcrystals_encoding.h +++ b/src/lib/pubkey/pqcrystals/pqcrystals_encoding.h @@ -139,6 +139,9 @@ constexpr void pack(const Polynomial& p, BufferStuffer& stuffer, M // Transform p[i] via a custom map function (that may be a NOOP). const typename trait::unsigned_T mapped_coeff = map(p[i + j]); const auto coeff_value = static_cast(mapped_coeff); + + // pack() is called only on data produced by us. If the values returned + // by the map function are not in the range [0, range] we have a bug. BOTAN_DEBUG_ASSERT(coeff_value <= range); // Bit-pack the coefficient into the collectors array and keep track of @@ -170,8 +173,9 @@ constexpr void pack(const Polynomial& p, BufferStuffer& stuffer, M * * This takes a byte sequence represented by @p byte_source and unpacks its * coefficients into the polynomial @p p. Optionally, the coefficients can be - * transformed using the @p unmap function after unpacking them. Note that the - * provided range is assumed for the coefficients _before_ the transformation. + * transformed using the @p unmap function after unpacking them. Note that + * the @p unmap function must be able to deal with out-of-range values, as the + * input to `unpack()` may be untrusted data. * * Kyber uses @p unmap to decompress the coefficients as needed, Dilithium uses * it to convert the coefficients back to signed integers. @@ -202,9 +206,13 @@ constexpr void unpack(Polynomial& p, ByteSourceT& byte_source, Unm bit_offset = bit_offset - trait::bits_in_collector; coeff_value |= collectors[++c] << (trait::bits_per_coeff - bit_offset); } - const auto mapped_coeff = static_cast(coeff_value & trait::value_mask); - BOTAN_DEBUG_ASSERT(mapped_coeff <= range); - p[i + j] = unmap(mapped_coeff); + + // unpack() may be called on data produced by an untrusted party. + // The values passed into the unmap function may be out of range, hence + // it is acceptable for unmap to return an out-of-range value then. + // + // For that reason we cannot use BOTAN_ASSERT[_DEBUG] on the values. + p[i + j] = unmap(static_cast(coeff_value & trait::value_mask)); } } } diff --git a/src/tests/data/pubkey/kyber_encodings.vec b/src/tests/data/pubkey/kyber_encodings.vec index 8be73136d4..cc28103268 100644 --- a/src/tests/data/pubkey/kyber_encodings.vec +++ b/src/tests/data/pubkey/kyber_encodings.vec @@ -39,11 +39,19 @@ Error = Private key does not have the correct byte count PrivateRaw = C6C9B2CA96CE5B715A6262CAA2989B72EB6730B54BC89B65BC021FAAC7AB88E5833B9A7454C94E49CC55EBE30C6B7702EBAB0A40728E1F108E8FF14AF217556E85551810C2716B4248B9B10EA7BD5D05CF12425CA648A653B26B1D645595D95323F153CB1ACAB9D29ED955817592C017C4465DB30066FB68AE3429E5453FB0199C6E9967608939057632674B3DA8BB587174BAF362433C4B9B75BA304F995AA6238BDE7398BD460619E00F61CB4A2040BF09C5120F51BA7808AE171B1C9C733F4025B872904F45AA8AFDA6B1B8B86407A1C619065634F78296D6915156C44735634CB2AB498574282754E020473F02201219327A4B748CAC50617A01D101C34FB20EE04BA70BE541794440F0E815778444A310295B39164C7633A8448408D16B47E68E80096DED5373840B830E84CBE1F25100788E53E3CD35AA365674A334DA913EA7A6EDE3C002987ED94A6D5AEB8B3272B3EB8097B6F723C7541782F90F3126BF8ABAC201F51D009B258C877CDA9304FD73242050693E380380D3898749CC7FAABAE09477319228B6D9606D876469D622B15A6A4845CDAF1A6603F25AADF5A271C225C4213FD9422743705B33FBC2FA9BB55B585A8F858DE578BC44C4B3AA919660045733E222D5409E5C4B789A109F458961AEF8AC798342C2E854E2701013729921574E340B9AEE44BF9A2971FCD80B6AF994F7385FB505359346417963AF61986076ABC54ED6B4130C5042EB04EB6C885E2C4888A63A52BA49B9C6BFA41A91CE650EF234A4ABB8B1122A15E167ADF4A78115881B6C2B26C9CC32E2A5552DB35AF280A0919A09047562CD33AC8332B74E6B962E3643B14019C7479CA1A29BC420038DF52FB6A2A7DF10C7798ACD4AC315423361C8334D38B0099D4A5136AB32D599A0CD5983C4864567509E9672C3BF6BAC914B0ED5D04AFCB1A8DA16776E0391E4F5655E7147F955C5078C798FD362C93A6C7E9914454A591CD93972107C61057A9AE417B776A1BF6683505A2D85D53A6AE0BC4740278B69BD0A608CB0484B04122FB3D46F6C06A3CC5523EC054030C9A8A585A5DE82CA41B39082052D970BCFE55389AA956BB9E2C62AC5CE80FA64A15B4DE4FB71DA7100E04B85596849F3E0326570227B48589E45B387E612C36C8BABF09EFA22A021184BB6197B011842DCE1C9682492836B2E96C059C92C15BD8825A8C0265B795234F8756E505C1EAA29E482BFE4FC0CE945B75E84817E5310245CB57A3A51E2F91CCCDC850CF59027497D124BB36449B28C62BB90181F01372B0379C03B994343C7476B3146C87C6E25B677F30CA7BDE3369F1177B8A90BB2561B1C637D7E9CAB2C8BC4E0717669871B4451313DA55879F07FD01637ED052A82A5C7B3D1427611CC5D9B17827B4F3A28610ECA998903B4CBB2187C849E7D862867391812A94139DB76328C4EECB6AD0590C31FB627E1A3C319DA5D3DE44FEF088027108A6F9762D1B6075C0C3835A83A44F230E7B0ACD77241AD0CBFE4822BF189C868161A6B330DCE8C1A39A3042D81BA5AB86603205747995E6C6AAAF33149ABF8B2061137A773074F6846277C6A67C6789814AD32B088CD688924948528648238050EC9C8955EC7897125CA5F708112735687017F51788FDB9C68526AB14E76C25C490E7794315BF686596CAB483624754C834A7AAA4268BB98131A739A617907456D139805830945C8BDD0104ECB41A96AF92B86C04B72D963E47AA9168A0BD72210672AA7D15642B1220541643C2CFC0636A6AD344022C01CA6B3B787B659A06DC50F85B59C5806AF9833876EDB66C558A3EA71585DB88F62E520CFF0C3FC8208B390CBA833BCCEB5B72576CAD3F564DEFB9B5C7589CC7A4B5F5B0E857A3E90E7B87797379E47A27774172A0AAE0417CDECA89E3114837F1C3B4B614AD9E774802B4E4226C6C7A56F603C931878422600CE5EE3A44C2015287AB3665544C470AEE0AC2A31F8345B459BFA2B7900257B0A1B0DF9086352660956A54598251A347860518B8E507CCA39ABB73215A9C347499CA94C2DC24B0FD66EB9AC5DAC214B46B65694D114B3893EBA6663EE3A6AE1846B7A2398569BB4B06A1EF34584AF9CC3F4E415FAA9096B4B96B0915BF2D5808AB71CD65C890226C4BEE74DD97A91B1B6363849870DA23EF08A1CFD487C194D66FF16C96F3B02FB97D3D087B1FD1D6E91878E8380318F7B4843FBFC07C4BC1D13EDCC88E413D23BD8E73176A0B05B6D2381822474393D22585AF90351770587A6FCDEA8254D8EEC0CEF8668E30B3D24525B36AB3C75063B8A03D4 PublicRaw = F9951AFAD2C96730335CF42103F9267A11C16E209D366603B04789378684987B655CD57B2DCBCC195B137227B9ECD3BAB0C0183B5A0667E407D7C86402D7719869CD6570B30B8C227ACB8B478320D64353CCB94809052558B1AC94C0CF498219442B988FD64F7CF6423C372F46F26791D3680817B3EBB24BFA98CA2EB22FDF48919D1595F06115C70CA050C04B17D6A91E2926677120EF24AEAF499B43542EC0C91DB4A3B7AD6A5DB646C164D70F496225A7800DD81C634C004796A79148E47A4E312F3BF56BE80C2E9D01C431767FF50496ADA127C1D286A2FC9FB98677BBF95203F63296881D5C536B39F22222C570AD921C82565E1C7C069D69BB3CD431A4CCB5F0C790C6578C1EB0185F84C706137A7052680BD8AAAC941D34A3756EA8B006F732B995612E330616BC22F9EACF4423A1DFE18B0E63BF18B24DB8E1B2D1097CB2E305A80372E08492161871D8480D55DA41ED80AEF73682873A3065472A98816846550C80608A8FA8A9342BA87FAA6105B5C8C194C7268C694D38A08AA83102239073E02581A6A604B06B0673B43CECC07A0586B65CA8EA79BE8A140CF329223434745B576BF9CB2A70709D2621359621A057A3BFFEE465E421CA1D48096E06ACD6309F40A64126B0491D03BF7272ABB1475B72A54E9A2675262AC831B00BE163235280B76D5B88101524077A76A97C7A4F2A5AA8A547F14203F8BA26D1368B23E832987696E965177BB648CC3325766A4E8935074D9C7344E031917AB0DE452092E2720D121A0E32CC9E5B2325500BD1B76E38360A36E972CEFC56E93482E47605AF9A69A6F42D89C6C21E2B0CD5E14534A75012CC905E3042BB0043CF0280185A90D245B86C7097F2C1694F2BAE0CC5C995E580343A96A2656A108769F1DC0B7717B0C4A1A635DA3DF4D248B3D29116A8B939FCC063250198982EEF436E3564881A2A2C0F94AEAD47690A9C194C56CF47868026BA8CDEBB7631E643E6B20A9F24061B1C496518B89F1A45A6C5518B742990413D9B0012F895872E4B258371123839BD209C1A004B4575567C391A6158A464B9428761D0191F4B6CE2630A6F77590B25FA894D654EB1905481FF4CC75C2A435630DB1CEE9254090FA8D11E +Error = Decoded polynomial coefficients out of range +PrivateRaw = 2B44523FC0A955F93A28A8A08DA078E6AD11122E26852647D8E0C6795E0577E65D8BD5D2F82C871554442FEBF6E132EC08A596619CF00A63C1F1B35B758CC37A +PublicRaw = FFFF1AFAD2C96730335CF42103F9267A11C16E209D366603B04789378684987B655CD57B2DCBCC195B137227B9ECD3BAB0C0183B5A0667E407D7C86402D7719869CD6570B30B8C227ACB8B478320D64353CCB94809052558B1AC94C0CF498219442B988FD64F7CF6423C372F46F26791D3680817B3EBB24BFA98CA2EB22FDF48919D1595F06115C70CA050C04B17D6A91E2926677120EF24AEAF499B43542EC0C91DB4A3B7AD6A5DB646C164D70F496225A7800DD81C634C004796A79148E47A4E312F3BF56BE80C2E9D01C431767FF50496ADA127C1D286A2FC9FB98677BBF95203F63296881D5C536B39F22222C570AD921C82565E1C7C069D69BB3CD431A4CCB5F0C790C6578C1EB0185F84C706137A7052680BD8AAAC941D34A3756EA8B006F732B995612E330616BC22F9EACF4423A1DFE18B0E63BF18B24DB8E1B2D1097CB2E305A80372E08492161871D8480D55DA41ED80AEF73682873A3065472A98816846550C80608A8FA8A9342BA87FAA6105B5C8C194C7268C694D38A08AA83102239073E02581A6A604B06B0673B43CECC07A0586B65CA8EA79BE8A140CF329223434745B576BF9CB2A70709D2621359621A057A3BFFEE465E421CA1D48096E06ACD6309F40A64126B0491D03BF7272ABB1475B72A54E9A2675262AC831B00BE163235280B76D5B88101524077A76A97C7A4F2A5AA8A547F14203F8BA26D1368B23E832987696E965177BB648CC3325766A4E8935074D9C7344E031917AB0DE452092E2720D121A0E32CC9E5B2325500BD1B76E38360A36E972CEFC56E93482E47605AF9A69A6F42D89C6C21E2B0CD5E14534A75012CC905E3042BB0043CF0280185A90D245B86C7097F2C1694F2BAE0CC5C995E580343A96A2656A108769F1DC0B7717B0C4A1A635DA3DF4D248B3D29116A8B939FCC063250198982EEF436E3564881A2A2C0F94AEAD47690A9C194C56CF47868026BA8CDEBB7631E643E6B20A9F24061B1C496518B89F1A45A6C5518B742990413D9B0012F895872E4B258371123839BD209C1A004B4575567C391A6158A464B9428761D0191F4B6CE2630A6F77590B25FA894D654EB1905481FF4CC75C2A435630DB1CEE9254090FA8D11E + PrivateRaw = 3305F0CF680FEAD1BCC50ABB0B04F288BD3266AE12188FFF50D316FC00681A81D72D4E46CB5A96ABBBD507E8D7BB4C523080550B142D1594B9A75113D6D2D70C PublicRaw = F9951AFAD2C96730335CF42103F9267A11C16E209D366603B04789378684987B655CD57B2DCBCC195B137227B9ECD3BAB0C0183B5A0667E407D7C86402D7719869CD6570B30B8C227ACB8B478320D64353CCB94809052558B1AC94C0CF498219442B988FD64F7CF6423C372F46F26791D3680817B3EBB24BFA98CA2EB22FDF48919D1595F06115C70CA050C04B17D6A91E2926677120EF24AEAF499B43542EC0C91DB4A3B7AD6A5DB646C164D70F496225A7800DD81C634C004796A79148E47A4E312F3BF56BE80C2E9D01C431767FF50496ADA127C1D286A2FC9FB98677BBF95203F63296881D5C536B39F22222C570AD921C82565E1C7C069D69BB3CD431A4CCB5F0C790C6578C1EB0185F84C706137A7052680BD8AAAC941D34A3756EA8B006F732B995612E330616BC22F9EACF4423A1DFE18B0E63BF18B24DB8E1B2D1097CB2E305A80372E08492161871D8480D55DA41ED80AEF73682873A3065472A98816846550C80608A8FA8A9342BA87FAA6105B5C8C194C7268C694D38A08AA83102239073E02581A6A604B06B0673B43CECC07A0586B65CA8EA79BE8A140CF329223434745B576BF9CB2A70709D2621359621A057A3BFFEE465E421CA1D48096E06ACD6309F40A64126B0491D03BF7272ABB1475B72A54E9A2675262AC831B00BE163235280B76D5B88101524077A76A97C7A4F2A5AA8A547F14203F8BA26D1368B23E832987696E965177BB648CC3325766A4E8935074D9C7344E031917AB0DE452092E2720D121A0E32CC9E5B2325500BD1B76E38360A36E972CEFC56E93482E47605AF9A69A6F42D89C6C21E2B0CD5E14534A75012CC905E3042BB0043CF0280185A90D245B86C7097F2C1694F2BAE0CC5C995E580343A96A2656A108769F1DC0B7717B0C4A1A635DA3DF4D248B3D29116A8B939FCC063250198982EEF436E3564881A2A2C0F94AEAD47690A9C194C56CF47868026BA8CDEBB7631E643E6B20A9F24061B1C496518B89F1A45A6C5518B742990413D9B0012F895872E4B258371123839BD209C1A004B4575567C391A6158A464B9428761D0191F4B6CE2630A6F77590B25FA894D654EB1905481FF4CC75C2A435630DB1CEE9254090FA8D11E [ML-KEM-768] +Error = Decoded polynomial coefficients out of range +PrivateRaw = 2B44523FC0A955F93A28A8A08DA078E6AD11122E26852647D8E0C6795E0577E65D8BD5D2F82C871554442FEBF6E132EC08A596619CF00A63C1F1B35B758CC37A +PublicRaw = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + PrivateRaw = 2B44523FC0A955F93A28A8A08DA078E6AD11122E26852647D8E0C6795E0577E65D8BD5D2F82C871554442FEBF6E132EC08A596619CF00A63C1F1B35B758CC37A PublicRaw = E7FA6FB9292D8BDB91C66181D0784DAE04A114C65DD4AC86605516B99849512C179898C85FBA5F9D9B39F0709CF8D4342F8202EFA02563FC3C2F492C90677D53D73855909A3ECCA2207144BEBABD20634C36BB228618C0EFCB68AFC22650DA363B66B9175565E43761EEA75771706949593870C95C4CFBAE1DB364A46742258A6708A76771565D07FA5750D15883473211D8C394488C99B2461C4AA0FD3C3735F587BE7CCD3E3321A1CC492CE1AC08B9143E04D017756E37094311A00CBE6C4C2A844973F9087F73C77E107421A75CC7D27231E1358847AB4614634166518FC94B787B7A40646F3BA04D62ECCDC7B4991588126689B94A10CE10EB3E054B4FECC084CAD4814DF98D5D6B28E034269474A493835CC3174293378E3C956BE9E3643AC936E626B2EE88BD7563354D743D5B856396471C403902156194BD99CF9364458BAA811D0C8EA7A36BFB23141DC73C48FA86ED1C4A5E7A1E7E80CE3BC4B3B9603D9D393015BAC94FDBA23CC9C657F83F28FC9CC7FB2ADFA28A92081089FA5BA39424A6B0BBFEDA383836A35425BEED4295BE4626149B90DBB744A4A718F8A38C778B379D4B970361CE507A9B3BC3030464AC20854F3A3C2D5CD5075AF54E5F2A6B6C17152B9AA941D08A3836553229A7341A3C73DA23F9F153D4244A97B828CE4794FA4C3E8EEB94A2270923324B6D563604650EAD65B7C1D97D2EA4057915C4A379AD84D224BB6862ACDC7315BC6C5DD8373053673814CEB50271558844E36498AEF96D480401BE44BD61E6AE611A595BD9989448997877A670A6AD77D9393AD8A60234B092A710383598867AA2AD9A5C96618D6AF83EE9416B8B3B8359E205436ACF98334C4B4BAB03E97165A2A9E5693498B82788939829E0AB60836B889675D5E65A92164AFAF3B98517C01753C711838D63E61FC793AFD6CA2DA2DB2763A280233672641198BA359DBF6081F39BCB3B5BC8FC87A22A8433BE67896F424BBD697FD1F72DD321A6BFCC77092A06BD577510BCCEE0F162DF4A49E6C976408B3F4A489705E966BFC95284C7472BC17733EA4B47C046115578EEE32CBF858CC14A14730136FD06995ED431628BBE8E75CE15DC874ECC93E0C38A3C14484F96C616F6CBF2042DEF27B13FF2CEDD3ABB7A74580AB9B8AD4058F0020493F6467048B31090C10EF83833988B1FD1580E62161121B7F380612E304E207BBAF24604892A60B83338165AA8F8992B2A8664A8BA71461C9D8EA100F5D5A3DACB2BB496A726938683904589C454849B26ADBA5448EB2928C5449DF93B8F620795BA3562681E1C0A6877F795876A603738464DFA2A91D8A50DF3B48388B1EB163751CBC4FECBAFA9A361A728BF7B75B308DA4CCDC578100038F2F2CD75A90491E9CE47B6B4011AB43823A660E76659B97984E626D61C8207F73CB3918DEDE5A21F99C15D8597C4905EA8C41271FC6D3234A5BB17CB9E64434A96A96F1BA183298AFBC505DEA078B178236A5A9009A3835A35786AC3406B650A65298BF9735B3DD3C483962377FC47F958A04A7CC6ECA0628C794599D09546E5C65C5CA487482CB84762E0F38B64418AF0B7268318A84F0875479870901668ECE34252D5C64F3028684255D6976E03065332399F17F462F787F236094607CC7BAA4C14B0AE9D21D2B3F63B