All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bouncycastle.pqc.legacy.crypto.test.QTESLATest Maven / Gradle / Ivy

Go to download

The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains JCE provider and lightweight API for the Bouncy Castle Cryptography APIs for JDK 1.5 to JDK 1.8.

There is a newer version: 1.79
Show newest version
package org.bouncycastle.pqc.legacy.crypto.test;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import junit.framework.TestCase;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.pqc.legacy.crypto.qtesla.QTESLAKeyGenerationParameters;
import org.bouncycastle.pqc.legacy.crypto.qtesla.QTESLAKeyPairGenerator;
import org.bouncycastle.pqc.legacy.crypto.qtesla.QTESLAPrivateKeyParameters;
import org.bouncycastle.pqc.legacy.crypto.qtesla.QTESLAPublicKeyParameters;
import org.bouncycastle.pqc.legacy.crypto.qtesla.QTESLASecurityCategory;
import org.bouncycastle.pqc.legacy.crypto.qtesla.QTESLASigner;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Integers;
import org.bouncycastle.util.encoders.Hex;

public class QTESLATest
    extends TestCase
{
    static SecureRandom secureRandom = new SecureRandom();

    private static String asString(Map values, String key)
    {
        if (values.containsKey(key))
        {
            return (String)values.get(key);
        }
        return null;
    }

    private static Integer asInt(Map values, String key, int def)
        throws Exception
    {
        String value = asString(values, key);
        if (value != null)
        {
            return Integers.valueOf(Integer.parseInt(value));
        }
        return Integers.valueOf(def);
    }

    private static byte[] asByteArray(Map values, String key)
    {
        String value = asString(values, key);
        if (value != null)
        {
            return Hex.decode(value);
        }
        return null;
    }


    private void doTestKAT(int securityCategory, byte[] pubKey, byte[] privKey, byte[] seed, byte[] msg, byte[] expected)
    {
        //
        // Validate Key Generation.
        //
        // Invoke the key generator with the Fixed Random that uses the seed from the vector.
        // This ensures we can generate the same keys as the vector.
        // USE A REAL RANDOM NUMBER GENERATOR IN PRODUCTION.
        //
        QTESLAKeyGenerationParameters keyGenerationParameters = new QTESLAKeyGenerationParameters(securityCategory, QTESLASecureRandomFactory.getFixedNoDiscard(seed, 256));
        QTESLAKeyPairGenerator keyGen = new QTESLAKeyPairGenerator();
        keyGen.init(keyGenerationParameters);
        AsymmetricCipherKeyPair acp = keyGen.generateKeyPair();

        //
        // Test generated keys from seed match supplied keys in vector.
        //
        assertTrue(Arrays.areEqual(pubKey, ((QTESLAPublicKeyParameters)acp.getPublic()).getPublicData()));
        assertTrue(Arrays.areEqual(privKey, ((QTESLAPrivateKeyParameters)acp.getPrivate()).getSecret()));

        //
        // Set up for sign / verify testing.
        //
        QTESLAPublicKeyParameters qPub = new QTESLAPublicKeyParameters(securityCategory, pubKey);
        QTESLAPrivateKeyParameters qPriv = new QTESLAPrivateKeyParameters(securityCategory, privKey);

        // For signing..
        QTESLASigner signer = new QTESLASigner();
        signer.init(true, new ParametersWithRandom(qPriv, QTESLASecureRandomFactory.getFixed(seed, 256)));
        byte[] sig = signer.generateSignature(msg);


        //
        // Verify signature matches vector, NB signatures in the vector are [sig][msg] hence the concatenation.
        //
        assertTrue(Arrays.areEqual(expected, Arrays.concatenate(sig, msg)));

        //
        // Set up for verification.
        //
        signer = new QTESLASigner();
        signer.init(false, qPub);

        // Verify verification.
        assertTrue(signer.verifySignature(msg, sig));


        //
        // Damage signature, expect failure.
        //
        {
            byte[] damagedSig = Arrays.copyOf(sig, sig.length);
            damagedSig[0] ^= 1;
            assertFalse(signer.verifySignature(msg, damagedSig));
        }

        //
        // Damage Message, expect failure.
        //
        {
            byte[] damagedMsg = Arrays.copyOf(msg, msg.length);
            damagedMsg[0] ^= 1;
            assertFalse(signer.verifySignature(damagedMsg, sig));
        }
    }

    public void testSignQ1p()
        throws Exception
    {
        byte[] seed = Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1");
        byte[] publicKey = Hex.decode("7561DAADC32E5C51E5C7AB83FB32FAA58E32A0D1E5134A07BBBC3AE889C85470D024D22EE8A269A9772C24B5971CEC2AD55A846C6D8DD917B76223B0799227F89FE8EB91020CA49BD528FF1915ED00D9F9709740A13110864F11C24CF0DD8DEA29BF288DCFE4B1CEB12A7135C5142D7F51376456892BEDF33A295D6D70AD058C51D565E8097D26440D57DDA86AD04C1E7ED45946654ADFE9F46A2D2CA0BEA6B2E57046D734A3989BD6B2C42B9413646C1A2F1B6C3E55502D8780681D1553601130BF98D26EA05C216E7C98FF0D5FC03D10384DC6E29316921F50AD19FF96772F998DA91AFC45CB3A00E67BAA713E2825C6DC2A0C0E8778444870A0483E588EDEC82199DA34B636966A85D9001C446C425415C8F8269697EAD5D69DBA92F77228808FE2AC5485612B0F28862D9EDA5D32D5CDD840BD45649C1D7C50044CC017D87E8D1012FE2EA0E26F2710467A13230C73F485F26154825038BEE477CE3154C36468B22124038C3C0A8E41C8681B20BFA3493CF88299E70D0D3FBCB5CF7EB82D39656A27FDA96E62CC84FE9A83DA858A9982F01803872833C48376E04C4E319744EA26EFD8FD4A1834B5058986DEF96CD22F4A30652A543D640C424AD107B54EC7D5E9244328BCBD166100969A54B003DB5F1D30B7D8462941A5C2E6E166563C1D7C7222D167E2627C98EB0F4274FB098C1BB288C8A12180E0A2F32596B54E3E68520B625B87B47F5E2FF64A1490CEC86E82E5E94359FDF6782E2138229D2C1EFF49B6BB6A722D033FF80BBA7A2CBC90D40E39E2E6777E062F4603BF87C407E2C20DD67E67DA5FB1AF696A05CB301B78A91A54F0881D5459E35E4070E0FA605C43E98C472C76DF9C31AAC8A7473DE2B989F297B230B751B4F32B61CF089F77317285D302011D7D6BABCF4C07C0C70AD4DE3E99C46EF595EA9A61A080DD48C74A0C961513C845DB6A5811750145D081B4D4E76DE5B15DBA5C1626289345049D5CB8771C7C129DA1C5DD8BEE596321647E853BCF8D661D194A1023845F420066244F6BB5A0D50533579AA0DB5A726DAB32EA2B35261AE5B6D0DEB12C31A79242E2D952AA1A1774CB90AE815BE9613A93B30AF30374F5C8181F54B4F5D56E07232F68C6D52B94BA64ED1B2B7CA37EE6C730E03816689853224242F0297A017DCE955C8B8D1B41BB3B12665759584D3373BC2C77999205ED56F6E8B6AAEC3346D1528C2AE938CE21C24400ECBC4BB520958E3303374C68A21D403A6E6F05DAB6A8C03FF07FF5B83A216B2EBBF8E357BCE51A52A56EAAF6B6F6D34B8BCE02E22BB67CAC6629A8E8C3F9F4E7A3AEE3B9AED01B2E3BCB3FE566E3DFFD8C4A9830AEF3D30DE63B5208A32433A4C42633C4840EC5493E891D22F409E35F02CFAE46A9D3DEED6C221916F9B27BD7EE3C1CC3C961E59B3DB42A59213FFB168DD170393832ACA8C79B52C194571681F0E8AAE2DB99C1201632659AE136E3B085985DC94981A5503D950E041763AD485E253DD8B329283929E2132EFA6B9EB08E79AF6027D6E2E7E30E3D6B785256BA2E012D003AB5C07FC4A0EAB484991B3CBC3B03346409491A876C82548E2BB253E344E80415E4625D3BF15975B59DC36FA00A7FD894D363172DB8C2ABE40364F549A00C5D5FA8B534E0CAE6A9AE83CB5E4BE6BAEE126B755C9CB8F76C1C8A6E6FE2567C1CBDA68370D4D8465D8B360880601206CAC0C9D0B67C0261A0E7D132CE9C4EBE21250F7F197480FD5E0D8DA83068EFA8B35475733FAB080BB888E1D1D89D124AEFD81BAB3D970FE059674B6E71455517B9BA322A236D272144963DA4A934A1D59D19F05FEECDF0F1E390376801E0454971E30C47109944BB4DF414E19326B8BA2013D5F6B3FA20188E5E9BCC63ED8BC55D8D7D3A086193F9E80DD32381B26BD534D34F298DC06378EDBA2E614E8C3D886297AD1B2B59FF78B8FEC0760336C5207082D82ADA01CAE49AE6500AD992B7FC0631491B909F449618E5F82B18995BC926F46BBEEE152AB2706521AEE3574295F26FC01A7AD154673B2D3B500E1043FF51340BAAA70CF8B3A65C65811318AC7B4A32A9519A00ACE331959AED26F86CACCD7A952F9D40E66FC31224AA1BD280F89A6B16F46AC39C5F6D709868F92D0FA519D39827D215D8BA7F2FA6B4DA67D5A791984E7A1BDB04A5A7B024762952121C109AA318FD2CE88EFAD90FFCC17419C2892C916BA7E7C482ACF41E5C386290F1174AE5C5BECC5A0D21E23B40FEAF220CB9A58AF8A5B5910DBE823359150388A4B0046090287B760B3978433317F4C2E1AF2A5BC87EFD2F59931F558ED226CEA7174B9BD4D1BBF6A10CE2C2D827DC4D36B2096B9309D42EA8D45B5AC2B1F7049CCA4078D4751B6B03DB06B8BEF3E119589CA61D7FE308AEF4561C4EE87DEAC2DB94C38C4760916D27E6BB2C169D1C88D35616F8C5EA6EDD4D48D6A5CFF409DD67D5BF704B832C3454A8BD14568B93F760EC179ECDED970C46BE39D5960352D2F5B3959758CA7A10C88E961D474449CCC21394D2099F6110BEE145E7005DA5501C0DFE3DF1094ACF99951A2974808A60108643E9F4177827A3F616009FA67E21F54C363BF8F162F33254D6CFED5E7C0D4AFCF9854620B9632FBE147284C6739F5FB4F211C20B1D9982215C2C4B1C02CFD041F8281E429940E8FCB66C32B694C2611D8D7F50A751D3969C2A3AB1FB2553CF0B26C0478C2988D9008EF4A1777E9F7F16122BDF208A5044BE728E9D928A52F133C1CC462984590D44E00FF7AE1254928A006054D008E780E5D10BA7E11E17E296D43D21F64544AAAF27E6AF9D4C7A381E4CC2873F358B3541454BA2D594602DA71273F50F99201CE60F93E1431D82D5402E8654206DFC651A3E36CA854A9B18E8DF0CE2809FA89BB465C68475020C51A773107D9B367E3C9A82370773A498FABAE4AE84D7685C9329FB226E0F5F6173C5107AF143BA84BB2CD9A9D2CEF930C5F8055A4E71C1CED7538F3D8FD194EDA2C98FFF3005BE8A73D09C7CBC8DDA07F4066BDCBFF23A1838520AEE0A28E69DE30019DA6AF61FE52A6FF10B4A9215F8F9ABBC9FB5081CA33463F891A53DC91E252E90EF4B0D72A35923208CA48C3793347F3D5FD28DE56538586D2C2C6C1FD50C4160D67DF11254BB36CB94B835734F5B831BF12845155B49FBE3B0F44CC5D6782983D9478C6B3A9F30626A797A2CDF8911F5BFA90E611B39B29314C9E4CB7F12A69D6DAA1916667AAC5AEA693B70BD741928014ACCD125137488BEE48BAABB02DA3C6A9604A16BF4D19F1C0D87FAC94840C721EDDF03CD8A841D2EFA5D52BAFB50726B45A476C47E251601B0127C092A4EB8A66A0CB7A2DB5503394AD36D54F37CC6EC73317E38AF9FB62BB758973971EE9E75E6D8AC67A0D44384439E7B11390E983975281A194F8173119905FE8FFD863D55CE7A6AD00683D4FF83263C81A447948DBF3F1EA2CA593B38E6719A8EECF654774599AB25D1EC9741AD3DE4DC23264C9700CDA27C463656E3A53C52B860B3264CD9FC0334D915E03EE74E54D4A2E2D795297FC748075A404A6C0AA6A22671A480AA3468B9FA9DA9A20DFDD8BB9100C8B6804DD6F16AB0C1B938FBEDC2F1A9DF43CC877B0F323042E9B1DFBB8A2BDA8986534D52B7EF06614DA22F63951716C2B04196567698552F3BA2067B2309AF06DB126727E1A82F731B0CC2904470429C1EC94456D1E66A0EA35A2A1C552CD3FA1A99AD3D748D563978800C32F5B6CA195938BA26CF721FFCDD9402EC4E37A9E1198D39516B4892919327592C67ED148672377A53C7AA9BD47D16282D977A40BB7E036A3FD11283DC2BD82EBA452474B827434014D0BF7C5483F28A432D378C6C78E6470F4026C27F531C367A2B950B0DD95114A106C72227CC33779C705B1C8926DFA2F996000535A0BB87BCFF4B9A12D829D04434AA579A11F6D15490A2C9FAF65042DB4CF88BE47368C589629703815FA6A2A1BD4A1447330E00CAE7645B2527FB82E864290C9574FCFD364F1A4FE3419642D24D98A6A33A0FC0E83505FD5151518555F9E03B2D5742630A119B2B32657AD6B2EE8B624508780F98B27E51BEFACBF3A7B920A282EA45358FA4145AA24E7CDCB3432C6F08D92421E2561EF811F1C60D7E51C840EFFD637D940F43D655FD1221F2A41027A71D90F978CB09E2264737053330E47E4C433F9B5AC94DB9B04EF7326410CC60DF7A220E1749A654FF6370D7C8197C808D35C2B6769E7CCD009BCA5835E4FDDA202D6C3DBE324AB97C879DA16184123CEEC3D5FDCC3CC71103E46E711CF8274D29B5CDF074BA889D6B0E51290428E0499022E634439804A488483D3927DDB78C248EBF9A6C9A678F4F482373580CB7FE2DAD993DAD34149F112D735C7E60DBA06BC44F0304551ABC09B9C91D4211FCD6025A865C90159CE3D842405A5143C46BE19DFDC64228A413D4225D7C94697C0F128B4336E9483192F3024014D2E9885447D0762F802087C7654786F76773DADA83CBC7396E5DDA29BEE95E93FD8C67D75C7D258765C7154DC822B925E8684DEB3443F85300C96E59192E8E99D2BCA21CB62713CC5EB23BF156FB3E97DAAA0695D84932FB9C9DB03E0BB13B103190246CD9AAF79C4B9C8916DB4B6BA02F5FA8B87B9E9B8D18DF6650E907E8A02BD67B4C00EBFE4E4C2CDA560D1874D7215CF0009DA57529EA831A8E71C88152849A8C2900134306C0C05A40BCE42A17CE9C025C100277D7F0CB35566BE5AC11B2466A2BB03B8DA8F10B7A30CA26FC4E0A3971E16DA0C5FCA52838361046549A77B9721027DF307AD3DC8B4EB3DC05BE4B0B0FB77482A794305BBE99499F6ADB05519D1D4A12740F91A363602232535458E94C7487DB90E777247864446B5A862164D9761DAAFEAE213440958E0C399B4DCC6C6BCCA44BF33512828B20402EB814384B9ECEC2B0C1316F3C545D4A65F31CC01B9E324F4468ED92896AD4572248E7298520FAAB161203A09D366EA8FB0041E8A79368822C557C67889DA58C63043C0B6E94B36C61CA41E5BB570448894EEE09F4F00A18A0164FE451B4D005AD5B9A1E3013C356652479D340594223238E068D9DC8C838902D72D97D35EA16E9B7A1852EF464E22D6CB4217BED00F1998134CC09098453C34CB9C0EA8ADD432E295A4B8DE30BDE1AB413F515153A75D02CE99B2EC8E45B3FBC1682973B769D688C8F6FA1AEBEE1795D1B9563AD1D462BAF1668D0426C457382E0EDA9A898FE694D319CCFFB53F378EBDF3AC7BBCDA8CB319470FDA31A1E4E920237A31A7F31392F54BD8991A0A98C65E8DCD9A9666B4403CF1C47B76D30391610161253A9057D23712ED9C71816D1C4B32A390AB6B4DE8F6D6032B8967B30471E80E2FF925DCA64887EA29FD14051BE7856B65B954A37F9577384245272B2098990F198EE1A9ACF1548299512F541E45922238F31115532A3AD6D744473F68DDCE411BEBFD933BE64AD1C7007258DEC9B112D4B9B5ED01FB4DE74E128C90BBAF64763A540E41B407151CFD8C2048A415304F05F7733238C6722AAB433ECE0CF900EEB02C24B20DEB96441E947ED1097A539C6A1B9E8B838E6CA2055ECC7064F43495815DCA3972B83401D5C8EC61708E692130512573672CCB5B9A56973E2EDDB1E3E1C965CBAD617D2590C277B91985007493DC9909BD6BF8578F2127141993ABF817B6DA2019DC6CF79207F96AB032B6D06C0512D049EA8663C46D5EDEB246F036C978496B1C1BC47E81CB63364F47B6913D0548EC13E1A37BCE62C68CE98BA1C29E71A3868491569B3093DAE40D42A5ED2972182CEBCE8F2589137699B142E3D3D7164939A081FDF237AA58ED794603943F2269A40B2967314C22E8668B15FCE244E145A3A49A3CE5BFC2C46ED93F40816A92DFB2880B5C4E755B2CC50914583D0FFAA28FD934E763118DA70C3D4137957333927FA4F0F637C4C52B9078E141089BCEDC7B4288019EDF92FD0ADED643C375E71C25ADBC8F94E04CA5A4E91C362D1C86BCAD31149636A895F443A491E6F558F9DC89ABA27967C7E66EF9C7302C100E4AD175021CFC295048BD5CADD8E86469EFA437034D7A01E38BDC64A6187F51F6D023CDB240F9FD0053FF7258D2C5802D1FA1C39DBC9D6D0E65026FCFEEC8205F77FBA1F64AA03B4D1C091AF43AB800839B4EBD378B28EA895111560183199390C3C6706EC35A05E4118A192E5A29C77CED86919C2A74A827913219B2C33E83249175B886B598CD25253A4B07D10C3AF92060C52FDA89B880923595865CA108EB0E78BA3F9E7B0465522CBA8DD6094131045E13A4C979B07BE8BD782AED050C3CF7C25ADE3C2A1CAE81738C55DC1ED0A33020EB6DC3464BDF6C16CC8223161991C62D149FCEA52140198B36B6379B47F5AA5EC066A5D5F561FDAC44EF3F967EE0E0A420AF89E22A053119DEF0D36FEAD69FF1B2C2476561DAF68D7682BD807CB4D2782C0B7FC39D7C6494BBE622352B534C21BFA25E6A37613CA73B85C98894EE1E588F20E73643F241F83003ECCD05B56255AA36782EB07283337E99144CD03B90680A048D4538D580728C0AD59C082104CD79099A08C193D40C3CAAFE3DBC968F9F8D9D0C74A7CA135F02C8F9C18380B00ECA21AE7E7A22133C1A65687C7F74CC5FB4BBD291183B803B1AF8441900134932217783D9EE86A565C8E9D316E0BC93E75941989C7E227D3170B0F78AF6F1D8FA70C697B4645A5F896B0C4971B6544D0C9786A774179E99EB4425C9838D57861C88E1DAE2E482D38BAE9D9C04DCC71D97449B13D3E9F6A52D81C91C0B02B581F4392C4EEE0514D4D39C84F323D7FE9A30FA87C5380913D3955C210B84444FE7CEDECF821D903E2102E8CAEF4020006F67B6D4B2146335AF10C6EA0A116A0110C5B9C935E349A6B29E8D609361B668E42BC0CD1E33D491D59FFD4A13E5B7742A8DBC918FE7F72BF18268121CE414D222C9AAA0522CE0487F2FB8545D19D7911BDCD4C81106EBA729FBD57365B1DE562A07660EDC92337C9A695F4DE495FFF27D63248BD4E4A96E405E39999A8AE35B6CF12AAB699FF741E298A3222D168EEE5ED1C0A372810A883BAF50E7DECA6F146F6DCFF1067EF4187AF79B70D8814CD50E39042417B4A1B7A230FE68504352093E91073307AD525F069F15DA88856C5140F8E89DFC8861E8678067FD054B163E02203E06AE92FA76FE2B68527737D99F2A13893699D7985F2F025DC3A430555FC23C9CE0C078E83CBCE363C5B5840FE09155F433178C46D257F5DBCEFC6AE9140F46A78872803B8FC6D0459C8A26279E1AC09711150E7F58F420A6AC2111647724DAED3867FAFAA58D4485F0265F0FF6A63C9F88CAB0FFBBB3579DC045903D682070C4DF15D3D02650213EA7A7D2FEE676BA92F923C1A0CBB2DADE5EA8F5623A548DC1060A2BFDE25AA6A09F80D8613D9673B66D2EBB124452921200BE54364DC705DC60BA75F2D0540C4168E6CDA16DA504D63068DE764E1823700BA4C97BF86058DDD2F5942DD5F4D3B19D6883412B6089FC6A6DAE5FAD95C3175AD53CBA016D59FED8107D0C64A1B50B1BC1380261A55D04998B99202078EAC318A36879D5C5AAD9299134EE66684179F8CACC52EE8106D23D0509D32E0459384B86CAE143B3E3C0EE95C8ACAE8D496E59131C80CDCA98B812E0452B8B9EFE94DE5424C50B935BCC927FF395D4D141DFC89A639703632E98451182D4D072A987A88B4409E0B5AD6F0CC62A5A10C3DC00B03597D1453B3257770E36949218F22D721302D3CE5DFC09D5604C943F73D75C996687931133DC4B6B061E5E74483FAD6EAA9E89D9467CE4C4FA061EAE71B02F3390DA9EE49D0EC92097321171D9771B1D65A4B54130345278FF8BD415FD5C9894B9F430E47F2967C650E376E614AF5F32A9456AB0C61A9278109404A484F28AC568922691C4C3965D465CD82FDFA330E3C9DD9E65687AD8827D18617C54EE0E3F6BF7E9FFABE7127AACD1B71573E2846AADBA44415407013EC01C5A7F70960870D1742DFD488B81A34DD7A286F4CC2C000E042352CE0120A637250B2A21AA998CFD1CA99BA21C321335F4A9222486AEF986FDA32C36F64172AFD225B5D358B59A06FBC1D388A99AD5780624E6A257A96910A4471C9ED1CDBF4AD41C74538A0933A460FFFA7B36C925E02D45D89A819725B8029742C189E561840F20EEB86252A6925116578A705FAC7B5B9A128E1B429E321C0B8EAC6B74C9590E6B02DEE15A0AEAB25B7D5533BB7B6D9299620BAA42C097304795B71A91DEC14A32B96438B0D4C313DD9A25A916554D2164D787E800B524D7E02215515244E7994C427574CA0E02FA18244A81E49C03C59E85524C8A76B77828A938A0BE90C77595567FD2619381FF0FD601777B7C226AEFF35E0B5BB7D6D88660027ED4E5E4265B2E0A775122A2E375F08DC24F4F327C9C2766F9CD8DF228AED7920250F1730A4A7D08531A26827EAD62E1BAA0A45EA9B8A26F5EB7CB6B36561F72FCFA31BD4509FAEC210D79E0E86B3048F8E629DABB24AF46C73547D40C0BFAB592078383832561F15DBD90E4073BE80079F3A489BCD51B98278337077F6791CAA0AC828A14865ADD78BBABF1113A171C06D10796415C7B0F4F3B732745BF9BEC82866D4508C1930B22E391FCB6D68AC89EE0D66A8B76BEAE525BC89AE9FC4C15A21169B7FE739DEAA80A7F19D5C669EBAC95A71EFE265A1570400315B8E853B7CB18E33E3ACCD4CF9E48546E36422E6E0ED282519DD5280FE5DB8B7877902188B17C4120444D729920963CA5AF34B94E365A8B0AF7947142C114C3F0A7FFF0714ACC3833CE4C0864185ECEFC7F36736F3E267DC956B0A1051ED765DF0BD12E1D25760A09EB05C16B327A30BAA1EA24552E15448BA1B3E1DF0E8550C7F4CB8240DD4146AB5FC002E33B4FAD65115439B10991417068B09AB2EE5B16D02C250A020B27580A9C1122604D1D69CA74CB478582614C56C4CEED92660B643C50712A132EF7D148BC27C4D1FA1E8680AE171CA8643EA4BE9EFD4F442B1BACB36221370D2DAAA509F78B76FA2611744D7617C12CE4B73914A26A0E64D15E8201C5BE8313FE0B0005102EDD25F95B440C96B90D9A92720AE271B542060B2EDB0245BCB3C83F901E2631B76C24768D28C18B01DA9C184DEF925D68C655E65A013B592D161841045F3EC54D0088449A64FFF0EA8E6AF632347BEAE431C5EF288A30AA74D95241EE7A6FF4E73DD22EAB04C6A6339CAA3A4E8C181E0751AEE8468771EECB0AAB3427493B0DCC2B19DD9A586DF437348C7CF9C5E3A8B1A630D5BBA6307CA395B4C797CEEBFAF5153258B325FAA9AAD673DE591DC8C0D589C61EB70CB7327B537AC1A31A6EA95C81C886CB3DE52427BBE10736E1BBDE9B8EA4AE6A2B44E7BC376B27C08E6FBB22A73B8D14CBB3BDA545B9430C30D6F3D67E0B42D7B122DCEC983B996188F79FE193F03CF1B446F166C5D8F2B1FBACD5C85678698EED8E5C76E2E0D55B955A2643FD1BBBC16D5F5BCA479AB5C1182FE2918BFD722DA84A010E60164C3ABF8137D293DC8719731F40ED404BCBF171E99F511F1E3CA723D59C34D78EFBBD463403F1E87337CA461447FE180EC53054A3F969218290042FF30D798B0C20A84C12A4D632CC9955FCE0D5DAB4AD68E71E5E4F5E446DA5FF1E2A5EF84BA2A3F222E4240355850B20CA5065330046E261E88156FC1A84442C60ECBF4C3D2242E27F8D806B022424D917CDA2830CC401C7AE9F1221FE533EABE3CD2B45E5DDC9379CEC4542B529977E18A8CCBD41B827CF900268CF7F6EB88C82415BA10930D1E157832F46E1B0F14E4944B720270C92509DC0DFC24F605A8AC015511B5554A4170F97064CFE6CF944D3B202A7C17DA356ECA45D92941C661B70B4AA679EC10DE7230C095F0590575FB28038E02A807ADD262BE38F3CA3F5A1D103EAF88EA59449AD040FEF17E702D2E46424A19216F2EA716B60070D5F8069215D580B296EDEA56FA3049972FA9A7CC947E1C56F669CD228B4C0446268BC47FB0D160EFDF7AD3218D3D57BC4B583319CA48A21D8D1BCCE5179421F776870F140CB9F18E2AA1DD2C5F877D91CBCCED0B4B8D84B111AA7160EE85B2C9B8AB69C1E3EE4431FFE4D3BD6A4DC6C8A800BD20CD8137AE1754BFC244CA7C5EC281381A237548A9D9AFFE7100B4254302EDD5461122D314B4938BA7B2A4423B40142E090E7AA3A6FD08578B40B17F189F9B16DABCB6DA64DA5470991398E9B3238DE4622FD2D12FCBA1928BC861ECC336BDE65A5BC43B6467CE293D37A1019EFBABA13ECE121B0ADE45AEB7C644C2D49C05B45F946DFB58E78B5373A4DB14884A241367672A563E3242D1856C8D1662D5F3691A6F5C769DCEF47778685BB448A8ECC5179EFE36CE6C7E5C04FB2644309018C064B2803CC1377D19F59D30B19E04D2981873CECC5C3E992C8889708B89E1990AFF746B340AF895962FADB2E2F926342137DAD8C1EBB09A616F658F40EBB28D3858D21B9F92A7FCE7FF2D421994CBE6072BCAC3E0DC3A030A212219D5DBDC906B07DBF29DB8EDCD213667F40CDE4AC7FB56E2A08A55EEAFDB4E71196FA642828D56983AAC268FFDD7E5BF02CEB2581B6DB158C91393A36C367F36AAAAFD2C40C2B4EB40963D1332155353412A2126C51BADEA6B842D2FE695B3CBA2E2584288304015905AFE6F808CF58A708290C4824CC484EED112384309A28E028D1E2EBD46CE419B5CEC1D9C9164C9A40218DCE50C9B466A8E3C357D3F86EE02A51F86FD361C000509AFB168275DDFE1F1BDCCBEB92CBB345E62CCD4F0328FB0A35FA8F28919927C1443D289AF808C8F034D28F79038DF670499E6521F080F0D365F2400D5D086CC7C0041D4DF80E4E4CE3906215D18EB720466CF8B43DE1C692BB71F63DF253EBD498A9020BA92517628263F65B8CC41A5D21BD76B6436029FED17639049766716B33F03E6F1786852AE47250BE2FDF8B6BC4F0084A823256B34A2C2FF4931119139FA60DFD0F376990DFCC08BA8A09B0EB3DC299CE091A1A15F1BF7C11FD35CC40BA9C2C624530441027ED45FF8C00EB0B2BDF7BCC18746DA7942C342D2A58A88EE549BD71915D1D907F0CF89C6E2CCF63025E0922E95E713AD78DDB8052E17007A83B966279E860A19F38FA484702A1229ED9BCE918B360B2C1EA70ABC182F62B2B6306802D70E4E2A83D46CAAAF950A1A20DBFBDA6ED1F21AF9C2AD2092875EB391A925951255D9FAE3292EC485B5C3CA16B003FEE565110E0A910A010C499AF322039140A83B28949AA8946FACC77ACC38CB4AE9E2AA250E34C0E63260BA989181C7470E0BD484C17704907A39EC96E2F4269CFFC95F311DBEE98360A999FFC28BEA9F9446A00758F83B5D492B602084DD47400864696BFB5119923EEC8D5CA82CF9F2BE623A7046A741A8F5A31259A37247F8EE9CF5EDA91B5BC5DE61AF067DAB0CE4C6C9881EFFB4E1D40C87184C38CEA0E07409AEBE92480294B852E21272AA71463C86354A37704B682825E1E7A1FB84A3329C376844286683055AAEC5C8BDD71BE9C88B3ABFA303E7885A0A027012B2B8EA8B83BFB1A8A8C4EC028DE82CC11CD18511E3F56137D84D326326AD2E0C479513F5F112E91454C6CA904010D328B94A26AFED5C8F14609037E17355E42B24222E7E5B1D84E61895C45090E769734E07AA03B00D08A0ECFE0967D7AE09D16843D54F6EEC12584F87EA7AD832B90DBBA8E39A0D21E867E3786D8FBAF19CDF7E56E9E9A44F421DD350D0D85AE095AE2F2BC0F9C7A8748279720BB0C0C6E7919CB83C40BDB3EAE1A6F87DFDAACCA342DF1C73AE02838ACCCAC609504A12F720356A57C4A3D21794A8106139C27503E6A6C47844962AC9FBBC0244601888B6F2CE6C41F8CB757F3E202F8B9A27D3057272745198A5CE7D2A4B227EA9CA6F0838B23EF2C66950F38693161AFBFD724EEA187F9D324C970B9F4BCD408161906A6648D18F4E6D52123F02694E39B6D8D1BE691C5AD20E793809F4D970893DAA133FB310146463EBF24617072871AEFC1CF9E885923416128479EDCCDB941FAFECE56906243A74FE19CAD9250141AE8B4CA6A343926D9076B964733D8978A109876475EC66691F659499CB552C28ADB0420E335F01DCC2BF041BD438E830FA09AE673C03930FF478300E5C1FCDFE6932AEBC289C3713A08B3136B37798527B2F67803C705107F985A1F4584AC2FEB24D195234DBFAF4097C1A1A6C50B6CBE1D31CEAE79299782B7A4482C21D1ED48EAD643681282282B1B0BF1150C20364431F62224FFD07EF87B9AA4CC4F73FD2284BE1064682DE6851DB21F786D1B6F73654DF04C06FB4F7E1CEF9480392AA851DB4308EFA35A16CF35B59DDE63F6208B90C776BE38CE0139E97C041D508DF432CA1D399910CD19A1A7C65C09A6CA89A5323D205ACC5BC75C312764B0B6599E10C2B8F4DAAAB9B298CC96CF3BADC21E4B797B52AE793B1BF08162DA0D4206FA64D703D100580435854460EE5085BFE241F8E149007EF2A373E2F48E55112AC1394721D0CC272C5C95D3EE9120A3F9F7758E5BBAC508FC5C20665132DEF552A7A65D122AFCFF8B1268ACB094DDB628D39ED6A5A0B86FDC96A7843C3043C083D8C06C8016AC05CC517BAFD15B6BC3EBCE8EE5F4C6D4A6B0722075586C66A769F714AEAEA49D6A0F5F330F8EF78EA55DB2FA5C4D057D3A1E17A92EE224745723BDD8FD7E6EAC10AFE9A0E22B702CA226F694FA079A95375E66B002F1E3C72C3CF63AF044C435A7EC2AB93570912535C40543A3B29AA9C8D07B0434443B32DE9C64B96385390FA0D6DFD80C2528697D253B9E7FD3AC3E5665E1FC0F707B7624FE526E5DF2242CB30740B6A965D82C4CC1F56468C8B43B4070C59F5F13A96E2D46ECCABDEC4702D1E8D57DE00F17200BF581760519FF2CA755AB85B1E6072000B0D7469B36C1D70290512A43F2B492C90C16AA08185C0920EB642054BD4D968396BA5393CA0364224CA7CE9EC77296E0E0108C691BFCE7B312524025B51F62C5A631440A21EAD737D224A9BEB8354C6F7293A710909A4C20E29F2DEFA90DC3AFCCA6608CBF4BD37800A4866A47DDDB7D6CB93E276CAD4C0BC6363B2204B8BDE55B1017181A57B3D8C21A452AC2E90329CF0A57734FD3D566CE07DAAA277390C0ED6A5F9E004F6A21AB57A29EAA2483421C06B7F26ED889E8FB5EDB83D7B323477C71CA2747065C8B6F25319E56CECC956A0F668EDC3667D16D2F511BCF247D1B0A0CB333741C2CDFBDF42E2397B92D582B2E2BAFBE4283F482460BC4EF92285A6C6901FCFD5A4207AB3ED31F0F96F887A9AA3106A11B8631DB062549A389BD938DAF7A78990698F57CD28BA283BD1627E33ADA7A5FED8F99D2916EC6369C3155969B420BD3226C7176EBADE1FDA729F841FDC40A4B71AD06CEE87D22CE513F044193ED05CCC89DA0B02BDCABA46B55077744E5CC67400CD2834299A5117C73ECC94BEC33014AC80A44B213E2454429E87186DE2AD19FCBB82717B410E17D53E0D3DC9E19549440B254C7476566B5FBEF170D9A72103FDECFA8C9734D1288524F5A14180BF729E56A3FAAD46EC1C565401646B60C319676274BCA3348D8ED8AD4CB033AD0E60F46E9326D7905EFD342FF4E62860140FE4EBB0B10A4A50BEAEEBC69454DCCA565480F0A343E69D82DC782356F4082E654245E50A9EB928B14A12C23554AC2BB690CDFF0A780F8B76450C894010C0AB69F32D1172F3A4093EE363285A1BCD8A28B652E02B0AC6AFDFEDC2FC544C70EE216805EE18F90FDB9629B315D2AB7DD2E4B07EEE3813802F131C9E9B1D28E566F9F92F0418B4D5D7C8F04A1B01FD4B311A2AB22B0663DAD07F993FA403F7752E9C4E8EF2E7D5B4015344E43914150AFA4C44E1761CAE104A9DE91BC81B6806264B4F888B7F24AB645F2F51D745C99FE1ABE1E733B8F9A54D6909E247E098D201293C3B70069D683DCE35AF0D91F490B1E439583129F45884ABEC6B25A5F59657C4F74846629746C48F25FBDDEEA64C931E3B07082204E35D3CE3DA543131FF6A284168D245EF1196B5138633030286F64B43B6BAEB12C266881E44F25B5A711553C8B30EB66070A12EE3B9DC78E9E91BCB1F5748F40DEA61B36711588A6A14060921D6776FA84BDA0B585A7CC1A855096763E91AAB01228F7EDD01C53C2EA0B5FB420CABCEC9A1EAB94EE0152A5D315AF370A1C192CC621FEF816C3CB4B9344DB176EB226138787C75280AA061D77ADF001267AD32C146C21CB02B7F0557624335E764B1BA948F81FBF69BD29641060EBB9AC541437900CBF32D8E206695E496E4747430243DD78D80CDD17320EADF225BD0CD0D158F95120AE130EFDF13441BF7578F4DCB3964DD9B507B95B1CBA60230D356C40C51A5D457F92BA19148D6A6E3F459A78A874921CCE17F654B72574398C481BA54976F940C1239CDC045F953EF5943BCBAADD35B9D3CAD3195337C4F28385D61282F97C8E1D63BFA91160F254EA157E7444E98DD5D871B839A9042E5E815BC681D6495EFD3A017806E8C28C4E0627DBF61D1240C12BBE5F08C3CEAD6A0635EF7CA2E816C56284BEE2E8ACEB4B4954B00B44DE006633AC5840A26D415C94A25A88F5B2B7B522669A9E9DD142CDC188B20A9D545E307081602646AFAB9A32B725D66EF158E98432BC2D889086DEC63D7C7B4BDDE13B3187FEECA3488AA1531E606839C04764902F0800955C6833C00FE3CCEDCDBF75F087227BF12F4603253F6177891F37D408D6D27EACCE1512ED2664BC87540510C43A1A89D8AA28C8DDBA97B1BC92DCBA9A68DFF3DC57338DC45F891BA704D17A18DE5E142B09ADD8F97A3A1266B251392C0449561E0D4EA4AD1B84D071D28038D5AAD50BC86CCC3569EDF18E5B24046668106D24767781EE56605BD41B1195D8470581517E3FD8109C0AC24448D57C146CC5D1335B140B9B4286AC0F6D2616030C0458BAD9310AB6EEE683489A1146F7A08E406F00A4CE8F514D4CDDECBD01EA05977F55C3871087B03777D0149BCB81E3A456510014E7AF448E03A36855777464E463261F419870C2E54A63B7E6D1BD9ABAE22855469FB9251394F58329F0225320C52A333CB228BFD6A0EF48420DD3C7C550C22459CFEB0073386DBE032D31C5B75ACD4F8C67AD82C71E7183969D20769A18318A725D1F22ED2BEDF1E9D72C4A8C240943370FED1DE7F23513AEA2AA1D5AEE27AFDAE647F03D98EA7121891227C9485A0A3EB5C72C263998B13CD68367AAC51CAB3CDE76674341A593B05546DE545DD0CEC049C4D445DFA36019F1448AA432ED1C0B70D11DCD69F188F10127FD96984DAEB3D2A7A3790196729BBA73443D109B38A96E394C3419F4E187EA26D474534588F2E9124E52E1001116886D350A2CB3C6CD3881FEBAEF336AC1162C84D0165F14DF8011AAF3587FCDE8D0A19FA260B652D682F57C65C3443AB602FBC27B8A0622C231880B1E62D095E1A4FCABFF880D86C3C4ACEA5946D981369678F4E34FD08A4625D9060363F22526D4D8DB175D6D854A9D15609F704322440AD183FE6FF3F4250213006D91FAC2985BCA13D4FEEA8020C8DC6770F517CDEC026E125C1AA3756A34C8CCCA219B0C214303637010586B8CD51DE2E9A29DD9675411C92540822514F10205820A2D6EA2EF3D742635D81A72AF24CE08C8FF0DC150219CBBC550AA20DD7797EA683ABF681C8042BF242C2799E55EE2C10577E3FDBB413B494447F121969AB56660700E260C31A01B88EDFFF3D8308940E295DC5AAC44CB9CDF672B3F7AD2AC7D7AC1CF668F913A64B8573A60947437E1270658919DAF571289CC5F2E0E7BDFE3E4911582B540B0B36732C5550358BD2738C70721D3C18A5BAE95A3A4DCC202C5634C390F1CB489CC907CB4DCAFE6FCAC299CAC3655BD7DB669DE2AE6AD77F3F78B4A6A154D7463944DA23515D1AB4C75456D601736BECB027B0D544062271A3345E0C9BF9333EB1B02D5038CF771C2EAA1FD6AAF8F40E9F077B907827A31193AEF045F4CB5C24A9F490819644834D01B57E09EC21038A2A701C7D21E0E92C67F3AC8151F9AB1442A9C122AD961DA84679584324F42308C384FE0A844714201EF64C0602EECC06C45FE04BFED8B664930A573B95FD7BDFDEAE6E6CB1ECC8EDA92939A92937C574CA50BFB8E6D4F92D713A4A0C3B9B098C2B45DCCD2544369A923C73DD95B6743D96549F9840781D6B0C1876B2A407640024C6A823292E4688D0A8F83EF689D7BA5A31F2D2A8039E355509F0C6A199D782346FEC75E9D9C4E5BCE1A1E668AB0F383E9B40545A36C4C90F92CCF6032DDFA51EFA03A1A026B53E092842AC49858E71322081457CC6006D81F0C3693F5892FEDBFB32290AA36B1F68199FE655E3BB57479012BC0090E58E4B9D6074A5665996666394ABC542B063174D67C7038172B85B646D330A152FF451DBAAFE2A215D1AFADD79CB9BB785E1B9F400C8DBC9F90E5E2BB54AE66760C0781C1233A33220521096AC8C5094FE49E8D631EA05188328C9732502ABD8A0EE35C8ABD039902A5C8A0C7BBFCB32508A2591731CFB1632F39DF33C2B6F36A6331E3DE35533A3CF7932D417E753ED68F1BEAF651465950B455F292A23F3675DF243043C727775C9BEED7E16789F048AB34A99E9F86DCE589211541DE582AC1B9139B25EA1F7862808DC97C598BBBAE330839320CC36DB8ABA774CADA3711DD41D44F9BD60918C2346704D0B591B76D8FE88C523155E3A7C24B388540C90B3097F4F39141CBDB541DE113F726C102B11A659E403C9AFF464A21AA4A5B3ED8132620623B48121C203B94A32DE0A42FFCB4EAC69FE2D7DE4E144C8C071DE51B1B11111E139AD656552A757DEF878844B97094A633EFE041288E275CE020A935D2F0110D09E7A9720E04FB3FDF8390B73CC92EF9378B802C2371A2FF1F396D95F587D362A615153AC8F687D84367B2C72F24C6A92C9B7119A92B95D69CF4BEE22A40CE1D0354551E2A867C78A71E38EF22F82240846DBC93EF1BA85D152752E9AF9A1ABF3896C1B36A082CF633645EECFE133E60354A05FE5BE0A16E843C8B9DD6A290F0FE1572614362B0E08C9569CCE55ABF8DC821356629F5313019D52496F7BC794BD2B1ED61260FFEA4D9BD07930EE2E66CAC9464710B87A318C6F66242C0764005DB8F9502046582F0A99B13B48A00F8796E846B2649E807D6E4B66A39BA514E07D53B0C5D85AB54BC30969E8CE0AEA37F5305F6F8312207B8505BFFB78DB0A29265D2DB06E0F617E626614371F8C49208B501ACAE806E3385458BCB52DB4D182B8F39ED5E4C2775D0E5882C189B38B4ABC1F49CE55688E6A044AC3717417D96053D0F22491BC3F5D93FA197FC246238864488B5C9A334773B11878AFB48D086CFFC5C8E389EC3AF54BC44F1C53B5273C8851F94D17F841F9D99F4EE8DC0A96640FDBA3B43F3B0174A44A2A8785CA45C1CC4462B4E201D21E3C749AD482B51079C76D44CD0264E074A10658AD6BC803314C0568A1B73699C5566DF80B5A6ADF40E80C5CA3EE7E4A989D4C40E6E043614F8829192308656EE7D81C7EBC53001640345FC9CA1D14CD3D2DBC4CA88E26224162918644A34EBA89408DB7B8B810476E33369014BE8195A7838BEE3BFCF49DE0CDFC19D50B57547879514D53B38CF19CA5456CE9DB44C08FA05E55A626984DAE33703DA1CDF56E277F20E3A9985423C9CCC85287D5BDBA4823CD95AB0D6334DB939FE601A8F1D18B60BE9D010B6D3DC14F6E2DF2524254EB0DF39895F498E4A9C46FE0BF5EE151CA3401D4DB116336F456C64E1B5349109341E94A852861D152FE10C400B98F98A7FE976852077D44765D191593723DED3E17A4DAF288BF54829DACCA038DB37070BE05B968849D8EF747A157AC51F33A31E1934574E6EA09220547E740D4472F70BA581E88D15C8C768F51FB6B006688F34BE85680AEF80B6FF74D1B84545EA641CEEC241110D4157E4BA521235529CED6A43F101D5CD9595E020E01019D819C214962576047D923CFE2EDA821DE59109C845DB58DDBBCDC2B0258CDD0D0B3D7833EF57CB9935F09044D50928B665E6460EA650634C55FE146F2B784331B815C36A9C966C9020C5A2FA9C351385ACE18405CB0EA0039F14F11EC1905D0D273C57E3F8FB218E5B3C5395BC8BECD356D513455FC70B9EC32598E677527F403F9D3A9F27FA14788D6D29F28019F44E80E2DA4787B13E60A43C8E66D81E53C3E42D02FA188C916B515D74C20724B2FBFDF902C720747481A4E268F13087900ACE6C33267CC83635E0B3ADB698D006801F967625E492B2066F6688857CBDF1452598F3319D9ECE68A85C1564A343B52913C8C09441E13C2D5F3BC59D61B624E9D671268708EAE80D74100446F8FE14A32E7DC41C4439793CFF61893B5A4D39883E99E0999541A82AF0DD8B93C07F07A07DA922217ABDDDD481EE8C5A2D6A6D0623AC3F2ED28116EA5C7ADCCC94FF0E201D1C989DC676D4043F33F37C442DD20C9AD459BCB89C2C50B453E6A91B087F3DFC72411C80E05B675A5E9A6129D0D145B3A119147FD00D37D92179B88E3AFE4E77F24A008A3D4B0DC86FBDD2773B53124E4DD5B30B08B048639A75C7094F966E81612D8C0AAF7E4B03E02E2930BD35F8506CED8B491C8038CE8C1160B5B4C11A2E57FDCA4F2B242C7F8B74E18C8C77801F09B277D4FF68FF40605A286801F24E50106005ECB27136EF343597CBDE60B132A57809D1F1FF82310E69BD5A8B851963486C73A681F04473A00E137737672B6E592572EB162422A17752CAD3E4D32076A49125B02B090731648762DD08362853C32A000167E8B32E35A372EA9D6686D82AB35656A4F4E00A6942CC4BA40E468A32C013C4BDFA76C73103E930BA5C75157B62DC3779E779ECC0956EC7758D83604D480590F88FE77B1FAD5AF7472B4905DDE62C59D14757ABD800EEE2D88E496BD3C544397A78A041E9161CB37E3F4E7B6E09C424EF12E9068B898944F53908FE800CAE8C10F625D800E0AFA6A53861E1A1F7C12E2ABA9E0D282EF848BD90C29A90A54AC5EC53BE5CAAD67C5D276E0C69C06862E1A79B9736E19C3AEA178BA8B0AE170BD304C4319DF261910B29B4B569D1B3507BF87468063AB4A53ED2D651DC7C47F6B42CCB57564E99DE5DD438A8069C8967E25DF402B8929EA8244AD67CA9B8818A03067B280A4DE2E1A1A22995BB778C40A704B0CE84A9248C7B6974970513B29AD3E58880E15054BF028AF26CFE30ADED531EAD501F5263F289E6FCCD67E978434296C52F42AA317E06C66DC4008564E971404E17AE00170211943105E89A8625B0B7D76E36768D00CCA75F68679938149075AEBCD04EEAF7C5C0BA4032C60B45515CEEBE0B57AD05C25B6A932ECB6F2ABFB088253039908315629AA975B8D2EA5CD5A1964C231934A310BD222C8896CB870A620293536A4D93E29538AE3EBF091B6EDC84083CD43DDE7DB84440C8DE69079EE11A1B7E354D2E02A326E0FA84821DB48C9B886C10D2EF5286F8DA7206C1CA5EA16852579B0201F8598446D4C0E66EE116507FA2E1F853C11890B32D419E093698B6B2EBAB9C4417178985B8DFE2767BD8624A2088E756EFAE37E98490982288094977A7A2538907653FA78952C133017BC129539300159E75E15C93807F8C8ADBC908C466D720E2A86D8BF9ACFC10263620B9B31DF7EEA587891D693154CF251AF35A2DC01B4A837FB8CA74F1AA744CC53AC3953B68D51F71D14CEFDAF91E3972B2993DE80F83C9E1E401128531A8C17462BA7892CB962BC084C91130CE933A36B2A74AEC1C5BFE498684351996E15CDAAA5C5AB9C767228397135845E82469085104AE5D97523C0C7634D0DDB2D7F49CA6822F6BDDD8AAFC7558A405D1BFC00DC8E80506B0B2A12767C0F905CD1F568991CF277EA51205F04354E80255FCE010B93E822CE08C30448C6F631E6D6339949E100F7AF00357E982378611BD2F17190CE6E2E71A43FB8E03ADAA98D54F78445B2C7C433409ED8CB75D2FC9344C7B2C24A5FC368D5D5C7CB9BEECE94647CC6B65C69DF829EA1EC6DA913052A534990D27756B2BC0AF7A533A945A49B4A3B20668D94254326E18226EC1605B63EF618C1259C81BA5342A20CF8DC64E39F4D7929694B516E64CD03614290D3C033B6B1C0B030171CE4EFA65491D1F376FD2469944524666EA0A28AAD35A48E15EE79E677FE2B38E9B11C1062731935F2513AAA3A5230A75A50AA446700B10C5A326776241100A2155BAE98E8CAB6843116B153E8BAF5D46399CF5F6EA67202B0144E34B0E1BAC6C1AA55FE7CA565E643BC1E15A1C09FC378CE37548D23BE3283A74D6B590CC8D25C64464B83A506D27AEC7F721A911F039AC4671A02564DC266A2EB78D6348A8D100E71F99FA861E499CEFBF200F8865D244BF30BA23EA4706290BD108EF82340B134F9E1ECE3C1226714C15BE72D5D33CB8DA2CF49181DED7BA3055E26D05DD8511D5ACA38192BD1AB7271B653005A6EBD06A3DE983CA28259478B4EAD1D66BA479A92F375E892075329BEA241C4897187F64C370F0171DC5D48CA543F3599FC8A87E7AD5C46306DE61518F471AE909B842601348CE0D92D657015772A7A59E957848263E359EB50DE8CCE2D2E37BC6DD42E4B880221F2B949BBE202A2648F42E9CD9E958D5481BFD186D444320FDB69B249C4EC2A11E4C6868D1E27E0F601163384AB8BA30B063B4D82C70CB26F061B2450AFA9C489A2FA3F11A09EA4CEF28B89DB6C5EF2A23CA9CBE1D980314807ADF85F64B3E627917AA0D05E35687BEE745FE67FB72842B1684587A9C41A8CB8D424E30BA1A7D0A17C6891738AD96B07C9881141FAC9B1C76AB47D0A1D32EE8B3D1B7F38003AB07C7CC89270E004942C3EEBA178E62D0F6EDA1C81B97E3EB2492AC594F710F2414119A481936A5E970F941C7BBD08DE49304D1E446CEF76E52713DD9485EA73CFC6B92DF4B09954DD20371C1E88087D73F0C885A68327486A812A1C9C36DA7E");
        byte[] sk = Hex.decode("FFFA0806FCF5F30D09FD0009F707FD010A0004FAFAF504F2F3FBF8FAFFF309FAFF1104FDF8010503F7FE0E08FAFF1806EFFEF2FEFCF1FD06050B07FA060C01FAF31300FDFD00070100F9F5110FF8FE0FF709F4FD0307FEFCF60800FF02F2FF0FF9EE07FF0703F002FD03FDF80902FC0B02F7F1F304010407FF0701FEFCFE15FC0503FE0804FE0704F202EF09FBF408070D07F206F307FFF502FDFD07F70404F5F8FB01F704E7F700F40501F7E00CFE080AFEFBFFFFF806FAF606F5FCFCFBFBFB00FD0AFBFFF5FC0B0EF411F4FEFC07020707F9F817FA0A0507FD0508EDF3FBF805FF00F1090607000508FB10EF0504FFFF0301FD0D09EFFE02FFFEF5FCFEF1FF0403F5F3FDFB0506F703FF09FDF3F9F5F7FFFE08110F0201F60701FAFFFA0A0404FE0809FF0AFEF20D09FF0A10F7FC020409F7F003FFFDFCF1F4F707F801F103FFF504090600F50301FA08FCF4F7F90C1310FCF20AF20E0CFA050103FCFA05FDEC0B08FE0502F8F9FD0C03F6FAFFFE01F8FB02FBEFF8FDFD0005030002F9FEF2000603FFFDF8F4F50206FAF9FC0BFE02FBF20BF400FFF90E0001F10FFC07FC12FBFDFEFB0702F8011008FDFF01FC06FF010501FE060A0201FF0302F116F4F8130008F60A100906F90309F7F605F4090C0510FEFAF8010609000A09020C1203F8FB02FFFC04FF0BF803050AFC06F1030808F6FB070000F80D0404FDEFF8F804FF090DF708FA050E02ED04FC0105FAFE080EFA06F706FA01FAF3F80404F905F3FFF0040406030505F808020B0803FF0FFDF8FB00F80102F7FAFDFEF3FAF9EDF0F704F302F10503FAF20302FBF306FAEC05F20A0400FE08F7F90506FBFEFCFFFA0E14080EF4041002F60702FAFCFD0F0B05F7FF080810FF030807FCF5FE020203FDFBFE021209FA05090205F800FB0011FC09FBFA14EC0610040C070A020C0CFE0006F900FFF8FBFE00FDFF0305F50B01FF02FCFE0708FDF7020006000011F800FEFBF7F6F6FBF4F50DFB09030D00060C030200F7FEF3F8070605FEF7FBE907F903010003FEF4F9F50FFBF0FD010810040502010AFDFCF00A0403FB030F0203F6FEFF060CFC010905040BEE0EFE01FFF6F6F508F90C0503050404FB090800FFFF110D0B0111FF0BF405FDF302FBFDFB00F603FD07F4F90102F5FF0908FBFEF901EF050DFB04010603120F05060B06FDF1FAFC0408FA07000E0DFAFDFD0AFCFCF9FBFA0AF201F904F9FAFAF80CF40003F6F9F8020805F709FD08F90BFCF7EFFE050AF901F4F9F4010704FEFA0CFF0506F103FA10FF09090109F90602F7EC03FE0107FF02FCF101FE05F9EEFDFAFC010301FE0202F80304050107F30107EF060100F9EEFCF60603FD02070CFDFCFE0AFDF90EFD03FF00FBF70308F50407F8090D010AEC0603030909FC020908FCFDF8140CFC1510F404FBF20AFCFE0FFB03FCFB0105F8070B00040AFAF6FFF8FB0C09FAFBF806000109FEFE0F050908FEFA02FF06FB03FF01EFF40AF9F502FE030DF4F6F9F4FCFCFBF9FC05FFF90A0204010901F903070C1007F70008EF03FF0DF705FCF8000F0A0312F204F810030D00F0F7F5050008FFF6F9070AF70600FCFDFE00FB03FDFCFC0302050506FDFE04FAFEFC0303F7EF05F3F70BFC06FE00F3FCF3F5F3000703FB07120604FFF9040A0406FBFAFAFB0309040C06000312F901F30200FA00F3F4FE030505FFFEFAF7030D1104FCFC02FCEEF116FD0B010B0EFDFBF904F3FAF3FDF90001030801FDFBF807030706F70304FF16FDF700FD010BFE090DFB0502FF0CFC00F5F00A09FBF604FF02050A04030AF3F505FAFCF809F900FDF00A06EC04F9FB0302FE02F6F60B0707F8F209FCEDFE08FBF709FC0805F904FDFE020AF4F2FD030303FFF8FAFB0CF60BFFFF07FC090613020502F7F2FD02FEF2F2F400020FFCFBF905F4FF03F706FAEEFA03FDFAF607030207F8040FF71101FEFFF50200F3FF02FBF901F50A0B0706FC050900FA000AFFF0F306F802FDF00BF910F202FEFC03070501EDFEFF06F805F7FE04ECE5FC0C00F50B0703000AFAF0FBFEF5070FFBF3F10600040AF3070704FA02FEF80703040107FDFD00100B04FF0402FD0419FDFBF5020901020B060BFEF5FCF207FD01F2F609FCFEFE14FC0D100900FAFBFD0AF802F80B01FD0008FDFB00FC0102F7E9F304FB04FEFCF60AFF0B07FB06FF0D020700EBFF040B00F5F8F0F905FD040807F3FE04FAF2F60AFAFFE70AF7FBF6030D0B01FC05F701FD0BFEFEFE02F70B0101F4FB08F1F600FC09F5FE03FBFD06FF0405FA03FEFCF7F601FD01060800040FF8F4F9FB070EFCF503FDF707F0EE0B05FFFC0703000201FBF9FB03F7FEFB06FFF200FB0004F704FB0904FFFE000603EE0A060BFF0AFBFF08F6FEFAF9F6FFFEFF0BFA0AFBFA08FFFFFDFF010BF60512F9FA01FDEB02FCF9050DFE01EDF5F3FA03041304FBFCFC01010CEB06011304FCFC04F7020BF6F10400FBF6F9FFF60FFEFFFBFF03FFFD03F108FFFCEDF501F5F8FA060305010501FCFE0DFD0600EF0304F907FF0000FB03FEFCFEFF0FFEF8000402FBFCF80D0709040B15FBFDF7050207FF01F8FFF7FA0608F8EFFB030AFC0DFA0DF8FC07F603FF07F408FD050EFBF9F5F5F60403F706FF030C1306FA0DFFFFFD090405020105F7020500FE07F701F608FEFD09F910ED0BF6FCFAEF0B01F4ED0708FB000213020700FAFBFFF9030AF6060A01070712FC0C03FCFFFC060407080104FFFC0904FCFAEFFE03F8FCFB01FBF0FE05FD06FD0F09FDFF03F40504FF00FEFBFDFD0EFBFFFE0B0AF4070C00F4FFF5FA05F10505FEEF00FD0303040103FC050407FC05F90104FD03F501FE07FD0BFBFE07FCF5F805020301000103060902041101FCF8FC00F301F503FD05020601FEFE070409090400030BF60B01F60DEB00FD1803FE0103F70AFFFBF90408F611F7FC0707FE080203F305F8FDFD04FAF5061005070301F607FAFFFD08020AFC02070BEE0907F9FB07FE02050F0AFE0FFCFE07F509FB0C01100306040E02070B0202FC010DFC09F801050102000CF5FDF9FFFB0004FDFB0A040506FAF00607FD0EF10DF5F8FB0408FEF3F4FE12FFF500FA02FFFCEFFDFDFF06FC09FF07051600F7050702F60608F102FBF60CF50400FB010401FE0A00FE0BF50CF809FEFF04EBFE11060501FEFC040B040605FF00000100FD0EFE00FE03F70DF4FAFF0400080AF80310F508EEFCF20AFA030C03000E0308050600F901EAFA0A05F800FAF6F808F803FAFEFF01FA010902F9FDF6F60A0CFEF702F408FAFEFD00F8F3FC05F9F60200FB05FDFEF50A01010D04F908E600050CF407FF0404F5FDFB06FFFEFC0006F604FFF707F809F900090E01FC0C040AFD0C0BFF05040AF30003090EFBFDF602F8F8FD0101060513FBF805FC0B00070AF500F803FDF50702F0FDF9FCFE01FD05F8010E04060000FAFC06FAFB16F6FBFFFF12090805F906FBF9FDF8FFFF1106FEF7100707FC0406FA11FE01FDF5110D0B0DFE01F807F6FF04F4EC00F50502FCE90AF9FC00FEFB150D0008F8F4140F010CFB00FB040F020207F209000BF8F409FFF704F3F7120511F90FF8030904FFFB020900F907F40BF605F607030BFEF301F207F1F801F9F705EEFD0FFE04F004FB0AFB0405FD0000FEF90DFF01FD060606F8F8F60D0AFA01F9FA051C00000B0FF9FE01FC00FD0E04070109FC0507E409F6F601080D02F302FCF702FC0BFEF302FFFF04090105FFF8F6F607FA0A00FFFDF5FC0AF3FAFF02ED09F2FBF7FBFF0CFEFD050D070505F8FBF700FEFEF80005FFFAFEEE0AFE0BFA000906020307F60607F80200FAFE0BF60604FF08F9FBF50606040802FD0004F4F8FCE9130911FE0305FEFEFDF905FFFF0D0B0EFA05000500000406ECFAF8FEF40AFA05F504F9F9010BFA0302FEFDFC07FB02FCF40D04FDFD0803EDFC0F00F7FE06FB090305FCF9FFFA130B0BFBFDF501F7FB03000D02FD0C0300FD0BFFFC13FC04F90E0804FCFBF803FDF7050907FB0702F8F9F8F2050102FC01F40A0614F6FDFDFCF00805FBF803FCF5090EF6FE0900FE04F8FAEE14EC000600FB0801F7FE0408FA0AFE05FDFDF907F5ED0A0C000409FCF305FF03090AFA0401F0FD0A000DFAFFFA02E9FBFE07070501F508FE03F50BF205FC05F50108F405F9FA02F101FAF50B06FC0AEDF4000DFB0D06FCFEFBFFFBF700FD0B07000703E70A0406FA050400080001F00E020D00FDFD0800FEFE04F90300050D05000D090700FC0E03F50304F90007F60D020002FFFE02FE0903010303040706F70800FA0706FEFDF10608FCFEF4F5F1F8F610FBFCF002020702070B0DF3F707FB07FEFB0F06FF0A07000C12EB0FF9FA09F6FDFB0B040AF7110302E9F6FDF0F90A04FF0AF3E6FF12FA010804030AFD1402FF05FA020E040201F502F908F00E030C02F8F9FFFB0201F903F3FFF6000606FBFBFB0204110CFAFCF40202FEECF3FE0003F007FF0AF90302FEF708F00DFD0B0AF906010206130809FB07FBFE0907FD080002F804F2FC0CFBF40301F40607F7FCFAF605FE06FD0DF4FCF60400F4FD0AFCFC0B0C01130807F502FE05F802FFFA0404F908F6FBF802FCFC060505FD00F604FE040802F20100FC070500F40305FE09FDFCF4F903010C010313FBFFFE06F005FB09F8F2F3FF0A05FC0AF6F7F506FA0409F9F900FFF7F307F602EC0CF8060EFB02FEFCF311F910FEF8F0F904090A0BFBFDFF03F8FA0E02F90D07FEFB02F9F206F40308030A00FAF704030204FA05FF0BF600FF0309FC05FEF9E8FEFA05F5F907051405FEF3F5F0FFF5010BFA06F6FD0D0504FEEF000CF7FF04F004050307FF0AFD00FA05FFFD05FC02000E060DFA0005040AFE0A01E1F2FA1508FD0804FFF900030906FCFAEB00FDFCF600010207FD05FB0005FF0206F8F900FEF902F903FC0BFD05F3F9F81306FBFEFFFF02F5F7EDF10FF50D03FFF6FD03FAFC0606F90B020B03FD07F90C0213F609FF07ED10F900F801F6F8FFFD05FD04061005F6090308FCFDF10400F9FE1702F811FDFE05130307FAFF0AFDF5F60907FEF5F9070709EF0009FEF8F8FFFE0A0CF6F30100040807F70D0D07FE0303F7F808F7060808000003F303F1FF01F700F7F70CF8FEFFFF01F70303F50600FFF800FF010AF3F4FAF001F105150C030803EFFDF6FAF3F6F900020C090A08FF0400050606040408030401120AF9F3F505F1020402050E010407FEFCF9FC010008FBFD09F7FCFC04F9F804F604FD0BFB02F407F8FB0702080404F5FE0A040B090305FCFE090C08F90705FEF8FEF8F1F501020DFA0D0504F8F900F9FFFB00010402F6FF0FFAF905EE09FF07FFFA180501F9FBF9FAFD040A0405FD11F00D11FFF900F2FE0406FD0BFDF8040DF2F80801041205FD06EAF8FA10F2FAF6FDF8030CF7F7FFF702E8F510FC070E0706FB04030D0C02F60C0904FC0AF5010700FBF703FAF708F501F7F9F80FFD050E0300F90D03FE1510F0F9F7F7FDFEEC101405020E14EE05F1F50C05FE09F5F602FA04FCFBFD01F003F6FBF6FE01030D05020310F6F8FAFF080906FDF6F6F604EFFA00020704FDF9FDFC07FA07FAFFFE05FFFAFD040C090E18FAF1F40400F3F40103FAF7040BFD1105FB05F7F505071006090BFEF7FBF90FFAF800FCEFFF07FAF8F905080B04F4F9F601F707FD0E01F5FFF3FF0700F4FA04FFFCFDF6FF0A0107FDFFF404F80AFF000402F20A06F90400FF13FFFF08060DF402040511FE05F7EF03FF0206F6FEFB0DFE08FAFA03F3F3FB0CFDF9F804FEFD0D03F9F8F2FDF7F8FD0B0BFAFDFCEDF3F60FF8F807FA01000203F9FF0C0AFF0D00EE07FC02F4FB050B03F608F9F4FC02F5FBF80200E905FBFEFE0BFC050904FEFD060006F7FB01FDFD010FFE04F6FC1207F511050CF8F6F900EBF7030CFA05ED090009030B02FBFC0608FDF60A01F608FC05FAF7F9FE0A040D010B0EFDF90402FBF702FE090FF5F7FD010301FA05FD0206F4FFF4030402FA0BFF1008FC03FE0A0607FCFD0108F60C010B080110110501080506F60708010BFFFA02060D100304FF03F8F503FC09FF08F00304FF01F1FC06010404F8FA0406FBF7000307020DF50C04F9F4FEF500F50105F70AFF100B0F00FC01FC03EB17F9F1F3F5FA080FFC0307FA0900FBFCF1FD0106FB0BFCFF0D0D1806FAF90EF7FDFDFE03FFF7080404FBFCF9FB0AFDFF13FB0502FD00E9F50CFB100800FDFD0FF4FF010307010AFAFC0DF906F4FC0514100B0A03010301F6FA0609010604F90009FA07FC05F7F2F601F3030DFE0A00FAF903F7F603FB05FAF8F9FE080F06F11604F3FCFFFEFE0200FCF8EEFB09030CFD0306F504FCFF070AFE030805080CF405F5F9070416FEF5FA0003F4070203061000FCFC05F9F809F9FFFDF908010BFD0602000D100BF8F8F9FEF6FEFB03FB01F6F70401FAFBFCFFFE0100F800FD0605FDFEF0FB0AFDFC080005FCFCFBFA08F7060205F1F60305020800FE0307FEFB00FA00100CFD060A02F70EFFFD060BFC03FCFA00F9030AFF03FAF7F414FC00FBF605FD03FFF0F802050BFA01F3FDF802FA010DF90107FA05010AF703FF050A0304FFF905F8FE05FDFBFA010302FD0E0800FCFCF8FF0401FEF30102FAF80D09FB0301ECFFF70301F3FC0513FF05F3010F04F70AFFFC060400F1F1F8F802FFF30FFEF8FF01F9F6050402FCFF01FCFBF80703F3100B05FC05FC06FD02FC18020B101102F40604FEF610F005F811FC04FAFBF50E02FFF713EF03F50700F3090301ED02F507FFFDFA0306FA030805FFFD0F0CFF00FAF0FB0609FFFC0C0307FFFD0F03FDF10606FAF900030C061705130C02F60AFF0DFA060304FA0106FDF7FCF404010AEF03F9ED06F7E8F9FDFF070901000105FBFC04FFF8F5FF0306F6F7050700F3FB0608FEF6FDFB0E04F804FEFDEDFC02FE0009FE0108F9F8070506EC1408EFFFFE0DF7F70DFEFDF8F80408F707F6FCF6F10105F80C0210FDF300F3FF0702F9F208FDFD0500FDEFFCFEFE0005000C05FBF90D0308F801F7F9F30302FBFBFC04FC09FB08FA0B00F0F5FD02F90C0506F80904F00B03FBF809FBF7F7FFF6F908060503F5FEFE07FB02F909FF0D0305060601FDFEF8F70007FAF30301F2FCF8EFF1F803F6F8F40D0603080100FDFE09F90002F71002FD01020DFE0105FDF0FF08020403060CEEFE0C06000001FC15FFEEF80FFA04F301FB09F1E90C0401031204F70EF4FE01FFFCF208F8030BFD1405FD05F205FEFA08FF1109ECF8FAF8F904090B0A01CFC6B92DF4B09954DD20371C1E88087D73F0C885A68327486A812A1C9C36DA7E4F5C254B6292FB5C3DB9561B8793D8AE3E1611423AC0A9F8CFC13E1C85FEC6B5"
            + "3E8FE33CAE1CEEC574275C02B17AE78A0018BD4212E087C0901E518796AAA752B6282A7D0DB145AE");


        //
        // Key generation.
        //

        //
        // The QTESLASecureRandomFactory.getFixedNoDiscard(...) is used so the same key values can be generated for a given seed
        // for testing purposes. You MUST use a real SecureRandom instance in reality.
        //
        QTESLAKeyGenerationParameters keyGenerationParameters = new QTESLAKeyGenerationParameters(QTESLASecurityCategory.PROVABLY_SECURE_I, QTESLASecureRandomFactory.getFixedNoDiscard(seed, 256));
        QTESLAKeyPairGenerator keyGen = new QTESLAKeyPairGenerator();
        keyGen.init(keyGenerationParameters);
        AsymmetricCipherKeyPair acp = keyGen.generateKeyPair();
        assertTrue(Arrays.areEqual(publicKey, ((QTESLAPublicKeyParameters)acp.getPublic()).getPublicData()));
        assertTrue(Arrays.areEqual(sk, ((QTESLAPrivateKeyParameters)acp.getPrivate()).getSecret()));


        byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");


        QTESLAPrivateKeyParameters qPriv = new QTESLAPrivateKeyParameters(QTESLASecurityCategory.PROVABLY_SECURE_I, sk);
        QTESLASigner signer = new QTESLASigner();

        signer.init(true, new ParametersWithRandom(qPriv, QTESLASecureRandomFactory.getFixed(seed, 256)));

        byte[] sig = signer.generateSignature(msg);

        //
        // The expect sig from the C version is [sig][message] so to validate we need to append the message to our generated signature.
        //
        byte[] expectedSig = Hex.decode("77CA5E0F6AB8586FAE62A68BD7A97DDA9435AF9335EF76BEF113A56D64420A8FDC2669E234AAD20193D2085F9E4B9DFFADA03E33DB4119FABBBCD91B19530793E0BAB71ED5CD61A607055E93FB56F66E8EBEBC6B63020E75579ACF8E2F16E5F6179AFD9E7B20DF0C010C1554791E6D41C224639B9D070AB04F6914E74644B14AEE4C7731675DB2B94237FFF00BFDAD7A070D73BD1DBACEC15094D091A0563F6ECC749FA57AFC4BF8B3009836DC5ED692DEC837953D776076F1D9A0AE98D812758A08F184751D33D29EEFB8868B97E21E115C568BEA8521B60E7D9ECD30CA5DD48EE82FEE169A2A3A1F9EC1366203FF1B1732C1598906F6715B88576EDA2C29FE0D0B4006B03E1B21171E923B5C3775ABBCCD2313186793CA98BA96B932D2561DD915E4B7BBB08B6946C37D71A4077CE9E3868148C33377068A70F1B21C13CDBBBF200F39F52E7A4C2B094F8EFE208CACDA1F7789A4FC471574ABA28BF06CB8940207E06A5B2A0B3CFD2BCBF6421A85D32F5831084A78C914FEDA6D7F1D6977397202CD24A4260B90C11077CA337F10A852A34FBAB455F2B7530E336AF3F2C6AE04B652D7E05FC02264E5C81E0E09FACD01A70F4F66F368D7F308284988BC3E6B4C026F97A65C0B31C1187CD05409C5BA4C22317C3A1CE08EAC8FC25A4D3644B610481E07FE372D19FA622365394E37A6BB61EE38E309736A807A2DD85AC76F88451354FB12AA778E3F05EC69D83AC8EED551070DA0ADC0222E7839D79E2847037A2DB1B2E0F15C376451CE38A11E57896F67D804DA2A4F84BA51308ED69F161A9E08207DA401D9F5EAABDC4F50B4D96357929126A208B0DB29D03CD71A2F203309E41DA4661162A48944F463AA642D67AE9C36FCDCA4F14AF4B136A943B74AD5B8CE6ED6BFDC3755B1750B901584B2C1614A2634D0DAD3F2D54BBE3BFBEE7C07E4D7AE64430364A5A0EE4E4A0D436D569B2DD6C8A121FB33329FD25E2FE7297E939736721E6A2BC37398DD16C7E2CA087182C5958A4D9A21EA3E2BA3754B630BDB7F146F090649ED7A4B1ABB64D58BF1CC54E3FCC8358EA6AF1B2C7622B633AEE12C6664A51F1437368E8D9EC67AB6C2939749A671465EE7A77F9368E145047072664084EAE6233F91010DD5148E2F7A66F69BC5849F80186CE26C0D927BCA38E5BB8A28302946A43CE57872FE5D735793337127E41EAAD931AD96FED22862FC0AEA72569E9B56596B8BC36A186D4D03C87D484111F18A6187CD7522EC926153488DB161D8ED5CB05F144C01ED77C7614FB42FCE566898BC9F5AA9FEF0F7093818A390A22EBA9404EBC3A79FB3543640DF29D2B5B9A601E916DB36CFD66A5067E7FA379A640D12B99D75B67F8ED059B761D3FBBDAC235024D071EAB9A2C6F13D5C6DAE1BF952EEA902A5A25F1FCFFCF8B0ABAFF0B242B48EBFFA6CBA9CE3654673F3AD4CD5E2ADD2F3E630B7FD48B1F7578CEE7D1ADC54360952ED1F1951F3F4ED531BA57BADDBFB7EC437E42A429DEC044EDBA509E5C18BCF8DE6118D6935EE38B3F94C12300D49E9F434F227407F3C7FA1ECED88D08DF04B4A518C508A3B93A3A8C60ADBB8C0B5F3923213CA531007E0CAA77E5361CBBA1CA6E49E81BA87ABE3C07E9E4590635486B44212298CBF9EC48920C0A899CF257421B734377B18141550B3954E23EEF90D6E20FEF7BF04C17DFE8083C0116786DC771DD802A69B2516396A45F6C755E05F228B4C31835F8E46591C80CD10250470B4421FD5C86B97B48C35CFE22721C3A9BA3DB8BCC739AC0CE5D2BE05B8569C0E3AB51CD112E4BF0137D6902E08E5636861E00EF3D58F3B05C63A5ED8286EE075B46EFBF2CE806301C059602FFEACD4CA5E25FC72424D4A982FE4AA6A6B7821AEEA4A4C209E78034CD69E5AEB14D57E5685E0F90BCA8507FF339CC084889541BACDC155FF24CEFBB79FB2496224A06517CD7A5B3B0C829A6ADE5FFDF70BF197E1927D080C20EFADA5BF44764D7062609F62B8C878C9A66063C957837DEF9065E571E12F44AE02E0A023F1DF37E0F5AE70C5F2699654450C112AE96927D2E46AD1F2116885D40D0373BF5B9AC1B07D99B3EBB739EE4B9F9E369F83F5806AE6A528E8E373B512A83E2DE089F3C52C155C282A78D3D8F1BD9EDA5399D48476DE5F2074F4122504E003072BABEC347678E91B921D5B583DB4B4030BDC547E948CDFA07019B180823AF3C88AFB4954F239662614826C26E3D68B12270D0426418D0DD2815F8AA43E6DBC95CAD2B55E0F5438AD46806B3CD319430550317B7C80BC8DECCBCBC651C6AA00EAC9E514FB92188F49E48C74CF2A595D42BDEC4AC6A5229179C16760885168B18D5C5F86CD500BED77F5D4041C4AA244A1679682E9A62BBD5303A9811DA40AB7D5233B13F19A6C30F700C7D5C670CE4C14B7F7CBB6BD593029B4E55D4FC010171FAE640FF6D6FDDDFEA1E1A197B41369580A38FA6979EBA701F4BB00D8F6D4B24E180A532FB235F12003EBA74BBB329D68488BE399EC1066F768204388CAB27D2766E1E5502E3F690DEE57273951BB799F237FC0F1CE7AC404F64DF1238F352D70658755184E027D0BAD33616A821E1C8CA4F281D14A531D34113AD17D971A3D5E6B022725D395AD3F594277690C96877CD96FD1761E226E5D3A98F16F33E590EEF6CFDDF7A9E5D2A2288F4B5195BF3CF923229D617D4D594D5A76BB74E2A5558DEB44CDBAEF71ADDC3A4AF7D1570347FE0F7C715656061D0B8967D5B8147D13AF1A515C1E07EDE157BEBF8F73FD0AAF94B7F3695BADC3D5D81454BA9A19DED20969CB1EA356DAEA186D25EE563CED503EE3B0F4ACC8B59C2071F4D7454ED60083BEB7F33DD9E4975E85AA49A17ADD101DA612EC0D74CFF4B1E6616A8B7A7F7771AB0BFC956152F4679F3864BA08000F232F62F33D3328443505B5CB6F15EF4CDB365B1CBDF0D750DB39F49EF9BF31484CB79CA5F5AD334E7CAFFE7D7AFEC3E821DFEF387AA0677085941522EBF93D2414E9C1365B534555895B635F731E44480556ECD61DF5DD27794209066C7BB7814170E12C6E3EE938B3FF0984F1BACBCAB09DA34D015721B55E156A9276E2AB76C9B80886CA04B825BD140D1AC4C6CE873A4D38AD274F007E989FCA8BC3D05B19475B414A9C093ECA93BBBB45F8A4DD0360807ECFAE3A7DFD1B0AE736B7C17755A404E5FF6DB6B9A61B16A66A08BCE6D6C6AB31917A9EAA5E4715387A634F232CF90CC9AA9E1609F0E567516567EE1FDDC535FC5E5628E811B43C9993FCA6E4241913C2665E4E7A09AF46FF90659CDF0D022A30D253FCCDA2EE8F18714E1869DA71730AF568161AA0417E7A7B338D94BD85F5D6C30A5D0E6C38F5EFAAEC68D7D383D2529F7F696A3A061BE6FAB1EF8F2D7F91C6F10A7898C87BE2679F0CB3EFE5883AE4D954C5913FDED07FDF70A8AE16B2D11E8B0D8A73904388071E49997FF18766592B59EC2443B3C7C5B465508A40559D764CD499D8574992FC571F483B0A3BC8B28BEC0D042585435CA275875E0ED0E9E00BF401D0071769EF7345FF3F7A87DD10204D154422C7F182754DC9AA3CB764EA920329199FDEF60D8D85F5ABA944050A3A4824EC5032B67FFC7C56A591B2A2C3843617AF31B1C1C75E029093BBEC7374C98E79C21A0EFF8DBD15298173D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8");
        byte[] generatedSig = Arrays.concatenate(sig, msg);

        assertTrue(Arrays.areEqual(expectedSig, generatedSig));


        //
        // Round trip against the public key generated by the C version.
        //

        QTESLAPublicKeyParameters qPub = new QTESLAPublicKeyParameters(QTESLASecurityCategory.PROVABLY_SECURE_I, publicKey);

        QTESLASigner verifier = new QTESLASigner();

        verifier.init(false, qPub);

        assertTrue(verifier.verifySignature(msg, sig));
        assertFalse(verifier.verifySignature(Arrays.append(msg, (byte)0x00), sig));
        assertFalse(verifier.verifySignature(msg, Arrays.append(sig, (byte)0x00)));
    }

    public void testSignQ3p()
        throws Exception
    {
        //
        // Values put in file because vectors exceeded length of string constant.
        //
        BufferedReader bin = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream("/org/bouncycastle/pqc/crypto/test/q3pIII.txt")));

        byte[] seed = Hex.decode(bin.readLine());
        byte[] msg = Hex.decode(bin.readLine());
        byte[] publicKey = Hex.decode(bin.readLine());
        byte[] sk = Hex.decode(bin.readLine());
        byte[] sm = Hex.decode(bin.readLine());

        bin.close();


        //
        // Key generation.
        //

        //
        // The QTESLASecureRandomFactory.getFixedNoDiscard(...) is used so the same key values can be generated for a given seed
        // for testing purposes. You MUST use a real SecureRandom instance in reality.
        //
        QTESLAKeyGenerationParameters keyGenerationParameters = new QTESLAKeyGenerationParameters(QTESLASecurityCategory.PROVABLY_SECURE_III, QTESLASecureRandomFactory.getFixedNoDiscard(seed, 256));
        QTESLAKeyPairGenerator keyGen = new QTESLAKeyPairGenerator();
        keyGen.init(keyGenerationParameters);
        AsymmetricCipherKeyPair acp = keyGen.generateKeyPair();
        assertTrue(Arrays.areEqual(publicKey, ((QTESLAPublicKeyParameters)acp.getPublic()).getPublicData()));
        assertTrue(Arrays.areEqual(sk, ((QTESLAPrivateKeyParameters)acp.getPrivate()).getSecret()));


        QTESLAPrivateKeyParameters qPriv = new QTESLAPrivateKeyParameters(QTESLASecurityCategory.PROVABLY_SECURE_III, sk);
        QTESLASigner signer = new QTESLASigner();

        signer.init(true, new ParametersWithRandom(qPriv, QTESLASecureRandomFactory.getFixed(seed, 256)));

        byte[] sig = signer.generateSignature(msg);


        //
        // The expected sig from the C version is [sig][message] so to validate we need to append the message to our generated signature.
        //
        byte[] expectedSig = sm;
        byte[] generatedSig = Arrays.concatenate(sig, msg);

        assertTrue(Arrays.areEqual(expectedSig, generatedSig));


        //
        // Round trip against the public key generated by the C version.
        //

        QTESLAPublicKeyParameters qPub = new QTESLAPublicKeyParameters(QTESLASecurityCategory.PROVABLY_SECURE_III, publicKey);

        QTESLASigner verifier = new QTESLASigner();

        verifier.init(false, qPub);

        assertTrue(verifier.verifySignature(msg, sig));
        assertFalse(verifier.verifySignature(Arrays.append(msg, (byte)0x00), sig));
        assertFalse(verifier.verifySignature(msg, Arrays.append(sig, (byte)0x00)));
    }

    public void testKATVectors()
        throws Exception
    {
        String[] files = new String[]{
            "/org/bouncycastle/pqc/crypto/test/qTeslaR2/KAT/ref/PQCsignKAT_qTesla-p-I.rsp",
            "/org/bouncycastle/pqc/crypto/test/qTeslaR2/KAT/ref/PQCsignKAT_qTesla-p-III.rsp",
        };

        for (int f = 0; f != files.length; f++)
        {
            String file = files[f];

            List vectors =
                new QTeslaKatParser(file, QTESLATest.class.getResourceAsStream(file))
                    .parse("count");

            TestCase.assertEquals(100, vectors.size());

            int type;

            if (file.endsWith("qTesla-p-I.rsp"))
            {
                type = QTESLASecurityCategory.PROVABLY_SECURE_I;
            }
            else if (file.endsWith("qTesla-p-III.rsp"))
            {
                type = QTESLASecurityCategory.PROVABLY_SECURE_III;
            }
            else
            {
                throw new Exception("unable to determine file type for. " + file);
            }

            for (int i = 0; i != vectors.size(); i++)
            {
                QTeslaKatVector vector = (QTeslaKatVector)vectors.get(i);
                try
                {
                    doTestKAT(type, vector.pk, vector.sk, vector.seed, vector.msg, vector.sm);
                }
                catch (Exception ex)
                {
                    throw new Exception(file + " count =" + vector.count + " failed", ex);
                }
            }
        }
    }

    public static class QTeslaKatVector
    {
        final int count;
        final byte[] seed;
        final int mlen;
        final byte[] msg;
        final byte[] pk;
        final byte[] sk;
        final int smlen;
        final byte[] sm;


        QTeslaKatVector(Map parameters)
            throws Exception
        {
            count = asInt(parameters, "count", -1).intValue();
            seed = asByteArray(parameters, "seed");
            mlen = asInt(parameters, "mlen", -1).intValue();
            msg = asByteArray(parameters, "msg");
            pk = asByteArray(parameters, "pk");
            sk = asByteArray(parameters, "sk");
            smlen = asInt(parameters, "smlen", -1).intValue();
            sm = asByteArray(parameters, "sm");
        }

        public boolean equals(Object o)
        {
            if (this == o)
            {
                return true;
            }
            if (o == null || getClass() != o.getClass())
            {
                return false;
            }

            QTeslaKatVector that = (QTeslaKatVector)o;

//            if (count != that.count)
//            {
//                return false;
//            }
            if (mlen != that.mlen)
            {
                return false;
            }
            if (smlen != that.smlen)
            {
                return false;
            }
            if (!Arrays.areEqual(seed, that.seed))
            {
                return false;
            }
            if (!Arrays.areEqual(msg, that.msg))
            {
                return false;
            }
            if (!Arrays.areEqual(pk, that.pk))
            {
                return false;
            }
            if (!Arrays.areEqual(sk, that.sk))
            {
                return false;
            }
            return Arrays.areEqual(sm, that.sm);
        }

        public int hashCode()
        {
            int result;// = count;
            result = Arrays.hashCode(seed);//   31 * result + java.util.Arrays.hashCode(seed);
            result = 31 * result + mlen;
            result = 31 * result + Arrays.hashCode(msg);
            result = 31 * result + Arrays.hashCode(pk);
            result = 31 * result + Arrays.hashCode(sk);
            result = 31 * result + smlen;
            result = 31 * result + Arrays.hashCode(sm);
            return result;
        }
    }

    public static class QTeslaKatParser
    {

        private final InputStream src;
        private final String srcLabel;

        public QTeslaKatParser(String label, InputStream src)
        {
            this.src = src;
            this.srcLabel = label;
        }


        public List parse(String blockDelimField)
            throws Exception
        {

            List vectors = new ArrayList();
            Map extractedParameters = new HashMap();
            BufferedReader bin = new BufferedReader(new InputStreamReader(src));
            Set duplicateTrap = new HashSet();


            String line = null;

            while ((line = bin.readLine()) != null)
            {
                line = line.trim();
                if (line.length() == 0 || line.startsWith("#"))
                {
                    continue;
                }


                //
                // Vector parameter.
                //
                if (line.indexOf('=') >= 0)
                {
                    String[] kv = line.split("=");

                    for (int t = 0; t < kv.length; t++)
                    {
                        kv[t] = kv[t].trim();
                    }

                    if (kv.length > 0)
                    {
                        if (!extractedParameters.isEmpty() && kv[0].equals(blockDelimField))
                        {
                            QTeslaKatVector vector = new QTeslaKatVector(extractedParameters);
                            vectors.add(vector);

                            if (duplicateTrap.contains(vector))
                            {
                                throw new Exception("Duplicate Vector encountered, set : " + vector.count + " in " + srcLabel);
                            }

                            duplicateTrap.add(vector);

                            extractedParameters.clear();
                        }

                        if (kv.length > 1)
                        {
                            extractedParameters.put(kv[0], kv[1]);
                        }
                        else
                        {
                            extractedParameters.put(kv[0], null);
                        }
                    }
                }
            }

            //
            // Trailing block.
            //
            if (!extractedParameters.isEmpty())
            {
                vectors.add(new QTeslaKatVector(extractedParameters));
            }

            return vectors;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy