diff --git a/TestVectors/blake3.txt b/TestVectors/blake3.txt new file mode 100644 index 000000000..439439542 --- /dev/null +++ b/TestVectors/blake3.txt @@ -0,0 +1,36 @@ +AlgorithmType: MessageDigest +Name: BLAKE3 +Source: https://github.com/BLAKE3-team/BLAKE3/blob/master/test_vectors/test_vectors.json +Comment: Official BLAKE3 test vectors + +# Empty string +Message: "" +Digest: AF1349B9F5F9A1A6A0404DEA36DCC9499BCB25C9ADC112B7CC9A93CAE41F3262 +Test: Verify + +# Short messages +Message: "abc" +Digest: 6437B3AC38465133FFB63B75273A8DB548C558465D79DB03FD359C6CD5BD9D85 +Test: Verify + +Message: "Hello, World!" +Digest: EDE5C0B10F2EC4979C69B52F61E42FF5B413519CE09BE0F14D098DCFE5F6F98D +Test: Verify + +# Single chunk (1024 bytes) +# Input: bytes 0-1023 where byte i = i % 251 +Message: r"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA" +Digest: 42214739F095A406F3FC83DEB889744AC00DF831C10DAA55189B5D121C855AF7 +Test: Verify + +# Two chunks (2048 bytes) +# Input: bytes 0-2047 where byte i = i % 251 +Message: r"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA" +Digest: E776B6028C7CD22A4D0BA182A8BF62205D2EF576467E838ED6F2529B85FBA24A +Test: Verify + +# Four chunks (4096 bytes) +# Input: bytes 0-4095 where byte i = i % 251 +Message: r"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA" +Digest: 015094013F57A5277B59D8475C0501042C0B642E531B0A1C8F58D2163229E969 +Test: Verify diff --git a/blake3.cpp b/blake3.cpp new file mode 100644 index 000000000..f78aa57bd --- /dev/null +++ b/blake3.cpp @@ -0,0 +1,501 @@ +// blake3.cpp - written and placed in the public domain by Colin Brown +// Based on the BLAKE3 team's reference implementation +// at http://github.com/BLAKE3-team/BLAKE3. + +#include "pch.h" +#include "blake3.h" +#include "misc.h" +#include "cpu.h" + +NAMESPACE_BEGIN(CryptoPP) + +////////////////////////////// Constants and Tables ////////////////////////////// + +// BLAKE3 initialization vector - same as SHA-256 +static const word32 BLAKE3_IV[8] = { + 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, + 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL +}; + +// BLAKE3 compression flags indicating block position and mode +enum { + CHUNK_START = 1 << 0, // First block of a chunk + CHUNK_END = 1 << 1, // Last block of a chunk + PARENT = 1 << 2, // Parent node in tree + ROOT = 1 << 3, // Root node (final output) + KEYED_HASH = 1 << 4, // Keyed hash (MAC) mode + DERIVE_KEY_CONTEXT = 1 << 5, // KDF context string + DERIVE_KEY_MATERIAL = 1 << 6 // KDF derived key output +}; + +// Message schedule permutations for the 7 rounds +// Each round uses a different permutation of the 16 message words +static const byte MSG_SCHEDULE[7][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8}, + {3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1}, + {10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6}, + {12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4}, + {9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7}, + {11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13} +}; + +////////////////////////////// Helper Functions ////////////////////////////// + +// 32-bit rotate right +static inline word32 rotr32(word32 w, unsigned int c) +{ + return (w >> c) | (w << (32 - c)); +} + +// Load 16 little-endian words from a 64-byte block +static inline void load_block_words(const byte block[64], word32 out[16]) +{ + for (size_t i = 0; i < 16; i++) { + out[i] = GetWord(false, LITTLE_ENDIAN_ORDER, block + i * 4); + } +} + +// Store 8 chaining value words as 32 little-endian bytes +static inline void store_cv_words(byte out[32], const word32 cv[8]) +{ + for (size_t i = 0; i < 8; i++) { + PutWord(false, LITTLE_ENDIAN_ORDER, out + i * 4, cv[i]); + } +} + +////////////////////////////// Core Algorithm ////////////////////////////// + +// The mixing function g() - heart of BLAKE3 compression +// Performs quarter-round mixing with two message words +static inline void g(word32 state[16], size_t a, size_t b, size_t c, size_t d, word32 x, word32 y) +{ + state[a] = state[a] + state[b] + x; + state[d] = rotr32(state[d] ^ state[a], 16); + state[c] = state[c] + state[d]; + state[b] = rotr32(state[b] ^ state[c], 12); + state[a] = state[a] + state[b] + y; + state[d] = rotr32(state[d] ^ state[a], 8); + state[c] = state[c] + state[d]; + state[b] = rotr32(state[b] ^ state[c], 7); +} + +// One round of BLAKE3 compression +// Applies the g() function in column-major then diagonal order +static inline void round_fn(word32 state[16], const word32 m[16], size_t round) +{ + // Column mixing + g(state, 0, 4, 8, 12, m[MSG_SCHEDULE[round][0]], m[MSG_SCHEDULE[round][1]]); + g(state, 1, 5, 9, 13, m[MSG_SCHEDULE[round][2]], m[MSG_SCHEDULE[round][3]]); + g(state, 2, 6, 10, 14, m[MSG_SCHEDULE[round][4]], m[MSG_SCHEDULE[round][5]]); + g(state, 3, 7, 11, 15, m[MSG_SCHEDULE[round][6]], m[MSG_SCHEDULE[round][7]]); + + // Diagonal mixing + g(state, 0, 5, 10, 15, m[MSG_SCHEDULE[round][8]], m[MSG_SCHEDULE[round][9]]); + g(state, 1, 6, 11, 12, m[MSG_SCHEDULE[round][10]], m[MSG_SCHEDULE[round][11]]); + g(state, 2, 7, 8, 13, m[MSG_SCHEDULE[round][12]], m[MSG_SCHEDULE[round][13]]); + g(state, 3, 4, 9, 14, m[MSG_SCHEDULE[round][14]], m[MSG_SCHEDULE[round][15]]); +} + +// Core compression function - processes one 64-byte block +// Performs 7 rounds of mixing to produce a 64-byte output +static void compress_internal(const word32 cv[8], const byte block[64], byte block_len, + word64 counter, byte flags, word32 out[16]) +{ + word32 block_words[16]; + load_block_words(block, block_words); + + word32 state[16] = { + cv[0], cv[1], cv[2], cv[3], + cv[4], cv[5], cv[6], cv[7], + BLAKE3_IV[0], BLAKE3_IV[1], BLAKE3_IV[2], BLAKE3_IV[3], + (word32)counter, (word32)(counter >> 32), (word32)block_len, (word32)flags + }; + + // 7 rounds of mixing + for (size_t round = 0; round < 7; round++) { + round_fn(state, block_words, round); + } + + // Output: XOR state columns + for (size_t i = 0; i < 8; i++) { + out[i] = state[i] ^ state[i + 8]; + out[i + 8] = state[i + 8] ^ cv[i]; + } +} + +////////////////////////////// State Management ////////////////////////////// + +void BLAKE3_ChunkState::Reset() +{ + std::memset(m_cv.data(), 0, m_cv.size() * sizeof(word32)); + std::memset(m_buf.data(), 0, m_buf.size()); + m_chunkCounter = 0; + m_buf_len = 0; + m_blocks_compressed = 0; + m_flags = 0; +} + +void BLAKE3_State::Reset() +{ + std::memcpy(m_key.data(), BLAKE3_IV, sizeof(BLAKE3_IV)); + m_chunk.Reset(); + std::memset(m_cv_stack.data(), 0, m_cv_stack.size() * sizeof(word32)); + m_cv_stack_len = 0; + m_flags = 0; +} + +////////////////////////////// BLAKE3 Class Implementation ////////////////////////////// + +// Wrapper for compression - updates chaining value in-place +void BLAKE3::Compress(word32 cv[8], const byte block[64], byte block_len, + word64 counter, byte flags) +{ + word32 output[16]; + compress_internal(cv, block, block_len, counter, flags, output); + std::memcpy(cv, output, 8 * sizeof(word32)); +} + +void BLAKE3::ChunkStateOutput(const BLAKE3_ChunkState& chunk, word32 out[8]) +{ + word32 output[16]; + byte flags = chunk.m_flags | CHUNK_END; + if (chunk.m_blocks_compressed == 0) { + flags |= CHUNK_START; + } + compress_internal(chunk.m_cv.data(), chunk.m_buf.data(), chunk.m_buf_len, + chunk.m_chunkCounter, flags, output); + std::memcpy(out, output, 8 * sizeof(word32)); +} + +void BLAKE3::ChunkStateUpdate(BLAKE3_ChunkState& chunk, const byte *input, size_t len) +{ + while (len > 0) { + // If buffer is full, compress it + if (chunk.m_buf_len == BLOCKSIZE) { + word32 cv[8]; + std::memcpy(cv, chunk.m_cv.data(), 8 * sizeof(word32)); + + byte block_flags = chunk.m_flags; + if (chunk.m_blocks_compressed == 0) { + block_flags |= CHUNK_START; + } + + Compress(cv, chunk.m_buf.data(), BLOCKSIZE, chunk.m_chunkCounter, block_flags); + std::memcpy(chunk.m_cv.data(), cv, 8 * sizeof(word32)); + + chunk.m_blocks_compressed++; + chunk.m_buf_len = 0; + std::memset(chunk.m_buf.data(), 0, BLOCKSIZE); + } + + // Fill buffer with input + size_t want = BLOCKSIZE - chunk.m_buf_len; + size_t take = (len < want) ? len : want; + std::memcpy(chunk.m_buf.data() + chunk.m_buf_len, input, take); + chunk.m_buf_len += (byte)take; + input += take; + len -= take; + } +} + +void BLAKE3::ParentCV(const word32 left_cv[8], const word32 right_cv[8], + const word32 key[8], byte flags, word32 out[8]) +{ + byte block[BLOCKSIZE]; + store_cv_words(block, left_cv); + store_cv_words(block + 32, right_cv); + + word32 output[16]; + compress_internal(key, block, BLOCKSIZE, 0, flags | PARENT, output); + std::memcpy(out, output, 8 * sizeof(word32)); +} + +// Add a chunk chaining value to the tree +// Uses the lazy merkle tree algorithm: merge nodes when chunk count is even +void BLAKE3::AddChunkCV(const word32 cv[8], word64 total_chunks) +{ + // Determine how many stack items to merge + // Merge whenever the total chunk count is even (LSB = 0) + size_t merge_count = 0; + while ((total_chunks & 1) == 0) { + total_chunks >>= 1; + merge_count++; + } + + // Merge with existing stack items + word32 parent_cv[8]; + std::memcpy(parent_cv, cv, 8 * sizeof(word32)); + + for (size_t i = 0; i < merge_count; i++) { + word32 left_cv[8]; + std::memcpy(left_cv, m_state.m_cv_stack.data() + (m_state.m_cv_stack_len - 1) * 8, + 8 * sizeof(word32)); + m_state.m_cv_stack_len--; + + ParentCV(left_cv, parent_cv, m_state.m_key.data(), m_state.m_flags, parent_cv); + } + + // Push result onto stack + std::memcpy(m_state.m_cv_stack.data() + m_state.m_cv_stack_len * 8, parent_cv, + 8 * sizeof(word32)); + m_state.m_cv_stack_len++; +} + +void BLAKE3::Output(const word32 cv[8], byte block[64], byte block_len, + word64 counter, byte flags, byte* out, size_t out_len) +{ + word32 output_block[16]; + size_t offset = 0; + + while (out_len > 0) { + compress_internal(cv, block, block_len, counter, flags | ROOT, output_block); + + size_t take = (out_len < 64) ? out_len : 64; + for (size_t i = 0; i < take / 4; i++) { + PutWord(false, LITTLE_ENDIAN_ORDER, out + offset + i * 4, output_block[i]); + } + + // Handle remaining bytes + if (take % 4 != 0) { + byte temp[4]; + PutWord(false, LITTLE_ENDIAN_ORDER, temp, output_block[take / 4]); + std::memcpy(out + offset + (take / 4) * 4, temp, take % 4); + } + + offset += take; + out_len -= take; + counter++; + } +} + +unsigned int BLAKE3::OptimalDataAlignment() const +{ + return GetAlignmentOf(); +} + +std::string BLAKE3::AlgorithmProvider() const +{ + return "C++"; +} + +// Constructors + +BLAKE3::BLAKE3(unsigned int digestSize) + : m_digestSize(digestSize), m_treeMode(false) +{ + CRYPTOPP_ASSERT(digestSize >= 1 && digestSize <= 1024); + m_state.Reset(); + m_state.m_flags = 0; + // Initialize key and chunk CV with IV for unkeyed mode + std::memcpy(m_state.m_key.data(), BLAKE3_IV, sizeof(BLAKE3_IV)); + std::memcpy(m_state.m_chunk.m_cv.data(), BLAKE3_IV, sizeof(BLAKE3_IV)); + m_state.m_chunk.m_flags = 0; +} + +BLAKE3::BLAKE3(const byte *key, size_t keyLength, unsigned int digestSize) + : m_digestSize(digestSize), m_treeMode(false) +{ + CRYPTOPP_ASSERT(keyLength == 32); + CRYPTOPP_ASSERT(digestSize >= 1 && digestSize <= 1024); + + m_keyBytes.resize(keyLength); + std::memcpy(m_keyBytes.data(), key, keyLength); + + m_state.Reset(); + + // Load key as words + for (size_t i = 0; i < 8; i++) { + m_state.m_key[i] = GetWord(false, LITTLE_ENDIAN_ORDER, key + i * 4); + } + + m_state.m_flags = KEYED_HASH; + std::memcpy(m_state.m_chunk.m_cv.data(), m_state.m_key.data(), 8 * sizeof(word32)); + m_state.m_chunk.m_flags = KEYED_HASH; +} + +BLAKE3::BLAKE3(const char* context, unsigned int digestSize) + : m_digestSize(digestSize), m_treeMode(false) +{ + CRYPTOPP_ASSERT(digestSize >= 1 && digestSize <= 1024); + + m_state.Reset(); + + // Derive context key + BLAKE3 hasher(32); + hasher.m_state.m_flags = DERIVE_KEY_CONTEXT; + hasher.m_state.m_chunk.m_flags = DERIVE_KEY_CONTEXT; + std::memcpy(hasher.m_state.m_chunk.m_cv.data(), BLAKE3_IV, sizeof(BLAKE3_IV)); + + hasher.Update((const byte*)context, std::strlen(context)); + + byte context_key[32]; + hasher.TruncatedFinal(context_key, 32); + + // Use derived key + for (size_t i = 0; i < 8; i++) { + m_state.m_key[i] = GetWord(false, LITTLE_ENDIAN_ORDER, context_key + i * 4); + } + + m_state.m_flags = DERIVE_KEY_MATERIAL; + std::memcpy(m_state.m_chunk.m_cv.data(), m_state.m_key.data(), 8 * sizeof(word32)); + m_state.m_chunk.m_flags = DERIVE_KEY_MATERIAL; +} + +void BLAKE3::Update(const byte *input, size_t length) +{ + while (length > 0) { + // If current chunk is full, finalize it and start new chunk + if (m_state.m_chunk.m_buf_len == BLOCKSIZE && + m_state.m_chunk.m_blocks_compressed * BLOCKSIZE >= CHUNKSIZE - BLOCKSIZE) { + + word32 chunk_cv[8]; + ChunkStateOutput(m_state.m_chunk, chunk_cv); + + word64 total_chunks = m_state.m_chunk.m_chunkCounter + 1; + AddChunkCV(chunk_cv, total_chunks); + + // Start new chunk + m_state.m_chunk.Reset(); + std::memcpy(m_state.m_chunk.m_cv.data(), m_state.m_key.data(), 8 * sizeof(word32)); + m_state.m_chunk.m_chunkCounter = total_chunks; + m_state.m_chunk.m_flags = m_state.m_flags; + } + + // Feed data to current chunk + size_t want = CHUNKSIZE - (m_state.m_chunk.m_blocks_compressed * BLOCKSIZE + + m_state.m_chunk.m_buf_len); + size_t take = (length < want) ? length : want; + + ChunkStateUpdate(m_state.m_chunk, input, take); + input += take; + length -= take; + } +} + +void BLAKE3::TruncatedFinal(byte *hash, size_t size) +{ + CRYPTOPP_ASSERT(size <= m_digestSize); + + // For single chunk (no tree), output from current chunk + if (m_state.m_cv_stack_len == 0) { + byte flags = m_state.m_flags | ROOT; + if (m_state.m_chunk.m_blocks_compressed == 0) { + flags |= CHUNK_START; + } + flags |= CHUNK_END; + + // Use Output() for proper XOF support + Output(m_state.m_chunk.m_cv.data(), m_state.m_chunk.m_buf.data(), + m_state.m_chunk.m_buf_len, 0, flags, hash, size); + } else { + // Multi-chunk tree hashing - match reference implementation's roll-up merge + // The output structure stores INPUTS for compression (input_cv + block), + // not the output CV + size_t chunk_len = m_state.m_chunk.m_blocks_compressed * BLOCKSIZE + m_state.m_chunk.m_buf_len; + + word32 output_input_cv[8]; // Input CV for compression + byte output_block[BLOCKSIZE]; + byte output_block_len; + word64 output_counter; + byte output_flags; + size_t num_cvs; + + // Determine starting output structure + if (chunk_len > 0) { + // Current chunk has data - output structure represents this chunk + num_cvs = m_state.m_cv_stack_len; + std::memcpy(output_input_cv, m_state.m_chunk.m_cv.data(), 8 * sizeof(word32)); + std::memcpy(output_block, m_state.m_chunk.m_buf.data(), m_state.m_chunk.m_buf_len); + output_block_len = m_state.m_chunk.m_buf_len; + output_counter = m_state.m_chunk.m_chunkCounter; + output_flags = m_state.m_chunk.m_flags | CHUNK_END; + if (m_state.m_chunk.m_blocks_compressed == 0) { + output_flags |= CHUNK_START; + } + } else { + // No data in current chunk - start with top two stack entries as parent + num_cvs = m_state.m_cv_stack_len - 2; + word32 left_cv[8], right_cv[8]; + std::memcpy(left_cv, m_state.m_cv_stack.data() + num_cvs * 8, 8 * sizeof(word32)); + std::memcpy(right_cv, m_state.m_cv_stack.data() + (num_cvs + 1) * 8, 8 * sizeof(word32)); + + store_cv_words(output_block, left_cv); + store_cv_words(output_block + 32, right_cv); + output_block_len = BLOCKSIZE; + output_counter = 0; + output_flags = m_state.m_flags | PARENT; + std::memcpy(output_input_cv, m_state.m_key.data(), 8 * sizeof(word32)); + } + + // Roll-up merge: combine output CV with stack entries + while (num_cvs > 0) { + num_cvs--; + + // Get the chaining value from current output structure by compressing + word32 output_cv[8]; + std::memcpy(output_cv, output_input_cv, 8 * sizeof(word32)); + + // Compress to get output CV + word32 compress_output[16]; + compress_internal(output_cv, output_block, output_block_len, output_counter, + output_flags, compress_output); + + // Extract CV (first 8 words) + for (size_t i = 0; i < 8; i++) { + output_cv[i] = compress_output[i]; + } + + // Build parent block: [stack_cv | output_cv] + byte parent_block[BLOCKSIZE]; + std::memcpy(parent_block, m_state.m_cv_stack.data() + num_cvs * 8, 8 * sizeof(word32)); + store_cv_words(parent_block + 32, output_cv); + + // Update output structure to represent this parent + std::memcpy(output_input_cv, m_state.m_key.data(), 8 * sizeof(word32)); + std::memcpy(output_block, parent_block, BLOCKSIZE); + output_block_len = BLOCKSIZE; + output_counter = 0; + output_flags = m_state.m_flags | PARENT; + } + + // Now compress the final output structure with ROOT flag + Output(output_input_cv, output_block, output_block_len, output_counter, + output_flags | ROOT, hash, size); + } + + Restart(); +} + +void BLAKE3::Restart() +{ + byte flags = m_state.m_flags; + word32 key[8]; + std::memcpy(key, m_state.m_key.data(), 8 * sizeof(word32)); + + m_state.Reset(); + + std::memcpy(m_state.m_key.data(), key, 8 * sizeof(word32)); + std::memcpy(m_state.m_chunk.m_cv.data(), key, 8 * sizeof(word32)); + m_state.m_flags = flags; + m_state.m_chunk.m_flags = flags; +} + +void BLAKE3::UncheckedSetKey(const byte* key, unsigned int length, const CryptoPP::NameValuePairs& params) +{ + CRYPTOPP_UNUSED(params); + CRYPTOPP_ASSERT(length == 32); + + m_keyBytes.resize(length); + std::memcpy(m_keyBytes.data(), key, length); + + for (size_t i = 0; i < 8; i++) { + m_state.m_key[i] = GetWord(false, LITTLE_ENDIAN_ORDER, key + i * 4); + } + + m_state.m_flags = KEYED_HASH; + std::memcpy(m_state.m_chunk.m_cv.data(), m_state.m_key.data(), 8 * sizeof(word32)); + m_state.m_chunk.m_flags = KEYED_HASH; +} + +NAMESPACE_END diff --git a/blake3.h b/blake3.h new file mode 100644 index 000000000..b9f603c65 --- /dev/null +++ b/blake3.h @@ -0,0 +1,207 @@ +// blake3.h - written and placed in the public domain by Colin Brown +// Based on the BLAKE3 team's reference implementation +// at http://github.com/BLAKE3-team/BLAKE3. + +/// \file blake3.h +/// \brief Classes for BLAKE3 message digests and keyed message digests +/// \details This implementation follows the BLAKE3 specification and reference +/// implementation. BLAKE3 supports standard hashing, keyed hashing (MAC), and +/// key derivation, with variable-length output. +/// \since Crypto++ 8.9 + +#ifndef CRYPTOPP_BLAKE3_H +#define CRYPTOPP_BLAKE3_H + +#include "cryptlib.h" +#include "secblock.h" +#include "seckey.h" + +NAMESPACE_BEGIN(CryptoPP) + +/// \brief BLAKE3 hash information +/// \since Crypto++ 8.9 +struct BLAKE3_Info : public VariableKeyLength<32,0,32,1,SimpleKeyingInterface::NOT_RESYNCHRONIZABLE> +{ + typedef VariableKeyLength<32,0,32,1,SimpleKeyingInterface::NOT_RESYNCHRONIZABLE> KeyBase; + CRYPTOPP_CONSTANT(MIN_KEYLENGTH = KeyBase::MIN_KEYLENGTH); + CRYPTOPP_CONSTANT(MAX_KEYLENGTH = KeyBase::MAX_KEYLENGTH); + CRYPTOPP_CONSTANT(DEFAULT_KEYLENGTH = KeyBase::DEFAULT_KEYLENGTH); + + CRYPTOPP_CONSTANT(BLOCKSIZE = 64); + CRYPTOPP_CONSTANT(DIGESTSIZE = 32); + CRYPTOPP_CONSTANT(CHUNKSIZE = 1024); + + CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "BLAKE3";} +}; + +/// \brief BLAKE3 chunk state for processing 1024-byte chunks +/// \details BLAKE3 processes input in 1024-byte chunks. Each chunk is further +/// divided into 64-byte blocks. The chunk state tracks the progress through +/// a single chunk and produces a chaining value when the chunk is complete. +/// \since Crypto++ 8.9 +struct CRYPTOPP_NO_VTABLE BLAKE3_ChunkState +{ + CRYPTOPP_CONSTANT(BLOCKSIZE = BLAKE3_Info::BLOCKSIZE); + CRYPTOPP_CONSTANT(CHUNKSIZE = BLAKE3_Info::CHUNKSIZE); + + BLAKE3_ChunkState() { + Reset(); + } + + /// \brief Reset the chunk state for a new chunk + void Reset(); + + inline word32* cv() { + return m_cv.data(); + } + + inline byte* buf() { + return m_buf.data(); + } + + FixedSizeAlignedSecBlock m_cv; + FixedSizeAlignedSecBlock m_buf; + word64 m_chunkCounter; + byte m_buf_len; + byte m_blocks_compressed; + byte m_flags; +}; + +/// \brief BLAKE3 hasher state with CV stack for tree hashing +/// \details The BLAKE3 state maintains a stack of chaining values (CVs) that form +/// a Merkle tree structure. As chunks are processed, their CVs are merged in a +/// tree pattern. The maximum depth of 54 allows for 2^54 chunks, supporting +/// exabyte-scale inputs. +/// \since Crypto++ 8.9 +struct CRYPTOPP_NO_VTABLE BLAKE3_State +{ + CRYPTOPP_CONSTANT(MAX_DEPTH = 54); + + BLAKE3_State() { + Reset(); + } + + /// \brief Reset the hasher state + void Reset(); + + inline word32* key() { + return m_key.data(); + } + + inline word32* cv_stack() { + return m_cv_stack.data(); + } + + FixedSizeAlignedSecBlock m_key; + BLAKE3_ChunkState m_chunk; + FixedSizeAlignedSecBlock m_cv_stack; + byte m_cv_stack_len; + byte m_flags; +}; + +/// \brief The BLAKE3 cryptographic hash function +/// \details BLAKE3 can function as a hash, keyed hash (MAC), or key derivation function. +/// It supports variable-length output. The mode is determined at construction and +/// cannot be changed. Use Restart() to reset the state while preserving the mode. +/// \sa BLAKE3 specification at +/// BLAKE3-specs +/// \since Crypto++ 8.9 +class BLAKE3 : public SimpleKeyingInterfaceImpl +{ +public: + CRYPTOPP_CONSTANT(DEFAULT_KEYLENGTH = BLAKE3_Info::DEFAULT_KEYLENGTH); + CRYPTOPP_CONSTANT(MIN_KEYLENGTH = BLAKE3_Info::MIN_KEYLENGTH); + CRYPTOPP_CONSTANT(MAX_KEYLENGTH = BLAKE3_Info::MAX_KEYLENGTH); + + CRYPTOPP_CONSTANT(DIGESTSIZE = BLAKE3_Info::DIGESTSIZE); + CRYPTOPP_CONSTANT(BLOCKSIZE = BLAKE3_Info::BLOCKSIZE); + CRYPTOPP_CONSTANT(CHUNKSIZE = BLAKE3_Info::CHUNKSIZE); + + typedef BLAKE3_State State; + + CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "BLAKE3";} + + virtual ~BLAKE3() {} + + /// \brief Construct a BLAKE3 hash + /// \param digestSize the digest size, in bytes (default 32) + /// \since Crypto++ 8.9 + BLAKE3(unsigned int digestSize = DIGESTSIZE); + + /// \brief Construct a BLAKE3 keyed hash (MAC) + /// \param key a byte array used to key the hash + /// \param keyLength the size of the byte array (must be 32 bytes) + /// \param digestSize the digest size, in bytes (default 32) + /// \since Crypto++ 8.9 + BLAKE3(const byte *key, size_t keyLength, unsigned int digestSize = DIGESTSIZE); + + /// \brief Construct a BLAKE3 key derivation function + /// \param context a string identifying the KDF context + /// \param digestSize the digest size, in bytes (default 32) + /// \since Crypto++ 8.9 + BLAKE3(const char* context, unsigned int digestSize = DIGESTSIZE); + + /// \brief Retrieve the object's name + /// \return the object's algorithm name with digest size + /// \details Returns "BLAKE3-256" for 32-byte output, "BLAKE3-512" for 64-byte, etc. + std::string AlgorithmName() const { + return std::string(BLAKE3_Info::StaticAlgorithmName()) + "-" + IntToString(DigestSize()*8); + } + + unsigned int BlockSize() const {return BLOCKSIZE;} + unsigned int DigestSize() const {return m_digestSize;} + unsigned int OptimalDataAlignment() const; + + /// \brief Updates the hash with additional input + /// \param input the additional input as a buffer + /// \param length the size of the buffer, in bytes + void Update(const byte *input, size_t length); + + /// \brief Restart the hash + /// \details Discards the current state and starts a new hash using the same + /// mode (standard hash, keyed hash, or KDF) as originally configured. + void Restart(); + + /// \brief Computes the hash of the current message + /// \param hash a pointer to the buffer to receive the hash + /// \param size the size of the truncated hash, in bytes + /// \details TruncatedFinal() calls Final() and then copies size bytes to hash. + /// The hash algorithm will be restarted ready for a new message. + void TruncatedFinal(byte *hash, size_t size); + + std::string AlgorithmProvider() const; + +protected: + // Compression function - processes one 64-byte block + void Compress(word32 cv[8], const byte block[BLOCKSIZE], byte block_len, + word64 counter, byte flags); + + // Output chaining value from chunk state + void ChunkStateOutput(const BLAKE3_ChunkState& chunk, word32 out[8]); + + // Update chunk state with input + void ChunkStateUpdate(BLAKE3_ChunkState& chunk, const byte *input, size_t len); + + // Add chunk CV to the tree + void AddChunkCV(const word32 cv[8], word64 total_chunks); + + // Merge parent nodes + void ParentCV(const word32 left_cv[8], const word32 right_cv[8], + const word32 key[8], byte flags, word32 out[8]); + + // Extract output (supports XOF) + void Output(const word32 cv[8], byte block[BLOCKSIZE], byte block_len, + word64 counter, byte flags, byte* out, size_t out_len); + + void UncheckedSetKey(const byte* key, unsigned int length, const CryptoPP::NameValuePairs& params); + +private: + State m_state; + AlignedSecByteBlock m_keyBytes; + word32 m_digestSize; + bool m_treeMode; +}; + +NAMESPACE_END + +#endif // CRYPTOPP_BLAKE3_H diff --git a/test.cpp b/test.cpp index b435fdac2..49411d5ef 100644 --- a/test.cpp +++ b/test.cpp @@ -1029,11 +1029,12 @@ bool Validate(int alg, bool thorough) case 85: result = ValidateSM3(); break; case 86: result = ValidateBLAKE2s(); break; case 87: result = ValidateBLAKE2b(); break; - case 88: result = ValidatePoly1305(); break; - case 89: result = ValidateSipHash(); break; - case 90: result = ValidateHashDRBG(); break; - case 91: result = ValidateHmacDRBG(); break; - case 92: result = ValidateNaCl(); break; + case 88: result = ValidateBLAKE3(); break; + case 89: result = ValidatePoly1305(); break; + case 90: result = ValidateSipHash(); break; + case 91: result = ValidateHashDRBG(); break; + case 92: result = ValidateHmacDRBG(); break; + case 93: result = ValidateNaCl(); break; case 100: result = ValidateCHAM(); break; case 101: result = ValidateSIMECK(); break; diff --git a/validat5.cpp b/validat5.cpp index d3281a1ec..8f72a1d10 100644 --- a/validat5.cpp +++ b/validat5.cpp @@ -24,6 +24,7 @@ #include "keccak.h" #include "tiger.h" #include "blake2.h" +#include "blake3.h" #include "ripemd.h" #include "siphash.h" #include "poly1305.h" @@ -2215,6 +2216,35 @@ bool ValidateBLAKE2b() return pass; } +bool ValidateBLAKE3() +{ + std::cout << "\nBLAKE3 validation suite running...\n\n"; + bool pass = true, fail; + + // Test 1: Algorithm name + fail = std::string(BLAKE3::StaticAlgorithmName()) != "BLAKE3"; + pass = !fail && pass; + std::cout << (fail ? "FAILED " : "passed ") << "Algorithm name\n"; + + // Test 2: Empty string hash + { + const std::string expected = "\xaf\x13\x49\xb9\xf5\xf9\xa1\xa6\xa0\x40\x4d\xea\x36\xdc\xc9\x49" + "\x9b\xcb\x25\xc9\xad\xc1\x12\xb7\xcc\x9a\x93\xca\xe4\x1f\x32\x62"; + BLAKE3 hash; + std::string digest; + StringSource(digest.data(), 0, true, new HashFilter(hash, new StringSink(digest))); + + fail = (digest != expected); + pass = !fail && pass; + std::cout << (fail ? "FAILED " : "passed ") << "Empty string\n"; + } + + // Use test vectors file for comprehensive testing + pass = RunTestDataFile("TestVectors/blake3.txt", MakeParameters(Name::TruncatedSize(), 32)) && pass; + + return pass; +} + bool ValidateSM3() { return RunTestDataFile("TestVectors/sm3.txt"); diff --git a/validate.h b/validate.h index 9053393f1..9e44d1fd8 100644 --- a/validate.h +++ b/validate.h @@ -65,6 +65,7 @@ bool ValidateLSH(); bool ValidateSM3(); bool ValidateBLAKE2s(); bool ValidateBLAKE2b(); +bool ValidateBLAKE3(); bool ValidatePoly1305(); bool ValidateSipHash();